xref: /haiku/src/system/boot/platform/bios_ia32/mmu.cpp (revision 8bacd281ba5156920f4b8cfa1da7520122beab78)
1 /*
2  * Copyright 2004-2008, Axel Dörfler, axeld@pinc-software.de.
3  * Based on code written by Travis Geiselbrecht for NewOS.
4  *
5  * Distributed under the terms of the MIT License.
6  */
7 
8 
9 #include "mmu.h"
10 
11 #include <string.h>
12 
13 #include <OS.h>
14 
15 #include <arch/cpu.h>
16 #include <arch/x86/descriptors.h>
17 #include <arch_kernel.h>
18 #include <boot/platform.h>
19 #include <boot/stdio.h>
20 #include <boot/kernel_args.h>
21 #include <boot/stage2.h>
22 #include <kernel.h>
23 
24 #include "bios.h"
25 #include "interrupts.h"
26 
27 
28 /*!	The (physical) memory layout of the boot loader is currently as follows:
29 	  0x0500 - 0x10000	protected mode stack
30 	  0x0500 - 0x09000	real mode stack
31 	 0x10000 - ?		code (up to ~500 kB)
32 	 0x90000			1st temporary page table (identity maps 0-4 MB)
33 	 0x91000			2nd (4-8 MB)
34 	 0x92000 - 0x92000	further page tables
35 	 0x9e000 - 0xa0000	SMP trampoline code
36 	[0xa0000 - 0x100000	BIOS/ROM/reserved area]
37 	0x100000			page directory
38 	     ...			boot loader heap (32 kB)
39 	     ...			free physical memory
40 
41 	The first 8 MB are identity mapped (0x0 - 0x0800000); paging is turned
42 	on. The kernel is mapped at 0x80000000, all other stuff mapped by the
43 	loader (kernel args, modules, driver settings, ...) comes after
44 	0x80020000 which means that there is currently only 2 MB reserved for
45 	the kernel itself (see kMaxKernelSize).
46 
47 	The layout in PXE mode differs a bit from this, see definitions below.
48 */
49 
50 //#define TRACE_MMU
51 #ifdef TRACE_MMU
52 #	define TRACE(x...) dprintf(x)
53 #else
54 #	define TRACE(x...) ;
55 #endif
56 
57 
58 //#define TRACE_MEMORY_MAP
59 	// Define this to print the memory map to serial debug,
60 	// You also need to define ENABLE_SERIAL in serial.cpp
61 	// for output to work.
62 
63 
64 // memory structure returned by int 0x15, ax 0xe820
65 struct extended_memory {
66 	uint64 base_addr;
67 	uint64 length;
68 	uint32 type;
69 };
70 
71 
72 segment_descriptor gBootGDT[BOOT_GDT_SEGMENT_COUNT];
73 
74 static const uint32 kDefaultPageTableFlags = 0x07;	// present, user, R/W
75 static const size_t kMaxKernelSize = 0x1000000;		// 16 MB for the kernel
76 static const size_t kIdentityMapEnd = 0x0800000;	// 8 MB
77 
78 // working page directory and page table
79 static uint32 *sPageDirectory = 0;
80 
81 #ifdef _PXE_ENV
82 
83 static addr_t sNextPhysicalAddress = 0x112000;
84 static addr_t sNextPhysicalKernelAddress = kIdentityMapEnd;
85 static addr_t sNextVirtualAddress = KERNEL_LOAD_BASE + kMaxKernelSize;
86 
87 static addr_t sNextPageTableAddress = 0x7d000;
88 static const uint32 kPageTableRegionEnd = 0x8b000;
89 	// we need to reserve 2 pages for the SMP trampoline code
90 
91 #else
92 
93 static addr_t sNextPhysicalAddress = 0x100000;
94 static addr_t sNextPhysicalKernelAddress = kIdentityMapEnd;
95 static addr_t sNextVirtualAddress = KERNEL_LOAD_BASE + kMaxKernelSize;
96 
97 static addr_t sNextPageTableAddress = 0x90000;
98 static const uint32 kPageTableRegionEnd = 0x9e000;
99 	// we need to reserve 2 pages for the SMP trampoline code
100 
101 #endif
102 
103 
104 static addr_t
105 allocate_virtual(size_t size)
106 {
107 	addr_t address = sNextVirtualAddress;
108 	sNextVirtualAddress += size;
109 
110 	return address;
111 }
112 
113 
114 static addr_t
115 allocate_physical(size_t size, bool forKernel)
116 {
117 	uint64 base;
118 	if (!forKernel) {
119 		base = sNextPhysicalAddress;
120 		if ((base + size) > kIdentityMapEnd) {
121 			panic("Out of identity-map physical memory!");
122 			return 0;
123 		}
124 
125 		sNextPhysicalAddress += size;
126 		return base;
127 	}
128 
129 	if (!get_free_address_range(gKernelArgs.physical_allocated_range,
130 			gKernelArgs.num_physical_allocated_ranges, sNextPhysicalKernelAddress,
131 			size, &base)) {
132 		panic("Out of physical memory!");
133 		return 0;
134 	}
135 
136 	insert_physical_allocated_range(base, size);
137 	sNextPhysicalKernelAddress = base + size;
138 		// TODO: Can overflow theoretically.
139 
140 	return base;
141 }
142 
143 
144 static addr_t
145 get_next_virtual_page()
146 {
147 	return allocate_virtual(B_PAGE_SIZE);
148 }
149 
150 
151 static addr_t
152 get_next_physical_page()
153 {
154 	return allocate_physical(B_PAGE_SIZE, true);
155 }
156 
157 
158 static uint32 *
159 get_next_page_table()
160 {
161 	TRACE("get_next_page_table, sNextPageTableAddress %#" B_PRIxADDR
162 		", kPageTableRegionEnd %#" B_PRIxADDR "\n", sNextPageTableAddress,
163 		kPageTableRegionEnd);
164 
165 	addr_t address = sNextPageTableAddress;
166 	if (address >= kPageTableRegionEnd)
167 		return (uint32 *)allocate_physical(B_PAGE_SIZE, false);
168 
169 	sNextPageTableAddress += B_PAGE_SIZE;
170 	return (uint32 *)address;
171 }
172 
173 
174 /*!	Adds a new page table for the specified base address */
175 static uint32*
176 add_page_table(addr_t base)
177 {
178 	if (gKernelArgs.arch_args.num_pgtables == MAX_BOOT_PTABLES) {
179 		panic("gKernelArgs.arch_args.pgtables overflow");
180 		return NULL;
181 	}
182 
183 	base = ROUNDDOWN(base, B_PAGE_SIZE * 1024);
184 
185 	// Get new page table and clear it out
186 	uint32 *pageTable = get_next_page_table();
187 	if (pageTable > (uint32 *)kIdentityMapEnd) {
188 		panic("tried to add page table beyond the identity mapped 8 MB "
189 			"region\n");
190 		return NULL;
191 	}
192 
193 	TRACE("add_page_table(base = %p), got page: %p\n", (void*)base, pageTable);
194 
195 	gKernelArgs.arch_args.pgtables[gKernelArgs.arch_args.num_pgtables++]
196 		= (uint32)pageTable;
197 
198 	for (int32 i = 0; i < 1024; i++)
199 		pageTable[i] = 0;
200 
201 	// put the new page table into the page directory
202 	sPageDirectory[base / (4 * 1024 * 1024)]
203 		= (uint32)pageTable | kDefaultPageTableFlags;
204 
205 	// update the virtual end address in the kernel args
206 	base += B_PAGE_SIZE * 1024;
207 	if (base > gKernelArgs.arch_args.virtual_end)
208 		gKernelArgs.arch_args.virtual_end = base;
209 
210 	return pageTable;
211 }
212 
213 
214 static void
215 unmap_page(addr_t virtualAddress)
216 {
217 	TRACE("unmap_page(virtualAddress = %p)\n", (void *)virtualAddress);
218 
219 	if (virtualAddress < KERNEL_LOAD_BASE) {
220 		panic("unmap_page: asked to unmap invalid page %p!\n",
221 			(void *)virtualAddress);
222 	}
223 
224 	// unmap the page from the correct page table
225 	uint32 *pageTable = (uint32 *)(sPageDirectory[virtualAddress
226 		/ (B_PAGE_SIZE * 1024)] & 0xfffff000);
227 	pageTable[(virtualAddress % (B_PAGE_SIZE * 1024)) / B_PAGE_SIZE] = 0;
228 
229 	asm volatile("invlpg (%0)" : : "r" (virtualAddress));
230 }
231 
232 
233 /*!	Creates an entry to map the specified virtualAddress to the given
234 	physicalAddress.
235 	If the mapping goes beyond the current page table, it will allocate
236 	a new one. If it cannot map the requested page, it panics.
237 */
238 static void
239 map_page(addr_t virtualAddress, addr_t physicalAddress, uint32 flags)
240 {
241 	TRACE("map_page: vaddr 0x%lx, paddr 0x%lx\n", virtualAddress,
242 		physicalAddress);
243 
244 	if (virtualAddress < KERNEL_LOAD_BASE) {
245 		panic("map_page: asked to map invalid page %p!\n",
246 			(void *)virtualAddress);
247 	}
248 
249 	uint32 *pageTable = (uint32 *)(sPageDirectory[virtualAddress
250 		/ (B_PAGE_SIZE * 1024)] & 0xfffff000);
251 
252 	if (pageTable == NULL) {
253 		// we need to add a new page table
254 		pageTable = add_page_table(virtualAddress);
255 
256 		if (pageTable == NULL) {
257 			panic("map_page: failed to allocate a page table for virtual "
258 				"address %p\n", (void*)virtualAddress);
259 			return;
260 		}
261 	}
262 
263 	physicalAddress &= ~(B_PAGE_SIZE - 1);
264 
265 	// map the page to the correct page table
266 	uint32 tableEntry = (virtualAddress % (B_PAGE_SIZE * 1024)) / B_PAGE_SIZE;
267 
268 	TRACE("map_page: inserting pageTable %p, tableEntry %" B_PRIu32
269 		", physicalAddress %#" B_PRIxADDR "\n", pageTable, tableEntry,
270 		physicalAddress);
271 
272 	pageTable[tableEntry] = physicalAddress | flags;
273 
274 	asm volatile("invlpg (%0)" : : "r" (virtualAddress));
275 
276 	TRACE("map_page: done\n");
277 }
278 
279 
280 #ifdef TRACE_MEMORY_MAP
281 static const char *
282 e820_memory_type(uint32 type)
283 {
284 	switch (type) {
285 		case 1: return "memory";
286 		case 2: return "reserved";
287 		case 3: return "ACPI reclaim";
288 		case 4: return "ACPI NVS";
289 		default: return "unknown/reserved";
290 	}
291 }
292 #endif
293 
294 
295 static uint32
296 get_memory_map(extended_memory **_extendedMemory)
297 {
298 	extended_memory *block = (extended_memory *)kExtraSegmentScratch;
299 	bios_regs regs = {0, 0, sizeof(extended_memory), 0, 0, (uint32)block, 0, 0};
300 	uint32 count = 0;
301 
302 	TRACE("get_memory_map()\n");
303 
304 	do {
305 		regs.eax = 0xe820;
306 		regs.edx = 'SMAP';
307 
308 		call_bios(0x15, &regs);
309 		if ((regs.flags & CARRY_FLAG) != 0)
310 			return 0;
311 
312 		regs.edi += sizeof(extended_memory);
313 		count++;
314 	} while (regs.ebx != 0);
315 
316 	*_extendedMemory = block;
317 
318 #ifdef TRACE_MEMORY_MAP
319 	dprintf("extended memory info (from 0xe820):\n");
320 	for (uint32 i = 0; i < count; i++) {
321 		dprintf("    base 0x%08Lx, len 0x%08Lx, type %lu (%s)\n",
322 			block[i].base_addr, block[i].length,
323 			block[i].type, e820_memory_type(block[i].type));
324 	}
325 #endif
326 
327 	return count;
328 }
329 
330 
331 static void
332 init_page_directory(void)
333 {
334 	TRACE("init_page_directory\n");
335 
336 	// allocate a new pgdir
337 	sPageDirectory = (uint32 *)allocate_physical(B_PAGE_SIZE, false);
338 	gKernelArgs.arch_args.phys_pgdir = (uint32)sPageDirectory;
339 
340 	// clear out the pgdir
341 	for (int32 i = 0; i < 1024; i++) {
342 		sPageDirectory[i] = 0;
343 	}
344 
345 	// Identity map the first 8 MB of memory so that their
346 	// physical and virtual address are the same.
347 	// These page tables won't be taken over into the kernel.
348 
349 	// make the first page table at the first free spot
350 	uint32 *pageTable = get_next_page_table();
351 
352 	for (int32 i = 0; i < 1024; i++) {
353 		pageTable[i] = (i * 0x1000) | kDefaultPageFlags;
354 	}
355 
356 	sPageDirectory[0] = (uint32)pageTable | kDefaultPageFlags;
357 
358 	// make the second page table
359 	pageTable = get_next_page_table();
360 
361 	for (int32 i = 0; i < 1024; i++) {
362 		pageTable[i] = (i * 0x1000 + 0x400000) | kDefaultPageFlags;
363 	}
364 
365 	sPageDirectory[1] = (uint32)pageTable | kDefaultPageFlags;
366 
367 	gKernelArgs.arch_args.num_pgtables = 0;
368 
369 	// switch to the new pgdir and enable paging
370 	asm("movl %0, %%eax;"
371 		"movl %%eax, %%cr3;" : : "m" (sPageDirectory) : "eax");
372 	// Important.  Make sure supervisor threads can fault on read only pages...
373 	asm("movl %%eax, %%cr0" : : "a" ((1 << 31) | (1 << 16) | (1 << 5) | 1));
374 }
375 
376 
377 //	#pragma mark -
378 
379 
380 /*!
381 	Neither \a virtualAddress nor \a size need to be aligned, but the function
382 	will map all pages the range intersects with.
383 	If physicalAddress is not page-aligned, the returned virtual address will
384 	have the same "misalignment".
385 */
386 extern "C" addr_t
387 mmu_map_physical_memory(addr_t physicalAddress, size_t size, uint32 flags)
388 {
389 	addr_t address = sNextVirtualAddress;
390 	addr_t pageOffset = physicalAddress & (B_PAGE_SIZE - 1);
391 
392 	physicalAddress -= pageOffset;
393 	size += pageOffset;
394 
395 	for (addr_t offset = 0; offset < size; offset += B_PAGE_SIZE) {
396 		map_page(get_next_virtual_page(), physicalAddress + offset, flags);
397 	}
398 
399 	return address + pageOffset;
400 }
401 
402 
403 extern "C" void *
404 mmu_allocate(void *virtualAddress, size_t size)
405 {
406 	TRACE("mmu_allocate: requested vaddr: %p, next free vaddr: 0x%lx, size: "
407 		"%ld\n", virtualAddress, sNextVirtualAddress, size);
408 
409 	size = (size + B_PAGE_SIZE - 1) / B_PAGE_SIZE;
410 		// get number of pages to map
411 
412 	if (virtualAddress != NULL) {
413 		// This special path is almost only useful for loading the
414 		// kernel into memory; it will only allow you to map the
415 		// 'kMaxKernelSize' bytes following the kernel base address.
416 		// Also, it won't check for already mapped addresses, so
417 		// you better know why you are here :)
418 		addr_t address = (addr_t)virtualAddress;
419 
420 		// is the address within the valid range?
421 		if (address < KERNEL_LOAD_BASE || address + size * B_PAGE_SIZE
422 			>= KERNEL_LOAD_BASE + kMaxKernelSize)
423 			return NULL;
424 
425 		for (uint32 i = 0; i < size; i++) {
426 			map_page(address, get_next_physical_page(), kDefaultPageFlags);
427 			address += B_PAGE_SIZE;
428 		}
429 
430 		return virtualAddress;
431 	}
432 
433 	void *address = (void *)sNextVirtualAddress;
434 
435 	for (uint32 i = 0; i < size; i++) {
436 		map_page(get_next_virtual_page(), get_next_physical_page(),
437 			kDefaultPageFlags);
438 	}
439 
440 	return address;
441 }
442 
443 
444 /*!	Allocates a single page and returns both its virtual and physical
445 	addresses.
446 */
447 void *
448 mmu_allocate_page(addr_t *_physicalAddress)
449 {
450 	addr_t virt = get_next_virtual_page();
451 	addr_t phys = get_next_physical_page();
452 
453 	map_page(virt, phys, kDefaultPageFlags);
454 
455 	if (_physicalAddress)
456 		*_physicalAddress = phys;
457 
458 	return (void *)virt;
459 }
460 
461 
462 /*!	Allocates the given physical range.
463 	\return \c true, if the range could be allocated, \c false otherwise.
464 */
465 bool
466 mmu_allocate_physical(addr_t base, size_t size)
467 {
468 	// check whether the physical memory range exists at all
469 	if (!is_address_range_covered(gKernelArgs.physical_memory_range,
470 			gKernelArgs.num_physical_memory_ranges, base, size)) {
471 		return false;
472 	}
473 
474 	// check whether the physical range is still free
475 	uint64 foundBase;
476 	if (!get_free_address_range(gKernelArgs.physical_allocated_range,
477 			gKernelArgs.num_physical_allocated_ranges, base, size, &foundBase)
478 		|| foundBase != base) {
479 		return false;
480 	}
481 
482 	return insert_physical_allocated_range(base, size) == B_OK;
483 }
484 
485 
486 /*!	This will unmap the allocated chunk of memory from the virtual
487 	address space. It might not actually free memory (as its implementation
488 	is very simple), but it might.
489 	Neither \a virtualAddress nor \a size need to be aligned, but the function
490 	will unmap all pages the range intersects with.
491 */
492 extern "C" void
493 mmu_free(void *virtualAddress, size_t size)
494 {
495 	TRACE("mmu_free(virtualAddress = %p, size: %ld)\n", virtualAddress, size);
496 
497 	addr_t address = (addr_t)virtualAddress;
498 	addr_t pageOffset = address % B_PAGE_SIZE;
499 	address -= pageOffset;
500 	size = (size + pageOffset + B_PAGE_SIZE - 1) / B_PAGE_SIZE * B_PAGE_SIZE;
501 
502 	// is the address within the valid range?
503 	if (address < KERNEL_LOAD_BASE || address + size > sNextVirtualAddress) {
504 		panic("mmu_free: asked to unmap out of range region (%p, size %lx)\n",
505 			(void *)address, size);
506 	}
507 
508 	// unmap all pages within the range
509 	for (size_t i = 0; i < size; i += B_PAGE_SIZE) {
510 		unmap_page(address);
511 		address += B_PAGE_SIZE;
512 	}
513 
514 	if (address == sNextVirtualAddress) {
515 		// we can actually reuse the virtual address space
516 		sNextVirtualAddress -= size;
517 	}
518 }
519 
520 
521 size_t
522 mmu_get_virtual_usage()
523 {
524 	return sNextVirtualAddress - KERNEL_LOAD_BASE;
525 }
526 
527 
528 bool
529 mmu_get_virtual_mapping(addr_t virtualAddress, addr_t *_physicalAddress)
530 {
531 	if (virtualAddress < KERNEL_LOAD_BASE) {
532 		panic("mmu_get_virtual_mapping: asked to lookup invalid page %p!\n",
533 			(void *)virtualAddress);
534 	}
535 
536 	uint32 dirEntry = sPageDirectory[virtualAddress / (B_PAGE_SIZE * 1024)];
537 	if ((dirEntry & (1 << 0)) == 0)
538 		return false;
539 
540 	uint32 *pageTable = (uint32 *)(dirEntry & 0xfffff000);
541 	uint32 tableEntry = pageTable[(virtualAddress % (B_PAGE_SIZE * 1024))
542 		/ B_PAGE_SIZE];
543 	if ((tableEntry & (1 << 0)) == 0)
544 		return false;
545 
546 	*_physicalAddress = tableEntry & 0xfffff000;
547 	return true;
548 }
549 
550 
551 /*!	Sets up the final and kernel accessible GDT and IDT tables.
552 	BIOS calls won't work any longer after this function has
553 	been called.
554 */
555 extern "C" void
556 mmu_init_for_kernel(void)
557 {
558 	TRACE("mmu_init_for_kernel\n");
559 
560 	STATIC_ASSERT(BOOT_GDT_SEGMENT_COUNT > KERNEL_CODE_SEGMENT
561 		&& BOOT_GDT_SEGMENT_COUNT > KERNEL_DATA_SEGMENT
562 		&& BOOT_GDT_SEGMENT_COUNT > USER_CODE_SEGMENT
563 		&& BOOT_GDT_SEGMENT_COUNT > USER_DATA_SEGMENT);
564 
565 	// set up a new gdt
566 
567 	// put standard segment descriptors in GDT
568 	clear_segment_descriptor(&gBootGDT[0]);
569 
570 	// seg 0x08 - kernel 4GB code
571 	set_segment_descriptor(&gBootGDT[KERNEL_CODE_SEGMENT], 0, 0xffffffff,
572 		DT_CODE_READABLE, DPL_KERNEL);
573 
574 	// seg 0x10 - kernel 4GB data
575 	set_segment_descriptor(&gBootGDT[KERNEL_DATA_SEGMENT], 0, 0xffffffff,
576 		DT_DATA_WRITEABLE, DPL_KERNEL);
577 
578 	// seg 0x1b - ring 3 user 4GB code
579 	set_segment_descriptor(&gBootGDT[USER_CODE_SEGMENT], 0, 0xffffffff,
580 		DT_CODE_READABLE, DPL_USER);
581 
582 	// seg 0x23 - ring 3 user 4GB data
583 	set_segment_descriptor(&gBootGDT[USER_DATA_SEGMENT], 0, 0xffffffff,
584 		DT_DATA_WRITEABLE, DPL_USER);
585 
586 	// load the GDT
587 	struct gdt_idt_descr gdtDescriptor;
588 	gdtDescriptor.limit = sizeof(gBootGDT);
589 	gdtDescriptor.base = gBootGDT;
590 
591 	asm("lgdt %0" : : "m" (gdtDescriptor));
592 
593 	TRACE("gdt at virtual address %p\n", gBootGDT);
594 
595 	// Save the memory we've virtually allocated (for the kernel and other
596 	// stuff)
597 	gKernelArgs.virtual_allocated_range[0].start = KERNEL_LOAD_BASE;
598 	gKernelArgs.virtual_allocated_range[0].size
599 		= sNextVirtualAddress - KERNEL_LOAD_BASE;
600 	gKernelArgs.num_virtual_allocated_ranges = 1;
601 
602 	// sort the address ranges
603 	sort_address_ranges(gKernelArgs.physical_memory_range,
604 		gKernelArgs.num_physical_memory_ranges);
605 	sort_address_ranges(gKernelArgs.physical_allocated_range,
606 		gKernelArgs.num_physical_allocated_ranges);
607 	sort_address_ranges(gKernelArgs.virtual_allocated_range,
608 		gKernelArgs.num_virtual_allocated_ranges);
609 
610 #ifdef TRACE_MEMORY_MAP
611 	{
612 		uint32 i;
613 
614 		dprintf("phys memory ranges:\n");
615 		for (i = 0; i < gKernelArgs.num_physical_memory_ranges; i++) {
616 			dprintf("    base %#018" B_PRIx64 ", length %#018" B_PRIx64 "\n",
617 				gKernelArgs.physical_memory_range[i].start,
618 				gKernelArgs.physical_memory_range[i].size);
619 		}
620 
621 		dprintf("allocated phys memory ranges:\n");
622 		for (i = 0; i < gKernelArgs.num_physical_allocated_ranges; i++) {
623 			dprintf("    base %#018" B_PRIx64 ", length %#018" B_PRIx64 "\n",
624 				gKernelArgs.physical_allocated_range[i].start,
625 				gKernelArgs.physical_allocated_range[i].size);
626 		}
627 
628 		dprintf("allocated virt memory ranges:\n");
629 		for (i = 0; i < gKernelArgs.num_virtual_allocated_ranges; i++) {
630 			dprintf("    base %#018" B_PRIx64 ", length %#018" B_PRIx64 "\n",
631 				gKernelArgs.virtual_allocated_range[i].start,
632 				gKernelArgs.virtual_allocated_range[i].size);
633 		}
634 	}
635 #endif
636 }
637 
638 
639 extern "C" void
640 mmu_init(void)
641 {
642 	TRACE("mmu_init\n");
643 
644 	gKernelArgs.arch_args.virtual_end = KERNEL_LOAD_BASE;
645 
646 	gKernelArgs.physical_allocated_range[0].start = sNextPhysicalKernelAddress;
647 	gKernelArgs.physical_allocated_range[0].size = 0;
648 	gKernelArgs.num_physical_allocated_ranges = 1;
649 		// remember the start of the allocated physical pages
650 
651 	init_page_directory();
652 
653 	// Map the page directory into kernel space at 0xffc00000-0xffffffff
654 	// this enables a mmu trick where the 4 MB region that this pgdir entry
655 	// represents now maps the 4MB of potential pagetables that the pgdir
656 	// points to. Thrown away later in VM bringup, but useful for now.
657 	sPageDirectory[1023] = (uint32)sPageDirectory | kDefaultPageFlags;
658 
659 	// also map it on the next vpage
660 	gKernelArgs.arch_args.vir_pgdir = get_next_virtual_page();
661 	map_page(gKernelArgs.arch_args.vir_pgdir, (uint32)sPageDirectory,
662 		kDefaultPageFlags);
663 
664 	// map in a kernel stack
665 	gKernelArgs.cpu_kstack[0].start = (addr_t)mmu_allocate(NULL,
666 		KERNEL_STACK_SIZE + KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE);
667 	gKernelArgs.cpu_kstack[0].size = KERNEL_STACK_SIZE
668 		+ KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE;
669 
670 	TRACE("kernel stack at 0x%" B_PRIx64 " to 0x%" B_PRIx64 "\n",
671 		gKernelArgs.cpu_kstack[0].start, gKernelArgs.cpu_kstack[0].start
672 		+ gKernelArgs.cpu_kstack[0].size);
673 
674 	extended_memory *extMemoryBlock;
675 	uint32 extMemoryCount = get_memory_map(&extMemoryBlock);
676 
677 	// figure out the memory map
678 	if (extMemoryCount > 0) {
679 		gKernelArgs.num_physical_memory_ranges = 0;
680 
681 		// first scan: add all usable ranges
682 		for (uint32 i = 0; i < extMemoryCount; i++) {
683 			// Type 1 is available memory
684 			if (extMemoryBlock[i].type != 1)
685 				continue;
686 
687 			uint64 base = extMemoryBlock[i].base_addr;
688 			uint64 length = extMemoryBlock[i].length;
689 			uint64 end = base + length;
690 
691 			// round everything up to page boundaries, exclusive of pages
692 			// it partially occupies
693 			base = ROUNDUP(base, B_PAGE_SIZE);
694 			end = ROUNDDOWN(end, B_PAGE_SIZE);
695 
696 			// We ignore all memory beyond 4 GB, if phys_addr_t is only
697 			// 32 bit wide.
698 			#if B_HAIKU_PHYSICAL_BITS == 32
699 				if (end > 0x100000000ULL)
700 					end = 0x100000000ULL;
701 			#endif
702 
703 			// Also ignore memory below 1 MB. Apparently some BIOSes fail to
704 			// provide the correct range type for some ranges (cf. #1925).
705 			// Later in the kernel we will reserve the range 0x0 - 0xa0000
706 			// and apparently 0xa0000 - 0x100000 never contain usable
707 			// memory, so we don't lose anything by doing that.
708 			if (base < 0x100000)
709 				base = 0x100000;
710 
711 			gKernelArgs.ignored_physical_memory
712 				+= length - (max_c(end, base) - base);
713 
714 			if (end <= base)
715 				continue;
716 
717 			status_t status = insert_physical_memory_range(base, end - base);
718 			if (status == B_ENTRY_NOT_FOUND) {
719 				panic("mmu_init(): Failed to add physical memory range "
720 					"%#" B_PRIx64 " - %#" B_PRIx64 " : all %d entries are "
721 					"used already!\n", base, end, MAX_PHYSICAL_MEMORY_RANGE);
722 			} else if (status != B_OK) {
723 				panic("mmu_init(): Failed to add physical memory range "
724 					"%#" B_PRIx64 " - %#" B_PRIx64 "\n", base, end);
725 			}
726 		}
727 
728 		uint64 initialPhysicalMemory = total_physical_memory();
729 
730 		// second scan: remove everything reserved that may overlap
731 		for (uint32 i = 0; i < extMemoryCount; i++) {
732 			if (extMemoryBlock[i].type == 1)
733 				continue;
734 
735 			uint64 base = extMemoryBlock[i].base_addr;
736 			uint64 end = ROUNDUP(base + extMemoryBlock[i].length, B_PAGE_SIZE);
737 			base = ROUNDDOWN(base, B_PAGE_SIZE);
738 
739 			status_t status = remove_physical_memory_range(base, end - base);
740 			if (status != B_OK) {
741 				panic("mmu_init(): Failed to remove physical memory range "
742 					"%#" B_PRIx64 " - %#" B_PRIx64 "\n", base, end);
743 			}
744 		}
745 
746 		// sort the ranges
747 		sort_address_ranges(gKernelArgs.physical_memory_range,
748 			gKernelArgs.num_physical_memory_ranges);
749 
750 		// On some machines we get several ranges that contain only a few pages
751 		// (or even only one) each, which causes us to run out of MTRRs later.
752 		// So we remove all ranges smaller than 64 KB, hoping that this will
753 		// leave us only with a few larger contiguous ranges (ideally one).
754 		for (int32 i = gKernelArgs.num_physical_memory_ranges - 1; i >= 0;
755 				i--) {
756 			uint64 size = gKernelArgs.physical_memory_range[i].size;
757 			if (size < 64 * 1024) {
758 				uint64 start = gKernelArgs.physical_memory_range[i].start;
759 				remove_physical_memory_range(start, size);
760 			}
761 		}
762 
763 		gKernelArgs.ignored_physical_memory
764 			+= initialPhysicalMemory - total_physical_memory();
765 	} else {
766 		bios_regs regs;
767 
768 		// We dont have an extended map, assume memory is contiguously mapped
769 		// at 0x0, but leave out the BIOS range ((640k - 1 page) to 1 MB).
770 		gKernelArgs.physical_memory_range[0].start = 0;
771 		gKernelArgs.physical_memory_range[0].size = 0x9f000;
772 		gKernelArgs.physical_memory_range[1].start = 0x100000;
773 
774 		regs.eax = 0xe801; // AX
775 		call_bios(0x15, &regs);
776 		if ((regs.flags & CARRY_FLAG) != 0) {
777 			regs.eax = 0x8800; // AH 88h
778 			call_bios(0x15, &regs);
779 			if ((regs.flags & CARRY_FLAG) != 0) {
780 				// TODO: for now!
781 				dprintf("No memory size - using 64 MB (fix me!)\n");
782 				uint32 memSize = 64 * 1024 * 1024;
783 				gKernelArgs.physical_memory_range[1].size = memSize - 0x100000;
784 			} else {
785 				dprintf("Get Extended Memory Size succeeded.\n");
786 				gKernelArgs.physical_memory_range[1].size = regs.eax * 1024;
787 			}
788 			gKernelArgs.num_physical_memory_ranges = 2;
789 		} else {
790 			dprintf("Get Memory Size for Large Configurations succeeded.\n");
791 			gKernelArgs.physical_memory_range[1].size = regs.ecx * 1024;
792 			gKernelArgs.physical_memory_range[2].start = 0x1000000;
793 			gKernelArgs.physical_memory_range[2].size = regs.edx * 64 * 1024;
794 			gKernelArgs.num_physical_memory_ranges = 3;
795 		}
796 	}
797 
798 	gKernelArgs.arch_args.page_hole = 0xffc00000;
799 }
800 
801 
802 //	#pragma mark -
803 
804 
805 extern "C" status_t
806 platform_allocate_region(void **_address, size_t size, uint8 protection,
807 	bool /*exactAddress*/)
808 {
809 	void *address = mmu_allocate(*_address, size);
810 	if (address == NULL)
811 		return B_NO_MEMORY;
812 
813 	*_address = address;
814 	return B_OK;
815 }
816 
817 
818 extern "C" status_t
819 platform_free_region(void *address, size_t size)
820 {
821 	mmu_free(address, size);
822 	return B_OK;
823 }
824 
825 
826 ssize_t
827 platform_allocate_heap_region(size_t size, void **_base)
828 {
829 	size = ROUNDUP(size, B_PAGE_SIZE);
830 	addr_t base = allocate_physical(size, false);
831 	if (base == 0)
832 		return B_NO_MEMORY;
833 
834 	*_base = (void*)base;
835 	return size;
836 }
837 
838 
839 void
840 platform_free_heap_region(void *_base, size_t size)
841 {
842 	addr_t base = (addr_t)_base;
843 	if (sNextPhysicalAddress == (base + size))
844 		sNextPhysicalAddress -= size;
845 
846 	// Failures don't matter very much as regions should be freed automatically,
847 	// since they're in the identity map and not stored in the kernel's page tables.
848 }
849 
850 
851 extern "C" status_t
852 platform_bootloader_address_to_kernel_address(void *address, addr_t *_result)
853 {
854 	TRACE("%s: called\n", __func__);
855 	// bios_ia32 really doesn't need an address converstion
856 	*_result = (addr_t)address;
857 	return B_OK;
858 }
859 
860 
861 extern "C" status_t
862 platform_kernel_address_to_bootloader_address(addr_t address, void **_result)
863 {
864 	TRACE("%s: called\n", __func__);
865 	// bios_ia32 really doesn't need an address converstion
866 	*_result = (void*)address;
867 	return B_OK;
868 }
869