blob: 885f577483c2639846ed443be9fb88d5c4f0284b [file] [log] [blame]
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +01001#include <stdalign.h>
2#include <stdatomic.h>
3#include <stddef.h>
4
5#include "cpio.h"
6#include "cpu.h"
7#include "dlog.h"
8#include "fdt.h"
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +01009#include "std.h"
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010010#include "vm.h"
11
12void *fdt;
13
14/* The stack to be used by the CPUs. */
15alignas(2 * sizeof(size_t)) char callstacks[STACK_SIZE * MAX_CPUS];
16
17/* State of all supported CPUs. The stack of the first one is initialized. */
18struct cpu cpus[MAX_CPUS] = {
19 {
Wedson Almeida Filho87009642018-07-02 10:20:07 +010020 .is_on = 1,
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010021 .stack_bottom = callstacks + STACK_SIZE,
22 },
23};
24
25bool fdt_find_node(struct fdt_node *node, const char *path)
26{
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010027 while (*path) {
28 if (!fdt_find_child(node, path))
29 return false;
30 path += strlen(path);
31 }
32
33 return true;
34}
35
Wedson Almeida Filho87009642018-07-02 10:20:07 +010036static uint64_t convert_number(const char *data, uint32_t size)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010037{
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010038 union {
39 volatile uint64_t v;
40 char a[8];
41 } t;
42
Wedson Almeida Filho87009642018-07-02 10:20:07 +010043 switch (size) {
44 case sizeof(uint32_t):
45 return ntohl(*(uint32_t *)data);
46 case sizeof(uint64_t):
47 memcpy(t.a, data, sizeof(uint64_t));
48 return ntohll(t.v);
49 default:
50 return 0;
51 }
52}
53
54static bool fdt_read_number(const struct fdt_node *node, const char *name,
55 uint64_t *value)
56{
57 const char *data;
58 uint32_t size;
59
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010060 if (!fdt_read_property(node, name, &data, &size))
61 return false;
62
63 switch (size) {
64 case sizeof(uint32_t):
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010065 case sizeof(uint64_t):
Wedson Almeida Filho87009642018-07-02 10:20:07 +010066 *value = convert_number(data, size);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010067 break;
68
69 default:
70 return false;
71 }
72
73 return true;
74}
75
76bool fdt_write_number(struct fdt_node *node, const char *name, uint64_t value)
77{
78 const char *data;
79 uint32_t size;
80 union {
81 volatile uint64_t v;
82 char a[8];
83 } t;
84
85 if (!fdt_read_property(node, name, &data, &size))
86 return false;
87
88 switch (size) {
89 case sizeof(uint32_t):
90 *(uint32_t *)data = ntohl(value);
91 break;
92
93 case sizeof(uint64_t):
94 t.v = ntohll(value);
95 memcpy((void *)data, t.a, sizeof(uint64_t));
96 break;
97
98 default:
99 return false;
100 }
101
102 return true;
103}
104
105static void relocate(const char *from, size_t size)
106{
107 extern char bin_end[];
108 size_t tmp = (size_t)&bin_end[0];
109 char *dest = (char *)((tmp + 0x80000 - 1) & ~(0x80000 - 1));
110 dlog("bin_end is at %p, copying to %p\n", &bin_end[0], dest);
111 memcpy(dest, from, size);
112}
113
114/* TODO: Remove this. */
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100115struct vm primary_vm;
116struct vm secondary_vm[MAX_VMS];
117uint32_t secondary_vm_count = 0;
118
119static void find_memory_range(const struct fdt_node *root,
120 uint64_t *block_start, uint64_t *block_size)
121{
122 struct fdt_node n = *root;
123 const char *name;
124 uint64_t address_size;
125 uint64_t size_size;
126 uint64_t entry_size;
127
128 /* Get the sizes of memory range addresses and sizes. */
129 if (fdt_read_number(&n, "#address-cells", &address_size))
130 address_size *= sizeof(uint32_t);
131 else
132 address_size = sizeof(uint32_t);
133
134 if (fdt_read_number(&n, "#size-cells", &size_size))
135 size_size *= sizeof(uint32_t);
136 else
137 size_size = sizeof(uint32_t);
138
139 entry_size = address_size + size_size;
140
141 /* Look for nodes with the device_type set to "memory". */
142 if (!fdt_first_child(&n, &name))
143 return;
144
145 do {
146 const char *data;
147 uint32_t size;
148 if (!fdt_read_property(&n, "device_type", &data, &size) ||
149 size != sizeof("memory") ||
150 memcmp(data, "memory", sizeof("memory")) != 0 ||
151 !fdt_read_property(&n, "reg", &data, &size)) {
152 continue;
153 }
154
155 /* Traverse all memory ranges within this node. */
156 while (size >= entry_size) {
157 uint64_t addr = convert_number(data, address_size);
158 uint64_t len = convert_number(data + address_size,
159 size_size);
160
161 if (len > *block_size) {
162 /* Remember the largest range we've found. */
163 *block_start = addr;
164 *block_size = len;
165 }
166
167 size -= entry_size;
168 data += entry_size;
169 }
170 } while (fdt_next_sibling(&n, &name));
171
172 /* TODO: Check for "reserved-memory" nodes. */
173}
174
175/**
176 * Finds the memory region where initrd is stored, and udpates the fdt node
177 * cursor to the node called "chosen".
178 */
179static bool find_initrd(struct fdt_node *n, uint64_t *begin, uint64_t *end)
180{
181 if (!fdt_find_node(n, "chosen\0")) {
182 dlog("Unable to find 'chosen'\n");
183 return false;
184 }
185
186 if (!fdt_read_number(n, "linux,initrd-start", begin)) {
187 dlog("Unable to read linux,initrd-start\n");
188 return false;
189 }
190
191 if (!fdt_read_number(n, "linux,initrd-end", end)) {
192 dlog("Unable to read linux,initrd-end\n");
193 return false;
194 }
195
196 return true;
197}
198
199struct memiter {
200 const char *next;
201 const char *limit;
202};
203
204static void memiter_init(struct memiter *it, const void *data, size_t size)
205{
206 it->next = data;
207 it->limit = it->next + size;
208}
209
210static bool memiter_isspace(struct memiter *it)
211{
212 switch (*it->next) {
213 case ' ':
214 case '\t':
215 case '\n':
216 case '\r':
217 return true;
218 default:
219 return false;
220 }
221}
222
223static void memiter_skip_space(struct memiter *it)
224{
225 while (it->next < it->limit && memiter_isspace(it))
226 it->next++;
227}
228
229static bool memiter_iseq(const struct memiter *it, const char *str)
230{
231 size_t len = strlen(str);
232 if (len != it->limit - it->next)
233 return false;
234 return memcmp(it->next, str, len) == 0;
235}
236
237static bool memiter_parse_str(struct memiter *it, struct memiter *str)
238{
239 /* Skip all white space and fail if we reach the end of the buffer. */
240 memiter_skip_space(it);
241 if (it->next >= it->limit)
242 return false;
243
244 str->next = it->next;
245
246 /* Find the end of the string. */
247 while (it->next < it->limit && !memiter_isspace(it))
248 it->next++;
249
250 str->limit = it->next;
251
252 return true;
253}
254
255static bool memiter_parse_uint(struct memiter *it, uint64_t *value)
256{
257 uint64_t v = 0;
258
259 /* Skip all white space and fail if we reach the end of the buffer. */
260 memiter_skip_space(it);
261 if (it->next >= it->limit)
262 return false;
263
264 /* Fail if it's not a number. */
265 if (*it->next < '0' && *it->next > '9')
266 return false;
267
268 /* Parse the number. */
269 do {
270 v = v * 10 + *it->next - '0';
271 it->next++;
272 } while (it->next < it->limit && *it->next >= '0' && *it->next <= '9');
273
274 *value = v;
275
276 return true;
277}
278
279static bool memiter_find_file(struct cpio *c, const struct memiter *filename,
280 struct memiter *it)
281{
282 const char *fname;
283 const void *fcontents;
284 size_t fsize;
285 struct cpio_iter iter;
286
287 cpio_init_iter(c, &iter);
288
289 while (cpio_next(&iter, &fname, &fcontents, &fsize)) {
290 if (memiter_iseq(filename, fname)) {
291 memiter_init(it, fcontents, fsize);
292 return true;
293 }
294 }
295
296 return false;
297}
298
299static bool find_file(struct cpio *c, const char *name, struct memiter *it)
300{
301 const char *fname;
302 const void *fcontents;
303 size_t fsize;
304 struct cpio_iter iter;
305
306 cpio_init_iter(c, &iter);
307
308 while (cpio_next(&iter, &fname, &fcontents, &fsize)) {
309 if (!strcmp(fname, name)) {
310 memiter_init(it, fcontents, fsize);
311 return true;
312 }
313 }
314
315 return false;
316}
317
318static bool load_secondary(struct cpio *c,
319 uint64_t mem_start, uint64_t *mem_size)
320{
321 struct memiter it;
322 struct memiter str;
323 uint64_t mem;
324 uint64_t cpu;
325 uint32_t count;
326
327 if (!find_file(c, "vms.txt", &it)) {
328 dlog("Unable to find vms.txt\n");
329 return false;
330 }
331
332 for (count = 0; memiter_parse_uint(&it, &mem) &&
333 memiter_parse_uint(&it, &cpu) &&
334 memiter_parse_str(&it, &str) &&
335 count < MAX_VMS; count++) {
336 struct memiter kernel;
337
338 if (!memiter_find_file(c, &str, &kernel)) {
339 dlog("Unable to load kernel for vm %u\n", count);
340 continue;
341 }
342
343 if (mem > *mem_size) {
344 dlog("Not enough memory for vm %u (%u bytes)\n", count,
345 mem);
346 continue;
347 }
348
349 if (mem < kernel.limit - kernel.next) {
350 dlog("Kernel is larger than available memory for vm %u\n", count);
351 continue;
352 }
353
354 *mem_size -= mem;
355 memcpy((void *)(mem_start + *mem_size), kernel.next,
356 kernel.limit - kernel.next);
357
358 dlog("Loaded VM%u with %u vcpus, entry at 0x%x\n", count, cpu,
359 mem_start + *mem_size);
360 vm_init(secondary_vm + count, cpu);
361 vm_start_vcpu(secondary_vm + count, 0,
362 mem_start + *mem_size, 0, false);
363 }
364
365 secondary_vm_count = count;
366
367 return true;
368}
369
370static bool load_primary(struct cpio *c, struct fdt_node *chosen)
371{
372 struct memiter it;
373
374 if (!find_file(c, "vmlinuz", &it)) {
375 dlog("Unable to find vmlinuz\n");
376 return false;
377 }
378
379 relocate(it.next, it.limit - it.next);
380
381 if (!find_file(c, "initrd.img", &it)) {
382 dlog("Unable to find initrd.img\n");
383 return false;
384 }
385
386 /* Patch FDT to point to new ramdisk. */
387 if (!fdt_write_number(chosen, "linux,initrd-start", (size_t)it.next)) {
388 dlog("Unable to write linux,initrd-start\n");
389 return false;
390 }
391
392 if (!fdt_write_number(chosen, "linux,initrd-end", (size_t)it.limit)) {
393 dlog("Unable to write linux,initrd-end\n");
394 return false;
395 }
396
397 /*
398 * Patch fdt to reserve memory.
399 */
400 {
401 size_t tmp = (size_t)&relocate;
402 tmp = (tmp + 0x80000 - 1) & ~(0x80000 - 1);
403
404 fdt_add_mem_reservation(fdt, tmp & ~0xfffff, 0x80000);
405 vm_init(&primary_vm, MAX_CPUS);
406 vm_start_vcpu(&primary_vm, 0, tmp, (size_t)fdt, true);
407 }
408
409 return true;
410}
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100411
412static void one_time_init(void)
413{
414 size_t i;
415
416 dlog("Initializing hafnium\n");
417
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100418 /* Initialize all CPUs. */
419 for (i = 0; i < MAX_CPUS; i++) {
420 struct cpu *c = cpus + i;
421 cpu_init(c);
422 c->id = i; /* TODO: Initialize ID. */
423 c->stack_bottom = callstacks + STACK_SIZE * (i + 1);
424 }
425
426 /* TODO: Code below this point should be removed from this function. */
427 /* TODO: Remove this. */
428
429 do {
430 struct fdt_node n;
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100431 uint64_t mem_start = 0;
432 uint64_t mem_size = 0;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100433
434 fdt_root_node(&n, fdt);
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100435 fdt_find_child(&n, "");
436
437 /* TODO: Use this. */
438 find_memory_range(&n, &mem_start, &mem_size);
439 dlog("Memory range: 0x%x - 0x%x\n", mem_start,
440 mem_start + mem_size - 1);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100441
442 uint64_t begin;
443 uint64_t end;
444
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100445 if (!find_initrd(&n, &begin, &end))
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100446 break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100447
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100448 dlog("Ramdisk range: 0x%x - 0x%x\n", begin, end - 1);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100449
450 struct cpio c;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100451 cpio_init(&c, (void *)begin, end - begin);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100452
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100453 load_secondary(&c, mem_start, &mem_size);
454 load_primary(&c, &n);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100455 } while (0);
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100456
457 arch_set_vm_mm(&primary_vm.page_table);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100458}
459
460/*
461 * The entry point of CPUs when they are turned on. It is supposed to initialise
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100462 * all state and return the first vCPU to run.
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100463 */
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100464struct vcpu *cpu_main(void)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100465{
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100466 struct cpu *c = cpu();
467
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100468 /* Do global one-time initialization just once. */
469 static atomic_flag inited = ATOMIC_FLAG_INIT;
470 if (!atomic_flag_test_and_set_explicit(&inited, memory_order_acq_rel))
471 one_time_init();
472
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100473 dlog("Starting up cpu %d\n", c - cpus);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100474
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100475 return primary_vm.vcpus + (c - cpus);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100476}