vmm: add helpers for making more memory
authorRonald G. Minnich <rminnich@gmail.com>
Thu, 9 Mar 2017 18:42:45 +0000 (10:42 -0800)
committerBarret Rhoden <brho@cs.berkeley.edu>
Fri, 7 Apr 2017 16:17:51 +0000 (12:17 -0400)
This is working and lets me add lots of memory.
It is only lightly tested at this point, however.

Change-Id: I892b7261d5220b59bb29f669a60e5f7774acc1cb
Signed-off-by: Ronald G. Minnich <rminnich@gmail.com>
[checkpatch fixes, renamed e820map() and memory(), added a call to
mmap_memory() that was lost, used MAX() instead of ? : ]

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
tests/dune/dune.c
tests/vmm/vmrunkernel.c
user/vmm/include/vmm/vmm.h
user/vmm/memory.c [new file with mode: 0644]

index 9df8157..96ded4b 100644 (file)
 #include <sys/eventfd.h>
 #include <sys/uio.h>
 
-#define MiB 0x100000ull
-#define GiB (1ull << 30)
-#define MinMemory (16*MiB)
-
 static struct virtual_machine local_vm, *vm = &local_vm;
 struct vmm_gpcore_init gpci;
 static void *ram;
index 1abad85..509a6b2 100644 (file)
@@ -124,10 +124,6 @@ volatile int quit = 0;
 /* total hack. If the vm runs away we want to get control again. */
 unsigned int maxresume = (unsigned int) -1;
 
-#define MiB 0x100000ull
-#define GiB (1ull << 30)
-#define MinMemory (16*MiB)
-void *kernel;
 unsigned long long memsize = GiB;
 uintptr_t memstart = MinMemory;
 uintptr_t stack;
@@ -681,6 +677,13 @@ int main(int argc, char **argv)
        memset(vm->low4k, 0xff, PGSIZE);
        vm->low4k[0x40e] = 0;
        vm->low4k[0x40f] = 0;
