blob: d5c2c229cacacd6286a3836d72d37834445f1a15 [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
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010014bool fdt_find_node(struct fdt_node *node, const char *path)
15{
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010016 while (*path) {
17 if (!fdt_find_child(node, path))
18 return false;
19 path += strlen(path);
20 }
21
22 return true;
23}
24
Wedson Almeida Filho87009642018-07-02 10:20:07 +010025static uint64_t convert_number(const char *data, uint32_t size)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010026{
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010027 union {
28 volatile uint64_t v;
29 char a[8];
30 } t;
31
Wedson Almeida Filho87009642018-07-02 10:20:07 +010032 switch (size) {
33 case sizeof(uint32_t):
Andrew Scull29d40392018-07-16 11:46:59 +010034 return be32toh(*(uint32_t *)data);
Wedson Almeida Filho87009642018-07-02 10:20:07 +010035 case sizeof(uint64_t):
36 memcpy(t.a, data, sizeof(uint64_t));
Andrew Scull29d40392018-07-16 11:46:59 +010037 return be64toh(t.v);
Wedson Almeida Filho87009642018-07-02 10:20:07 +010038 default:
39 return 0;
40 }
41}
42
43static bool fdt_read_number(const struct fdt_node *node, const char *name,
44 uint64_t *value)
45{
46 const char *data;
47 uint32_t size;
48
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010049 if (!fdt_read_property(node, name, &data, &size))
50 return false;
51
52 switch (size) {
53 case sizeof(uint32_t):
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010054 case sizeof(uint64_t):
Wedson Almeida Filho87009642018-07-02 10:20:07 +010055 *value = convert_number(data, size);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010056 break;
57
58 default:
59 return false;
60 }
61
62 return true;
63}
64
65bool fdt_write_number(struct fdt_node *node, const char *name, uint64_t value)
66{
67 const char *data;
68 uint32_t size;
69 union {
70 volatile uint64_t v;
71 char a[8];
72 } t;
73
74 if (!fdt_read_property(node, name, &data, &size))
75 return false;
76
77 switch (size) {
78 case sizeof(uint32_t):
Andrew Scull29d40392018-07-16 11:46:59 +010079 *(uint32_t *)data = be32toh(value);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010080 break;
81
82 case sizeof(uint64_t):
Andrew Scull29d40392018-07-16 11:46:59 +010083 t.v = be64toh(value);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010084 memcpy((void *)data, t.a, sizeof(uint64_t));
85 break;
86
87 default:
88 return false;
89 }
90
91 return true;
92}
93
94static void relocate(const char *from, size_t size)
95{
96 extern char bin_end[];
97 size_t tmp = (size_t)&bin_end[0];
98 char *dest = (char *)((tmp + 0x80000 - 1) & ~(0x80000 - 1));
99 dlog("bin_end is at %p, copying to %p\n", &bin_end[0], dest);
100 memcpy(dest, from, size);
101}
102
103/* TODO: Remove this. */
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100104struct vm primary_vm;
105struct vm secondary_vm[MAX_VMS];
106uint32_t secondary_vm_count = 0;
107
108static void find_memory_range(const struct fdt_node *root,
109 uint64_t *block_start, uint64_t *block_size)
110{
111 struct fdt_node n = *root;
112 const char *name;
113 uint64_t address_size;
114 uint64_t size_size;
115 uint64_t entry_size;
116
117 /* Get the sizes of memory range addresses and sizes. */
118 if (fdt_read_number(&n, "#address-cells", &address_size))
119 address_size *= sizeof(uint32_t);
120 else
121 address_size = sizeof(uint32_t);
122
123 if (fdt_read_number(&n, "#size-cells", &size_size))
124 size_size *= sizeof(uint32_t);
125 else
126 size_size = sizeof(uint32_t);
127
128 entry_size = address_size + size_size;
129
130 /* Look for nodes with the device_type set to "memory". */
131 if (!fdt_first_child(&n, &name))
132 return;
133
134 do {
135 const char *data;
136 uint32_t size;
137 if (!fdt_read_property(&n, "device_type", &data, &size) ||
138 size != sizeof("memory") ||
139 memcmp(data, "memory", sizeof("memory")) != 0 ||
140 !fdt_read_property(&n, "reg", &data, &size)) {
141 continue;
142 }
143
144 /* Traverse all memory ranges within this node. */
145 while (size >= entry_size) {
146 uint64_t addr = convert_number(data, address_size);
147 uint64_t len = convert_number(data + address_size,
148 size_size);
149
150 if (len > *block_size) {
151 /* Remember the largest range we've found. */
152 *block_start = addr;
153 *block_size = len;
154 }
155
156 size -= entry_size;
157 data += entry_size;
158 }
159 } while (fdt_next_sibling(&n, &name));
160
161 /* TODO: Check for "reserved-memory" nodes. */
162}
163
164/**
165 * Finds the memory region where initrd is stored, and udpates the fdt node
166 * cursor to the node called "chosen".
167 */
168static bool find_initrd(struct fdt_node *n, uint64_t *begin, uint64_t *end)
169{
170 if (!fdt_find_node(n, "chosen\0")) {
171 dlog("Unable to find 'chosen'\n");
172 return false;
173 }
174
175 if (!fdt_read_number(n, "linux,initrd-start", begin)) {
176 dlog("Unable to read linux,initrd-start\n");
177 return false;
178 }
179
180 if (!fdt_read_number(n, "linux,initrd-end", end)) {
181 dlog("Unable to read linux,initrd-end\n");
182 return false;
183 }
184
185 return true;
186}
187
188struct memiter {
189 const char *next;
190 const char *limit;
191};
192
193static void memiter_init(struct memiter *it, const void *data, size_t size)
194{
195 it->next = data;
196 it->limit = it->next + size;
197}
198
199static bool memiter_isspace(struct memiter *it)
200{
201 switch (*it->next) {
202 case ' ':
203 case '\t':
204 case '\n':
205 case '\r':
206 return true;
207 default:
208 return false;
209 }
210}
211
212static void memiter_skip_space(struct memiter *it)
213{
214 while (it->next < it->limit && memiter_isspace(it))
215 it->next++;
216}
217
218static bool memiter_iseq(const struct memiter *it, const char *str)
219{
220 size_t len = strlen(str);
221 if (len != it->limit - it->next)
222 return false;
223 return memcmp(it->next, str, len) == 0;
224}
225
226static bool memiter_parse_str(struct memiter *it, struct memiter *str)
227{
228 /* Skip all white space and fail if we reach the end of the buffer. */
229 memiter_skip_space(it);
230 if (it->next >= it->limit)
231 return false;
232
233 str->next = it->next;
234
235 /* Find the end of the string. */
236 while (it->next < it->limit && !memiter_isspace(it))
237 it->next++;
238
239 str->limit = it->next;
240
241 return true;
242}
243
244static bool memiter_parse_uint(struct memiter *it, uint64_t *value)
245{
246 uint64_t v = 0;
247
248 /* Skip all white space and fail if we reach the end of the buffer. */
249 memiter_skip_space(it);
250 if (it->next >= it->limit)
251 return false;
252
253 /* Fail if it's not a number. */
254 if (*it->next < '0' && *it->next > '9')
255 return false;
256
257 /* Parse the number. */
258 do {
259 v = v * 10 + *it->next - '0';
260 it->next++;
261 } while (it->next < it->limit && *it->next >= '0' && *it->next <= '9');
262
263 *value = v;
264
265 return true;
266}
267
268static bool memiter_find_file(struct cpio *c, const struct memiter *filename,
269 struct memiter *it)
270{
271 const char *fname;
272 const void *fcontents;
273 size_t fsize;
274 struct cpio_iter iter;
275
276 cpio_init_iter(c, &iter);
277
278 while (cpio_next(&iter, &fname, &fcontents, &fsize)) {
279 if (memiter_iseq(filename, fname)) {
280 memiter_init(it, fcontents, fsize);
281 return true;
282 }
283 }
284
285 return false;
286}
287
288static bool find_file(struct cpio *c, const char *name, struct memiter *it)
289{
290 const char *fname;
291 const void *fcontents;
292 size_t fsize;
293 struct cpio_iter iter;
294
295 cpio_init_iter(c, &iter);
296
297 while (cpio_next(&iter, &fname, &fcontents, &fsize)) {
298 if (!strcmp(fname, name)) {
299 memiter_init(it, fcontents, fsize);
300 return true;
301 }
302 }
303
304 return false;
305}
306
307static bool load_secondary(struct cpio *c,
308 uint64_t mem_start, uint64_t *mem_size)
309{
310 struct memiter it;
311 struct memiter str;
312 uint64_t mem;
313 uint64_t cpu;
314 uint32_t count;
315
316 if (!find_file(c, "vms.txt", &it)) {
317 dlog("Unable to find vms.txt\n");
318 return false;
319 }
320
321 for (count = 0; memiter_parse_uint(&it, &mem) &&
322 memiter_parse_uint(&it, &cpu) &&
323 memiter_parse_str(&it, &str) &&
324 count < MAX_VMS; count++) {
325 struct memiter kernel;
326
327 if (!memiter_find_file(c, &str, &kernel)) {
328 dlog("Unable to load kernel for vm %u\n", count);
329 continue;
330 }
331
332 if (mem > *mem_size) {
333 dlog("Not enough memory for vm %u (%u bytes)\n", count,
334 mem);
335 continue;
336 }
337
338 if (mem < kernel.limit - kernel.next) {
339 dlog("Kernel is larger than available memory for vm %u\n", count);
340 continue;
341 }
342
343 *mem_size -= mem;
344 memcpy((void *)(mem_start + *mem_size), kernel.next,
345 kernel.limit - kernel.next);
346
347 dlog("Loaded VM%u with %u vcpus, entry at 0x%x\n", count, cpu,
348 mem_start + *mem_size);
349 vm_init(secondary_vm + count, cpu);
350 vm_start_vcpu(secondary_vm + count, 0,
351 mem_start + *mem_size, 0, false);
352 }
353
354 secondary_vm_count = count;
355
356 return true;
357}
358
359static bool load_primary(struct cpio *c, struct fdt_node *chosen)
360{
361 struct memiter it;
362
363 if (!find_file(c, "vmlinuz", &it)) {
364 dlog("Unable to find vmlinuz\n");
365 return false;
366 }
367
368 relocate(it.next, it.limit - it.next);
369
370 if (!find_file(c, "initrd.img", &it)) {
371 dlog("Unable to find initrd.img\n");
372 return false;
373 }
374
375 /* Patch FDT to point to new ramdisk. */
376 if (!fdt_write_number(chosen, "linux,initrd-start", (size_t)it.next)) {
377 dlog("Unable to write linux,initrd-start\n");
378 return false;
379 }
380
381 if (!fdt_write_number(chosen, "linux,initrd-end", (size_t)it.limit)) {
382 dlog("Unable to write linux,initrd-end\n");
383 return false;
384 }
385
386 /*
387 * Patch fdt to reserve memory.
388 */
389 {
390 size_t tmp = (size_t)&relocate;
391 tmp = (tmp + 0x80000 - 1) & ~(0x80000 - 1);
392
393 fdt_add_mem_reservation(fdt, tmp & ~0xfffff, 0x80000);
394 vm_init(&primary_vm, MAX_CPUS);
395 vm_start_vcpu(&primary_vm, 0, tmp, (size_t)fdt, true);
396 }
397
398 return true;
399}
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100400
401static void one_time_init(void)
402{
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100403 dlog("Initializing hafnium\n");
404
Wedson Almeida Filho3fcbcff2018-07-10 23:53:39 +0100405 cpu_module_init();
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100406
407 /* TODO: Code below this point should be removed from this function. */
408 /* TODO: Remove this. */
409
410 do {
411 struct fdt_node n;
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100412 uint64_t mem_start = 0;
413 uint64_t mem_size = 0;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100414
415 fdt_root_node(&n, fdt);
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100416 fdt_find_child(&n, "");
417
418 /* TODO: Use this. */
419 find_memory_range(&n, &mem_start, &mem_size);
420 dlog("Memory range: 0x%x - 0x%x\n", mem_start,
421 mem_start + mem_size - 1);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100422
423 uint64_t begin;
424 uint64_t end;
425
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100426 if (!find_initrd(&n, &begin, &end))
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100427 break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100428
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100429 dlog("Ramdisk range: 0x%x - 0x%x\n", begin, end - 1);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100430
431 struct cpio c;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100432 cpio_init(&c, (void *)begin, end - begin);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100433
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100434 load_secondary(&c, mem_start, &mem_size);
435 load_primary(&c, &n);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100436 } while (0);
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100437
438 arch_set_vm_mm(&primary_vm.page_table);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100439}
440
441/*
442 * The entry point of CPUs when they are turned on. It is supposed to initialise
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100443 * all state and return the first vCPU to run.
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100444 */
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100445struct vcpu *cpu_main(void)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100446{
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100447 struct cpu *c = cpu();
448
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100449 /* Do global one-time initialization just once. */
450 static atomic_flag inited = ATOMIC_FLAG_INIT;
451 if (!atomic_flag_test_and_set_explicit(&inited, memory_order_acq_rel))
452 one_time_init();
453
Wedson Almeida Filho3fcbcff2018-07-10 23:53:39 +0100454 dlog("Starting up cpu %d\n", cpu_index(c));
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100455
Wedson Almeida Filho3fcbcff2018-07-10 23:53:39 +0100456 return primary_vm.vcpus + cpu_index(c);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100457}