L4Re/departure

Annotated libexec/lib/src/memory_area.cc

545:1ce97d0e8823
19 months ago Paul Boddie Added initial support for detaching regions. Without this, it is likely that file access operations will eventually fail due to unanticipated side-effects resulting from the accumulation of regions and dataspaces in the region mapper.
paul@515 1
/*
paul@515 2
 * Memory area functionality.
paul@515 3
 *
paul@515 4
 * Copyright (C) 2022, 2023 Paul Boddie <paul@boddie.org.uk>
paul@515 5
 *
paul@515 6
 * This program is free software; you can redistribute it and/or
paul@515 7
 * modify it under the terms of the GNU General Public License as
paul@515 8
 * published by the Free Software Foundation; either version 2 of
paul@515 9
 * the License, or (at your option) any later version.
paul@515 10
 *
paul@515 11
 * This program is distributed in the hope that it will be useful,
paul@515 12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
paul@515 13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
paul@515 14
 * GNU General Public License for more details.
paul@515 15
 *
paul@515 16
 * You should have received a copy of the GNU General Public License
paul@515 17
 * along with this program; if not, write to the Free Software
paul@515 18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
paul@515 19
 * Boston, MA  02110-1301, USA
paul@515 20
 */
paul@515 21
paul@515 22
#include <l4/re/c/rm.h>
paul@515 23
paul@515 24
#include <mem/memory_utils.h>
paul@515 25
paul@515 26
#include "mapped_region.h"
paul@515 27
paul@515 28
paul@515 29
paul@515 30
/* Copy an area. */
paul@515 31
paul@515 32
MemoryArea *MemoryArea::copy()
paul@515 33
{
paul@515 34
  return new MemoryArea(_start, _end);
paul@515 35
}
paul@515 36
paul@515 37
/* In general, a region cannot support other regions. */
paul@515 38
paul@515 39
long MemoryArea::add(MemoryArea &area)
paul@515 40
{
paul@515 41
  (void) area;
paul@515 42
  return -L4_EPERM;
paul@515 43
}
paul@515 44
paul@515 45
long MemoryArea::remove(MemoryArea &area)
paul@515 46
{
paul@515 47
  (void) area;
paul@515 48
  return -L4_EPERM;
paul@515 49
}
paul@515 50
paul@515 51
/* In general, a region supports a memory access within its bounds. */
paul@515 52
paul@545 53
long MemoryArea::find(address_t addr, MemoryArea **area, MemoryArea **parent)
paul@515 54
{
paul@545 55
  /* The parent is merely propagated through this method, but is present for
paul@545 56
     conformance to the generic method signature. */
paul@545 57
paul@545 58
  (void) parent;
paul@545 59
paul@515 60
  if ((addr >= _start) && (addr < _end))
paul@515 61
  {
paul@515 62
    *area = this;
paul@515 63
    return L4_EOK;
paul@515 64
  }
paul@515 65
  else
paul@515 66
    return -L4_ENOMEM;
paul@515 67
}
paul@515 68
paul@515 69
/* In general, a region does not support the identification of a location for
paul@515 70
   region insertion. */
paul@515 71
paul@515 72
long MemoryArea::find(address_t *start, address_t *size, map_flags_t flags,
paul@515 73
                      unsigned char align, MemoryArea **area)
paul@515 74
{
paul@515 75
  (void) start; (void) size; (void) flags; (void) align; (void) area;
paul@515 76
  return -L4_ENOMEM;
paul@515 77
}
paul@515 78
paul@515 79
paul@515 80
paul@515 81
/* Copy a reserved area. */
paul@515 82
paul@515 83
MemoryArea *ReservedMemoryArea::copy()
paul@515 84
{
paul@515 85
  return new ReservedMemoryArea(_start, _end);
paul@515 86
}
paul@515 87
paul@515 88
/* A reserved area does not support any memory access. */
paul@515 89
paul@545 90
long ReservedMemoryArea::find(address_t addr, MemoryArea **area,
paul@545 91
                              MemoryArea **parent)
