kconfig: use pkg-config for ncurses detection
[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 <parlib/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 #include <vmm/util.h>
14 #include <parlib/ros_debug.h>
15 #include <fcntl.h>
16
17
18 static char *entrynames[] = {
19         [E820_RAM] "E820_RAM",
20         [E820_RESERVED] "E820_RESERVED",
21         [E820_ACPI] "E820_ACPI",
22         [E820_NVS] "E820_NVS",
23         [E820_UNUSABLE] "E820_UNUSABLE",
24 };
25
26 static void dumpe820(struct e820entry *e, int nr)
27 {
28         for (int i = 0; i < nr; i++) {
29                 fprintf(stderr, "%d:%p %p %p %s\n",
30                         i, e[i].addr, e[i].size, e[i].type,
31                         entrynames[e[i].type]);
32         }
33 }
34
35 // e820map creates an e820 map in the bootparams struct.  If we've
36 // gotten here, then memsize and memstart are valid.  It returns
37 // pointer to the first page after the map for our bump allocator.  We
38 // assume the ranges passed in are validated already.
39 void *init_e820map(struct virtual_machine *vm, struct boot_params *bp)
40 {
41         uintptr_t memstart = vm->minphys;
42         size_t memsize = vm->maxphys - vm->minphys + 1;
43         uintptr_t lowmem = 0;
44
45         // Everything in Linux at this level is PGSIZE.
46         memset(bp, 0, PGSIZE);
47
48         bp->e820_entries = 0;
49
50         // The first page is always reserved.
51         bp->e820_map[bp->e820_entries].addr = 0;
52         bp->e820_map[bp->e820_entries].size = PGSIZE;
53         bp->e820_map[bp->e820_entries++].type = E820_RESERVED;
54
55         /* Give it just a tiny bit of memory -- 60k -- at low memory. */
56         bp->e820_map[bp->e820_entries].addr = PGSIZE;
57         bp->e820_map[bp->e820_entries].size = LOW64K - PGSIZE;
58         bp->e820_map[bp->e820_entries++].type = E820_RAM;
59
60         // All other memory from 64k to memstart is reserved.
61         bp->e820_map[bp->e820_entries].addr = LOW64K;
62         bp->e820_map[bp->e820_entries].size = memstart - LOW64K;
63         bp->e820_map[bp->e820_entries++].type = E820_RESERVED;
64
65         // If memory starts below RESERVED, then add an entry for memstart to
66         // the smaller of RESERVED or memsize.
67         if (memstart < RESERVED) {
68                 bp->e820_map[bp->e820_entries].addr = memstart;
69                 if (memstart + memsize > RESERVED)
70                         bp->e820_map[bp->e820_entries].size = RESERVED -
71                                                               memstart;
72                 else
73                         bp->e820_map[bp->e820_entries].size = memsize;
74                 lowmem = bp->e820_map[bp->e820_entries].size;
75                 bp->e820_map[bp->e820_entries++].type = E820_RAM;
76         }
77
78         bp->e820_map[bp->e820_entries].addr = RESERVED;
79         bp->e820_map[bp->e820_entries].size = RESERVEDSIZE;
80         bp->e820_map[bp->e820_entries++].type = E820_RESERVED;
81
82         if ((memstart + memsize) > RESERVED) {
83                 bp->e820_map[bp->e820_entries].addr = MAX(memstart, _4GiB);
84                 bp->e820_map[bp->e820_entries].size = memsize - lowmem;
85                 bp->e820_map[bp->e820_entries++].type = E820_RAM;
86         }
87
88         return (void *)bp + PGSIZE;
89 }
90
91 /* checkmemaligned verifies alignment attributes of your memory space.
92  * It terminates your process with extreme prejudice if they are
93  * incorrect in some way. */
94 void checkmemaligned(uintptr_t memstart, size_t memsize)
95 {
96         if (!ALIGNED(memstart, PML1_REACH))
97                 errx(1, "memstart (%#x) wrong: must be aligned to %#x",
98                      memstart, PML1_REACH);
99         if (!ALIGNED(memsize, PML1_REACH))
100                 errx(1, "memsize (%#x) wrong: must be aligned to %#x",
101                      memsize, PML1_REACH);
102 }
103
104 // memory allocates memory for the VM. It's a complicated mess because of the
105 // break for APIC and other things. We just go ahead and leave the region from
106 // RESERVED to _4GiB for that.  The memory is either split, all low, or all
107 // high. This code is designed for a kernel. Dune-style code does not need it
108 // as it does not have the RESERVED restrictions. Dune-style code can use this,
109 // however, by setting memstart to 4 GiB. This code can be called multiple
110 // times with more ranges. It does not check for overlaps.
111 void mmap_memory(struct virtual_machine *vm, uintptr_t memstart, size_t memsize)
112 {
113         void *r1, *r2;
114         unsigned long r1size = memsize;
115
116         // Let's do some minimal validation, so we don't drive
117         // people crazy.
118         checkmemaligned(memstart, memsize);
119         if ((memstart >= RESERVED) && (memstart < _4GiB))
120                 errx(1, "memstart (%#x) wrong: must be < %#x or >= %#x\n",
121                      memstart, RESERVED, _4GiB);
122         if (memstart < MinMemory)
123                 errx(1, "memstart (%#x) wrong: must be > %#x\n",
124                      memstart, MinMemory);
125
126         // Note: this test covers the split case as well as the
127         // 'all above 4G' case.
128         if ((memstart + memsize) > RESERVED) {
129                 unsigned long long r2start = MAX(memstart, _4GiB);
130
131                 r1size = memstart < RESERVED ? RESERVED - memstart : 0;
132                 r2 = mmap((void *)r2start, memsize - r1size,
133                           PROT_READ | PROT_WRITE | PROT_EXEC,
134                           MAP_POPULATE | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
135                 if (r2 != (void *)r2start) {
136                         fprintf(stderr,
137                                 "High region: Could not mmap 0x%lx bytes at 0x%lx\n",
138                                 memsize, r2start);
139                         exit(1);
140                 }
141                 if (memstart >= _4GiB)
142                         goto done;
143         }
144
145         r1 = mmap((void *)memstart, r1size,
146                       PROT_READ | PROT_WRITE | PROT_EXEC,
147                       MAP_POPULATE | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
148         if (r1 != (void *)memstart) {
149                 fprintf(stderr,
150                         "Low region: Could not mmap 0x%lx bytes at 0x%lx\n",
151                         memsize, memstart);
152                 exit(1);
153         }
154
155 done:
156         if ((vm->minphys == 0) || (vm->minphys > memstart))
157                 vm->minphys = memstart;
158
159         if (vm->maxphys < memstart + memsize - 1)
160                 vm->maxphys = memstart + memsize - 1;
161 }
162
163 bool mmap_file(const char *path, uintptr_t memstart, size_t memsize,
164                uint64_t protections, size_t offset)
165 {
166         int fd = open(path, O_RDONLY);
167
168         if (fd == -1) {
169                 fprintf(stderr, "Unable to open %s for reading.\n", path);
170                 return false;
171         }
172
173         void *addr = mmap((void*) memstart, memsize, protections, MAP_PRIVATE,
174                           fd, offset);
175         int err = errno;
176
177         close(fd);
178
179         if (addr == MAP_FAILED) {
180                 fprintf(stderr, "Failed to mmap %s, got error %d\n", path, err);
181                 return false;
182         }
183
184         if ((uint64_t) addr != (uint64_t) memstart) {
185                 fprintf(stderr, "Could not mmap %s correctly.\n", path);
186                 if (munmap(addr, memsize) == -1)
187                         perror("Failed to unmap memory; leaking a mapping");
188                 return false;
189         }
190         return true;
191 }
192
193
194 /* populate_stack fills the stack with an argv, envp, and auxv.
195  * We assume the stack pointer is backed by real memory.
196  * It will go hard with you if it does not. For your own health,
197  * stack should be 16-byte aligned. */
198 void *populate_stack(uintptr_t *stack, int argc, char *argv[],
199                          int envc, char *envp[],
200                          int auxc, struct elf_aux auxv[])
201 {
202         /* Func to get the lengths of the argument and environment strings. */
203         int get_lens(int argc, char *argv[], int arg_lens[])
204         {
205                 int total = 0;
206
207                 if (!argc)
208                         return 0;
209                 for (int i = 0; i < argc; i++) {
210                         arg_lens[i] = strlen(argv[i]) + 1;
211                         total += arg_lens[i];
212                 }
213                 return total;
214         }
215
216         /* Function to help map the argument and environment strings, to their
217          * final location. */
218         int remap(int argc, char *argv[], char *new_argv[],
219               char new_argbuf[], int arg_lens[])
220         {
221                 int offset = 0;
222
223                 if (!argc)
224                         return 0;
225                 for (int i = 0; i < argc; i++) {
226                         memcpy(new_argbuf + offset, argv[i], arg_lens[i]);
227                         fprintf(stderr, "data: memcpy(%p, %p, %ld)\n",
228                                 new_argbuf + offset, argv[i], arg_lens[i]);
229                         fprintf(stderr, "arg: set arg %d, @%p, to %p\n", i,
230                                 &new_argv[i], new_argbuf + offset);
231                         new_argv[i] = new_argbuf + offset;
232                         offset += arg_lens[i];
233                 }
234                 new_argv[argc] = NULL;
235                 return offset;
236         }
237
238         /* Start tracking the size of the buffer necessary to hold all of our
239          * data on the stack. Preallocate space for argc, argv, envp, and auxv
240          * in this buffer. */
241         int bufsize = 0;
242
243         bufsize += 1 * sizeof(size_t);
244         bufsize += (auxc + 1) * sizeof(struct elf_aux);
245         bufsize += (envc + 1) * sizeof(char**);
246         bufsize += (argc + 1) * sizeof(char**);
247         fprintf(stderr, "Bufsize for pointers and argc is %d\n", bufsize);
248
249         /* Add in the size of the env and arg strings. */
250         int arg_lens[argc];
251         int env_lens[envc];
252
253         bufsize += get_lens(argc, argv, arg_lens);
254         bufsize += get_lens(envc, envp, env_lens);
255         fprintf(stderr, "Bufsize for pointers, argc, and strings is %d\n",
256                 bufsize);
257
258         /* Adjust bufsize so that our buffer will ultimately be 16 byte aligned.
259          */
260         bufsize = (bufsize + 15) & ~0xf;
261         fprintf(stderr,
262                 "Bufsize for pointers, argc, and strings is rounded is %d\n",
263                 bufsize);
264
265         /* Set up pointers to all of the appropriate data regions we map to. */
266         size_t *new_argc = (size_t*)((uint8_t*)stack - bufsize);
267         char **new_argv = (char**)(new_argc + 1);
268         char **new_envp = new_argv + argc + 1;
269         struct elf_aux *new_auxv = (struct elf_aux*)(new_envp + envc + 1);
270         char *new_argbuf = (char*)(new_auxv + auxc + 1);
271
272         fprintf(stderr, "There are %d args, %d env, and %d aux\n", new_argc,
273                 envc, auxc);
274         fprintf(stderr, "Locations: argc: %p, argv: %p, envp: %p, auxv: %p\n",
275                 new_argc, new_argv, new_envp, new_auxv);
276         fprintf(stderr, "Locations: argbuf: %p, ", new_argbuf);
277         fprintf(stderr, "Sizeof argc is %d\n", sizeof(size_t));
278         /* Map argc into its final location. */
279         *new_argc = argc;
280
281         /* Map all data for argv and envp into its final location. */
282         int offset = 0;
283
284         offset = remap(argc, argv, new_argv, new_argbuf, arg_lens);
285         if (offset == -1)
286                 return 0;
287         fprintf(stderr, "Locations: argbuf: %p, envbuf: %p, ", new_argbuf,
288                 new_argbuf + offset);
289         offset = remap(envc, envp, new_envp, new_argbuf + offset, env_lens);
290         if (offset == -1)
291                 return 0;
292
293         /* Map auxv into its final location. */
294         struct elf_aux null_aux = {0, 0};
295
296         memcpy(new_auxv, auxv, auxc * sizeof(struct elf_aux));
297         memcpy(new_auxv + auxc, &null_aux, sizeof(struct elf_aux));
298         fprintf(stderr, "auxbuf: %p\n", new_auxv);
299         hexdump(stdout, new_auxv, auxc * sizeof(struct elf_aux));
300         return (uint8_t*)stack - bufsize;
301 }