From 474ee5a81880dcf60789e7c2a3054b3afb34c3ca Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Mon, 21 May 2018 13:29:31 +1200 Subject: RISC-V: Support separate firmware and kernel payload Support for separate firmware and kernel payload is added by updating BBL to read optional preloaded kernel address attributes from device-tree using a similar mechanism to that used to pass init ramdisk addresses to linux kernel. chosen { riscv,kernel-start = <0x00000000 0x80200000>; riscv,kernel-end = <0x00000000 0x80590634>; }; These attributes are added by QEMU and read by BBL when combining -bios and -kernel options. e.g. $ qemu-system-riscv64 -machine virt -bios bbl -kernel vmlinux With this change, bbl can be compiled without --with-payload and the dummy payload alignment is altered to make the memory footprint of the firmware-only bbl smaller. The dummy payload message is updated to indicate the alternative load method. This load method could also be supported by a first stage boot loader that reads seperate firmware and kernel from SPI flash. The main advantage of this new mechanism is that it eases kernel development by avoiding the riscv-pk packaging step after kernel builds, makes building per repository artefacts for CI simpler, and mimics bootloaders on other platforms that can load a kernel image file directly. Ultimately BBL should use an SPI driver to load the kernel image however this mechanism supports use cases such such as QEMU's -bios, -kernel and -initrd options following examples from other platforms that pass kernel entry to firmware via device-tree. Cc: Palmer Dabbelt Cc: Alistair Francis Signed-off-by: Michael Clark --- bbl/bbl.ac | 9 +++++-- bbl/bbl.c | 16 +++++++++---- bbl/payload.S | 8 +++++++ config.h.in | 3 +++ configure | 15 +++++++++--- dummy_payload/dummy_entry.S | 10 ++++++-- machine/fdt.c | 57 +++++++++++++++++++++++++++++++++++++++++++++ machine/fdt.h | 5 ++++ machine/minit.c | 3 +++ 9 files changed, 115 insertions(+), 11 deletions(-) diff --git a/bbl/bbl.ac b/bbl/bbl.ac index 80d3b06..55ff50b 100644 --- a/bbl/bbl.ac +++ b/bbl/bbl.ac @@ -4,8 +4,13 @@ AS_IF([test "x$enable_logo" == "xyes"], [ ]) AC_ARG_WITH([payload], AS_HELP_STRING([--with-payload], [Set ELF payload for bbl]), - [AC_SUBST([BBL_PAYLOAD], $with_payload, [Kernel payload for bbl])], - [AC_SUBST([BBL_PAYLOAD], [dummy_payload], [Kernel payload for bbl])]) + [ + AC_SUBST([BBL_PAYLOAD], $with_payload, [Kernel payload for bbl]) + AC_DEFINE(RELAXED_ALIGNMENT,[0],[Use relaxed payload alignment]) + ], [ + AC_SUBST([BBL_PAYLOAD], [dummy_payload], [Kernel payload for bbl]) + AC_DEFINE(RELAXED_ALIGNMENT,[1],[Use relaxed payload alignment]) + ]) AC_ARG_WITH([logo], AS_HELP_STRING([--with-logo], [Specify a better logo]), [AC_SUBST([BBL_LOGO_FILE], $with_logo, [Logo for bbl])], diff --git a/bbl/bbl.c b/bbl/bbl.c index 1b96a9d..93746ff 100644 --- a/bbl/bbl.c +++ b/bbl/bbl.c @@ -7,13 +7,21 @@ #include "fdt.h" #include +extern char _payload_start, _payload_end; /* internal payload */ static const void* entry_point; long disabled_hart_mask; static uintptr_t dtb_output() { - extern char _payload_end; - uintptr_t end = (uintptr_t) &_payload_end; + /* + * Place DTB after the payload, either the internal payload or a + * preloaded external payload specified in device-tree, if present. + * + * Note: linux kernel calls __va(dtb) to get the device-tree virtual + * address. The kernel's virtual mapping begins at its load address, + * thus mandating device-tree is in physical memory after the kernel. + */ + uintptr_t end = kernel_end ? (uintptr_t)kernel_end : (uintptr_t)&_payload_end; return (end + MEGAPAGE_SIZE - 1) / MEGAPAGE_SIZE * MEGAPAGE_SIZE; } @@ -53,7 +61,6 @@ void boot_other_hart(uintptr_t unused __attribute__((unused))) void boot_loader(uintptr_t dtb) { - extern char _payload_start; filter_dtb(dtb); #ifdef PK_ENABLE_LOGO print_logo(); @@ -62,6 +69,7 @@ void boot_loader(uintptr_t dtb) fdt_print(dtb_output()); #endif mb(); - entry_point = &_payload_start; + /* Use optional FDT preloaded external payload if present */ + entry_point = kernel_start ? kernel_start : &_payload_start; boot_other_hart(0); } diff --git a/bbl/payload.S b/bbl/payload.S index 6a175aa..b6797aa 100644 --- a/bbl/payload.S +++ b/bbl/payload.S @@ -1,7 +1,15 @@ +#include "config.h" #include "encoding.h" .section ".payload","a",@progbits + +#if RELAXED_ALIGNMENT + /* align payload minimally */ + .align 3 +#else + /* align payload to megapage */ .align RISCV_PGSHIFT + RISCV_PGLEVEL_BITS +#endif .globl _payload_start, _payload_end _payload_start: diff --git a/config.h.in b/config.h.in index 4674a2e..a350c78 100644 --- a/config.h.in +++ b/config.h.in @@ -42,6 +42,9 @@ /* Define if the DTS is to be displayed */ #undef PK_PRINT_DEVICE_TREE +/* Use relaxed payload alignment */ +#undef RELAXED_ALIGNMENT + /* Define if subproject MCPPBS_SPROJ_NORM is enabled */ #undef SOFTFLOAT_ENABLED diff --git a/configure b/configure index 5af7cee..058eea1 100755 --- a/configure +++ b/configure @@ -4092,7 +4092,6 @@ case "${BUILD_32BIT}" in CFLAGS="$default_CFLAGS" LDFLAGS= install_subdir=$host_alias - ;; esac @@ -4251,10 +4250,20 @@ fi # Check whether --with-payload was given. if test "${with_payload+set}" = set; then : - withval=$with_payload; BBL_PAYLOAD=$with_payload + withval=$with_payload; + BBL_PAYLOAD=$with_payload + + +$as_echo "#define RELAXED_ALIGNMENT 0" >>confdefs.h + else - BBL_PAYLOAD=dummy_payload + + BBL_PAYLOAD=dummy_payload + + +$as_echo "#define RELAXED_ALIGNMENT 1" >>confdefs.h + fi diff --git a/dummy_payload/dummy_entry.S b/dummy_payload/dummy_entry.S index 92d4652..2473d8c 100644 --- a/dummy_payload/dummy_entry.S +++ b/dummy_payload/dummy_entry.S @@ -18,5 +18,11 @@ _start: .data str: - .asciz "This is bbl's dummy_payload. To boot a real kernel, reconfigure\n\ -bbl with the flag --with-payload=PATH, then rebuild bbl.\n" + .asciz "This is bbl's dummy_payload. To boot a real kernel, reconfigure bbl +with the flag --with-payload=PATH, then rebuild bbl. Alternatively, +bbl can be used in firmware-only mode by adding device-tree nodes +for an external payload and use QEMU's -bios and -kernel options.\n + chosen { + riscv,kernel-start = ; + riscv,kernel-end = ; + };\n\n" diff --git a/machine/fdt.c b/machine/fdt.c index 061b19e..27f5462 100644 --- a/machine/fdt.c +++ b/machine/fdt.c @@ -548,6 +548,63 @@ void filter_compat(uintptr_t fdt, const char *compat) fdt_scan(fdt, &cb); } +//////////////////////////////////////////// CHOSEN SCAN //////////////////////////////////////// + +struct chosen_scan { + const struct fdt_scan_node *chosen; + void* kernel_start; + void* kernel_end; +}; + +static void chosen_open(const struct fdt_scan_node *node, void *extra) +{ + struct chosen_scan *scan = (struct chosen_scan *)extra; + if (!strcmp(node->name, "chosen")) { + scan->chosen = node; + } +} + +static int chosen_close(const struct fdt_scan_node *node, void *extra) +{ + struct chosen_scan *scan = (struct chosen_scan *)extra; + if (scan->chosen && scan->chosen == node) { + scan->chosen = NULL; + } + return 0; +} + +static void chosen_prop(const struct fdt_scan_prop *prop, void *extra) +{ + struct chosen_scan *scan = (struct chosen_scan *)extra; + uint64_t val; + if (!scan->chosen) return; + if (!strcmp(prop->name, "riscv,kernel-start")) { + fdt_get_address(prop->node->parent, prop->value, &val); + scan->kernel_start = (void*)val; + } else if (!strcmp(prop->name, "riscv,kernel-end")) { + fdt_get_address(prop->node->parent, prop->value, &val); + scan->kernel_end = (void*)val; + } +} + +void query_chosen(uintptr_t fdt) +{ + struct fdt_cb cb; + struct chosen_scan chosen; + + memset(&cb, 0, sizeof(cb)); + cb.open = chosen_open; + cb.close = chosen_close; + cb.prop = chosen_prop; + + memset(&chosen, 0, sizeof(chosen)); + cb.extra = &chosen; + + fdt_scan(fdt, &cb); + kernel_start = chosen.kernel_start; + kernel_end = chosen.kernel_end; +} + //////////////////////////////////////////// HART FILTER //////////////////////////////////////// struct hart_filter { diff --git a/machine/fdt.h b/machine/fdt.h index d436778..6c8a6fe 100644 --- a/machine/fdt.h +++ b/machine/fdt.h @@ -59,6 +59,7 @@ void query_mem(uintptr_t fdt); void query_harts(uintptr_t fdt); void query_plic(uintptr_t fdt); void query_clint(uintptr_t fdt); +void query_chosen(uintptr_t fdt); // Remove information from FDT void filter_harts(uintptr_t fdt, long *disabled_hart_mask); @@ -68,6 +69,10 @@ void filter_compat(uintptr_t fdt, const char *compat); // The hartids of available harts extern uint64_t hart_mask; +// Optional FDT preloaded external payload +extern void* kernel_start; +extern void* kernel_end; + #ifdef PK_PRINT_DEVICE_TREE // Prints the device tree to the console as a DTS void fdt_print(uintptr_t fdt); diff --git a/machine/minit.c b/machine/minit.c index 32c6fd7..ceae6b7 100644 --- a/machine/minit.c +++ b/machine/minit.c @@ -16,6 +16,8 @@ uintptr_t mem_size; volatile uint64_t* mtime; volatile uint32_t* plic_priorities; size_t plic_ndevs; +void* kernel_start; +void* kernel_end; static void mstatus_init() { @@ -162,6 +164,7 @@ void init_first_hart(uintptr_t hartid, uintptr_t dtb) query_harts(dtb); query_clint(dtb); query_plic(dtb); + query_chosen(dtb); wake_harts(); -- cgit v1.1