paul@515 92
{
paul@545 93
  (void) addr; (void) area; (void) parent;
paul@515 94
  return -L4_ENOMEM;
paul@515 95
}
paul@515 96
paul@515 97
paul@515 98
paul@515 99
/* Discard all allocated areas. */
paul@515 100
paul@515 101
AvailableMemoryArea::~AvailableMemoryArea()
paul@515 102
{
paul@515 103
  MemoryAreas::iterator it;
paul@515 104
paul@515 105
  for (it = _allocated.begin(); it != _allocated.end(); it++)
paul@515 106
    delete *it;
paul@515 107
paul@515 108
  _allocated.clear();
paul@515 109
}
paul@515 110
paul@515 111
/* Copy an unused available memory area. */
paul@515 112
paul@515 113
MemoryArea *AvailableMemoryArea::copy()
paul@515 114
{
paul@515 115
  return new AvailableMemoryArea(_start, _end);
paul@515 116
}
paul@515 117
paul@515 118
/* Add an area. */
paul@515 119
paul@515 120
long AvailableMemoryArea::add(MemoryArea &area)
paul@515 121
{
paul@515 122
  MemoryArea *a = area.copy();
paul@515 123
paul@515 124
  _areas[area.area_start()] = a;
paul@515 125
  _allocated.insert(a);
paul@515 126
paul@515 127
  return L4_EOK;
paul@515 128
}
paul@515 129
paul@515 130
/* Remove an area. */
paul@515 131
paul@515 132
long AvailableMemoryArea::remove(MemoryArea &area)
paul@515 133
{
paul@515 134
  MemoryAreaMap::iterator it = _areas.find(area.area_start());
paul@515 135
paul@515 136
  if (it != _areas.end())
paul@515 137
  {
paul@515 138
    _areas.erase(it);
paul@515 139
paul@515 140
    MemoryAreas::iterator ita = _allocated.find(it->second);
paul@515 141
paul@515 142
    if (ita != _allocated.end())
paul@515 143
    {
paul@515 144
      delete *ita;
paul@515 145
      _allocated.erase(ita);
paul@515 146
    }
paul@515 147
  }
paul@515 148
paul@515 149
  return L4_EOK;
paul@515 150
}
paul@515 151
paul@515 152
/* Find an region able to support a memory access. */
paul@515 153
paul@545 154
long AvailableMemoryArea::find(address_t addr, MemoryArea **area,
paul@545 155
                               MemoryArea **parent)
paul@515 156
{
paul@515 157
  MemoryAreaMap::iterator it = _areas.upper_bound(addr);
paul@515 158
paul@515 159
  /* Consider any area preceding or encompassing the desired address. */
paul@515 160
paul@515 161
  if (it != _areas.begin())
paul@515 162
  {
paul@515 163
    it--;
paul@515 164
paul@515 165
    /* Test whether the desired region start is encompassed by the preceding
paul@515 166
       area. */
paul@515 167
paul@515 168
    MemoryArea *r = it->second;
paul@515 169
paul@545 170
    *parent = this;
paul@545 171
paul@515 172
    if (r->supports(addr))
paul@545 173
      return r->find(addr, area, parent);
paul@515 174
  }
paul@515 175
paul@515 176
  /* Otherwise, no area within this area supports the address. */
paul@515 177
paul@515 178
  return -L4_ENOMEM;
paul@515 179
}
paul@515 180
paul@515 181
/* Find an area suitable for attaching a memory region. */
paul@515 182
paul@515 183
long AvailableMemoryArea::find(address_t *start, address_t *size,
paul@515 184
                               map_flags_t flags, unsigned char align,
paul@515 185
                               MemoryArea **area)
