vmm: Added more syscalls and helpers to linuxemu
[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 #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 - memstart;
71                 else
72                         bp->e820_map[bp->e820_entries].size = memsize;
73                 lowmem = bp->e820_map[bp->e820_entries].size;
74                 bp->e820_map[bp->e820_entries++].type = E820_RAM;
75         }
76
77         bp->e820_map[bp->e820_entries].addr = RESERVED;
78         bp->e820_map[bp->e820_entries].size = RESERVEDSIZE;
79         bp->e820_map[bp->e820_entries++].type = E820_RESERVED;
80
81         if ((memstart + memsize) > RESERVED) {
82                 bp->e820_map[bp->e820_entries].addr = MAX(memstart, _4GiB);
83                 bp->e820_map[bp->e820_entries].size = memsize - lowmem;
84                 bp->e820_map[bp->e820_entries++].type = E820_RAM;
85         }
86
87         dumpe820(bp->e820_map, bp->e820_entries);
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, "Low region: Could not mmap 0x%lx bytes at 0x%lx\n",
150                         memsize, memstart);
151                 exit(1);
152         }
153
154 done:
155         if ((vm->minphys == 0) || (vm->minphys > memstart))
156                 vm->minphys = memstart;
157
158         if (vm->maxphys < memstart + memsize - 1)
159                 vm->maxphys = memstart + memsize - 1;
160 }
161
162 bool mmap_file(const char *path, uintptr_t memstart, size_t memsize,
163                uint64_t protections, size_t offset)
164 {
165         int fd = open(path, O_RDONLY);
166
167         if (fd == -1) {
168                 fprintf(stderr, "Unable to open %s for reading.\n", path);
169                 return false;
170         }
171
172         void *addr = mmap((void*) memstart, memsize, protections, MAP_PRIVATE,
173                           fd, offset);
174         int err = errno;
175
176         close(fd);
177
178         if (addr == MAP_FAILED) {
179                 fprintf(stderr, "Failed to mmap %s, got error %d\n", path, err);
180                 return false;
181         }
182
183         if ((uint64_t) addr != (uint64_t) memstart) {
184                 fprintf(stderr, "Could not mmap %s correctly.\n", path);
185                 if (munmap(addr, memsize) == -1)
186                         perror("Failed to unmap memory; leaking a mapping");
187                 return false;
188         }
189         return true;
190 }
191
192
193 /* populate_stack fills the stack with an argv, envp, and auxv.
194  * We assume the stack pointer is backed by real memory.
195  * It will go hard with you if it does not. For your own health,
196  * stack should be 16-byte aligned. */
197 void *populate_stack(uintptr_t *stack, int argc, char *argv[],
198                          int envc, char *envp[],
199                          int auxc, struct elf_aux auxv[])
200 {
201         /* Function to get the lengths of the argument and environment strings. */
202         int get_lens(int argc, char *argv[], int arg_lens[])
203         {
204                 int total = 0;
205
206                 if (!argc)
207                         return 0;
208                 for (int i = 0; i < argc; i++) {
209                         arg_lens[i] = strlen(argv[i]) + 1;
210                         total += arg_lens[i];
211                 }
212                 return total;
213         }
214
215         /* Function to help map the argument and environment strings, to their
216          * final location. */
217         int remap(int argc, char *argv[], char *new_argv[],
218               char new_argbuf[], int arg_lens[])
219         {
220                 int offset = 0;
221
222                 if (!argc)
223                         return 0;
224                 for (int i = 0; i < argc; i++) {
225                         memcpy(new_argbuf + offset, argv[i], arg_lens[i]);
226                         fprintf(stderr, "data: memcpy(%p, %p, %ld)\n",
227                                 new_argbuf + offset, argv[i], arg_lens[i]);
228                         fprintf(stderr, "arg: set arg %d, @%p, to %p\n", i,
229                                 &new_argv[i], new_argbuf + offset);
230                         new_argv[i] = new_argbuf + offset;
231                         offset += arg_lens[i];
232                 }
233                 new_argv[argc] = NULL;
234                 return offset;
235         }
236
237         /* Start tracking the size of the buffer necessary to hold all of our data
238          * on the stack. Preallocate space for argc, argv, envp, and auxv in this
239          * buffer. */
240         int bufsize = 0;
241
242         bufsize += 1 * sizeof(size_t);
243         bufsize += (auxc + 1) * sizeof(struct elf_aux);
244         bufsize += (envc + 1) * sizeof(char**);
245         bufsize += (argc + 1) * sizeof(char**);
246         fprintf(stderr, "Bufsize for pointers and argc is %d\n", bufsize);
247
248         /* Add in the size of the env and arg strings. */
249         int arg_lens[argc];
250         int env_lens[envc];
251
252         bufsize += get_lens(argc, argv, arg_lens);
253         bufsize += get_lens(envc, envp, env_lens);
254         fprintf(stderr, "Bufsize for pointers, argc, and strings is %d\n",
255                 bufsize);
256
257         /* Adjust bufsize so that our buffer will ultimately be 16 byte aligned. */
258         bufsize = (bufsize + 15) & ~0xf;
259         fprintf(stderr,
260                 "Bufsize for pointers, argc, and strings is rounded is %d\n",
261                 bufsize);
262
263         /* Set up pointers to all of the appropriate data regions we map to. */
264         size_t *new_argc = (size_t*)((uint8_t*)stack - bufsize);
265         char **new_argv = (char**)(new_argc + 1);
266         char **new_envp = new_argv + argc + 1;
267         struct elf_aux *new_auxv = (struct elf_aux*)(new_envp + envc + 1);
268         char *new_argbuf = (char*)(new_auxv + auxc + 1);
269
270         fprintf(stderr, "There are %d args, %d env, and %d aux\n", new_argc,
271                 envc, auxc);
272         fprintf(stderr, "Locations: argc: %p, argv: %p, envp: %p, auxv: %p\n",
273                 new_argc, new_argv, new_envp, new_auxv);
274         fprintf(stderr, "Locations: argbuf: %p, ", new_argbuf);
275         fprintf(stderr, "Sizeof argc is %d\n", sizeof(size_t));
276         /* Map argc into its final location. */
277         *new_argc = argc;
278
279         /* Map all data for argv and envp into its final location. */
280         int offset = 0;
281
282         offset = remap(argc, argv, new_argv, new_argbuf, arg_lens);
283         if (offset == -1)
284                 return 0;
285         fprintf(stderr, "Locations: argbuf: %p, envbuf: %p, ", new_argbuf,
286                 new_argbuf + offset);
287         offset = remap(envc, envp, new_envp, new_argbuf + offset, env_lens);
288         if (offset == -1)
289                 return 0;
290
291         /* Map auxv into its final location. */
292         struct elf_aux null_aux = {0, 0};
293
294         memcpy(new_auxv, auxv, auxc * sizeof(struct elf_aux));
295         memcpy(new_auxv + auxc, &null_aux, sizeof(struct elf_aux));
296         fprintf(stderr, "auxbuf: %p\n", new_auxv);
297         hexdump(stdout, new_auxv, auxc * sizeof(struct elf_aux));
298         return (uint8_t*)stack - bufsize;
299 }