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, ®s);
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, ®s);
779 if ((regs.flags & CARRY_FLAG) != 0) {
780 regs.eax = 0x8800; // AH 88h
781 call_bios(0x15, ®s);
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