|  | /* Copyright (c) 2013 The Regents of the University of California. | 
|  | * Copyright (c) 2016 Google Inc | 
|  | * Barret Rhoden <brho@cs.berkeley.edu> | 
|  | * See LICENSE for details. */ | 
|  |  | 
|  | #include <page_alloc.h> | 
|  | #include <pmap.h> | 
|  | #include <kmalloc.h> | 
|  | #include <multiboot.h> | 
|  | #include <arena.h> | 
|  |  | 
|  | /* Helper.  Adds free entries to the base arena.  Most entries are page aligned, | 
|  | * though on some machines below EXTPHYSMEM we may have some that aren't. */ | 
|  | static void parse_mboot_region(struct multiboot_mmap_entry *entry, void *data) | 
|  | { | 
|  | physaddr_t boot_freemem_paddr = (physaddr_t)data; | 
|  | physaddr_t start = entry->addr; | 
|  | size_t len = entry->len; | 
|  | extern char end[]; | 
|  |  | 
|  | if (entry->type != MULTIBOOT_MEMORY_AVAILABLE) | 
|  | return; | 
|  | /* Skip anything over max_paddr - might be bad entries(?) */ | 
|  | if (start >= max_paddr) | 
|  | return; | 
|  | if (start + len > max_paddr) | 
|  | len = max_paddr - start; | 
|  | /* For paranoia, skip anything below EXTPHYSMEM.  If we ever change | 
|  | * this, we'll need to deal with smp_boot's trampoline page. */ | 
|  | if ((start < EXTPHYSMEM) && (start + len < EXTPHYSMEM)) | 
|  | return; | 
|  | if ((start < EXTPHYSMEM) && (EXTPHYSMEM <= start + len)) { | 
|  | len = start + len - EXTPHYSMEM; | 
|  | start = EXTPHYSMEM; | 
|  | } | 
|  | /* Skip over any pages already allocated in boot_alloc(). | 
|  | * (boot_freemem_paddr is the next free addr.) */ | 
|  | if ((start < boot_freemem_paddr) | 
|  | && (boot_freemem_paddr <= start + len)) { | 
|  | len = start + len - boot_freemem_paddr; | 
|  | start = boot_freemem_paddr; | 
|  | } | 
|  | /* Skip any part that intersects with the kernel, which is linked and | 
|  | * loaded from EXTPHYSMEM to end in kernel64.ld */ | 
|  | if (regions_collide_unsafe(EXTPHYSMEM, PADDR(end), start, start + len)) | 
|  | { | 
|  | len = start + len - PADDR(end); | 
|  | start = PADDR(end); | 
|  | } | 
|  | /* We need to give the arena PGSIZE-quantum segments. */ | 
|  | if (PGOFF(start)) { | 
|  | len -= PGOFF(start); | 
|  | start = ROUNDUP(start, PGSIZE); | 
|  | } | 
|  | len = ROUNDDOWN(len, PGSIZE); | 
|  | if (!len) | 
|  | return; | 
|  | arena_add(base_arena, KADDR(start), len, MEM_WAIT); | 
|  | } | 
|  |  | 
|  | /* Since we can't parse multiboot mmap entries, we need to just guess at what | 
|  | * pages are free and which ones aren't. | 
|  | * | 
|  | * Despite the lack of info from mbi, I know there is a magic hole in physical | 
|  | * memory that we can't use, from the IOAPIC_PBASE on up [0xfec00000, | 
|  | * 0xffffffff] (I'm being pessimistic).  But, that's not pessimistic enough! | 
|  | * Qemu still doesn't like that.   From using 0xe0000000 instead works for mine. | 
|  | * According to http://wiki.osdev.org/Memory_Map_(x86), some systems could | 
|  | * reserve from [0xc0000000, 0xffffffff].  Anyway, in lieu of real memory | 
|  | * detection, I'm just skipping that entire region. | 
|  | * | 
|  | * We may or may not have more free memory above this magic hole, depending on | 
|  | * both the amount of RAM we have as well as 32 vs 64 bit. | 
|  | * | 
|  | * So we'll go with two free memory regions: | 
|  | * | 
|  | * 	[ 0, ROUNDUP(boot_freemem_paddr, PGSIZE) ) = busy | 
|  | * 	[ ROUNDUP(boot_freemem_paddr, PGSIZE), TOP_OF_1 ) = free | 
|  | * 	[ MAGIC_HOLE, 0x0000000100000000 ) = busy | 
|  | * 	(and maybe this:) | 
|  | * 	[ 0x0000000100000000, max_paddr ) = free | 
|  | * | 
|  | * where TOP_OF_1 is the min of IOAPIC_PBASE and max_paddr. | 
|  | * | 
|  | * As with parsing mbi regions, this will ignore the hairy areas below | 
|  | * EXTPHYSMEM, and mark the entire kernel and anything we've boot alloc'd as | 
|  | * busy. */ | 
|  | static void account_for_pages(physaddr_t boot_freemem_paddr) | 
|  | { | 
|  | physaddr_t top_of_busy = ROUNDUP(boot_freemem_paddr, PGSIZE); | 
|  | physaddr_t top_of_free_1 = MIN(0xc0000000, max_paddr); | 
|  | physaddr_t start_of_free_2; | 
|  |  | 
|  | printk("Warning: poor memory detection (qemu?). May lose 1GB of RAM\n"); | 
|  | arena_add(base_arena, KADDR(top_of_busy), top_of_free_1 - top_of_busy, | 
|  | MEM_WAIT); | 
|  | /* If max_paddr is less than the start of our potential second free mem | 
|  | * region, we can just leave.  We also don't want to poke around the | 
|  | * pages array either (and accidentally run off the end of the array). | 
|  | * | 
|  | * Additionally, 32 bit doesn't acknowledge pmem above the 4GB mark. */ | 
|  | start_of_free_2 = 0x0000000100000000; | 
|  | if (max_paddr < start_of_free_2) | 
|  | return; | 
|  | arena_add(base_arena, KADDR(start_of_free_2), max_paddr - | 
|  | start_of_free_2, MEM_WAIT); | 
|  | } | 
|  |  | 
|  | /* Initialize base arena based on available free memory.  After this, do not use | 
|  | * boot_alloc. */ | 
|  | void base_arena_init(struct multiboot_info *mbi) | 
|  | { | 
|  | /* First, all memory is busy / not free by default. | 
|  | * | 
|  | * To avoid a variety of headaches, any memory below 1MB is considered | 
|  | * busy.  Likewise, everything in the kernel, up to _end is also busy. | 
|  | * And everything we've already boot_alloc'd is busy.  These chunks of | 
|  | * memory are reported as 'free' by multiboot.  All of this memory is | 
|  | * below boot_freemem_paddr.  We don't treat anything below that as | 
|  | * free. | 
|  | * | 
|  | * We'll also abort the mapping for any addresses over max_paddr, since | 
|  | * we'll never use them.  'pages' does not track them either. | 
|  | * | 
|  | * One special note: we actually use the memory at 0x1000 for smp_boot. | 
|  | * It'll never get freed; just FYI. */ | 
|  | physaddr_t boot_freemem_paddr; | 
|  | void *base_pg; | 
|  |  | 
|  | /* Need to do the boot-allocs before our last look at the top of | 
|  | * boot_freemem. */ | 
|  | base_pg = boot_alloc(PGSIZE, PGSHIFT); | 
|  | base_arena = arena_builder(base_pg, "base", PGSIZE, NULL, NULL, NULL, | 
|  | 0); | 
|  | boot_freemem_paddr = PADDR(ROUNDUP(boot_freemem, PGSIZE)); | 
|  | if (mboot_has_mmaps(mbi)) { | 
|  | mboot_foreach_mmap(mbi, parse_mboot_region, | 
|  | (void*)boot_freemem_paddr); | 
|  | } else { | 
|  | /* No multiboot mmap regions (probably run from qemu with | 
|  | * -kernel) */ | 
|  | account_for_pages(boot_freemem_paddr); | 
|  | } | 
|  | } |