xref: /haiku/src/system/kernel/arch/x86/arch_vm.cpp (revision 088cebb96f8acf912cb13f1d92ce45a1729c25d6)
1 /*
2  * Copyright 2009-2010, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Copyright 2008, Jérôme Duval.
4  * Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de.
5  * Distributed under the terms of the MIT License.
6  *
7  * Copyright 2001, Travis Geiselbrecht. All rights reserved.
8  * Distributed under the terms of the NewOS License.
9  */
10 
11 
12 #include <stdlib.h>
13 #include <string.h>
14 
15 #include <algorithm>
16 #include <new>
17 
18 #include <KernelExport.h>
19 
20 #include <boot/kernel_args.h>
21 #include <smp.h>
22 #include <util/AutoLock.h>
23 #include <vm/vm.h>
24 #include <vm/vm_page.h>
25 #include <vm/vm_priv.h>
26 #include <vm/VMAddressSpace.h>
27 #include <vm/VMArea.h>
28 
29 #include <arch/vm.h>
30 #include <arch/int.h>
31 #include <arch/cpu.h>
32 
33 #include <arch/x86/bios.h>
34 
35 
36 //#define TRACE_ARCH_VM
37 #ifdef TRACE_ARCH_VM
38 #	define TRACE(x) dprintf x
39 #else
40 #	define TRACE(x) ;
41 #endif
42 
43 // 0: disabled, 1: some, 2: more
44 #define TRACE_MTRR_ARCH_VM 1
45 
46 #if TRACE_MTRR_ARCH_VM >= 1
47 #	define TRACE_MTRR(x...) dprintf(x)
48 #else
49 #	define TRACE_MTRR(x...)
50 #endif
51 
52 #if TRACE_MTRR_ARCH_VM >= 2
53 #	define TRACE_MTRR2(x...) dprintf(x)
54 #else
55 #	define TRACE_MTRR2(x...)
56 #endif
57 
58 
59 void *gDmaAddress;
60 
61 
62 namespace {
63 
64 struct memory_type_range : DoublyLinkedListLinkImpl<memory_type_range> {
65 	uint64						base;
66 	uint64						size;
67 	uint32						type;
68 	area_id						area;
69 };
70 
71 
72 struct memory_type_range_point
73 		: DoublyLinkedListLinkImpl<memory_type_range_point> {
74 	uint64				address;
75 	memory_type_range*	range;
76 
77 	bool IsStart() const	{ return range->base == address; }
78 
79 	bool operator<(const memory_type_range_point& other) const
80 	{
81 		return address < other.address;
82 	}
83 };
84 
85 
86 struct update_mtrr_info {
87 	uint64	ignoreUncacheableSize;
88 	uint64	shortestUncacheableSize;
89 };
90 
91 
92 typedef DoublyLinkedList<memory_type_range> MemoryTypeRangeList;
93 
94 } // namespace
95 
96 
97 static mutex sMemoryTypeLock = MUTEX_INITIALIZER("memory type ranges");
98 static MemoryTypeRangeList sMemoryTypeRanges;
99 static int32 sMemoryTypeRangeCount = 0;
100 
101 static const uint32 kMaxMemoryTypeRegisters	= 32;
102 static x86_mtrr_info sMemoryTypeRegisters[kMaxMemoryTypeRegisters];
103 static uint32 sMemoryTypeRegisterCount;
104 static uint32 sMemoryTypeRegistersUsed;
105 
106 static memory_type_range* sTemporaryRanges = NULL;
107 static memory_type_range_point* sTemporaryRangePoints = NULL;
108 static int32 sTemporaryRangeCount = 0;
109 static int32 sTemporaryRangePointCount = 0;
110 
111 
112 static void
113 set_mtrrs()
114 {
115 	x86_set_mtrrs(IA32_MTR_WRITE_BACK, sMemoryTypeRegisters,
116 		sMemoryTypeRegistersUsed);
117 
118 #if TRACE_MTRR_ARCH_VM
119 	TRACE_MTRR("set MTRRs to:\n");
120 	for (uint32 i = 0; i < sMemoryTypeRegistersUsed; i++) {
121 		const x86_mtrr_info& info = sMemoryTypeRegisters[i];
122 		TRACE_MTRR("  mtrr: %2" B_PRIu32 ": base: %#10" B_PRIx64  ", size: %#10"
123 			B_PRIx64 ", type: %u\n", i, info.base, info.size,
124 			info.type);
125 	}
126 #endif
127 }
128 
129 
130 static bool
131 add_used_mtrr(uint64 base, uint64 size, uint32 type)
132 {
133 	if (sMemoryTypeRegistersUsed == sMemoryTypeRegisterCount)
134 		return false;
135 
136 	x86_mtrr_info& mtrr = sMemoryTypeRegisters[sMemoryTypeRegistersUsed++];
137 	mtrr.base = base;
138 	mtrr.size = size;
139 	mtrr.type = type;
140 
141 	return true;
142 }
143 
144 
145 static bool
146 add_mtrrs_for_range(uint64 base, uint64 size, uint32 type)
147 {
148 	for (uint64 interval = B_PAGE_SIZE; size > 0; interval <<= 1) {
149 		if ((base & interval) != 0) {
150 			if (!add_used_mtrr(base, interval, type))
151 				return false;
152 			base += interval;
153 			size -= interval;
154 		}
155 
156 		if ((size & interval) != 0) {
157 			if (!add_used_mtrr(base + size - interval, interval, type))
158 				return false;
159 			size -= interval;
160 		}
161 	}
162 
163 	return true;
164 }
165 
166 
167 static memory_type_range*
168 find_range(area_id areaID)
169 {
170 	for (MemoryTypeRangeList::Iterator it = sMemoryTypeRanges.GetIterator();
171 			memory_type_range* range = it.Next();) {
172 		if (range->area == areaID)
173 			return range;
174 	}
175 
176 	return NULL;
177 }
178 
179 
180 static void
181 optimize_memory_ranges(MemoryTypeRangeList& ranges, uint32 type,
182 	bool removeRanges)
183 {
184 	uint64 previousEnd = 0;
185 	uint64 nextStart = 0;
186 	MemoryTypeRangeList::Iterator it = ranges.GetIterator();
187 	memory_type_range* range = it.Next();
188 	while (range != NULL) {
189 		if (range->type != type) {
190 			previousEnd = range->base + range->size;
191 			nextStart = 0;
192 			range = it.Next();
193 			continue;
194 		}
195 
196 		// find the start of the next range we cannot join this one with
197 		if (nextStart == 0) {
198 			MemoryTypeRangeList::Iterator nextIt = it;
199 			while (memory_type_range* nextRange = nextIt.Next()) {
200 				if (nextRange->type != range->type) {
201 					nextStart = nextRange->base;
202 					break;
203 				}
204 			}
205 
206 			if (nextStart == 0) {
207 				// no upper limit -- set an artificial one, so we don't need to
208 				// special case below
209 				nextStart = (uint64)1 << 32;
210 			}
211 		}
212 
213 		// Align the range's base and end to the greatest power of two possible.
214 		// As long as we can align both without intersecting any differently
215 		// range, we can extend the range without making it more complicated.
216 		// Once one side hit a limit we need to be careful. We can still
217 		// continue aligning the other side, if the range crosses the power of
218 		// two boundary.
219 		uint64 rangeBase = range->base;
220 		uint64 rangeEnd = rangeBase + range->size;
221 		uint64 interval = B_PAGE_SIZE * 2;
222 		while (true) {
223 			uint64 alignedBase = rangeBase & ~(interval - 1);
224 			uint64 alignedEnd = (rangeEnd + interval - 1) & ~(interval - 1);
225 
226 			if (alignedBase < previousEnd)
227 				alignedBase += interval;
228 
229 			if (alignedEnd > nextStart)
230 				alignedEnd -= interval;
231 
232 			if (alignedBase >= alignedEnd)
233 				break;
234 
235 			rangeBase = std::min(rangeBase, alignedBase);
236 			rangeEnd = std::max(rangeEnd, alignedEnd);
237 
238 			interval <<= 1;
239 		}
240 
241 		range->base = rangeBase;
242 		range->size = rangeEnd - rangeBase;
243 
244 		if (removeRanges)
245 			it.Remove();
246 
247 		previousEnd = rangeEnd;
248 
249 		// Skip the subsequent ranges we have swallowed and possible cut one
250 		// we now partially intersect with.
251 		while ((range = it.Next()) != NULL) {
252 			if (range->base >= rangeEnd)
253 				break;
254 
255 			if (range->base + range->size > rangeEnd) {
256 				// we partially intersect -- cut the range
257 				range->size = range->base + range->size - rangeEnd;
258 				range->base = rangeEnd;
259 				break;
260 			}
261 
262 			// we have swallowed this range completely
263 			range->size = 0;
264 			it.Remove();
265 		}
266 	}
267 }
268 
269 
270 static bool
271 ensure_temporary_ranges_space(int32 count)
272 {
273 	if (sTemporaryRangeCount >= count && sTemporaryRangePointCount >= count)
274 		return true;
275 
276 	// round count to power of 2
277 	int32 unalignedCount = count;
278 	count = 8;
279 	while (count < unalignedCount)
280 		count <<= 1;
281 
282 	// resize ranges array
283 	if (sTemporaryRangeCount < count) {
284 		memory_type_range* ranges = new(std::nothrow) memory_type_range[count];
285 		if (ranges == NULL)
286 			return false;
287 
288 		delete[] sTemporaryRanges;
289 
290 		sTemporaryRanges = ranges;
291 		sTemporaryRangeCount = count;
292 	}
293 
294 	// resize points array
295 	if (sTemporaryRangePointCount < count) {
296 		memory_type_range_point* points
297 			= new(std::nothrow) memory_type_range_point[count];
298 		if (points == NULL)
299 			return false;
300 
301 		delete[] sTemporaryRangePoints;
302 
303 		sTemporaryRangePoints = points;
304 		sTemporaryRangePointCount = count;
305 	}
306 
307 	return true;
308 }
309 
310 
311 status_t
312 update_mtrrs(update_mtrr_info& updateInfo)
313 {
314 	// resize the temporary points/ranges arrays, if necessary
315 	if (!ensure_temporary_ranges_space(sMemoryTypeRangeCount * 2))
316 		return B_NO_MEMORY;
317 
318 	// get the range points and sort them
319 	memory_type_range_point* rangePoints = sTemporaryRangePoints;
320 	int32 pointCount = 0;
321 	for (MemoryTypeRangeList::Iterator it = sMemoryTypeRanges.GetIterator();
322 			memory_type_range* range = it.Next();) {
323 		if (range->type == IA32_MTR_UNCACHED) {
324 			// Ignore uncacheable ranges below a certain size, if requested.
325 			// Since we always enforce uncacheability via the PTE attributes,
326 			// this is no problem (though not recommended for performance
327 			// reasons).
328 			if (range->size <= updateInfo.ignoreUncacheableSize)
329 				continue;
330 			if (range->size < updateInfo.shortestUncacheableSize)
331 				updateInfo.shortestUncacheableSize = range->size;
332 		}
333 
334 		rangePoints[pointCount].address = range->base;
335 		rangePoints[pointCount++].range = range;
336 		rangePoints[pointCount].address = range->base + range->size;
337 		rangePoints[pointCount++].range = range;
338 	}
339 
340 	std::sort(rangePoints, rangePoints + pointCount);
341 
342 #if TRACE_MTRR_ARCH_VM >= 2
343 	TRACE_MTRR2("memory type range points:\n");
344 	for (int32 i = 0; i < pointCount; i++) {
345 		TRACE_MTRR2("%12" B_PRIx64 " (%p)\n", rangePoints[i].address,
346 			rangePoints[i].range);
347 	}
348 #endif
349 
350 	// Compute the effective ranges. When ranges overlap, we go with the
351 	// stricter requirement. The types are not necessarily totally ordered, so
352 	// the order we use below is not always correct. To keep it simple we
353 	// consider it the reponsibility of the callers not to define overlapping
354 	// memory ranges with uncomparable types.
355 
356 	memory_type_range* ranges = sTemporaryRanges;
357 	typedef DoublyLinkedList<memory_type_range_point> PointList;
358 	PointList pendingPoints;
359 	memory_type_range* activeRange = NULL;
360 	int32 rangeCount = 0;
361 
362 	for (int32 i = 0; i < pointCount; i++) {
363 		memory_type_range_point* point = &rangePoints[i];
364 		bool terminateRange = false;
365 		if (point->IsStart()) {
366 			// a range start point
367 			pendingPoints.Add(point);
368 			if (activeRange != NULL && activeRange->type > point->range->type)
369 				terminateRange = true;
370 		} else {
371 			// a range end point -- remove the pending start point
372 			for (PointList::Iterator it = pendingPoints.GetIterator();
373 					memory_type_range_point* pendingPoint = it.Next();) {
374 				if (pendingPoint->range == point->range) {
375 					it.Remove();
376 					break;
377 				}
378 			}
379 
380 			if (point->range == activeRange)
381 				terminateRange = true;
382 		}
383 
384 		if (terminateRange) {
385 			ranges[rangeCount].size = point->address - ranges[rangeCount].base;
386 			rangeCount++;
387 			activeRange = NULL;
388 		}
389 
390 		if (activeRange != NULL || pendingPoints.IsEmpty())
391 			continue;
392 
393 		// we need to start a new range -- find the strictest pending range
394 		for (PointList::Iterator it = pendingPoints.GetIterator();
395 				memory_type_range_point* pendingPoint = it.Next();) {
396 			memory_type_range* pendingRange = pendingPoint->range;
397 			if (activeRange == NULL || activeRange->type > pendingRange->type)
398 				activeRange = pendingRange;
399 		}
400 
401 		memory_type_range* previousRange = rangeCount > 0
402 			? &ranges[rangeCount - 1] : NULL;
403 		if (previousRange == NULL || previousRange->type != activeRange->type
404 				|| previousRange->base + previousRange->size
405 					< activeRange->base) {
406 			// we can't join with the previous range -- add a new one
407 			ranges[rangeCount].base = point->address;
408 			ranges[rangeCount].type = activeRange->type;
409 		} else
410 			rangeCount--;
411 	}
412 
413 #if TRACE_MTRR_ARCH_VM >= 2
414 	TRACE_MTRR2("effective memory type ranges:\n");
415 	for (int32 i = 0; i < rangeCount; i++) {
416 		TRACE_MTRR2("%12" B_PRIx64 " - %12" B_PRIx64 ": %" B_PRIu32 "\n",
417 			ranges[i].base, ranges[i].base + ranges[i].size, ranges[i].type);
418 	}
419 #endif
420 
421 	// Extend ranges to be more MTRR-friendly. A range is MTRR friendly, when it
422 	// has a power of two size and a base address aligned to the size. For
423 	// ranges without this property we need more than one MTRR. We improve
424 	// MTRR-friendliness by aligning a range's base and end address to the
425 	// greatest power of two (base rounded down, end up) such that the extended
426 	// range does not intersect with any other differently typed range. We join
427 	// equally typed ranges, if possible. There are two exceptions to the
428 	// intersection requirement: Uncached ranges may intersect with any other
429 	// range; the resulting type will still be uncached. Hence we can ignore
430 	// uncached ranges when extending the other ranges. Write-through ranges may
431 	// intersect with write-back ranges; the resulting type will be
432 	// write-through. Hence we can ignore write-through ranges when extending
433 	// write-back ranges.
434 
435 	MemoryTypeRangeList rangeList;
436 	for (int32 i = 0; i < rangeCount; i++)
437 		rangeList.Add(&ranges[i]);
438 
439 	static const uint32 kMemoryTypes[] = {
440 		IA32_MTR_UNCACHED,
441 		IA32_MTR_WRITE_COMBINING,
442 		IA32_MTR_WRITE_PROTECTED,
443 		IA32_MTR_WRITE_THROUGH,
444 		IA32_MTR_WRITE_BACK
445 	};
446 	static const int32 kMemoryTypeCount = sizeof(kMemoryTypes)
447 		/ sizeof(*kMemoryTypes);
448 
449 	for (int32 i = 0; i < kMemoryTypeCount; i++) {
450 		uint32 type = kMemoryTypes[i];
451 
452 		// Remove uncached and write-through ranges after processing them. This
453 		// let's us leverage their intersection property with any other
454 		// respectively write-back ranges.
455 		bool removeRanges = type == IA32_MTR_UNCACHED
456 			|| type == IA32_MTR_WRITE_THROUGH;
457 
458 		optimize_memory_ranges(rangeList, type, removeRanges);
459 	}
460 
461 #if TRACE_MTRR_ARCH_VM >= 2
462 	TRACE_MTRR2("optimized memory type ranges:\n");
463 	for (int32 i = 0; i < rangeCount; i++) {
464 		if (ranges[i].size > 0) {
465 			TRACE_MTRR2("%12" B_PRIx64 " - %12" B_PRIx64 ": %" B_PRIu32 "\n",
466 				ranges[i].base, ranges[i].base + ranges[i].size,
467 				ranges[i].type);
468 		}
469 	}
470 #endif
471 
472 	// compute the mtrrs from the ranges
473 	sMemoryTypeRegistersUsed = 0;
474 	for (int32 i = 0; i < kMemoryTypeCount; i++) {
475 		uint32 type = kMemoryTypes[i];
476 
477 		// skip write-back ranges -- that'll be the default type anyway
478 		if (type == IA32_MTR_WRITE_BACK)
479 			continue;
480 
481 		for (int32 i = 0; i < rangeCount; i++) {
482 			if (ranges[i].size == 0 || ranges[i].type != type)
483 				continue;
484 
485 			if (!add_mtrrs_for_range(ranges[i].base, ranges[i].size, type))
486 				return B_BUSY;
487 		}
488 	}
489 
490 	set_mtrrs();
491 
492 	return B_OK;
493 }
494 
495 
496 status_t
497 update_mtrrs()
498 {
499 	// Until we know how many MTRRs we have, pretend everything is OK.
500 	if (sMemoryTypeRegisterCount == 0)
501 		return B_OK;
502 
503 	update_mtrr_info updateInfo;
504 	updateInfo.ignoreUncacheableSize = 0;
505 
506 	while (true) {
507 		TRACE_MTRR2("update_mtrrs(): Trying with ignoreUncacheableSize %#"
508 			B_PRIx64 ".\n", updateInfo.ignoreUncacheableSize);
509 
510 		updateInfo.shortestUncacheableSize = ~(uint64)0;
511 		status_t error = update_mtrrs(updateInfo);
512 		if (error != B_BUSY) {
513 			if (error == B_OK && updateInfo.ignoreUncacheableSize > 0) {
514 				TRACE_MTRR("update_mtrrs(): Succeeded setting MTRRs after "
515 					"ignoring uncacheable ranges up to size %#" B_PRIx64 ".\n",
516 					updateInfo.ignoreUncacheableSize);
517 			}
518 			return error;
519 		}
520 
521 		// Not enough MTRRs. Retry with less uncacheable ranges.
522 		if (updateInfo.shortestUncacheableSize == ~(uint64)0) {
523 			// Ugh, even without any uncacheable ranges the available MTRRs do
524 			// not suffice.
525 			panic("update_mtrrs(): Out of MTRRs!");
526 			return B_BUSY;
527 		}
528 
529 		ASSERT(updateInfo.ignoreUncacheableSize
530 			< updateInfo.shortestUncacheableSize);
531 
532 		updateInfo.ignoreUncacheableSize = updateInfo.shortestUncacheableSize;
533 	}
534 }
535 
536 
537 static status_t
538 add_memory_type_range(area_id areaID, uint64 base, uint64 size, uint32 type)
539 {
540 	// translate the type
541 	if (type == 0)
542 		return B_OK;
543 
544 	switch (type) {
545 		case B_MTR_UC:
546 			type = IA32_MTR_UNCACHED;
547 			break;
548 		case B_MTR_WC:
549 			type = IA32_MTR_WRITE_COMBINING;
550 			break;
551 		case B_MTR_WT:
552 			type = IA32_MTR_WRITE_THROUGH;
553 			break;
554 		case B_MTR_WP:
555 			type = IA32_MTR_WRITE_PROTECTED;
556 			break;
557 		case B_MTR_WB:
558 			type = IA32_MTR_WRITE_BACK;
559 			break;
560 		default:
561 			return B_BAD_VALUE;
562 	}
563 
564 	TRACE_MTRR2("add_memory_type_range(%" B_PRId32 ", %#" B_PRIx64 ", %#"
565 		B_PRIx64 ", %" B_PRIu32 ")\n", areaID, base, size, type);
566 
567 	MutexLocker locker(sMemoryTypeLock);
568 
569 	memory_type_range* range = areaID >= 0 ? find_range(areaID) : NULL;
570 	int32 oldRangeType = -1;
571 	if (range != NULL) {
572 		if (range->base != base || range->size != size)
573 			return B_BAD_VALUE;
574 		if (range->type == type)
575 			return B_OK;
576 
577 		oldRangeType = range->type;
578 		range->type = type;
579 	} else {
580 		range = new(std::nothrow) memory_type_range;
581 		if (range == NULL)
582 			return B_NO_MEMORY;
583 
584 		range->area = areaID;
585 		range->base = base;
586 		range->size = size;
587 		range->type = type;
588 		sMemoryTypeRanges.Add(range);
589 		sMemoryTypeRangeCount++;
590 	}
591 
592 	status_t error = update_mtrrs();
593 	if (error != B_OK) {
594 		// revert the addition of the range/change of its type
595 		if (oldRangeType < 0) {
596 			sMemoryTypeRanges.Remove(range);
597 			sMemoryTypeRangeCount--;
598 			delete range;
599 		} else
600 			range->type = oldRangeType;
601 
602 		update_mtrrs();
603 		return error;
604 	}
605 
606 	return B_OK;
607 }
608 
609 
610 static void
611 remove_memory_type_range(area_id areaID)
612 {
613 	MutexLocker locker(sMemoryTypeLock);
614 
615 	memory_type_range* range = find_range(areaID);
616 	if (range != NULL) {
617 		TRACE_MTRR2("remove_memory_type_range(%" B_PRId32 ", %#" B_PRIx64 ", %#"
618 			B_PRIx64 ", %" B_PRIu32 ")\n", range->area, range->base,
619 			range->size, range->type);
620 
621 		sMemoryTypeRanges.Remove(range);
622 		sMemoryTypeRangeCount--;
623 		delete range;
624 
625 		update_mtrrs();
626 	} else {
627 		dprintf("remove_memory_type_range(): no range known for area %" B_PRId32
628 			"\n", areaID);
629 	}
630 }
631 
632 
633 //	#pragma mark -
634 
635 
636 status_t
637 arch_vm_init(kernel_args *args)
638 {
639 	TRACE(("arch_vm_init: entry\n"));
640 	return 0;
641 }
642 
643 
644 /*!	Marks DMA region as in-use, and maps it into the kernel space */
645 status_t
646 arch_vm_init_post_area(kernel_args *args)
647 {
648 	area_id id;
649 
650 	TRACE(("arch_vm_init_post_area: entry\n"));
651 
652 	// account for DMA area and mark the pages unusable
653 	vm_mark_page_range_inuse(0x0, 0xa0000 / B_PAGE_SIZE);
654 
655 	// map 0 - 0xa0000 directly
656 	id = map_physical_memory("dma_region", 0x0, 0xa0000,
657 		B_ANY_KERNEL_ADDRESS | B_MTR_WB,
658 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, &gDmaAddress);
659 	if (id < 0) {
660 		panic("arch_vm_init_post_area: unable to map dma region\n");
661 		return B_NO_MEMORY;
662 	}
663 
664 #ifndef __x86_64__
665 	return bios_init();
666 #else
667 	return B_OK;
668 #endif
669 }
670 
671 
672 /*!	Gets rid of all yet unmapped (and therefore now unused) page tables */
673 status_t
674 arch_vm_init_end(kernel_args *args)
675 {
676 	TRACE(("arch_vm_init_endvm: entry\n"));
677 
678 	// throw away anything in the kernel_args.pgtable[] that's not yet mapped
679 	vm_free_unused_boot_loader_range(KERNEL_LOAD_BASE,
680 		args->arch_args.virtual_end - KERNEL_LOAD_BASE);
681 
682 	return B_OK;
683 }
684 
685 
686 status_t
687 arch_vm_init_post_modules(kernel_args *args)
688 {
689 	// the x86 CPU modules are now accessible
690 
691 	sMemoryTypeRegisterCount = x86_count_mtrrs();
692 	if (sMemoryTypeRegisterCount == 0)
693 		return B_OK;
694 
695 	// not very likely, but play safe here
696 	if (sMemoryTypeRegisterCount > kMaxMemoryTypeRegisters)
697 		sMemoryTypeRegisterCount = kMaxMemoryTypeRegisters;
698 
699 	// set the physical memory ranges to write-back mode
700 	for (uint32 i = 0; i < args->num_physical_memory_ranges; i++) {
701 		add_memory_type_range(-1, args->physical_memory_range[i].start,
702 			args->physical_memory_range[i].size, B_MTR_WB);
703 	}
704 
705 	return B_OK;
706 }
707 
708 
709 void
710 arch_vm_aspace_swap(struct VMAddressSpace *from, struct VMAddressSpace *to)
711 {
712 	// This functions is only invoked when a userland thread is in the process
713 	// of dying. It switches to the kernel team and does whatever cleanup is
714 	// necessary (in case it is the team's main thread, it will delete the
715 	// team).
716 	// It is however not necessary to change the page directory. Userland team's
717 	// page directories include all kernel mappings as well. Furthermore our
718 	// arch specific translation map data objects are ref-counted, so they won't
719 	// go away as long as they are still used on any CPU.
720 }
721 
722 
723 bool
724 arch_vm_supports_protection(uint32 protection)
725 {
726 	// x86 always has the same read/write properties for userland and the
727 	// kernel.
728 	// That's why we do not support user-read/kernel-write access. While the
729 	// other way around is not supported either, we don't care in this case
730 	// and give the kernel full access.
731 	if ((protection & (B_READ_AREA | B_WRITE_AREA)) == B_READ_AREA
732 		&& (protection & B_KERNEL_WRITE_AREA) != 0) {
733 		return false;
734 	}
735 
736 	// Userland and the kernel have the same setting of NX-bit.
737 	// That's why we do not allow any area that user can access, but not execute
738 	// and the kernel can execute.
739 	if ((protection & (B_READ_AREA | B_WRITE_AREA)) != 0
740 		&& (protection & B_EXECUTE_AREA) == 0
741 		&& (protection & B_KERNEL_EXECUTE_AREA) != 0) {
742 		return false;
743 	}
744 
745 	return true;
746 }
747 
748 
749 void
750 arch_vm_unset_memory_type(struct VMArea *area)
751 {
752 	if (area->MemoryType() == 0)
753 		return;
754 
755 	remove_memory_type_range(area->id);
756 }
757 
758 
759 status_t
760 arch_vm_set_memory_type(struct VMArea *area, phys_addr_t physicalBase,
761 	uint32 type)
762 {
763 	return add_memory_type_range(area->id, physicalBase, area->Size(), type);
764 }
765