+       // Why is this here? Because the static initializer is getting
+       // set to 1.  Yes, 1. This might be part of the weirdness
+       // Barrett is reporting with linker sets. So let's leave it
+       // here until we trust our toolchain.
+       if (memsize != GiB)
+               fprintf(stderr, "static initializers are broken\n");
+       memsize = GiB;
 
        while ((c = getopt_long(argc, argv, "dvm:M:S:c:gsf:k:N:n:t:hR:",
                                long_options, &option_index)) != -1) {
@@ -783,19 +786,12 @@ int main(int argc, char **argv)
 
        if ((uintptr_t)(memstart + memsize) >= (uintptr_t)BRK_START) {
                fprintf(stderr,
-                       "memstart 0x%lx memsize 0x%lx -> 0x%lx is too large; overlaps BRK_START at %p\n",
+                       "memstart 0x%llx memsize 0x%llx -> 0x%llx is too large; overlaps BRK_START at %p\n",
                        memstart, memsize, memstart + memsize, BRK_START);
                exit(1);
        }
 
-       kernel = mmap((void *)memstart, memsize,
-                     PROT_READ | PROT_WRITE | PROT_EXEC,
-                     MAP_POPULATE | MAP_ANONYMOUS, -1, 0);
-       if (kernel != (void *)memstart) {
-               fprintf(stderr, "Could not mmap 0x%lx bytes at 0x%lx\n",
-                       memsize, memstart);
-               exit(1);
-       }
+       mmap_memory(memstart, memsize);
 
        entry = load_kernel(argv[0]);
        if (entry == 0) {
@@ -804,7 +800,7 @@ int main(int argc, char **argv)
        }
 
 
-       // The low 1m so we can fill in bullshit like ACPI. */
+       // The low 1m is so we can fill in bullshit like ACPI.
        // And, sorry, due to the STUPID format of the RSDP for now we need the low 1M.
        low1m = mmap((int*)4096, MiB-4096, PROT_READ | PROT_WRITE,
                     MAP_POPULATE | MAP_ANONYMOUS, -1, 0);
@@ -912,37 +908,8 @@ int main(int argc, char **argv)
 
        a = (void *)(((unsigned long)a + 0xfff) & ~0xfff);
 
-       /* Allocate memory for, and zero the bootparams
-        * page before writing to it, or Linux thinks
-        * we're talking crazy.
-        */
        bp = a;
-       memset(bp, 0, 4096);
-
-       /* Put the e820 memory region information in the boot_params */
-       bp->e820_entries = 5;
-       int e820i = 0;
-
-       /* Give it just a tiny bit of memory -- 60k -- at low memory. */
-       bp->e820_map[e820i].addr = 0;
-       bp->e820_map[e820i].size = 4 * 1024;
-       bp->e820_map[e820i++].type = E820_RESERVED;
-
-       bp->e820_map[e820i].addr = 4 * 1024;
-       bp->e820_map[e820i].size = 64 * 1024 - 4 * 1024;
-       bp->e820_map[e820i++].type = E820_RAM;
-
-       bp->e820_map[e820i].addr = 64 * 1024;
-       bp->e820_map[e820i].size = memstart - 64 * 1024;
-       bp->e820_map[e820i++].type = E820_RESERVED;
-
-       bp->e820_map[e820i].addr = memstart;
-       bp->e820_map[e820i].size = memsize;
-       bp->e820_map[e820i++].type = E820_RAM;
-
-       bp->e820_map[e820i].addr = 0xf0000000;
-       bp->e820_map[e820i].size = 0x10000000;
-       bp->e820_map[e820i++].type = E820_RESERVED;
+       a = init_e820map(bp, memstart, memsize);
 
        /* The MMIO address of the console device is really the address of an
         * unbacked EPT page: accesses to this page will cause a page fault that
@@ -950,10 +917,11 @@ int main(int argc, char **argv)
         * known MMIO address, and fulfill the MMIO read or write on the guest's
         * behalf accordingly. We place the virtio space at 512 GB higher than the
         * guest physical memory to avoid a full page table walk. */
-       uint64_t virtio_mmio_base_addr = ROUNDUP((bp->e820_map[e820i - 1].addr +
-                                                 bp->e820_map[e820i - 1].size),
-                                                512 * GiB);
+       uint64_t virtio_mmio_base_addr;
 
+       virtio_mmio_base_addr = ROUNDUP((bp->e820_map[bp->e820_entries - 1].addr +
+                                        bp->e820_map[bp->e820_entries - 1].size),
+                                        512ULL * GiB);
        cons_mmio_dev.addr =
                virtio_mmio_base_addr + PGSIZE * VIRTIO_MMIO_CONSOLE_DEV;
        cons_mmio_dev.vqdev = &cons_vqdev;
@@ -1041,8 +1009,11 @@ int main(int argc, char **argv)
        npml2 = DIV_ROUND_UP(memstart + memsize, PML2_REACH);
        nptp += npml2;
 
-       fprintf(stderr, "Memstart + memsize is %llx; %d pml4 %d pml3 %d pml2\n",
-               memstart + memsize, npml4, npml3, npml2);
+       fprintf(stderr,
+               "Memstart is %llx, memsize is %llx, memstart + memsize is %llx; ",
+               memstart, memsize, memstart + memsize);
+       fprintf(stderr, " %d pml4 %d pml3 %d pml2\n",
+               npml4, npml3, npml2);
 
        /* Place these page tables right after VM memory. We
         * used to use posix_memalign but that puts them
@@ -1066,7 +1037,7 @@ int main(int argc, char **argv)
         * mappings. */
 
        p2m->pte[PML2(0)] = (uint64_t)0 | PTE_KERN_RW | PTE_PS;
-
+       memsize = GiB;
        fprintf(stderr, "Map %p for %zu bytes\n", memstart, memsize);
        for (uintptr_t p4 = memstart; p4 < memstart + memsize;
             p4 += PML4_PTE_REACH, p1++) {
@@ -1102,6 +1073,8 @@ int main(int argc, char **argv)
        vm_tf->tf_rsp = stack;
        vm_tf->tf_rsi = (uint64_t) bp;
        vm->up_gpcs = 1;
+       fprintf(stderr, "Start guest: cr3 %p rip %p stack %p\n",
+               p512, entry, stack);
        start_guest_thread(vm->gths[0]);
 
        uthread_sleep_forever();
index 4f9e4ee..3694ace 100644 (file)
@@ -8,6 +8,23 @@
 
 #include <ros/vmm.h>
 #include <vmm/sched.h>
+#include <vmm/linux_bootparam.h>
+
+// We need to reserve an area of the low 4G for thinks like tables, APIC, and
+// so on. So far, 256 MiB has been more than enough, so ...
+#define MiB 0x100000ull
+#define MinMemory (16*MiB)
+#define GiB (0x40000000ULL)
+#define _4GiB (0x100000000ULL)
+// BIOS conventions from 1978 make it smart to reserve the low 64k
+#define LOW64K 65536
+// The RESERVED area is for all the random junk like devices, ACPI, etc.
+// We just give it the top 1 GiB of the 32-bit address space, which
+// nicely translates to one GiB PTE.
+#define RESERVED 0xC0000000ULL
+#define RESERVEDSIZE (_4GiB - RESERVED)
+// Start the VM at 16 MiB, a standard number for 64 bit kernels on amd64
+#define KERNSTART 0x1000000
 
 #define VM_PAGE_FAULT                  14
 
@@ -91,3 +108,9 @@ static struct virtual_machine *get_my_vm(void)
 {
        return ((struct vmm_thread*)current_uthread)->vm;
 }
+
+/* memory helpers */
+void *init_e820map(struct boot_params *bp,
+                   unsigned long long memstart,
+                   unsigned long long memsize);
+void mmap_memory(unsigned long long memstart, unsigned long long memsize);
diff --git a/user/vmm/memory.c b/user/vmm/memory.c
new file mode 100644 (file)
index 0000000..40e980f
--- /dev/null
@@ -0,0 +1,153 @@
+/* Copyright (c) 2017 Google Inc.
+ * See LICENSE for details.
+ *
+ * Memory, paging, e820, bootparams and other helpers */
+
+#include <stdio.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <parlib/arch/arch.h>
+#include <parlib/ros_debug.h>
+#include <unistd.h>
+#include <errno.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ros/syscall.h>
+#include <sys/mman.h>
+#include <vmm/vmm.h>
+#include <vmm/acpi/acpi.h>
+#include <ros/arch/mmu.h>
+#include <ros/arch/membar.h>
+#include <ros/vmm.h>
+#include <parlib/uthread.h>
+#include <vmm/linux_bootparam.h>
+#include <getopt.h>
+
+#include <vmm/sched.h>
+#include <vmm/net.h>
+#include <sys/eventfd.h>
+#include <sys/uio.h>
+#include <sys/param.h>
+#include <parlib/opts.h>
+
+static char *entrynames[] = {
+       [E820_RAM] "E820_RAM",
+       [E820_RESERVED] "E820_RESERVED",
+       [E820_ACPI] "E820_ACPI",
+       [E820_NVS] "E820_NVS",
+       [E820_UNUSABLE] "E820_UNUSABLE",
+};
+
+static void dumpe820(struct e820entry *e, int nr)
+{
+       for (int i = 0; i < nr; i++) {
+               fprintf(stderr, "%d:%p %p %p %s\n",
+                       i, e[i].addr, e[i].size, e[i].type,
+                       entrynames[e[i].type]);
+       }
+}
+
+// e820map creates an e820 map in the bootparams struct.  If we've
+// gotten here, then memsize and memstart are valid.  It returns
+// pointer to the first page after the map for our bump allocator.  We
+// assume the ranges passed in are validated already.
+void *init_e820map(struct boot_params *bp,
+                   unsigned long long memstart,
+                   unsigned long long memsize)
+{
+       unsigned long long lowmem = 0;
+       // Everything in Linux at this level is PGSIZE.
+       memset(bp, 0, PGSIZE);
+
+       bp->e820_entries = 0;
+
+       // The first page is always reserved.
+       bp->e820_map[bp->e820_entries].addr = 0;
+       bp->e820_map[bp->e820_entries].size = PGSIZE;
+       bp->e820_map[bp->e820_entries++].type = E820_RESERVED;
+
+       /* Give it just a tiny bit of memory -- 60k -- at low memory. */
+       bp->e820_map[bp->e820_entries].addr = PGSIZE;
+       bp->e820_map[bp->e820_entries].size = LOW64K - PGSIZE;
+       bp->e820_map[bp->e820_entries++].type = E820_RAM;
+
+       // All other memory from 64k to memstart is reserved.
+       bp->e820_map[bp->e820_entries].addr = LOW64K;
+       bp->e820_map[bp->e820_entries].size = memstart - LOW64K;
+       bp->e820_map[bp->e820_entries++].type = E820_RESERVED;
+
+       // If memory starts below RESERVED, then add an entry for memstart to
+       // the smaller of RESERVED or memsize.
+       if (memstart < RESERVED) {
+               bp->e820_map[bp->e820_entries].addr = memstart;
+               if (memstart + memsize > RESERVED)
+                       bp->e820_map[bp->e820_entries].size = RESERVED - memstart;
+               else
+                       bp->e820_map[bp->e820_entries].size = memsize;
+               lowmem = bp->e820_map[bp->e820_entries].size;
+               bp->e820_map[bp->e820_entries++].type = E820_RAM;
+       }
+
+       bp->e820_map[bp->e820_entries].addr = RESERVED;
+       bp->e820_map[bp->e820_entries].size = RESERVEDSIZE;
+       bp->e820_map[bp->e820_entries++].type = E820_RESERVED;
+
+       if ((memstart + memsize) > RESERVED) {
+               bp->e820_map[bp->e820_entries].addr = MAX(memstart, _4GiB);
+               bp->e820_map[bp->e820_entries].size = memsize - lowmem;
+               bp->e820_map[bp->e820_entries++].type = E820_RAM;
+       }
+
+       dumpe820(bp->e820_map, bp->e820_entries);
+       return (void *)bp + PGSIZE;
+}
+
+// memory allocates memory for the VM. It's a complicated mess because of the
+// break for APIC and other things. We just go ahead and leave the region from
+// RESERVED to _4GiB for that.  The memory is either split, all low, or all
+// high.
+void mmap_memory(unsigned long long memstart, unsigned long long memsize)
+{
+       void *r1, *r2;
+       unsigned long r1size = memsize;
+
+       // Let's do some minimal validation, so we don't drive
+       // people crazy.
+       if ((memstart >= RESERVED) && (memstart < _4GiB))
+               errx(1, "memstart (%#x) wrong: must be < %#x or >= %#x\n",
+                    memstart, RESERVED, _4GiB);
+       if (memstart < MinMemory)
+               errx(1, "memstart (%#x) wrong: must be > %#x\n",
+                    memstart, MinMemory);
+
+       // Note: this test covers the split case as well as the
+       // 'all above 4G' case.
+       if ((memstart + memsize) > RESERVED) {
+               unsigned long long r2start = MAX(memstart, _4GiB);
+
+               r1size = memstart < RESERVED ? RESERVED - memstart : 0;
+               r2 = mmap((void *)r2start, memsize - r1size,
+                         PROT_READ | PROT_WRITE | PROT_EXEC,
+                         MAP_POPULATE | MAP_ANONYMOUS, -1, 0);
+               if (r2 != (void *)r2start) {
+                       fprintf(stderr,
+                               "High region: Could not mmap 0x%lx bytes at 0x%lx\n",
+                               memsize, r2start);
+                       exit(1);
+               }
+               if (memstart >= _4GiB)
+                       return;
+       }
+
+       r1 = mmap((void *)memstart, r1size,
+                     PROT_READ | PROT_WRITE | PROT_EXEC,
+                     MAP_POPULATE | MAP_ANONYMOUS, -1, 0);
+       if (r1 != (void *)memstart) {
+               fprintf(stderr, "Low region: Could not mmap 0x%lx bytes at 0x%lx\n",
+                       memsize, memstart);
+               exit(1);
+       }
+}