akaros/kern/arch/x86/page_alloc.c
<<
>>
Prefs
   1/* Copyright (c) 2013 The Regents of the University of California.
   2 * Copyright (c) 2016 Google Inc
   3 * Barret Rhoden <brho@cs.berkeley.edu>
   4 * See LICENSE for details. */
   5
   6#include <page_alloc.h>
   7#include <pmap.h>
   8#include <kmalloc.h>
   9#include <multiboot.h>
  10#include <arena.h>
  11
  12/* Helper.  Adds free entries to the base arena.  Most entries are page aligned,
  13 * though on some machines below EXTPHYSMEM we may have some that aren't. */
  14static void parse_mboot_region(struct multiboot_mmap_entry *entry, void *data)
  15{
  16        physaddr_t boot_freemem_paddr = (physaddr_t)data;
  17        physaddr_t start = entry->addr;
  18        size_t len = entry->len;
  19        extern char end[];
  20
  21        if (entry->type != MULTIBOOT_MEMORY_AVAILABLE)
  22                return;
  23        /* Skip anything over max_paddr - might be bad entries(?) */
  24        if (start >= max_paddr)
  25                return;
  26        if (start + len > max_paddr)
  27                len = max_paddr - start;
  28        /* For paranoia, skip anything below EXTPHYSMEM.  If we ever change
  29         * this, we'll need to deal with smp_boot's trampoline page. */
  30        if ((start < EXTPHYSMEM) && (start + len < EXTPHYSMEM))
  31                return;
  32        if ((start < EXTPHYSMEM) && (EXTPHYSMEM <= start + len)) {
  33                len = start + len - EXTPHYSMEM;
  34                start = EXTPHYSMEM;
  35        }
  36        /* Skip over any pages already allocated in boot_alloc().
  37         * (boot_freemem_paddr is the next free addr.) */
  38        if ((start < boot_freemem_paddr)
  39            && (boot_freemem_paddr <= start + len)) {
  40                len = start + len - boot_freemem_paddr;
  41                start = boot_freemem_paddr;
  42        }
  43        /* Skip any part that intersects with the kernel, which is linked and
  44         * loaded from EXTPHYSMEM to end in kernel64.ld */
  45        if (regions_collide_unsafe(EXTPHYSMEM, PADDR(end), start, start + len))
  46        {
  47                len = start + len - PADDR(end);
  48                start = PADDR(end);
  49        }
  50        /* We need to give the arena PGSIZE-quantum segments. */
  51        if (PGOFF(start)) {
  52                len -= PGOFF(start);
  53                start = ROUNDUP(start, PGSIZE);
  54        }
  55        len = ROUNDDOWN(len, PGSIZE);
  56        if (!len)
  57                return;
  58        arena_add(base_arena, KADDR(start), len, MEM_WAIT);
  59}
  60
  61/* Since we can't parse multiboot mmap entries, we need to just guess at what
  62 * pages are free and which ones aren't.
  63 *
  64 * Despite the lack of info from mbi, I know there is a magic hole in physical
  65 * memory that we can't use, from the IOAPIC_PBASE on up [0xfec00000,
  66 * 0xffffffff] (I'm being pessimistic).  But, that's not pessimistic enough!
  67 * Qemu still doesn't like that.   From using 0xe0000000 instead works for mine.
  68 * According to http://wiki.osdev.org/Memory_Map_(x86), some systems could
  69 * reserve from [0xc0000000, 0xffffffff].  Anyway, in lieu of real memory
  70 * detection, I'm just skipping that entire region.
  71 *
  72 * We may or may not have more free memory above this magic hole, depending on
  73 * both the amount of RAM we have as well as 32 vs 64 bit.
  74 *
  75 * So we'll go with two free memory regions:
  76 *
  77 *      [ 0, ROUNDUP(boot_freemem_paddr, PGSIZE) ) = busy
  78 *      [ ROUNDUP(boot_freemem_paddr, PGSIZE), TOP_OF_1 ) = free
  79 *      [ MAGIC_HOLE, 0x0000000100000000 ) = busy
  80 *      (and maybe this:)
  81 *      [ 0x0000000100000000, max_paddr ) = free
  82 *
  83 * where TOP_OF_1 is the min of IOAPIC_PBASE and max_paddr.
  84 *
  85 * As with parsing mbi regions, this will ignore the hairy areas below
  86 * EXTPHYSMEM, and mark the entire kernel and anything we've boot alloc'd as
  87 * busy. */
  88static void account_for_pages(physaddr_t boot_freemem_paddr)
  89{
  90        physaddr_t top_of_busy = ROUNDUP(boot_freemem_paddr, PGSIZE);
  91        physaddr_t top_of_free_1 = MIN(0xc0000000, max_paddr);
  92        physaddr_t start_of_free_2;
  93
  94        printk("Warning: poor memory detection (qemu?). May lose 1GB of RAM\n");
  95        arena_add(base_arena, KADDR(top_of_busy), top_of_free_1 - top_of_busy,
  96                  MEM_WAIT);
  97        /* If max_paddr is less than the start of our potential second free mem
  98         * region, we can just leave.  We also don't want to poke around the
  99         * pages array either (and accidentally run off the end of the array).
 100         *
 101         * Additionally, 32 bit doesn't acknowledge pmem above the 4GB mark. */
 102        start_of_free_2 = 0x0000000100000000;
 103        if (max_paddr < start_of_free_2)
 104                return;
 105        arena_add(base_arena, KADDR(start_of_free_2), max_paddr -
 106                  start_of_free_2, MEM_WAIT);
 107}
 108
 109/* Initialize base arena based on available free memory.  After this, do not use
 110 * boot_alloc. */
 111void base_arena_init(struct multiboot_info *mbi)
 112{
 113        /* First, all memory is busy / not free by default.
 114         *
 115         * To avoid a variety of headaches, any memory below 1MB is considered
 116         * busy.  Likewise, everything in the kernel, up to _end is also busy.
 117         * And everything we've already boot_alloc'd is busy.  These chunks of
 118         * memory are reported as 'free' by multiboot.  All of this memory is
 119         * below boot_freemem_paddr.  We don't treat anything below that as
 120         * free.
 121         *
 122         * We'll also abort the mapping for any addresses over max_paddr, since
 123         * we'll never use them.  'pages' does not track them either.
 124         *
 125         * One special note: we actually use the memory at 0x1000 for smp_boot.
 126         * It'll never get freed; just FYI. */
 127        physaddr_t boot_freemem_paddr;
 128        void *base_pg;
 129
 130        /* Need to do the boot-allocs before our last look at the top of
 131         * boot_freemem. */
 132        base_pg = boot_alloc(PGSIZE, PGSHIFT);
 133        base_arena = arena_builder(base_pg, "base", PGSIZE, NULL, NULL, NULL,
 134                                   0);
 135        boot_freemem_paddr = PADDR(ROUNDUP(boot_freemem, PGSIZE));
 136        if (mboot_has_mmaps(mbi)) {
 137                mboot_foreach_mmap(mbi, parse_mboot_region,
 138                                   (void*)boot_freemem_paddr);
 139        } else {
 140                /* No multiboot mmap regions (probably run from qemu with
 141                 * -kernel) */
 142                account_for_pages(boot_freemem_paddr);
 143        }
 144}
 145