paul@515 186
{
paul@515 187
  /* Obtain the alignment increment and a properly aligned size. */
paul@515 188
paul@515 189
  address_t increment = 1UL << align;
paul@515 190
  address_t region_size = round(*size, increment);
paul@515 191
paul@515 192
  /* Align any desired location. */
paul@515 193
paul@515 194
  address_t region_start = trunc(*start, increment);
paul@515 195
paul@515 196
  /* Enforce a minimum address. */
paul@515 197
paul@515 198
  if (region_start < _start)
paul@515 199
    region_start = round(_start, increment);
paul@515 200
paul@515 201
  /* Search for existing regions after the desired, conformant start address. */
paul@515 202
paul@515 203
  MemoryAreaMap::iterator it = _areas.upper_bound(region_start);
paul@515 204
paul@515 205
  /* Consider any area preceding or encompassing the desired address. */
paul@515 206
paul@515 207
  if (it != _areas.begin())
paul@515 208
  {
paul@515 209
    MemoryAreaMap::iterator next = it;
paul@515 210
paul@515 211
    /* Step back to the preceding area to get its details. */
paul@515 212
paul@515 213
    it--;
paul@515 214
    MemoryArea *pr = it->second;
paul@515 215
    it = next;
paul@515 216
paul@515 217
    /* Test whether the desired region start is encompassed by the preceding
paul@515 218
       area. */
paul@515 219
paul@515 220
    if (region_start < pr->area_end())
paul@515 221
    {
paul@515 222
      /* Where the preceding area is mapped or a reserved area, adjust the start
paul@515 223
         of any search range. If an exact request is made for a region, deny the
paul@515 224
         request. */
paul@515 225
paul@515 226
      if (pr->is_mapped() || pr->is_reserved())
paul@515 227
      {
paul@515 228
        if (!(flags & L4RE_RM_F_SEARCH_ADDR))
paul@515 229
          return -L4_ENOMEM;
paul@515 230
paul@515 231
        region_start = round(pr->area_end(), increment);
paul@515 232
      }
paul@515 233
paul@515 234
      /* Where the preceding area is an area within which regions might be
paul@515 235
         established, search within that area. */
paul@515 236
paul@515 237
      else
paul@515 238
        return pr->find(start, size, flags, align, area);
paul@515 239
    }
paul@515 240
  }
paul@515 241
paul@515 242
  /* Consider the regions following the current region start candidate. */
paul@515 243
paul@515 244
  MemoryArea *r;
paul@515 245
paul@515 246
  while ((it != _areas.end()) && !(_end && ((region_start + region_size) > _end)))
paul@515 247
  {
paul@515 248
    r = it->second;
paul@515 249
paul@515 250
    /* Obtain the limit of available space being considered. */
paul@515 251
paul@515 252
    address_t end_limit = r->area_start();
paul@515 253
paul@515 254
    /* Test if not enough space exists between the preceding region (or start of
paul@515 255
       memory) and the current region. */
paul@515 256
paul@515 257
    if ((region_start + region_size) > end_limit)
paul@515 258
    {
paul@515 259
      /* If an exact request is made for a region, deny the request. */
paul@515 260
paul@515 261
      if (!(flags & L4RE_RM_F_SEARCH_ADDR))
paul@515 262
        return -L4_ENOMEM;
paul@515 263
paul@515 264
      /* Otherwise, investigate subsequent regions if not enough space exists
paul@515 265
         between the preceding region (or start of memory) and the current
paul@515 266
         region. */
paul@515 267
paul@515 268
      region_start = round(r->area_end(), increment);
paul@515 269
      it++;
paul@515 270
    }
paul@515 271
paul@515 272
    /* Otherwise, the region can be positioned. */
paul@515 273
paul@515 274
    else
paul@515 275
      break;
paul@515 276
  }
paul@515 277
paul@515 278
  /* Test for enough memory constrained by any predefined limit. */
paul@515 279
paul@515 280
  if (_end && ((region_start + region_size) > _end))
paul@515 281
    return -L4_ENOMEM;
paul@515 282
paul@515 283
  /* Return the configured start and size. */
paul@515 284
paul@515 285
  *start = region_start;
paul@515 286
  *size = region_size;
paul@515 287
paul@515 288
  /* Return the area within which any region will be added. */
paul@515 289
paul@515 290
  *area = this;
paul@515 291
paul@515 292
  return L4_EOK;
paul@515 293
}
paul@515 294
paul@515 295
/* Obtain a recursive iterator over a memory area. */
paul@515 296
paul@515 297
AvailableMemoryArea::iterator AvailableMemoryArea::begin()
paul@515 298
{
paul@515 299
  return AvailableMemoryArea::iterator(_areas.begin(), _areas.end());
paul@515 300
}
paul@515 301
paul@515 302
AvailableMemoryArea::iterator AvailableMemoryArea::end()
paul@515 303
{
paul@515 304
  return AvailableMemoryArea::iterator(_areas.end(), _areas.end());
paul@515 305
}
paul@515 306
paul@515 307
MemoryAreaMap::iterator AvailableMemoryArea::areas_begin()
paul@515 308
{
paul@515 309
  return _areas.begin();
paul@515 310
}
paul@515 311
paul@515 312
MemoryAreaMap::iterator AvailableMemoryArea::areas_end()
paul@515 313
{
paul@515 314
  return _areas.end();
paul@515 315
}
paul@515 316
paul@515 317
paul@515 318
paul@515 319
/* Initialise a recursive iterator over a memory area. */
paul@515 320
paul@515 321
AvailableMemoryArea::iterator::iterator(MemoryAreaMap::iterator it,
paul@515 322
                                        MemoryAreaMap::iterator end)
