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