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