proc: fix refcounting bug in proc_get_set()
[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         dumpe820(bp->e820_map, bp->e820_entries);
89         return (void *)bp + PGSIZE;
90 }
91
92 /* checkmemaligned verifies alignment attributes of your memory space.
93  * It terminates your process with extreme prejudice if they are
94  * incorrect in some way. */
95 void checkmemaligned(uintptr_t memstart, size_t memsize)
96 {
97         if (!ALIGNED(memstart, PML1_REACH))
98                 errx(1, "memstart (%#x) wrong: must be aligned to %#x",
99                      memstart, PML1_REACH);
100         if (!ALIGNED(memsize, PML1_REACH))
101                 errx(1, "memsize (%#x) wrong: must be aligned to %#x",
102                      memsize, PML1_REACH);
103 }
104
105 // memory allocates memory for the VM. It's a complicated mess because of the
106 // break for APIC and other things. We just go ahead and leave the region from
107 // RESERVED to _4GiB for that.  The memory is either split, all low, or all
108 // high. This code is designed for a kernel. Dune-style code does not need it
109 // as it does not have the RESERVED restrictions. Dune-style code can use this,
110 // however, by setting memstart to 4 GiB. This code can be called multiple
111 // times with more ranges. It does not check for overlaps.
112 void mmap_memory(struct virtual_machine *vm, uintptr_t memstart, size_t memsize)
113 {
114         void *r1, *r2;
115         unsigned long r1size = memsize;
116
117         // Let's do some minimal validation, so we don't drive
118         // people crazy.
119         checkmemaligned(memstart, memsize);
120         if ((memstart >= RESERVED) && (memstart < _4GiB))
121                 errx(1, "memstart (%#x) wrong: must be < %#x or >= %#x\n",
122                      memstart, RESERVED, _4GiB);
123         if (memstart < MinMemory)
124                 errx(1, "memstart (%#x) wrong: must be > %#x\n",
125                      memstart, MinMemory);
126
127         // Note: this test covers the split case as well as the
128         // 'all above 4G' case.
129         if ((memstart + memsize) > RESERVED) {
130                 unsigned long long r2start = MAX(memstart, _4GiB);
131
132                 r1size = memstart < RESERVED ? RESERVED - memstart : 0;
133                 r2 = mmap((void *)r2start, memsize - r1size,
134                           PROT_READ | PROT_WRITE | PROT_EXEC,
135                           MAP_POPULATE | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
136                 if (r2 != (void *)r2start) {
137                         fprintf(stderr,
138                                 "High region: Could not mmap 0x%lx bytes at 0x%lx\n",
139                                 memsize, r2start);
140                         exit(1);
141                 }
142                 if (memstart >= _4GiB)
143                         goto done;
144         }
145
146         r1 = mmap((void *)memstart, r1size,
147                       PROT_READ | PROT_WRITE | PROT_EXEC,
148                       MAP_POPULATE | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
149         if (r1 != (void *)memstart) {
150                 fprintf(stderr,
151                         "Low region: Could not mmap 0x%lx bytes at 0x%lx\n",
152                         memsize, memstart);
153                 exit(1);
154         }
155
156 done:
157         if ((vm->minphys == 0) || (vm->minphys > memstart))
158                 vm->minphys = memstart;
159
160         if (vm->maxphys < memstart + memsize - 1)
161                 vm->maxphys = memstart + memsize - 1;
162 }
163
164 bool mmap_file(const char *path, uintptr_t memstart, size_t memsize,
165                uint64_t protections, size_t offset)
166 {
167         int fd = open(path, O_RDONLY);
168
169         if (fd == -1) {
170                 fprintf(stderr, "Unable to open %s for reading.\n", path);
171                 return false;
172         }
173
174         void *addr = mmap((void*) memstart, memsize, protections, MAP_PRIVATE,
175                           fd, offset);
176         int err = errno;
177
178         close(fd);
179
180         if (addr == MAP_FAILED) {
181                 fprintf(stderr, "Failed to mmap %s, got error %d\n", path, err);
182                 return false;
183         }
184
185         if ((uint64_t) addr != (uint64_t) memstart) {
186                 fprintf(stderr, "Could not mmap %s correctly.\n", path);
187                 if (munmap(addr, memsize) == -1)
188                         perror("Failed to unmap memory; leaking a mapping");
189                 return false;
190         }
191         return true;
192 }
193
194
195 /* populate_stack fills the stack with an argv, envp, and auxv.
196  * We assume the stack pointer is backed by real memory.
197  * It will go hard with you if it does not. For your own health,
198  * stack should be 16-byte aligned. */
199 void *populate_stack(uintptr_t *stack, int argc, char *argv[],
200                          int envc, char *envp[],
201                          int auxc, struct elf_aux auxv[])
202 {
203         /* Func to get the lengths of the argument and environment strings. */
204         int get_lens(int argc, char *argv[], int arg_lens[])
205         {
206                 int total = 0;
207
208                 if (!argc)
209                         return 0;
210                 for (int i = 0; i < argc; i++) {
211                         arg_lens[i] = strlen(argv[i]) + 1;
212                         total += arg_lens[i];
213                 }
214                 return total;
215         }
216
217         /* Function to help map the argument and environment strings, to their
218          * final location. */
219         int remap(int argc, char *argv[], char *new_argv[],
220               char new_argbuf[], int arg_lens[])
221         {
222                 int offset = 0;
223
224                 if (!argc)
225                         return 0;
226                 for (int i = 0; i < argc; i++) {
227                         memcpy(new_argbuf + offset, argv[i], arg_lens[i]);
228                         fprintf(stderr, "data: memcpy(%p, %p, %ld)\n",
229                                 new_argbuf + offset, argv[i], arg_lens[i]);
230                         fprintf(stderr, "arg: set arg %d, @%p, to %p\n", i,
231                                 &new_argv[i], new_argbuf + offset);
232                         new_argv[i] = new_argbuf + offset;
233                         offset += arg_lens[i];
234                 }
235                 new_argv[argc] = NULL;
236                 return offset;
237         }
238
239         /* Start tracking the size of the buffer necessary to hold all of our
240          * data on the stack. Preallocate space for argc, argv, envp, and auxv
241          * in this buffer. */
242         int bufsize = 0;
243
244         bufsize += 1 * sizeof(size_t);
245         bufsize += (auxc + 1) * sizeof(struct elf_aux);
246         bufsize += (envc + 1) * sizeof(char**);
247         bufsize += (argc + 1) * sizeof(char**);
248         fprintf(stderr, "Bufsize for pointers and argc is %d\n", bufsize);
249
250         /* Add in the size of the env and arg strings. */
251         int arg_lens[argc];
252         int env_lens[envc];
253
254         bufsize += get_lens(argc, argv, arg_lens);
255         bufsize += get_lens(envc, envp, env_lens);
256         fprintf(stderr, "Bufsize for pointers, argc, and strings is %d\n",
257                 bufsize);
258
259         /* Adjust bufsize so that our buffer will ultimately be 16 byte aligned.
260          */
261         bufsize = (bufsize + 15) & ~0xf;
262         fprintf(stderr,
263                 "Bufsize for pointers, argc, and strings is rounded is %d\n",
264                 bufsize);
265
266         /* Set up pointers to all of the appropriate data regions we map to. */
267         size_t *new_argc = (size_t*)((uint8_t*)stack - bufsize);
268         char **new_argv = (char**)(new_argc + 1);
269         char **new_envp = new_argv + argc + 1;
270         struct elf_aux *new_auxv = (struct elf_aux*)(new_envp + envc + 1);
271         char *new_argbuf = (char*)(new_auxv + auxc + 1);
272
273         fprintf(stderr, "There are %d args, %d env, and %d aux\n", new_argc,
274                 envc, auxc);
275         fprintf(stderr, "Locations: argc: %p, argv: %p, envp: %p, auxv: %p\n",
276                 new_argc, new_argv, new_envp, new_auxv);
277         fprintf(stderr, "Locations: argbuf: %p, ", new_argbuf);
278         fprintf(stderr, "Sizeof argc is %d\n", sizeof(size_t));
279         /* Map argc into its final location. */
280         *new_argc = argc;
281
282         /* Map all data for argv and envp into its final location. */
283         int offset = 0;
284
285         offset = remap(argc, argv, new_argv, new_argbuf, arg_lens);
286         if (offset == -1)
287                 return 0;
288         fprintf(stderr, "Locations: argbuf: %p, envbuf: %p, ", new_argbuf,
289                 new_argbuf + offset);
290         offset = remap(envc, envp, new_envp, new_argbuf + offset, env_lens);
291         if (offset == -1)
292                 return 0;
293
294         /* Map auxv into its final location. */
295         struct elf_aux null_aux = {0, 0};
296
297         memcpy(new_auxv, auxv, auxc * sizeof(struct elf_aux));
298         memcpy(new_auxv + auxc, &null_aux, sizeof(struct elf_aux));
299         fprintf(stderr, "auxbuf: %p\n", new_auxv);
300         hexdump(stdout, new_auxv, auxc * sizeof(struct elf_aux));
301         return (uint8_t*)stack - bufsize;
302 }