1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "boot-shim.h"
6 #include "debug.h"
7 #include "devicetree.h"
8 #include "util.h"
9
10 #include <ddk/platform-defs.h>
11 #include <libzbi/zbi.h>
12 #include <limits.h>
13 #include <stdbool.h>
14 #include <stddef.h>
15 #include <string.h>
16 #include <zircon/boot/driver-config.h>
17
18 // uncomment to dump device tree at boot
19 // #define PRINT_DEVICE_TREE
20
21 // Uncomment to list ZBI items.
22 #ifndef PRINT_ZBI
23 #define PRINT_ZBI 0
24 #endif
25
26 // used in boot-shim-config.h and in this file below
append_boot_item(zbi_header_t * container,uint32_t type,uint32_t extra,const void * payload,uint32_t length)27 static void append_boot_item(zbi_header_t* container,
28 uint32_t type, uint32_t extra,
29 const void* payload, uint32_t length) {
30 zbi_result_t result = zbi_append_section(
31 container, SIZE_MAX, length, type, extra, 0, payload);
32 if (result != ZBI_RESULT_OK) {
33 fail("zbi_append_section failed\n");
34 }
35 }
36
37 // defined in boot-shim-config.h
38 static void append_board_boot_item(zbi_header_t* container);
39
40 #if USE_DEVICE_TREE_CPU_COUNT
41 static void set_cpu_count(uint32_t cpu_count);
42 #endif
43
44 // Include board specific definitions
45 #include "boot-shim-config.h"
46
47 #define ROUNDUP(a, b) (((a) + ((b)-1)) & ~((b)-1))
48
49 #if HAS_DEVICE_TREE
50 typedef enum {
51 NODE_NONE,
52 NODE_CHOSEN,
53 NODE_MEMORY,
54 NODE_CPU,
55 NODE_INTC,
56 } node_t;
57
58 typedef struct {
59 node_t node;
60 uintptr_t initrd_start;
61 size_t memory_base;
62 size_t memory_size;
63 char* cmdline;
64 size_t cmdline_length;
65 uint32_t cpu_count;
66 int gic_version;
67 } device_tree_context_t;
68
node_callback(int depth,const char * name,void * cookie)69 static int node_callback(int depth, const char *name, void *cookie) {
70 #ifdef PRINT_DEVICE_TREE
71 uart_puts("node: ");
72 uart_puts(name);
73 uart_puts("\n");
74 #endif
75
76 device_tree_context_t* ctx = cookie;
77
78 if (!strcmp(name, "chosen")) {
79 ctx->node = NODE_CHOSEN;
80 } else if (!strcmp(name, "memory") || !strcmp(name, "memory@00000000")) {
81 ctx->node = NODE_MEMORY;
82 } else if (!strncmp(name, "cpu@", 4)) {
83 ctx->node = NODE_CPU;
84 ctx->cpu_count++;
85 } else if (!strcmp(name, "intc")) {
86 ctx->node = NODE_INTC;
87 } else {
88 ctx->node = NODE_NONE;
89 }
90
91 return 0;
92 }
93
prop_callback(const char * name,uint8_t * data,uint32_t size,void * cookie)94 static int prop_callback(const char *name, uint8_t *data, uint32_t size, void *cookie) {
95 #ifdef PRINT_DEVICE_TREE
96 uart_puts(" prop: ");
97 uart_puts(name);
98 uart_puts(" size: ");
99 uart_print_hex(size);
100 #endif
101
102 device_tree_context_t* ctx = cookie;
103
104 switch (ctx->node) {
105 case NODE_CHOSEN:
106 if (!strcmp(name, "linux,initrd-start")) {
107 if (size == sizeof(uint32_t)) {
108 ctx->initrd_start = dt_rd32(data);
109 } else if (size == sizeof(uint64_t)) {
110 uint64_t most = dt_rd32(data);
111 uint64_t least = dt_rd32(data + 4);
112 ctx->initrd_start = (most << 32) | least;
113 } else {
114 fail("bad size for linux,initrd-start in device tree\n");
115 }
116 } else if (!strcmp(name, "bootargs")) {
117 ctx->cmdline = (char *)data;
118 ctx->cmdline_length = size;
119 }
120 break;
121 case NODE_MEMORY:
122 if (!strcmp(name, "reg") && size == 16) {
123 // memory size is big endian uint64_t at offset 0
124 uint64_t most = dt_rd32(data + 0);
125 uint64_t least = dt_rd32(data + 4);
126 ctx->memory_base = (most << 32) | least;
127 // memory size is big endian uint64_t at offset 8
128 most = dt_rd32(data + 8);
129 least = dt_rd32(data + 12);
130 ctx->memory_size = (most << 32) | least;
131 }
132 break;
133 case NODE_INTC:
134 if (!strcmp(name, "compatible")) {
135 if (!strncmp((const char *)data, "arm,gic-v3", size)) {
136 ctx->gic_version = 3;
137 } else if (!strncmp((const char *)data, "arm,cortex-a15-gic", size)) {
138 ctx->gic_version = 2;
139 }
140 #ifdef PRINT_DEVICE_TREE
141 uart_puts(" gic version ");
142 uart_print_hex(ctx->gic_version);
143 #endif
144 }
145 break;
146 default:
147 ;
148 }
149
150 #ifdef PRINT_DEVICE_TREE
151 uart_puts("\n");
152 #endif
153
154 return 0;
155 }
156
157 // Parse the device tree to find our ZBI, kernel command line, and RAM size.
read_device_tree(void * device_tree,device_tree_context_t * ctx)158 static void* read_device_tree(void* device_tree, device_tree_context_t* ctx) {
159 ctx->node = NODE_NONE;
160 ctx->initrd_start = 0;
161 ctx->memory_base = 0;
162 ctx->memory_size = 0;
163 ctx->cmdline = NULL;
164 ctx->cpu_count = 0;
165 ctx->gic_version = -1;
166
167 devicetree_t dt;
168 dt.error = uart_puts;
169 int ret = dt_init(&dt, device_tree, 0xffffffff);
170 if (ret) {
171 fail("dt_init failed\n");
172 }
173 dt_walk(&dt, node_callback, prop_callback, ctx);
174
175 #if USE_DEVICE_TREE_CPU_COUNT
176 set_cpu_count(ctx->cpu_count);
177 #endif
178 #if USE_DEVICE_TREE_GIC_VERSION
179 set_gic_version(ctx->gic_version);
180 #endif
181
182 // Use the device tree initrd as the ZBI.
183 return (void*)ctx->initrd_start;
184 }
185
append_from_device_tree(zbi_header_t * zbi,device_tree_context_t * ctx)186 static void append_from_device_tree(zbi_header_t* zbi,
187 device_tree_context_t* ctx) {
188 // look for optional RAM size in device tree
189 // do this last so device tree can override value in boot-shim-config.h
190 if (ctx->memory_size) {
191 zbi_mem_range_t mem_range;
192 mem_range.paddr = ctx->memory_base;
193 mem_range.length = ctx->memory_size;
194 mem_range.type = ZBI_MEM_RANGE_RAM;
195
196 uart_puts("Setting RAM base and size device tree value: ");
197 uart_print_hex(ctx->memory_base);
198 uart_puts(" ");
199 uart_print_hex(ctx->memory_size);
200 uart_puts("\n");
201 append_boot_item(zbi, ZBI_TYPE_MEM_CONFIG, 0,
202 &mem_range, sizeof(mem_range));
203 } else {
204 uart_puts("RAM size not found in device tree\n");
205 }
206
207 // append kernel command line
208 if (ctx->cmdline && ctx->cmdline_length) {
209 append_boot_item(zbi, ZBI_TYPE_CMDLINE, 0,
210 ctx->cmdline, ctx->cmdline_length);
211 }
212 }
213
214 #else
215
216 typedef struct {} device_tree_context_t;
read_device_tree(void * device_tree,device_tree_context_t * ctx)217 static void* read_device_tree(void* device_tree, device_tree_context_t* ctx) {
218 return NULL;
219 }
append_from_device_tree(zbi_header_t * zbi,device_tree_context_t * ctx)220 static void append_from_device_tree(zbi_header_t* zbi,
221 device_tree_context_t* ctx) {
222 }
223
224 #endif // HAS_DEVICE_TREE
225
dump_words(const char * what,const void * data)226 static void dump_words(const char* what, const void* data) {
227 uart_puts(what);
228 const uint64_t* words = data;
229 for (int i = 0; i < 8; ++i) {
230 uart_puts(i == 4 ? "\n " : " ");
231 uart_print_hex(words[i]);
232 }
233 uart_puts("\n");
234 }
235
list_zbi_cb(zbi_header_t * item,void * payload,void * ctx)236 static zbi_result_t list_zbi_cb(zbi_header_t* item, void* payload, void* ctx) {
237 uart_print_hex((uintptr_t)item);
238 uart_puts(": length=0x");
239 uart_print_hex(item->length);
240 uart_puts(" type=0x");
241 uart_print_hex(item->type);
242 uart_puts(" extra=0x");
243 uart_print_hex(item->extra);
244 uart_puts("\n");
245 return ZBI_RESULT_OK;
246 }
247
list_zbi(zbi_header_t * zbi)248 static void list_zbi(zbi_header_t* zbi) {
249 uart_puts("ZBI container length 0x");
250 uart_print_hex(zbi->length);
251 uart_puts("\n");
252 zbi_for_each(zbi, &list_zbi_cb, NULL);
253 uart_puts("ZBI container ends 0x");
254 uart_print_hex((uintptr_t)(zbi + 1) + zbi->length);
255 uart_puts("\n");
256 }
257
boot_shim(void * device_tree)258 boot_shim_return_t boot_shim(void* device_tree) {
259 uart_puts("boot_shim: hi there!\n");
260
261 zircon_kernel_t* kernel = NULL;
262
263 // Check the ZBI from device tree.
264 device_tree_context_t ctx;
265 zbi_header_t* zbi = read_device_tree(device_tree, &ctx);
266 if (zbi != NULL) {
267 zbi_header_t* bad_hdr;
268 zbi_result_t check = zbi_check(zbi, &bad_hdr);
269 if (check == ZBI_RESULT_OK && zbi->length > sizeof(zbi_header_t) &&
270 zbi[1].type == ZBI_TYPE_KERNEL_ARM64) {
271 kernel = (zircon_kernel_t*) zbi;
272 } else {
273 // No valid ZBI in device tree.
274 // We will look in embedded_zbi instead.
275 zbi = NULL;
276 }
277 }
278
279 // If there is a complete ZBI from device tree, ignore whatever might
280 // have been appended to the shim image. If not, the kernel is appended.
281 if (kernel == NULL) {
282 zbi_header_t* bad_hdr;
283 zbi_result_t check = zbi_check(&embedded_zbi, &bad_hdr);
284 if (check != ZBI_RESULT_OK) {
285 fail("no ZBI from device tree and no valid ZBI embedded\n");
286 }
287 if (embedded_zbi.hdr_file.length > sizeof(zbi_header_t) &&
288 embedded_zbi.hdr_kernel.type == ZBI_TYPE_KERNEL_ARM64) {
289 kernel = &embedded_zbi;
290 } else {
291 fail("no ARM64 kernel in ZBI from device tree or embedded ZBI\n");
292 }
293 }
294
295 // If there was no ZBI at all from device tree then use the embedded ZBI
296 // along with the embedded kernel. Otherwise always use the ZBI from
297 // device tree, whether the kernel is in that ZBI or was embedded.
298 if (zbi == NULL) {
299 zbi = &kernel->hdr_file;
300 }
301
302 // Add board-specific ZBI items.
303 append_board_boot_item(zbi);
304
305 // Append items from device tree.
306 append_from_device_tree(zbi, &ctx);
307
308 uint8_t* const kernel_end =
309 (uint8_t*)&kernel->data_kernel +
310 kernel->hdr_kernel.length +
311 kernel->data_kernel.reserve_memory_size;
312
313 uart_puts("Kernel at ");
314 uart_print_hex((uintptr_t)kernel);
315 uart_puts(" to ");
316 uart_print_hex((uintptr_t)kernel_end);
317 uart_puts(" reserved ");
318 uart_print_hex(kernel->data_kernel.reserve_memory_size);
319 uart_puts("\nZBI at ");
320 uart_print_hex((uintptr_t)zbi);
321 uart_puts(" to ");
322 uart_print_hex((uintptr_t)(zbi + 1) + zbi->length);
323 uart_puts("\n");
324
325 if ((uint8_t*)zbi < kernel_end && zbi != &kernel->hdr_file) {
326 fail("expected kernel to be loaded lower in memory than initrd\n");
327 }
328
329 if (PRINT_ZBI) {
330 list_zbi(zbi);
331 }
332
333 if (zbi == &kernel->hdr_file || (uintptr_t)zbi % 4096 != 0) {
334 // The ZBI needs to be page-aligned, so move it up.
335 // If it's a complete ZBI, splice out the kernel and move it higher.
336 zbi_header_t* old = zbi;
337 zbi = (void*)(((uintptr_t)old + 4095) & -(uintptr_t)4096);
338 if (old == &kernel->hdr_file) {
339 // Length of the kernel item payload, without header.
340 uint32_t kernel_len = kernel->hdr_kernel.length;
341
342 // Length of the ZBI container, including header, without kernel.
343 uint32_t zbi_len = kernel->hdr_file.length - kernel_len;
344
345 uart_puts("Splitting kernel ");
346 uart_print_hex(kernel_len);
347 uart_puts(" from ZBI ");
348 uart_print_hex(zbi_len);
349
350 // First move the kernel up out of the way.
351 uintptr_t zbi_end = (uintptr_t)(old + 1) + old->length;
352 if (zbi_end < (uintptr_t)zbi + zbi_len) {
353 zbi_end = (uintptr_t)zbi + zbi_len;
354 }
355 kernel = (void*)((zbi_end + KERNEL_ALIGN - 1) &
356 -(uintptr_t)KERNEL_ALIGN);
357 uart_puts("\nKernel to ");
358 uart_print_hex((uintptr_t)kernel);
359 memcpy(kernel, old, (2 * sizeof(*zbi)) + kernel_len);
360 // Fix up the kernel's solo container size.
361 kernel->hdr_file.length = sizeof(*zbi) + kernel_len;
362
363 // Now move the ZBI into its aligned place and fix up the
364 // container header to exclude the kernel.
365 uart_puts(" ZBI to ");
366 uart_print_hex((uintptr_t)zbi);
367 zbi_header_t header = *old;
368 header.length -= kernel->hdr_file.length;
369 void* payload = (uint8_t*)(old + 1) + kernel->hdr_file.length;
370 memmove(zbi + 1, payload, header.length);
371 *zbi = header;
372
373 uart_puts("\nKernel container length ");
374 uart_print_hex(kernel->hdr_file.length);
375 uart_puts(" ZBI container length ");
376 uart_print_hex(zbi->length);
377 uart_puts("\n");
378 } else {
379 uart_puts("Relocating whole ZBI for alignment\n");
380 memmove(zbi, old, sizeof(*old) + old->length);
381 }
382 }
383
384 if ((uintptr_t)kernel % KERNEL_ALIGN != 0) {
385 // The kernel has to be relocated for alignment.
386 uart_puts("Relocating kernel for alignment\n");
387 zbi_header_t* old = &kernel->hdr_file;
388 kernel = (void*)(((uintptr_t)(zbi + 1) + zbi->length +
389 KERNEL_ALIGN - 1) & -(uintptr_t)KERNEL_ALIGN);
390 memmove(kernel, old, sizeof(*old) + old->length);
391 }
392
393 boot_shim_return_t result = {
394 .entry = (uintptr_t)kernel + kernel->data_kernel.entry,
395 .zbi = zbi,
396 };
397 uart_puts("Entering kernel at ");
398 uart_print_hex(result.entry);
399 uart_puts(" with ZBI ");
400 uart_print_hex((uintptr_t)result.zbi);
401 uart_puts("\n");
402 return result;
403 }
404