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/protocol/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
27static 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
38static void append_board_boot_item(zbi_header_t* container);
39
40#if USE_DEVICE_TREE_CPU_COUNT
41static 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
50typedef enum {
51    NODE_NONE,
52    NODE_CHOSEN,
53    NODE_MEMORY,
54    NODE_CPU,
55    NODE_INTC,
56} node_t;
57
58typedef 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
69static 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
94static 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.
158static 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
186static 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
216typedef struct {} device_tree_context_t;
217static void* read_device_tree(void* device_tree, device_tree_context_t* ctx) {
218    return NULL;
219}
220static void append_from_device_tree(zbi_header_t* zbi,
221                                    device_tree_context_t* ctx) {
222}
223
224#endif // HAS_DEVICE_TREE
225
226static 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
236static 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
248static 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
258boot_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