x86 can handle missing multiboot mmaps
authorBarret Rhoden <brho@cs.berkeley.edu>
Thu, 8 Aug 2013 20:02:00 +0000 (13:02 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Thu, 8 Aug 2013 20:02:00 +0000 (13:02 -0700)
If you run qemu with -kernel akaros-kernel, instead of with a hard disk image
(like mnt/hdd.img), qemu won't fake all of the multiboot memory maps.  x86 was
using this info to figure out what memory was free.  We now handle the case
where we only get a maximum memory amount from multiboot.

Incidentally, the memory reported by multiboot from the BIOS usually is
insufficient if you have more than 4GB of RAM, at least in my experience.  This
is why I was parsing the mmap regions, where multiboot details memory about
4GB.  Qemu with -kernel, which doesn't tell us the mmaps, does tell us all of
physical memory via the BIOS methods (mboot_detect_memory()).

With this change, you can run very large kernel images in qemu.  Qemu takes
forever to load large images (> 100MB) when it tries to load from a disk image.
You can load a 300MB+ kernel directly via -kernel in about a minute.

kern/arch/x86/page_alloc.c
kern/include/multiboot.h
kern/src/multiboot.c
kern/src/pmap.c

index eb26a8e..4a3d7e8 100644 (file)
@@ -148,6 +148,23 @@ static void check_mboot_region(struct multiboot_mmap_entry *entry, void *data)
        }
 }
 
+/* Since we can't parse multiboot mmap entries, we need to just guess at what
+ * pages are free and which ones aren't.  We'll just go with:
+ *
+ *             [ 0, ROUNDUP(boot_freemem_paddr, PGSIZE) ) = busy
+ *             [ ROUNDUP(boot_freemem_paddr, PGSIZE), max_paddr ) = free
+ *
+ * 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);
+       for (physaddr_t i = 0; i < top_of_busy; i += PGSIZE)
+               page_setref(pa64_to_page(i), 1);
+       for (physaddr_t i = top_of_busy; i < max_paddr; i += PGSIZE)
+               track_free_page(pa64_to_page(i));
+}
+
 /* Initialize the memory free lists.  After this, do not use boot_alloc. */
 void page_alloc_init(struct multiboot_info *mbi)
 {
@@ -171,9 +188,14 @@ void page_alloc_init(struct multiboot_info *mbi)
         * sections are jumbo-aligned. */
        physaddr_t boot_freemem_paddr = PADDR(PTRROUNDUP(boot_freemem, PGSIZE));
 
-       mboot_foreach_mmap(mbi, parse_mboot_region, (void*)boot_freemem_paddr);
+       if (mboot_has_mmaps(mbi)) {
+               mboot_foreach_mmap(mbi, parse_mboot_region, (void*)boot_freemem_paddr);
+               /* Test the page alloc - if this gets slow, we can CONFIG it */
+               mboot_foreach_mmap(mbi, check_mboot_region, (void*)boot_freemem_paddr);
+       } else {
+               /* No multiboot mmap regions (probably run from qemu with -kernel) */
+               account_for_pages(boot_freemem_paddr);
+       }
        printk("Number of free pages: %lu\n", nr_free_pages);
-       /* Test the page alloc - if this gets slow, we can CONFIG it */
-       mboot_foreach_mmap(mbi, check_mboot_region, (void*)boot_freemem_paddr);
        printk("Page alloc init successful\n");
 }
index dcd2bbe..2890959 100644 (file)
@@ -226,6 +226,7 @@ typedef struct multiboot_mod_list multiboot_module_t;
 
 typedef void (*mboot_foreach_t)(struct multiboot_mmap_entry*, void*);
 
+bool mboot_has_mmaps(struct multiboot_info *mbi);
 void mboot_detect_memory(struct multiboot_info *mbi);
 void mboot_print_mmap(struct multiboot_info *mbi);
 void mboot_foreach_mmap(struct multiboot_info *mbi, mboot_foreach_t func,
index 2ebf8f2..8092dc3 100644 (file)
 #include <arch/apic.h>
 #endif
 
+/* Misc dead code to read from mboot.  We'll need to do this to run a legit
+ * initrd from grub (module /initramfs.cpio, or whatever). */
+static void mboot_parsing(struct multiboot_info *mbi)
+{
+       if (mbi->flags & MULTIBOOT_INFO_BOOTDEV)
+               printk("MBI: boot_device = 0x%08x\n", mbi->boot_device);
+       if (mbi->flags & MULTIBOOT_INFO_CMDLINE)
+               printk("MBI: command line: %s\n",
+                      (char*)((physaddr_t)mbi->cmdline + KERNBASE));
+       if (mbi->flags & MULTIBOOT_INFO_MODS) {
+               printk("MBI: nr mods, %d: mods addr %p\n", mbi->mods_count,
+                      mbi->mods_addr);
+       }
+}
+
+bool mboot_has_mmaps(struct multiboot_info *mbi)
+{
+       return mbi->flags & MULTIBOOT_INFO_ELF_SHDR;
+}
+
 /* This only notices bios detectable memory - there's a lot more in the higher
  * paddrs. */
 void mboot_detect_memory(struct multiboot_info *mbi)
@@ -53,8 +73,8 @@ void mboot_foreach_mmap(struct multiboot_info *mbi, mboot_foreach_t func,
                         void *data)
 {
        struct multiboot_mmap_entry *mmap_b, *mmap_e, *mmap_i;
-       if (!(mbi->flags & MULTIBOOT_INFO_ELF_SHDR)) {
-               printk("No memory mapping info from multiboot\n");
+       if (!mboot_has_mmaps(mbi)) {
+               printd("No memory mapping info from multiboot\n");
                return;
        }
        mmap_b = (struct multiboot_mmap_entry*)((size_t)mbi->mmap_addr + KERNBASE);
index d4ba124..4645704 100644 (file)
@@ -131,7 +131,7 @@ static void boot_alloc_init(void)
                boot_freelimit = boot_zone_end;
        } else {
                boot_freemem = end_kva;
-               boot_freelimit = max_paddr;
+               boot_freelimit = max_paddr + KERNBASE;
        }
        printd("boot_zone: %p, paddr base: 0x%llx, paddr len: 0x%llx\n", boot_zone,
               boot_zone ? boot_zone->addr : 0,