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