paul@515 323
{
paul@515 324
  _iterators.push(it);
paul@515 325
  _ends.push(end);
paul@515 326
paul@515 327
  /* Descend to the first non-container area. */
paul@515 328
paul@515 329
  descend_all();
paul@515 330
}
paul@515 331
paul@515 332
AvailableMemoryArea::iterator::iterator()
paul@515 333
{
paul@515 334
}
paul@515 335
paul@515 336
/* Return the current underlying iterator. */
paul@515 337
paul@515 338
MemoryAreaMap::iterator &AvailableMemoryArea::iterator::area_iterator()
paul@515 339
{
paul@515 340
  return _iterators.top();
paul@515 341
}
paul@515 342
paul@515 343
/* Return the end point of the current underlying iterator. */
paul@515 344
paul@515 345
MemoryAreaMap::iterator &AvailableMemoryArea::iterator::area_end()
paul@515 346
{
paul@515 347
  return _ends.top();
paul@515 348
}
paul@515 349
paul@515 350
/* Return the current area. */
paul@515 351
paul@515 352
MemoryArea *AvailableMemoryArea::iterator::operator *()
paul@515 353
{
paul@515 354
  return area_iterator()->second;
paul@515 355
}
paul@515 356
paul@515 357
/* Return whether this iterator references the same area as the other
paul@515 358
   iterator. */
paul@515 359
paul@515 360
bool AvailableMemoryArea::iterator::operator ==(AvailableMemoryArea::iterator other)
paul@515 361
{
paul@515 362
  return area_iterator() == other.area_iterator();
paul@515 363
}
paul@515 364
paul@515 365
/* Return whether this iterator references a different area to the other
paul@515 366
   iterator. */
paul@515 367
paul@515 368
bool AvailableMemoryArea::iterator::operator !=(AvailableMemoryArea::iterator other)
paul@515 369
{
paul@515 370
  return area_iterator() != other.area_iterator();
paul@515 371
}
paul@515 372
paul@515 373
/* Advance this iterator to the next area. */
paul@515 374
paul@515 375
AvailableMemoryArea::iterator &AvailableMemoryArea::iterator::operator ++()
paul@515 376
{
paul@515 377
  /* Advance the area iterator. */
paul@515 378
paul@515 379
  area_iterator()++;
paul@515 380
paul@515 381
  /* Descend to the next non-container area. */
paul@515 382
paul@515 383
  descend_all();
paul@515 384
paul@515 385
  return *this;
paul@515 386
}
paul@515 387
paul@515 388
AvailableMemoryArea::iterator &AvailableMemoryArea::iterator::operator ++(int)
paul@515 389
{
paul@515 390
  return ++(*this);
paul@515 391
}
paul@515 392
paul@515 393
/* Advance this iterator to the next area not containing other areas. */
paul@515 394
paul@515 395
void AvailableMemoryArea::iterator::descend_all()
paul@515 396
{
paul@515 397
  while ((area_iterator() != area_end()) || (_iterators.size() > 1))
paul@515 398
  {
paul@515 399
    /* Handle empty areas by ascending to refer to the area. */
paul@515 400
paul@515 401
    if (area_iterator() == area_end())
paul@515 402
    {
paul@515 403
      ascend();
paul@515 404
      break;
paul@515 405
    }
paul@515 406
paul@515 407
    MemoryArea *r = area_iterator()->second;
paul@515 408
paul@515 409
    /* Handle areas by descending into them. */
paul@515 410
paul@515 411
    if (!r->is_reserved() && !r->is_mapped())
paul@515 412
      descend();
paul@515 413
paul@515 414
    /* Yield any non-container areas. */
paul@515 415
paul@515 416
    else
paul@515 417
      break;
paul@515 418
  }
paul@515 419
}
paul@515 420
paul@515 421
/* Ascend from a memory area. */
paul@515 422
paul@515 423
void AvailableMemoryArea::iterator::ascend()
paul@515 424
{
paul@515 425
  _iterators.pop();
paul@515 426
  _ends.pop();
paul@515 427
}
paul@515 428
paul@515 429
/* Descend into a memory area. */
paul@515 430
paul@515 431
void AvailableMemoryArea::iterator::descend()
paul@515 432
{
paul@515 433
  AvailableMemoryArea *a = dynamic_cast<AvailableMemoryArea *>(area_iterator()->second);
paul@515 434
paul@515 435
  if (a != NULL)
paul@515 436
  {
paul@515 437
    _iterators.push(a->areas_begin());
paul@515 438
    _ends.push(a->areas_end());
paul@515 439
  }
paul@515 440
}
paul@515 441
paul@515 442
/* vim: tabstop=2 expandtab shiftwidth=2
paul@515 443
*/