1 /*
2 * Copyright 2009-2010, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2002-2010, Axel Dörfler, axeld@pinc-software.de.
4 * Distributed under the terms of the MIT License.
5 *
6 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
7 * Distributed under the terms of the NewOS License.
8 */
9
10
11 #include "VMUserAddressSpace.h"
12
13 #include <stdlib.h>
14
15 #include <algorithm>
16
17 #include <KernelExport.h>
18
19 #include <heap.h>
20 #include <thread.h>
21 #include <util/atomic.h>
22 #include <util/Random.h>
23 #include <vm/vm.h>
24 #include <vm/VMArea.h>
25
26
27 //#define TRACE_VM
28 #ifdef TRACE_VM
29 # define TRACE(x) dprintf x
30 #else
31 # define TRACE(x) ;
32 #endif
33
34
35 #ifdef B_HAIKU_64_BIT
36 const addr_t VMUserAddressSpace::kMaxRandomize = 0x8000000000ul;
37 const addr_t VMUserAddressSpace::kMaxInitialRandomize = 0x20000000000ul;
38 #else
39 const addr_t VMUserAddressSpace::kMaxRandomize = 0x800000ul;
40 const addr_t VMUserAddressSpace::kMaxInitialRandomize = 0x2000000ul;
41 #endif
42
43
44 /*! Verifies that an area with the given aligned base and size fits into
45 the spot defined by base and limit and checks for overflows.
46 */
47 static inline bool
is_valid_spot(addr_t base,addr_t alignedBase,addr_t size,addr_t limit)48 is_valid_spot(addr_t base, addr_t alignedBase, addr_t size, addr_t limit)
49 {
50 return (alignedBase >= base && alignedBase + (size - 1) > alignedBase
51 && alignedBase + (size - 1) <= limit);
52 }
53
54
55 static inline bool
is_base_address_spec(uint32 addressSpec)56 is_base_address_spec(uint32 addressSpec)
57 {
58 return addressSpec == B_BASE_ADDRESS
59 || addressSpec == B_RANDOMIZED_BASE_ADDRESS;
60 }
61
62
63 static inline addr_t
align_address(addr_t address,size_t alignment)64 align_address(addr_t address, size_t alignment)
65 {
66 return ROUNDUP(address, alignment);
67 }
68
69
70 static inline addr_t
align_address(addr_t address,size_t alignment,uint32 addressSpec,addr_t baseAddress)71 align_address(addr_t address, size_t alignment, uint32 addressSpec,
72 addr_t baseAddress)
73 {
74 if (is_base_address_spec(addressSpec))
75 address = std::max(address, baseAddress);
76 return align_address(address, alignment);
77 }
78
79
80 // #pragma mark - VMUserAddressSpace
81
82
VMUserAddressSpace(team_id id,addr_t base,size_t size)83 VMUserAddressSpace::VMUserAddressSpace(team_id id, addr_t base, size_t size)
84 :
85 VMAddressSpace(id, base, size, "address space"),
86 fNextInsertHint(0)
87 {
88 }
89
90
~VMUserAddressSpace()91 VMUserAddressSpace::~VMUserAddressSpace()
92 {
93 }
94
95
96 inline VMArea*
FirstArea() const97 VMUserAddressSpace::FirstArea() const
98 {
99 VMUserArea* area = fAreas.LeftMost();
100 while (area != NULL && area->id == RESERVED_AREA_ID)
101 area = fAreas.Next(area);
102 return area;
103 }
104
105
106 inline VMArea*
NextArea(VMArea * _area) const107 VMUserAddressSpace::NextArea(VMArea* _area) const
108 {
109 VMUserArea* area = static_cast<VMUserArea*>(_area);
110 area = fAreas.Next(area);
111 while (area != NULL && area->id == RESERVED_AREA_ID)
112 area = fAreas.Next(area);
113 return area;
114 }
115
116
117 VMArea*
CreateArea(const char * name,uint32 wiring,uint32 protection,uint32 allocationFlags)118 VMUserAddressSpace::CreateArea(const char* name, uint32 wiring,
119 uint32 protection, uint32 allocationFlags)
120 {
121 return VMUserArea::Create(this, name, wiring, protection, allocationFlags);
122 }
123
124
125 void
DeleteArea(VMArea * _area,uint32 allocationFlags)126 VMUserAddressSpace::DeleteArea(VMArea* _area, uint32 allocationFlags)
127 {
128 VMUserArea* area = static_cast<VMUserArea*>(_area);
129 area->~VMUserArea();
130 free_etc(area, allocationFlags);
131 }
132
133
134 //! You must hold the address space's read lock.
135 VMArea*
LookupArea(addr_t address) const136 VMUserAddressSpace::LookupArea(addr_t address) const
137 {
138 VMUserArea* area = fAreas.FindClosest(address, true);
139 if (area == NULL || area->id == RESERVED_AREA_ID)
140 return NULL;
141
142 return area->ContainsAddress(address) ? area : NULL;
143 }
144
145
146 //! You must hold the address space's read lock.
147 VMArea*
FindClosestArea(addr_t address,bool less) const148 VMUserAddressSpace::FindClosestArea(addr_t address, bool less) const
149 {
150 VMUserArea* area = fAreas.FindClosest(address, less);
151 while (area != NULL && area->id == RESERVED_AREA_ID)
152 area = fAreas.Next(area);
153 return area;
154 }
155
156
157 /*! This inserts the area you pass into the address space.
158 It will also set the "_address" argument to its base address when
159 the call succeeds.
160 You need to hold the VMAddressSpace write lock.
161 */
162 status_t
InsertArea(VMArea * _area,size_t size,const virtual_address_restrictions * addressRestrictions,uint32 allocationFlags,void ** _address)163 VMUserAddressSpace::InsertArea(VMArea* _area, size_t size,
164 const virtual_address_restrictions* addressRestrictions,
165 uint32 allocationFlags, void** _address)
166 {
167 VMUserArea* area = static_cast<VMUserArea*>(_area);
168
169 addr_t searchBase, searchEnd;
170 status_t status;
171
172 switch (addressRestrictions->address_specification) {
173 case B_EXACT_ADDRESS:
174 searchBase = (addr_t)addressRestrictions->address;
175 searchEnd = (addr_t)addressRestrictions->address + (size - 1);
176 break;
177
178 case B_BASE_ADDRESS:
179 case B_RANDOMIZED_BASE_ADDRESS:
180 searchBase = std::max(fBase, (addr_t)addressRestrictions->address);
181 searchEnd = fEndAddress;
182 break;
183
184 case B_ANY_ADDRESS:
185 case B_ANY_KERNEL_ADDRESS:
186 case B_ANY_KERNEL_BLOCK_ADDRESS:
187 case B_RANDOMIZED_ANY_ADDRESS:
188 searchBase = std::max(fBase, (addr_t)USER_BASE_ANY);
189 searchEnd = fEndAddress;
190 break;
191
192 default:
193 return B_BAD_VALUE;
194 }
195
196 status = _InsertAreaSlot(searchBase, size, searchEnd,
197 addressRestrictions->address_specification,
198 addressRestrictions->alignment, area, allocationFlags);
199 if (status == B_OK) {
200 if (_address != NULL)
201 *_address = (void*)area->Base();
202 fFreeSpace -= area->Size();
203 }
204
205 return status;
206 }
207
208
209 //! You must hold the address space's write lock.
210 void
RemoveArea(VMArea * _area,uint32 allocationFlags)211 VMUserAddressSpace::RemoveArea(VMArea* _area, uint32 allocationFlags)
212 {
213 VMUserArea* area = static_cast<VMUserArea*>(_area);
214
215 fAreas.Remove(area);
216
217 if (area->id != RESERVED_AREA_ID) {
218 IncrementChangeCount();
219 fFreeSpace += area->Size();
220 }
221 }
222
223
224 bool
CanResizeArea(VMArea * area,size_t newSize)225 VMUserAddressSpace::CanResizeArea(VMArea* area, size_t newSize)
226 {
227 VMUserArea* next = fAreas.Next(static_cast<VMUserArea*>(area));
228 addr_t newEnd = area->Base() + (newSize - 1);
229
230 if (next == NULL)
231 return fEndAddress >= newEnd;
232
233 if (next->Base() > newEnd)
234 return true;
235
236 // If the area was created inside a reserved area, it can
237 // also be resized in that area
238 // TODO: if there is free space after the reserved area, it could
239 // be used as well...
240 return next->id == RESERVED_AREA_ID
241 && (uint64)next->cache_offset <= (uint64)area->Base()
242 && next->Base() + (next->Size() - 1) >= newEnd;
243 }
244
245
246 status_t
ResizeArea(VMArea * _area,size_t newSize,uint32 allocationFlags)247 VMUserAddressSpace::ResizeArea(VMArea* _area, size_t newSize,
248 uint32 allocationFlags)
249 {
250 VMUserArea* area = static_cast<VMUserArea*>(_area);
251
252 addr_t newEnd = area->Base() + (newSize - 1);
253 VMUserArea* next = fAreas.Next(area);
254 if (next != NULL && next->Base() <= newEnd) {
255 if (next->id != RESERVED_AREA_ID
256 || (uint64)next->cache_offset > (uint64)area->Base()
257 || next->Base() + (next->Size() - 1) < newEnd) {
258 panic("resize situation for area %p has changed although we "
259 "should have the address space lock", area);
260 return B_ERROR;
261 }
262
263 // resize reserved area
264 addr_t offset = area->Base() + newSize - next->Base();
265 if (next->Size() <= offset) {
266 RemoveArea(next, allocationFlags);
267 next->~VMUserArea();
268 free_etc(next, allocationFlags);
269 } else {
270 status_t error = ShrinkAreaHead(next, next->Size() - offset,
271 allocationFlags);
272 if (error != B_OK)
273 return error;
274 }
275 }
276
277 area->SetSize(newSize);
278 return B_OK;
279 }
280
281
282 status_t
ShrinkAreaHead(VMArea * area,size_t size,uint32 allocationFlags)283 VMUserAddressSpace::ShrinkAreaHead(VMArea* area, size_t size,
284 uint32 allocationFlags)
285 {
286 size_t oldSize = area->Size();
287 if (size == oldSize)
288 return B_OK;
289
290 area->SetBase(area->Base() + oldSize - size);
291 area->SetSize(size);
292
293 return B_OK;
294 }
295
296
297 status_t
ShrinkAreaTail(VMArea * area,size_t size,uint32 allocationFlags)298 VMUserAddressSpace::ShrinkAreaTail(VMArea* area, size_t size,
299 uint32 allocationFlags)
300 {
301 size_t oldSize = area->Size();
302 if (size == oldSize)
303 return B_OK;
304
305 area->SetSize(size);
306
307 return B_OK;
308 }
309
310
311 status_t
ReserveAddressRange(size_t size,const virtual_address_restrictions * addressRestrictions,uint32 flags,uint32 allocationFlags,void ** _address)312 VMUserAddressSpace::ReserveAddressRange(size_t size,
313 const virtual_address_restrictions* addressRestrictions,
314 uint32 flags, uint32 allocationFlags, void** _address)
315 {
316 // check to see if this address space has entered DELETE state
317 if (fDeleting) {
318 // okay, someone is trying to delete this address space now, so we
319 // can't insert the area, let's back out
320 return B_BAD_TEAM_ID;
321 }
322
323 VMUserArea* area = VMUserArea::CreateReserved(this, flags, allocationFlags);
324 if (area == NULL)
325 return B_NO_MEMORY;
326
327 status_t status = InsertArea(area, size, addressRestrictions,
328 allocationFlags, _address);
329 if (status != B_OK) {
330 area->~VMUserArea();
331 free_etc(area, allocationFlags);
332 return status;
333 }
334
335 area->cache_offset = area->Base();
336 // we cache the original base address here
337
338 Get();
339 return B_OK;
340 }
341
342
343 status_t
UnreserveAddressRange(addr_t address,size_t size,uint32 allocationFlags)344 VMUserAddressSpace::UnreserveAddressRange(addr_t address, size_t size,
345 uint32 allocationFlags)
346 {
347 // check to see if this address space has entered DELETE state
348 if (fDeleting) {
349 // okay, someone is trying to delete this address space now, so we can't
350 // remove the area, so back out
351 return B_BAD_TEAM_ID;
352 }
353
354 // the area must be completely part of the reserved range
355 VMUserArea* area = fAreas.FindClosest(address, false);
356 if (area == NULL)
357 return B_OK;
358
359 addr_t endAddress = address + size - 1;
360 for (VMUserAreaTree::Iterator it = fAreas.GetIterator(area);
361 (area = it.Next()) != NULL
362 && area->Base() + area->Size() - 1 <= endAddress;) {
363
364 if (area->id == RESERVED_AREA_ID) {
365 // remove reserved range
366 RemoveArea(area, allocationFlags);
367 Put();
368 area->~VMUserArea();
369 free_etc(area, allocationFlags);
370 }
371 }
372
373 return B_OK;
374 }
375
376
377 void
UnreserveAllAddressRanges(uint32 allocationFlags)378 VMUserAddressSpace::UnreserveAllAddressRanges(uint32 allocationFlags)
379 {
380 for (VMUserAreaTree::Iterator it = fAreas.GetIterator();
381 VMUserArea* area = it.Next();) {
382 if (area->id == RESERVED_AREA_ID) {
383 RemoveArea(area, allocationFlags);
384 Put();
385 area->~VMUserArea();
386 free_etc(area, allocationFlags);
387 }
388 }
389 }
390
391
392 void
Dump() const393 VMUserAddressSpace::Dump() const
394 {
395 VMAddressSpace::Dump();
396 kprintf("area_list:\n");
397
398 for (VMUserAreaTree::ConstIterator it = fAreas.GetIterator();
399 VMUserArea* area = it.Next();) {
400 kprintf(" area 0x%" B_PRIx32 ": ", area->id);
401 kprintf("base_addr = 0x%lx ", area->Base());
402 kprintf("size = 0x%lx ", area->Size());
403 kprintf("name = '%s' ",
404 area->id != RESERVED_AREA_ID ? area->name : "reserved");
405 kprintf("protection = 0x%" B_PRIx32 "\n", area->protection);
406 }
407 }
408
409
410 inline bool
_IsRandomized(uint32 addressSpec) const411 VMUserAddressSpace::_IsRandomized(uint32 addressSpec) const
412 {
413 return fRandomizingEnabled
414 && (addressSpec == B_RANDOMIZED_ANY_ADDRESS
415 || addressSpec == B_RANDOMIZED_BASE_ADDRESS);
416 }
417
418
419 addr_t
_RandomizeAddress(addr_t start,addr_t end,size_t alignment,bool initial)420 VMUserAddressSpace::_RandomizeAddress(addr_t start, addr_t end,
421 size_t alignment, bool initial)
422 {
423 ASSERT((start & addr_t(alignment - 1)) == 0);
424 ASSERT(start <= end);
425
426 if (start == end)
427 return start;
428
429 addr_t range = end - start + 1;
430 if (initial)
431 range = std::min(range, kMaxInitialRandomize);
432 else
433 range = std::min(range, kMaxRandomize);
434
435 addr_t random = secure_get_random<addr_t>();
436 random %= range;
437 random &= ~addr_t(alignment - 1);
438
439 return start + random;
440 }
441
442
443 /*! Finds a reserved area that covers the region spanned by \a start and
444 \a size, inserts the \a area into that region and makes sure that
445 there are reserved regions for the remaining parts.
446 */
447 status_t
_InsertAreaIntoReservedRegion(addr_t start,size_t size,VMUserArea * area,uint32 allocationFlags)448 VMUserAddressSpace::_InsertAreaIntoReservedRegion(addr_t start, size_t size,
449 VMUserArea* area, uint32 allocationFlags)
450 {
451 VMUserArea* reserved = fAreas.FindClosest(start, true);
452 if (reserved == NULL
453 || !reserved->ContainsAddress(start)
454 || !reserved->ContainsAddress(start + size - 1)) {
455 return B_ENTRY_NOT_FOUND;
456 }
457
458 // This area covers the requested range
459 if (reserved->id != RESERVED_AREA_ID) {
460 // but it's not reserved space, it's a real area
461 return B_BAD_VALUE;
462 }
463
464 // Now we have to transfer the requested part of the reserved
465 // range to the new area - and remove, resize or split the old
466 // reserved area.
467
468 if (start == reserved->Base()) {
469 // the area starts at the beginning of the reserved range
470
471 if (size == reserved->Size()) {
472 // the new area fully covers the reserved range
473 fAreas.Remove(reserved);
474 Put();
475 reserved->~VMUserArea();
476 free_etc(reserved, allocationFlags);
477 } else {
478 // resize the reserved range behind the area
479 reserved->SetBase(reserved->Base() + size);
480 reserved->SetSize(reserved->Size() - size);
481 }
482 } else if (start + size == reserved->Base() + reserved->Size()) {
483 // the area is at the end of the reserved range
484 // resize the reserved range before the area
485 reserved->SetSize(start - reserved->Base());
486 } else {
487 // the area splits the reserved range into two separate ones
488 // we need a new reserved area to cover this space
489 VMUserArea* newReserved = VMUserArea::CreateReserved(this,
490 reserved->protection, allocationFlags);
491 if (newReserved == NULL)
492 return B_NO_MEMORY;
493
494 Get();
495
496 // resize regions
497 newReserved->SetBase(start + size);
498 newReserved->SetSize(
499 reserved->Base() + reserved->Size() - start - size);
500 newReserved->cache_offset = reserved->cache_offset;
501
502 reserved->SetSize(start - reserved->Base());
503
504 fAreas.Insert(newReserved);
505 }
506
507 area->SetBase(start);
508 area->SetSize(size);
509 fAreas.Insert(area);
510 IncrementChangeCount();
511
512 return B_OK;
513 }
514
515
516 /*! Must be called with this address space's write lock held */
517 status_t
_InsertAreaSlot(addr_t start,addr_t size,addr_t end,uint32 addressSpec,size_t alignment,VMUserArea * area,uint32 allocationFlags)518 VMUserAddressSpace::_InsertAreaSlot(addr_t start, addr_t size, addr_t end,
519 uint32 addressSpec, size_t alignment, VMUserArea* area,
520 uint32 allocationFlags)
521 {
522 TRACE(("VMUserAddressSpace::_InsertAreaSlot: address space %p, start "
523 "0x%lx, size %ld, end 0x%lx, addressSpec %" B_PRIu32 ", area %p\n",
524 this, start, size, end, addressSpec, area));
525
526 // do some sanity checking
527 if (start < fBase || size == 0 || end > fEndAddress
528 || start + (size - 1) > end)
529 return B_BAD_ADDRESS;
530
531 if (addressSpec == B_EXACT_ADDRESS && area->id != RESERVED_AREA_ID) {
532 // search for a reserved area
533 status_t status = _InsertAreaIntoReservedRegion(start, size, area,
534 allocationFlags);
535 if (status == B_OK || status == B_BAD_VALUE)
536 return status;
537
538 // There was no reserved area, and the slot doesn't seem to be used
539 // already
540 // TODO: this could be further optimized.
541 }
542
543 if (alignment == 0)
544 alignment = B_PAGE_SIZE;
545 if (addressSpec == B_ANY_KERNEL_BLOCK_ADDRESS) {
546 // align the memory to the next power of two of the size
547 while (alignment < size)
548 alignment <<= 1;
549 }
550
551 start = align_address(start, alignment);
552
553 bool useHint
554 = addressSpec != B_EXACT_ADDRESS && !is_base_address_spec(addressSpec);
555
556 addr_t originalStart = 0;
557 if (fRandomizingEnabled && addressSpec == B_RANDOMIZED_BASE_ADDRESS) {
558 originalStart = start;
559 start = _RandomizeAddress(start, end - size + 1, alignment, true);
560 } else if (useHint
561 && start <= fNextInsertHint && fNextInsertHint <= end - size + 1) {
562 originalStart = start;
563 start = fNextInsertHint;
564 }
565
566 // walk up to the spot where we should start searching
567 second_chance:
568 VMUserArea* next = fAreas.FindClosest(start + size, false);
569 VMUserArea* last = next != NULL
570 ? fAreas.Previous(next) : fAreas.FindClosest(start + size, true);
571
572 // find the right spot depending on the address specification - the area
573 // will be inserted directly after "last" ("next" is not referenced anymore)
574
575 bool foundSpot = false;
576 switch (addressSpec) {
577 case B_ANY_ADDRESS:
578 case B_ANY_KERNEL_ADDRESS:
579 case B_ANY_KERNEL_BLOCK_ADDRESS:
580 case B_RANDOMIZED_ANY_ADDRESS:
581 case B_BASE_ADDRESS:
582 case B_RANDOMIZED_BASE_ADDRESS:
583 {
584 VMUserAreaTree::Iterator it = fAreas.GetIterator(
585 next != NULL ? next : fAreas.LeftMost());
586
587 // find a hole big enough for a new area
588 if (last == NULL) {
589 // see if we can build it at the beginning of the virtual map
590 addr_t alignedBase = align_address(start, alignment);
591 addr_t nextBase = next == NULL
592 ? end : std::min(next->Base() - 1, end);
593 if (is_valid_spot(start, alignedBase, size, nextBase)) {
594 addr_t rangeEnd = std::min(nextBase - size + 1, end);
595 if (_IsRandomized(addressSpec)) {
596 alignedBase = _RandomizeAddress(alignedBase, rangeEnd,
597 alignment);
598 }
599
600 foundSpot = true;
601 area->SetBase(alignedBase);
602 break;
603 }
604
605 last = next;
606 next = it.Next();
607 }
608
609 // keep walking
610 while (next != NULL && next->Base() + next->Size() - 1 <= end) {
611 addr_t alignedBase = align_address(last->Base() + last->Size(),
612 alignment, addressSpec, start);
613 addr_t nextBase = std::min(end, next->Base() - 1);
614
615 if (is_valid_spot(last->Base() + (last->Size() - 1),
616 alignedBase, size, nextBase)) {
617 addr_t rangeEnd = std::min(nextBase - size + 1, end);
618 if (_IsRandomized(addressSpec)) {
619 alignedBase = _RandomizeAddress(alignedBase,
620 rangeEnd, alignment);
621 }
622
623 foundSpot = true;
624 area->SetBase(alignedBase);
625 break;
626 }
627
628 last = next;
629 next = it.Next();
630 }
631
632 if (foundSpot)
633 break;
634
635 addr_t alignedBase = align_address(last->Base() + last->Size(),
636 alignment, addressSpec, start);
637
638 if (next == NULL && is_valid_spot(last->Base() + (last->Size() - 1),
639 alignedBase, size, end)) {
640 if (_IsRandomized(addressSpec)) {
641 alignedBase = _RandomizeAddress(alignedBase, end - size + 1,
642 alignment);
643 }
644
645 // got a spot
646 foundSpot = true;
647 area->SetBase(alignedBase);
648 break;
649 } else if (is_base_address_spec(addressSpec)) {
650 // we didn't find a free spot in the requested range, so we'll
651 // try again without any restrictions
652 if (!_IsRandomized(addressSpec)) {
653 start = USER_BASE_ANY;
654 addressSpec = B_ANY_ADDRESS;
655 } else if (start == originalStart) {
656 start = USER_BASE_ANY;
657 addressSpec = B_RANDOMIZED_ANY_ADDRESS;
658 } else {
659 start = originalStart;
660 addressSpec = B_RANDOMIZED_BASE_ADDRESS;
661 }
662
663 goto second_chance;
664 } else if (useHint
665 && originalStart != 0 && start != originalStart) {
666 start = originalStart;
667 goto second_chance;
668 } else if (area->id != RESERVED_AREA_ID) {
669 // We didn't find a free spot - if there are any reserved areas,
670 // we can now test those for free space
671 // TODO: it would make sense to start with the biggest of them
672 it = fAreas.GetIterator();
673 next = it.Next();
674 for (last = NULL; next != NULL; next = it.Next()) {
675 if (next->id != RESERVED_AREA_ID) {
676 last = next;
677 continue;
678 } else if (next->Base() + size - 1 > end)
679 break;
680
681 // TODO: take free space after the reserved area into
682 // account!
683 addr_t alignedBase = align_address(next->Base(), alignment);
684 if (next->Base() == alignedBase && next->Size() == size) {
685 // The reserved area is entirely covered, and thus,
686 // removed
687 fAreas.Remove(next);
688
689 foundSpot = true;
690 area->SetBase(alignedBase);
691 next->~VMUserArea();
692 free_etc(next, allocationFlags);
693 break;
694 }
695
696 if ((next->protection & RESERVED_AVOID_BASE) == 0
697 && alignedBase == next->Base()
698 && next->Size() >= size) {
699 addr_t rangeEnd = std::min(
700 next->Base() + next->Size() - size, end);
701 if (_IsRandomized(addressSpec)) {
702 alignedBase = _RandomizeAddress(next->Base(),
703 rangeEnd, alignment);
704 }
705 addr_t offset = alignedBase - next->Base();
706
707 // The new area will be placed at the beginning of the
708 // reserved area and the reserved area will be offset
709 // and resized
710 foundSpot = true;
711 next->SetBase(next->Base() + offset + size);
712 next->SetSize(next->Size() - offset - size);
713 area->SetBase(alignedBase);
714 break;
715 }
716
717 if (is_valid_spot(next->Base(), alignedBase, size,
718 std::min(next->Base() + next->Size() - 1, end))) {
719 // The new area will be placed at the end of the
720 // reserved area, and the reserved area will be resized
721 // to make space
722
723 if (_IsRandomized(addressSpec)) {
724 addr_t alignedNextBase = align_address(next->Base(),
725 alignment);
726
727 addr_t startRange = next->Base() + next->Size();
728 startRange -= size + kMaxRandomize;
729 startRange = ROUNDDOWN(startRange, alignment);
730 startRange = std::max(startRange, alignedNextBase);
731
732 addr_t rangeEnd
733 = std::min(next->Base() + next->Size() - size,
734 end);
735 alignedBase = _RandomizeAddress(startRange,
736 rangeEnd, alignment);
737 } else {
738 alignedBase = ROUNDDOWN(
739 next->Base() + next->Size() - size, alignment);
740 }
741
742 foundSpot = true;
743 next->SetSize(alignedBase - next->Base());
744 area->SetBase(alignedBase);
745 break;
746 }
747
748 last = next;
749 }
750 }
751
752 break;
753 }
754
755 case B_EXACT_ADDRESS:
756 // see if we can create it exactly here
757 if ((last == NULL || last->Base() + (last->Size() - 1) < start)
758 && (next == NULL || next->Base() > start + (size - 1))) {
759 foundSpot = true;
760 area->SetBase(start);
761 break;
762 }
763 break;
764 default:
765 return B_BAD_VALUE;
766 }
767
768 if (!foundSpot)
769 return addressSpec == B_EXACT_ADDRESS ? B_BAD_VALUE : B_NO_MEMORY;
770
771 if (useHint)
772 fNextInsertHint = area->Base() + size;
773
774 area->SetSize(size);
775 fAreas.Insert(area);
776 IncrementChangeCount();
777 return B_OK;
778 }
779