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 | */ |