blob: 0c18d7909d53adf42733c31c7b9c06644ec4e889 [file] [log] [blame]
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +01001#include <stdalign.h>
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +01002#include <stddef.h>
3
Wedson Almeida Filhofed69022018-07-11 15:39:12 +01004#include "alloc.h"
5#include "api.h"
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +01006#include "cpio.h"
7#include "cpu.h"
8#include "dlog.h"
9#include "fdt.h"
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010010#include "mm.h"
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010011#include "std.h"
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010012#include "vm.h"
13
14void *fdt;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010015char ptable_buf[PAGE_SIZE * 20];
16struct mm_ptable ptable;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010017
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010018bool fdt_find_node(struct fdt_node *node, const char *path)
19{
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010020 while (*path) {
21 if (!fdt_find_child(node, path))
22 return false;
23 path += strlen(path);
24 }
25
26 return true;
27}
28
Wedson Almeida Filho87009642018-07-02 10:20:07 +010029static uint64_t convert_number(const char *data, uint32_t size)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010030{
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010031 union {
32 volatile uint64_t v;
33 char a[8];
34 } t;
35
Wedson Almeida Filho87009642018-07-02 10:20:07 +010036 switch (size) {
37 case sizeof(uint32_t):
Andrew Scull29d40392018-07-16 11:46:59 +010038 return be32toh(*(uint32_t *)data);
Wedson Almeida Filho87009642018-07-02 10:20:07 +010039 case sizeof(uint64_t):
40 memcpy(t.a, data, sizeof(uint64_t));
Andrew Scull29d40392018-07-16 11:46:59 +010041 return be64toh(t.v);
Wedson Almeida Filho87009642018-07-02 10:20:07 +010042 default:
43 return 0;
44 }
45}
46
47static bool fdt_read_number(const struct fdt_node *node, const char *name,
48 uint64_t *value)
49{
50 const char *data;
51 uint32_t size;
52
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010053 if (!fdt_read_property(node, name, &data, &size))
54 return false;
55
56 switch (size) {
57 case sizeof(uint32_t):
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010058 case sizeof(uint64_t):
Wedson Almeida Filho87009642018-07-02 10:20:07 +010059 *value = convert_number(data, size);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010060 break;
61
62 default:
63 return false;
64 }
65
66 return true;
67}
68
69bool fdt_write_number(struct fdt_node *node, const char *name, uint64_t value)
70{
71 const char *data;
72 uint32_t size;
73 union {
74 volatile uint64_t v;
75 char a[8];
76 } t;
77
78 if (!fdt_read_property(node, name, &data, &size))
79 return false;
80
81 switch (size) {
82 case sizeof(uint32_t):
Andrew Scull29d40392018-07-16 11:46:59 +010083 *(uint32_t *)data = be32toh(value);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010084 break;
85
86 case sizeof(uint64_t):
Andrew Scull29d40392018-07-16 11:46:59 +010087 t.v = be64toh(value);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010088 memcpy((void *)data, t.a, sizeof(uint64_t));
89 break;
90
91 default:
92 return false;
93 }
94
95 return true;
96}
97
Wedson Almeida Filhofed69022018-07-11 15:39:12 +010098/**
99 * Copies data to an unmapped location by mapping it for write, copying the
100 * data, then unmapping it.
101 */
102static bool copy_to_unmaped(paddr_t to, const void *from, size_t size)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100103{
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100104 if (!mm_ptable_map(&ptable, (vaddr_t)to, (vaddr_t)to + size, to,
105 MM_MODE_W | MM_MODE_STAGE1))
106 return false;
107
108 memcpy((void *)to, from, size);
109
110 mm_ptable_unmap(&ptable, to, to + size, MM_MODE_STAGE1);
111
112 return true;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100113}
114
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100115static bool relocate(const char *from, size_t size)
116{
117 /* TODO: This is a hack. We must read the alignment from the binary. */
118 extern char bin_end[];
119 size_t tmp = (size_t)&bin_end[0];
120 paddr_t dest = (tmp + 0x80000 - 1) & ~(0x80000 - 1);
121 dlog("bin_end is at %p, copying to %p\n", &bin_end[0], dest);
122 return copy_to_unmaped(dest, from, size);
123}
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100124
125static void find_memory_range(const struct fdt_node *root,
126 uint64_t *block_start, uint64_t *block_size)
127{
128 struct fdt_node n = *root;
129 const char *name;
130 uint64_t address_size;
131 uint64_t size_size;
132 uint64_t entry_size;
133
134 /* Get the sizes of memory range addresses and sizes. */
135 if (fdt_read_number(&n, "#address-cells", &address_size))
136 address_size *= sizeof(uint32_t);
137 else
138 address_size = sizeof(uint32_t);
139
140 if (fdt_read_number(&n, "#size-cells", &size_size))
141 size_size *= sizeof(uint32_t);
142 else
143 size_size = sizeof(uint32_t);
144
145 entry_size = address_size + size_size;
146
147 /* Look for nodes with the device_type set to "memory". */
148 if (!fdt_first_child(&n, &name))
149 return;
150
151 do {
152 const char *data;
153 uint32_t size;
154 if (!fdt_read_property(&n, "device_type", &data, &size) ||
155 size != sizeof("memory") ||
156 memcmp(data, "memory", sizeof("memory")) != 0 ||
157 !fdt_read_property(&n, "reg", &data, &size)) {
158 continue;
159 }
160
161 /* Traverse all memory ranges within this node. */
162 while (size >= entry_size) {
163 uint64_t addr = convert_number(data, address_size);
164 uint64_t len = convert_number(data + address_size,
165 size_size);
166
167 if (len > *block_size) {
168 /* Remember the largest range we've found. */
169 *block_start = addr;
170 *block_size = len;
171 }
172
173 size -= entry_size;
174 data += entry_size;
175 }
176 } while (fdt_next_sibling(&n, &name));
177
178 /* TODO: Check for "reserved-memory" nodes. */
179}
180
181/**
182 * Finds the memory region where initrd is stored, and udpates the fdt node
183 * cursor to the node called "chosen".
184 */
185static bool find_initrd(struct fdt_node *n, uint64_t *begin, uint64_t *end)
186{
187 if (!fdt_find_node(n, "chosen\0")) {
188 dlog("Unable to find 'chosen'\n");
189 return false;
190 }
191
192 if (!fdt_read_number(n, "linux,initrd-start", begin)) {
193 dlog("Unable to read linux,initrd-start\n");
194 return false;
195 }
196
197 if (!fdt_read_number(n, "linux,initrd-end", end)) {
198 dlog("Unable to read linux,initrd-end\n");
199 return false;
200 }
201
202 return true;
203}
204
205struct memiter {
206 const char *next;
207 const char *limit;
208};
209
210static void memiter_init(struct memiter *it, const void *data, size_t size)
211{
212 it->next = data;
213 it->limit = it->next + size;
214}
215
216static bool memiter_isspace(struct memiter *it)
217{
218 switch (*it->next) {
219 case ' ':
220 case '\t':
221 case '\n':
222 case '\r':
223 return true;
224 default:
225 return false;
226 }
227}
228
229static void memiter_skip_space(struct memiter *it)
230{
231 while (it->next < it->limit && memiter_isspace(it))
232 it->next++;
233}
234
235static bool memiter_iseq(const struct memiter *it, const char *str)
236{
237 size_t len = strlen(str);
238 if (len != it->limit - it->next)
239 return false;
240 return memcmp(it->next, str, len) == 0;
241}
242
243static bool memiter_parse_str(struct memiter *it, struct memiter *str)
244{
245 /* Skip all white space and fail if we reach the end of the buffer. */
246 memiter_skip_space(it);
247 if (it->next >= it->limit)
248 return false;
249
250 str->next = it->next;
251
252 /* Find the end of the string. */
253 while (it->next < it->limit && !memiter_isspace(it))
254 it->next++;
255
256 str->limit = it->next;
257
258 return true;
259}
260
261static bool memiter_parse_uint(struct memiter *it, uint64_t *value)
262{
263 uint64_t v = 0;
264
265 /* Skip all white space and fail if we reach the end of the buffer. */
266 memiter_skip_space(it);
267 if (it->next >= it->limit)
268 return false;
269
270 /* Fail if it's not a number. */
271 if (*it->next < '0' && *it->next > '9')
272 return false;
273
274 /* Parse the number. */
275 do {
276 v = v * 10 + *it->next - '0';
277 it->next++;
278 } while (it->next < it->limit && *it->next >= '0' && *it->next <= '9');
279
280 *value = v;
281
282 return true;
283}
284
285static bool memiter_find_file(struct cpio *c, const struct memiter *filename,
286 struct memiter *it)
287{
288 const char *fname;
289 const void *fcontents;
290 size_t fsize;
291 struct cpio_iter iter;
292
293 cpio_init_iter(c, &iter);
294
295 while (cpio_next(&iter, &fname, &fcontents, &fsize)) {
296 if (memiter_iseq(filename, fname)) {
297 memiter_init(it, fcontents, fsize);
298 return true;
299 }
300 }
301
302 return false;
303}
304
305static bool find_file(struct cpio *c, const char *name, struct memiter *it)
306{
307 const char *fname;
308 const void *fcontents;
309 size_t fsize;
310 struct cpio_iter iter;
311
312 cpio_init_iter(c, &iter);
313
314 while (cpio_next(&iter, &fname, &fcontents, &fsize)) {
315 if (!strcmp(fname, name)) {
316 memiter_init(it, fcontents, fsize);
317 return true;
318 }
319 }
320
321 return false;
322}
323
324static bool load_secondary(struct cpio *c,
325 uint64_t mem_start, uint64_t *mem_size)
326{
327 struct memiter it;
328 struct memiter str;
329 uint64_t mem;
330 uint64_t cpu;
331 uint32_t count;
332
333 if (!find_file(c, "vms.txt", &it)) {
334 dlog("Unable to find vms.txt\n");
335 return false;
336 }
337
338 for (count = 0; memiter_parse_uint(&it, &mem) &&
339 memiter_parse_uint(&it, &cpu) &&
340 memiter_parse_str(&it, &str) &&
341 count < MAX_VMS; count++) {
342 struct memiter kernel;
343
344 if (!memiter_find_file(c, &str, &kernel)) {
345 dlog("Unable to load kernel for vm %u\n", count);
346 continue;
347 }
348
349 if (mem > *mem_size) {
350 dlog("Not enough memory for vm %u (%u bytes)\n", count,
351 mem);
352 continue;
353 }
354
355 if (mem < kernel.limit - kernel.next) {
356 dlog("Kernel is larger than available memory for vm %u\n", count);
357 continue;
358 }
359
360 *mem_size -= mem;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100361 if (!copy_to_unmaped(mem_start + *mem_size, kernel.next,
362 kernel.limit - kernel.next)) {
363 dlog("Unable to copy kernel for vm %u\n", count);
364 continue;
365 }
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100366
367 dlog("Loaded VM%u with %u vcpus, entry at 0x%x\n", count, cpu,
368 mem_start + *mem_size);
369 vm_init(secondary_vm + count, cpu);
370 vm_start_vcpu(secondary_vm + count, 0,
371 mem_start + *mem_size, 0, false);
372 }
373
374 secondary_vm_count = count;
375
376 return true;
377}
378
379static bool load_primary(struct cpio *c, struct fdt_node *chosen)
380{
381 struct memiter it;
382
383 if (!find_file(c, "vmlinuz", &it)) {
384 dlog("Unable to find vmlinuz\n");
385 return false;
386 }
387
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100388 if (!relocate(it.next, it.limit - it.next)) {
389 dlog("Unable to relocate kernel for primary vm.\n");
390 return false;
391 }
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100392
393 if (!find_file(c, "initrd.img", &it)) {
394 dlog("Unable to find initrd.img\n");
395 return false;
396 }
397
398 /* Patch FDT to point to new ramdisk. */
399 if (!fdt_write_number(chosen, "linux,initrd-start", (size_t)it.next)) {
400 dlog("Unable to write linux,initrd-start\n");
401 return false;
402 }
403
404 if (!fdt_write_number(chosen, "linux,initrd-end", (size_t)it.limit)) {
405 dlog("Unable to write linux,initrd-end\n");
406 return false;
407 }
408
409 /*
410 * Patch fdt to reserve memory.
411 */
412 {
413 size_t tmp = (size_t)&relocate;
414 tmp = (tmp + 0x80000 - 1) & ~(0x80000 - 1);
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100415 fdt_add_mem_reservation(fdt, tmp & ~0xfffff, 0x80000);
416 vm_init(&primary_vm, MAX_CPUS);
417 vm_start_vcpu(&primary_vm, 0, tmp, (size_t)fdt, true);
418 }
419
420 return true;
421}
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100422
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100423/**
424 * Performs one-time initialisation of the hypervisor.
425 */
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100426static void one_time_init(void)
427{
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100428 extern char text_begin[];
429 extern char text_end[];
430 extern char rodata_begin[];
431 extern char rodata_end[];
432 extern char data_begin[];
433 extern char data_end[];
434
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100435 dlog("Initializing hafnium\n");
436
Wedson Almeida Filho3fcbcff2018-07-10 23:53:39 +0100437 cpu_module_init();
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100438 halloc_init((size_t)ptable_buf, sizeof(ptable_buf));
439
440 if (!mm_ptable_init(&ptable, MM_MODE_NOSYNC | MM_MODE_STAGE1)) {
441 dlog("Unable to allocate memory for page table.\n");
442 for (;;);
443 }
444
445 dlog("text: 0x%x - 0x%x\n", text_begin, text_end);
446 dlog("rodata: 0x%x - 0x%x\n", rodata_begin, rodata_end);
447 dlog("data: 0x%x - 0x%x\n", data_begin, data_end);
448
449 /* Map page for uart. */
450 mm_ptable_map_page(&ptable, PL011_BASE, PL011_BASE,
451 MM_MODE_R | MM_MODE_W | MM_MODE_D | MM_MODE_NOSYNC |
452 MM_MODE_STAGE1);
453
454 /* Map each section. */
455 mm_ptable_map(&ptable, (vaddr_t)text_begin, (vaddr_t)text_end,
456 (paddr_t)text_begin,
457 MM_MODE_X | MM_MODE_NOSYNC | MM_MODE_STAGE1);
458
459 mm_ptable_map(&ptable, (vaddr_t)rodata_begin, (vaddr_t)rodata_end,
460 (paddr_t)rodata_begin,
461 MM_MODE_R | MM_MODE_NOSYNC | MM_MODE_STAGE1);
462
463 mm_ptable_map(&ptable, (vaddr_t)data_begin, (vaddr_t)data_end,
464 (paddr_t)data_begin,
465 MM_MODE_R | MM_MODE_W | MM_MODE_NOSYNC | MM_MODE_STAGE1);
466
467 arch_mm_init((paddr_t)ptable.table);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100468
469 /* TODO: Code below this point should be removed from this function. */
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100470 do {
471 struct fdt_node n;
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100472 uint64_t mem_start = 0;
473 uint64_t mem_size = 0;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100474 uint64_t new_mem_size;
475
476 /* Map in the fdt header. */
477 if (!mm_ptable_map(&ptable, (vaddr_t)fdt,
478 (vaddr_t)fdt + fdt_header_size(),
479 (paddr_t)fdt,
480 MM_MODE_R | MM_MODE_STAGE1)) {
481 dlog("Unable to map FDT header.\n");
482 break;
483 }
484
485 /*
486 * Map the rest of the fdt plus an extra page for adding new
487 * memory reservations.
488 */
489 if (!mm_ptable_map(&ptable, (vaddr_t)fdt,
490 (vaddr_t)fdt + fdt_total_size(fdt),
491 (paddr_t)fdt,
492 MM_MODE_R | MM_MODE_STAGE1)) {
493 dlog("Unable to map FDT.\n");
494 break;
495 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100496
497 fdt_root_node(&n, fdt);
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100498 fdt_find_child(&n, "");
499
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100500 find_memory_range(&n, &mem_start, &mem_size);
501 dlog("Memory range: 0x%x - 0x%x\n", mem_start,
502 mem_start + mem_size - 1);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100503
504 uint64_t begin;
505 uint64_t end;
506
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100507 if (!find_initrd(&n, &begin, &end))
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100508 break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100509
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100510 dlog("Ramdisk range: 0x%x - 0x%x\n", begin, end - 1);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100511 mm_ptable_map(&ptable, begin, end, begin,
512 MM_MODE_R | MM_MODE_STAGE1);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100513
514 struct cpio c;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100515 cpio_init(&c, (void *)begin, end - begin);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100516
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100517 /* Map the fdt in r/w mode in preparation for extending it. */
518 if (!mm_ptable_map(&ptable, (vaddr_t)fdt,
519 (vaddr_t)fdt + fdt_total_size(fdt) +
520 PAGE_SIZE,
521 (paddr_t)fdt,
522 MM_MODE_R | MM_MODE_W | MM_MODE_STAGE1)) {
523 dlog("Unable to map FDT in r/w mode.\n");
524 break;
525 }
526 new_mem_size = mem_size;
527 load_secondary(&c, mem_start, &new_mem_size);
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100528 load_primary(&c, &n);
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100529
530 /* Patch fdt to reserve memory for secondary VMs. */
531 fdt_add_mem_reservation(fdt, mem_start + new_mem_size,
532 mem_size - new_mem_size);
533
534 /* Unmap FDT. */
535 if (!mm_ptable_unmap(&ptable, (vaddr_t)fdt,
536 (vaddr_t)fdt + fdt_total_size(fdt) +
537 PAGE_SIZE, MM_MODE_STAGE1)) {
538 dlog("Unable to unmap the FDT.\n");
539 break;
540 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100541 } while (0);
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100542
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100543 mm_ptable_defrag(&ptable);
544
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100545 arch_set_vm_mm(&primary_vm.page_table);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100546}
547
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100548/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100549 * The entry point of CPUs when they are turned on. It is supposed to initialise
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100550 * all state and return the first vCPU to run.
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100551 */
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100552struct vcpu *cpu_main(void)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100553{
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100554 struct cpu *c = cpu();
555
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100556 /* Do global one-time initialization just once. We avoid using atomics
557 * by only touching the variable from cpu 0. */
558 static volatile bool inited = false;
559 if (cpu_index(c) == 0 && !inited) {
560 inited = true;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100561 one_time_init();
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100562 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100563
Wedson Almeida Filho3fcbcff2018-07-10 23:53:39 +0100564 dlog("Starting up cpu %d\n", cpu_index(c));
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100565
Wedson Almeida Filho3fcbcff2018-07-10 23:53:39 +0100566 return primary_vm.vcpus + cpu_index(c);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100567}