xref: /haiku/src/system/boot/platform/bios_ia32/mmu.cpp (revision 72d9beca78de37a6f8a8ab333de9ea15d423ccd0)
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
allocate_virtual(size_t size)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
allocate_physical(size_t size,bool inIdentityMap=true)115 allocate_physical(size_t size, bool inIdentityMap = true)
116 {
117 	if ((size % B_PAGE_SIZE) != 0)
118 		panic("request for non-page-aligned physical memory!");
119 
120 	addr_t* nextAddress = &sNextPhysicalKernelAddress;
121 	if (inIdentityMap) {
122 		nextAddress = &sNextPhysicalAddress;
123 		if ((*nextAddress + size) > kIdentityMapEnd) {
124 			panic("request too large for identity-map physical memory!");
125 			return 0;
126 		}
127 	}
128 
129 	uint64 base = *nextAddress;
130 	if (!get_free_address_range(gKernelArgs.physical_allocated_range,
131 			gKernelArgs.num_physical_allocated_ranges, base, size, &base)) {
132 		panic("Out of physical memory!");
133 		return 0;
134 	}
135 
136 	insert_physical_allocated_range(base, size);
137 	*nextAddress = base + size;
138 		// TODO: Can overflow theoretically.
139 
140 	return base;
141 }
142 
143 
144 static addr_t
get_next_virtual_page()145 get_next_virtual_page()
146 {
147 	return allocate_virtual(B_PAGE_SIZE);
148 }
149 
150 
151 static addr_t
get_next_physical_page()152 get_next_physical_page()
153 {
154 	return allocate_physical(B_PAGE_SIZE, false);
155 }
156 
157 
158 static uint32 *
get_next_page_table()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);
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*
add_page_table(addr_t base)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
unmap_page(addr_t virtualAddress)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
map_page(addr_t virtualAddress,addr_t physicalAddress,uint32 flags)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 *
e820_memory_type(uint32 type)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
get_memory_map(extended_memory ** _extendedMemory)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
init_page_directory(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);
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
mmu_map_physical_memory(addr_t physicalAddress,size_t size,uint32 flags)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 *
mmu_allocate(void * virtualAddress,size_t size)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 = HOWMANY(size, 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 *
mmu_allocate_page(addr_t * _physicalAddress)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
mmu_allocate_physical(addr_t base,size_t size)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
mmu_free(void * virtualAddress,size_t size)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 += pageOffset;
501 	size = ROUNDUP(size, B_PAGE_SIZE);
502 
503 	// is the address within the valid range?
504 	if (address < KERNEL_LOAD_BASE || address + size > sNextVirtualAddress) {
505 		panic("mmu_free: asked to unmap out of range region (%p, size %lx)\n",
506 			(void *)address, size);
507 	}
508 
509 	// unmap all pages within the range
510 	for (size_t i = 0; i < size; i += B_PAGE_SIZE) {
511 		unmap_page(address);
512 		address += B_PAGE_SIZE;
513 	}
514 
515 	if (address == sNextVirtualAddress) {
516 		// we can actually reuse the virtual address space
517 		sNextVirtualAddress -= size;
518 	}
519 }
520 
521 
522 size_t
mmu_get_virtual_usage()523 mmu_get_virtual_usage()
524 {
525 	return sNextVirtualAddress - KERNEL_LOAD_BASE;
526 }
527 
528 
529 bool
mmu_get_virtual_mapping(addr_t virtualAddress,addr_t * _physicalAddress)530 mmu_get_virtual_mapping(addr_t virtualAddress, addr_t *_physicalAddress)
531 {
532 	if (virtualAddress < KERNEL_LOAD_BASE) {
533 		panic("mmu_get_virtual_mapping: asked to lookup invalid page %p!\n",
534 			(void *)virtualAddress);
535 	}
536 
537 	uint32 dirEntry = sPageDirectory[virtualAddress / (B_PAGE_SIZE * 1024)];
538 	if ((dirEntry & (1 << 0)) == 0)
539 		return false;
540 
541 	uint32 *pageTable = (uint32 *)(dirEntry & 0xfffff000);
542 	uint32 tableEntry = pageTable[(virtualAddress % (B_PAGE_SIZE * 1024))
543 		/ B_PAGE_SIZE];
544 	if ((tableEntry & (1 << 0)) == 0)
545 		return false;
546 
547 	*_physicalAddress = tableEntry & 0xfffff000;
548 	return true;
549 }
550 
551 
552 /*!	Sets up the final and kernel accessible GDT and IDT tables.
553 	BIOS calls won't work any longer after this function has
554 	been called.
555 */
556 extern "C" void
mmu_init_for_kernel(void)557 mmu_init_for_kernel(void)
558 {
559 	TRACE("mmu_init_for_kernel\n");
560 
561 	STATIC_ASSERT(BOOT_GDT_SEGMENT_COUNT > KERNEL_CODE_SEGMENT
562 		&& BOOT_GDT_SEGMENT_COUNT > KERNEL_DATA_SEGMENT
563 		&& BOOT_GDT_SEGMENT_COUNT > USER_CODE_SEGMENT
564 		&& BOOT_GDT_SEGMENT_COUNT > USER_DATA_SEGMENT);
565 
566 	// set up a new gdt
567 
568 	// put standard segment descriptors in GDT
569 	clear_segment_descriptor(&gBootGDT[0]);
570 
571 	// seg 0x08 - kernel 4GB code
572 	set_segment_descriptor(&gBootGDT[KERNEL_CODE_SEGMENT], 0, 0xffffffff,
573 		DT_CODE_READABLE, DPL_KERNEL);
574 
575 	// seg 0x10 - kernel 4GB data
576 	set_segment_descriptor(&gBootGDT[KERNEL_DATA_SEGMENT], 0, 0xffffffff,
577 		DT_DATA_WRITEABLE, DPL_KERNEL);
578 
579 	// seg 0x1b - ring 3 user 4GB code
580 	set_segment_descriptor(&gBootGDT[USER_CODE_SEGMENT], 0, 0xffffffff,
581 		DT_CODE_READABLE, DPL_USER);
582 
583 	// seg 0x23 - ring 3 user 4GB data
584 	set_segment_descriptor(&gBootGDT[USER_DATA_SEGMENT], 0, 0xffffffff,
585 		DT_DATA_WRITEABLE, DPL_USER);
586 
587 	// load the GDT
588 	struct gdt_idt_descr gdtDescriptor;
589 	gdtDescriptor.limit = sizeof(gBootGDT);
590 	gdtDescriptor.base = gBootGDT;
591 
592 	asm("lgdt %0" : : "m" (gdtDescriptor));
593 
594 	TRACE("gdt at virtual address %p\n", gBootGDT);
595 
596 	// Save the memory we've virtually allocated (for the kernel and other
597 	// stuff)
598 	gKernelArgs.virtual_allocated_range[0].start = KERNEL_LOAD_BASE;
599 	gKernelArgs.virtual_allocated_range[0].size
600 		= sNextVirtualAddress - KERNEL_LOAD_BASE;
601 	gKernelArgs.num_virtual_allocated_ranges = 1;
602 
603 	// sort the address ranges
604 	sort_address_ranges(gKernelArgs.physical_memory_range,
605 		gKernelArgs.num_physical_memory_ranges);
606 	sort_address_ranges(gKernelArgs.physical_allocated_range,
607 		gKernelArgs.num_physical_allocated_ranges);
608 	sort_address_ranges(gKernelArgs.virtual_allocated_range,
609 		gKernelArgs.num_virtual_allocated_ranges);
610 
611 #ifdef TRACE_MEMORY_MAP
612 	{
613 		uint32 i;
614 
615 		dprintf("phys memory ranges:\n");
616 		for (i = 0; i < gKernelArgs.num_physical_memory_ranges; i++) {
617 			dprintf("    base %#018" B_PRIx64 ", length %#018" B_PRIx64 "\n",
618 				gKernelArgs.physical_memory_range[i].start,
619 				gKernelArgs.physical_memory_range[i].size);
620 		}
621 
622 		dprintf("allocated phys memory ranges:\n");
623 		for (i = 0; i < gKernelArgs.num_physical_allocated_ranges; i++) {
624 			dprintf("    base %#018" B_PRIx64 ", length %#018" B_PRIx64 "\n",
625 				gKernelArgs.physical_allocated_range[i].start,
626 				gKernelArgs.physical_allocated_range[i].size);
627 		}
628 
629 		dprintf("allocated virt memory ranges:\n");
630 		for (i = 0; i < gKernelArgs.num_virtual_allocated_ranges; i++) {
631 			dprintf("    base %#018" B_PRIx64 ", length %#018" B_PRIx64 "\n",
632 				gKernelArgs.virtual_allocated_range[i].start,
633 				gKernelArgs.virtual_allocated_range[i].size);
634 		}
635 	}
636 #endif
637 }
638 
639 
640 extern "C" void
mmu_init(void)641 mmu_init(void)
642 {
643 	TRACE("mmu_init\n");
644 
645 	gKernelArgs.arch_args.virtual_end = KERNEL_LOAD_BASE;
646 
647 	gKernelArgs.physical_allocated_range[0].start = sNextPhysicalAddress;
648 	gKernelArgs.physical_allocated_range[0].size = 0;
649 	gKernelArgs.physical_allocated_range[1].start = sNextPhysicalKernelAddress;
650 	gKernelArgs.physical_allocated_range[1].size = 0;
651 	gKernelArgs.num_physical_allocated_ranges = 2;
652 		// remember the start of the allocated physical pages
653 
654 	init_page_directory();
655 
656 	// Map the page directory into kernel space at 0xffc00000-0xffffffff
657 	// this enables a mmu trick where the 4 MB region that this pgdir entry
658 	// represents now maps the 4MB of potential pagetables that the pgdir
659 	// points to. Thrown away later in VM bringup, but useful for now.
660 	sPageDirectory[1023] = (uint32)sPageDirectory | kDefaultPageFlags;
661 
662 	// also map it on the next vpage
663 	gKernelArgs.arch_args.vir_pgdir = get_next_virtual_page();
664 	map_page(gKernelArgs.arch_args.vir_pgdir, (uint32)sPageDirectory,
665 		kDefaultPageFlags);
666 
667 	// map in a kernel stack
668 	gKernelArgs.cpu_kstack[0].start = (addr_t)mmu_allocate(NULL,
669 		KERNEL_STACK_SIZE + KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE);
670 	gKernelArgs.cpu_kstack[0].size = KERNEL_STACK_SIZE
671 		+ KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE;
672 
673 	TRACE("kernel stack at 0x%" B_PRIx64 " to 0x%" B_PRIx64 "\n",
674 		gKernelArgs.cpu_kstack[0].start, gKernelArgs.cpu_kstack[0].start
675 		+ gKernelArgs.cpu_kstack[0].size);
676 
677 	extended_memory *extMemoryBlock;
678 	uint32 extMemoryCount = get_memory_map(&extMemoryBlock);
679 
680 	// figure out the memory map
681 	if (extMemoryCount > 0) {
682 		gKernelArgs.num_physical_memory_ranges = 0;
683 
684 		// first scan: add all usable ranges
685 		for (uint32 i = 0; i < extMemoryCount; i++) {
686 			// Type 1 is available memory
687 			if (extMemoryBlock[i].type != 1)
688 				continue;
689 
690 			uint64 base = extMemoryBlock[i].base_addr;
691 			uint64 length = extMemoryBlock[i].length;
692 			uint64 end = base + length;
693 
694 			// round everything up to page boundaries, exclusive of pages
695 			// it partially occupies
696 			base = ROUNDUP(base, B_PAGE_SIZE);
697 			end = ROUNDDOWN(end, B_PAGE_SIZE);
698 
699 			// We ignore all memory beyond 4 GB, if phys_addr_t is only
700 			// 32 bit wide.
701 			#if B_HAIKU_PHYSICAL_BITS == 32
702 				if (end > 0x100000000ULL)
703 					end = 0x100000000ULL;
704 			#endif
705 
706 			// Also ignore memory below 1 MB. Apparently some BIOSes fail to
707 			// provide the correct range type for some ranges (cf. #1925).
708 			// Later in the kernel we will reserve the range 0x0 - 0xa0000
709 			// and apparently 0xa0000 - 0x100000 never contain usable
710 			// memory, so we don't lose anything by doing that.
711 			if (base < 0x100000)
712 				base = 0x100000;
713 
714 			gKernelArgs.ignored_physical_memory
715 				+= length - (max_c(end, base) - base);
716 
717 			if (end <= base)
718 				continue;
719 
720 			status_t status = insert_physical_memory_range(base, end - base);
721 			if (status == B_ENTRY_NOT_FOUND) {
722 				panic("mmu_init(): Failed to add physical memory range "
723 					"%#" B_PRIx64 " - %#" B_PRIx64 " : all %d entries are "
724 					"used already!\n", base, end, MAX_PHYSICAL_MEMORY_RANGE);
725 			} else if (status != B_OK) {
726 				panic("mmu_init(): Failed to add physical memory range "
727 					"%#" B_PRIx64 " - %#" B_PRIx64 "\n", base, end);
728 			}
729 		}
730 
731 		uint64 initialPhysicalMemory = total_physical_memory();
732 
733 		// second scan: remove everything reserved that may overlap
734 		for (uint32 i = 0; i < extMemoryCount; i++) {
735 			if (extMemoryBlock[i].type == 1)
736 				continue;
737 
738 			uint64 base = extMemoryBlock[i].base_addr;
739 			uint64 end = ROUNDUP(base + extMemoryBlock[i].length, B_PAGE_SIZE);
740 			base = ROUNDDOWN(base, B_PAGE_SIZE);
741 
742 			status_t status = remove_physical_memory_range(base, end - base);
743 			if (status != B_OK) {
744 				panic("mmu_init(): Failed to remove physical memory range "
745 					"%#" B_PRIx64 " - %#" B_PRIx64 "\n", base, end);
746 			}
747 		}
748 
749 		// sort the ranges
750 		sort_address_ranges(gKernelArgs.physical_memory_range,
751 			gKernelArgs.num_physical_memory_ranges);
752 
753 		// On some machines we get several ranges that contain only a few pages
754 		// (or even only one) each, which causes us to run out of MTRRs later.
755 		// So we remove all ranges smaller than 64 KB, hoping that this will
756 		// leave us only with a few larger contiguous ranges (ideally one).
757 		for (int32 i = gKernelArgs.num_physical_memory_ranges - 1; i >= 0;
758 				i--) {
759 			uint64 size = gKernelArgs.physical_memory_range[i].size;
760 			if (size < 64 * 1024) {
761 				uint64 start = gKernelArgs.physical_memory_range[i].start;
762 				remove_physical_memory_range(start, size);
763 			}
764 		}
765 
766 		gKernelArgs.ignored_physical_memory
767 			+= initialPhysicalMemory - total_physical_memory();
768 	} else {
769 		bios_regs regs;
770 
771 		// We dont have an extended map, assume memory is contiguously mapped
772 		// at 0x0, but leave out the BIOS range ((640k - 1 page) to 1 MB).
773 		gKernelArgs.physical_memory_range[0].start = 0;
774 		gKernelArgs.physical_memory_range[0].size = 0x9f000;
775 		gKernelArgs.physical_memory_range[1].start = 0x100000;
776 
777 		regs.eax = 0xe801; // AX
778 		call_bios(0x15, &regs);
779 		if ((regs.flags & CARRY_FLAG) != 0) {
780 			regs.eax = 0x8800; // AH 88h
781 			call_bios(0x15, &regs);
782 			if ((regs.flags & CARRY_FLAG) != 0) {
783 				// TODO: for now!
784 				dprintf("No memory size - using 64 MB (fix me!)\n");
785 				uint32 memSize = 64 * 1024 * 1024;
786 				gKernelArgs.physical_memory_range[1].size = memSize - 0x100000;
787 			} else {
788 				dprintf("Get Extended Memory Size succeeded.\n");
789 				gKernelArgs.physical_memory_range[1].size = regs.eax * 1024;
790 			}
791 			gKernelArgs.num_physical_memory_ranges = 2;
792 		} else {
793 			dprintf("Get Memory Size for Large Configurations succeeded.\n");
794 			gKernelArgs.physical_memory_range[1].size = regs.ecx * 1024;
795 			gKernelArgs.physical_memory_range[2].start = 0x1000000;
796 			gKernelArgs.physical_memory_range[2].size = regs.edx * 64 * 1024;
797 			gKernelArgs.num_physical_memory_ranges = 3;
798 		}
799 	}
800 
801 	gKernelArgs.arch_args.page_hole = 0xffc00000;
802 }
803 
804 
805 //	#pragma mark -
806 
807 
808 extern "C" status_t
platform_allocate_region(void ** _address,size_t size,uint8 protection,bool)809 platform_allocate_region(void **_address, size_t size, uint8 protection,
810 	bool /*exactAddress*/)
811 {
812 	void *address = mmu_allocate(*_address, size);
813 	if (address == NULL)
814 		return B_NO_MEMORY;
815 
816 	*_address = address;
817 	return B_OK;
818 }
819 
820 
821 extern "C" status_t
platform_free_region(void * address,size_t size)822 platform_free_region(void *address, size_t size)
823 {
824 	mmu_free(address, size);
825 	return B_OK;
826 }
827 
828 
829 ssize_t
platform_allocate_heap_region(size_t size,void ** _base)830 platform_allocate_heap_region(size_t size, void **_base)
831 {
832 	size = ROUNDUP(size, B_PAGE_SIZE);
833 	addr_t base = allocate_physical(size);
834 	if (base == 0)
835 		return B_NO_MEMORY;
836 
837 	*_base = (void*)base;
838 	return size;
839 }
840 
841 
842 void
platform_free_heap_region(void * _base,size_t size)843 platform_free_heap_region(void *_base, size_t size)
844 {
845 	addr_t base = (addr_t)_base;
846 	remove_physical_allocated_range(base, size);
847 	if (sNextPhysicalAddress == (base + size))
848 		sNextPhysicalAddress -= size;
849 
850 	// Failures don't matter very much as regions should be freed automatically,
851 	// since they're in the identity map and not stored in the kernel's page tables.
852 }
853 
854 
855 extern "C" status_t
platform_bootloader_address_to_kernel_address(void * address,addr_t * _result)856 platform_bootloader_address_to_kernel_address(void *address, addr_t *_result)
857 {
858 	TRACE("%s: called\n", __func__);
859 	// bios_ia32 really doesn't need an address converstion
860 	*_result = (addr_t)address;
861 	return B_OK;
862 }
863 
864 
865 extern "C" status_t
platform_kernel_address_to_bootloader_address(addr_t address,void ** _result)866 platform_kernel_address_to_bootloader_address(addr_t address, void **_result)
867 {
868 	TRACE("%s: called\n", __func__);
869 	// bios_ia32 really doesn't need an address converstion
870 	*_result = (void*)address;
871 	return B_OK;
872 }
873