e820map: move over to the new size scheme
[akaros.git] / user / vmm / memory.c
1 /* Copyright (c) 2017 Google Inc.
2  * See LICENSE for details.
3  *
4  * Memory, paging, e820, bootparams and other helpers */
5
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <sys/mman.h>
9 #include <ros/arch/mmu.h>
10 #include <vmm/linux_bootparam.h>
11 #include <vmm/vmm.h>
12 #include <err.h>
13
14 #define ALIGNED(p, a)   (!(((uintptr_t)(p)) & ((a)-1)))
15
16 static char *entrynames[] = {
17         [E820_RAM] "E820_RAM",
18         [E820_RESERVED] "E820_RESERVED",
19         [E820_ACPI] "E820_ACPI",
20         [E820_NVS] "E820_NVS",
21         [E820_UNUSABLE] "E820_UNUSABLE",
22 };
23
24 static void dumpe820(struct e820entry *e, int nr)
25 {
26         for (int i = 0; i < nr; i++) {
27                 fprintf(stderr, "%d:%p %p %p %s\n",
28                         i, e[i].addr, e[i].size, e[i].type,
29                         entrynames[e[i].type]);
30         }
31 }
32
33 // e820map creates an e820 map in the bootparams struct.  If we've
34 // gotten here, then memsize and memstart are valid.  It returns
35 // pointer to the first page after the map for our bump allocator.  We
36 // assume the ranges passed in are validated already.
37 void *init_e820map(struct virtual_machine *vm, struct boot_params *bp)
38 {
39         uintptr_t memstart = vm->minphys;
40         size_t memsize = vm->maxphys - vm->minphys + 1;
41         uintptr_t lowmem = 0;
42
43         // Everything in Linux at this level is PGSIZE.
44         memset(bp, 0, PGSIZE);
45
46         bp->e820_entries = 0;
47
48         // The first page is always reserved.
49         bp->e820_map[bp->e820_entries].addr = 0;
50         bp->e820_map[bp->e820_entries].size = PGSIZE;
51         bp->e820_map[bp->e820_entries++].type = E820_RESERVED;
52
53         /* Give it just a tiny bit of memory -- 60k -- at low memory. */
54         bp->e820_map[bp->e820_entries].addr = PGSIZE;
55         bp->e820_map[bp->e820_entries].size = LOW64K - PGSIZE;
56         bp->e820_map[bp->e820_entries++].type = E820_RAM;
57
58         // All other memory from 64k to memstart is reserved.
59         bp->e820_map[bp->e820_entries].addr = LOW64K;
60         bp->e820_map[bp->e820_entries].size = memstart - LOW64K;
61         bp->e820_map[bp->e820_entries++].type = E820_RESERVED;
62
63         // If memory starts below RESERVED, then add an entry for memstart to
64         // the smaller of RESERVED or memsize.
65         if (memstart < RESERVED) {
66                 bp->e820_map[bp->e820_entries].addr = memstart;
67                 if (memstart + memsize > RESERVED)
68                         bp->e820_map[bp->e820_entries].size = RESERVED - memstart;
69                 else
70                         bp->e820_map[bp->e820_entries].size = memsize;
71                 lowmem = bp->e820_map[bp->e820_entries].size;
72                 bp->e820_map[bp->e820_entries++].type = E820_RAM;
73         }
74
75         bp->e820_map[bp->e820_entries].addr = RESERVED;
76         bp->e820_map[bp->e820_entries].size = RESERVEDSIZE;
77         bp->e820_map[bp->e820_entries++].type = E820_RESERVED;
78
79         if ((memstart + memsize) > RESERVED) {
80                 bp->e820_map[bp->e820_entries].addr = MAX(memstart, _4GiB);
81                 bp->e820_map[bp->e820_entries].size = memsize - lowmem;
82                 bp->e820_map[bp->e820_entries++].type = E820_RAM;
83         }
84
85         dumpe820(bp->e820_map, bp->e820_entries);
86         return (void *)bp + PGSIZE;
87 }
88
89 /* checkmemaligned verifies alignment attributes of your memory space.
90  * It terminates your process with extreme prejudice if they are
91  * incorrect in some way. */
92 void checkmemaligned(uintptr_t memstart, size_t memsize)
93 {
94         if (!ALIGNED(memstart, PML1_REACH))
95                 errx(1, "memstart (%#x) wrong: must be aligned to %#x",
96                      memstart, PML1_REACH);
97         if (!ALIGNED(memsize, PML1_REACH))
98                 errx(1, "memsize (%#x) wrong: must be aligned to %#x",
99                      memsize, PML1_REACH);
100 }
101
102 // memory allocates memory for the VM. It's a complicated mess because of the
103 // break for APIC and other things. We just go ahead and leave the region from
104 // RESERVED to _4GiB for that.  The memory is either split, all low, or all
105 // high. This code is designed for a kernel. Dune-style code does not need it
106 // as it does not have the RESERVED restrictions. Dune-style code can use this,
107 // however, by setting memstart to 4 GiB. This code can be called multiple
108 // times with more ranges. It does not check for overlaps.
109 void mmap_memory(struct virtual_machine *vm, uintptr_t memstart, size_t memsize)
110 {
111         void *r1, *r2;
112         unsigned long r1size = memsize;
113
114         // Let's do some minimal validation, so we don't drive
115         // people crazy.
116         checkmemaligned(memstart, memsize);
117         if ((memstart >= RESERVED) && (memstart < _4GiB))
118                 errx(1, "memstart (%#x) wrong: must be < %#x or >= %#x\n",
119                      memstart, RESERVED, _4GiB);
120         if (memstart < MinMemory)
121                 errx(1, "memstart (%#x) wrong: must be > %#x\n",
122                      memstart, MinMemory);
123
124         // Note: this test covers the split case as well as the
125         // 'all above 4G' case.
126         if ((memstart + memsize) > RESERVED) {
127                 unsigned long long r2start = MAX(memstart, _4GiB);
128
129                 r1size = memstart < RESERVED ? RESERVED - memstart : 0;
130                 r2 = mmap((void *)r2start, memsize - r1size,
131                           PROT_READ | PROT_WRITE | PROT_EXEC,
132                           MAP_POPULATE | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
133                 if (r2 != (void *)r2start) {
134                         fprintf(stderr,
135                                 "High region: Could not mmap 0x%lx bytes at 0x%lx\n",
136                                 memsize, r2start);
137                         exit(1);
138                 }
139                 if (memstart >= _4GiB)
140                         goto done;
141         }
142
143         r1 = mmap((void *)memstart, r1size,
144                       PROT_READ | PROT_WRITE | PROT_EXEC,
145                       MAP_POPULATE | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
146         if (r1 != (void *)memstart) {
147                 fprintf(stderr, "Low region: Could not mmap 0x%lx bytes at 0x%lx\n",
148                         memsize, memstart);
149                 exit(1);
150         }
151
152 done:
153         if ((vm->minphys == 0) || (vm->minphys > memstart))
154                 vm->minphys = memstart;
155
156         if (vm->maxphys < memstart + memsize - 1)
157                 vm->maxphys = memstart + memsize - 1;
158 }