From patchwork Wed May 23 00:15:17 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael Clark X-Patchwork-Id: 918664 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=nongnu.org (client-ip=2001:4830:134:3::11; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=sifive.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=sifive.com header.i=@sifive.com header.b="ft8t+ERF"; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 40rDLp0Sg5z9s0q for ; Wed, 23 May 2018 10:45:05 +1000 (AEST) Received: from localhost ([::1]:58623 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fLHtn-0002Pt-PT for incoming@patchwork.ozlabs.org; Tue, 22 May 2018 20:45:03 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:43066) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fLHUI-0000mV-BM for qemu-devel@nongnu.org; Tue, 22 May 2018 20:18:44 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fLHUD-0001bA-K0 for qemu-devel@nongnu.org; Tue, 22 May 2018 20:18:42 -0400 Received: from mail-pg0-x242.google.com ([2607:f8b0:400e:c05::242]:42649) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1fLHUD-0001aS-4F for qemu-devel@nongnu.org; Tue, 22 May 2018 20:18:37 -0400 Received: by mail-pg0-x242.google.com with SMTP id p9-v6so8567699pgc.9 for ; Tue, 22 May 2018 17:18:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sifive.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=8eWmWEsnjbCzU5g1uuOeJ8aBWer5FYlodp0qau506iw=; b=ft8t+ERFlMu6WC8qgShUM6zLji7cZTwu7EFqEmghbxhXBdU6bFfSAiNJIOJ7UM2oak vVysS8Fr004vMYkgwF9/pfm3G+X20U2gUiSaocy7JrcGli8E4ag9HyDLLQ3g0mkCsdj4 BEpt/IBbr/H5vV1hCtv8BX+ShKHuCd1sM1lRH6nIIFZjiElvGp0VH1HQlqjQ/MC5JUhv QUC8oIJKvkLYnAaKPmwVFuiE1cCyaYR1BrmezScvkRWPXMc+pL56IXujwRRZpzNgOFD6 LuRmQ17vk5JLuz4lVyfu1IMnN+YuAnTk3G59aCimxs74+kOf18XWvUtDtaMynZmc7ZiV vRXQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=8eWmWEsnjbCzU5g1uuOeJ8aBWer5FYlodp0qau506iw=; b=IM5/5RWlPWV3p8veIZpI7jvgEe/XWht181pcFXpY3+tw3o63ykWH74m4CPudB3ax0G Y3jF0osJKCuzMJw6/q6gCll4twg7ozWsqCuw0wdMzJKGg3RpxsmSDaChkc6aoHWPzdai Hojeak9vSiDIOWIL4ilMDnPbwVXL4JVyoGh1+swzFUoM2hJESA+EQtJucWnujbqPqs2B Vz0ra+TqNWPvqpWSMFSOpCIZS3cpncQgSCRjUNr/5hkGdsfzlgzzFRyN/zqPKzElwbdA r3QnLwePiAow6NYodN5yovqknUaG1wtuAtRWZchIpmgcyLhOHF1sdhHsoJEKeubOdEmr 5Ykw== X-Gm-Message-State: ALKqPwd+XrO094M7ODWDn/OmuMjb7VTeW76QCVaxCGcq8qCHhmxDWSyr 2mxOjyXH3DYw05bvxwTRH/mmOHMdrHU= X-Google-Smtp-Source: AB8JxZoyw0e7ThrmKrvlEroGifnEk3lUqavkwX8r94lnHupXceF1IDLw4N2CucIaMvCxFds/Abem6g== X-Received: by 2002:a62:32c6:: with SMTP id y189-v6mr563773pfy.241.1527034715850; Tue, 22 May 2018 17:18:35 -0700 (PDT) Received: from localhost.localdomain (122-58-167-22-fibre.bb.spark.co.nz. [122.58.167.22]) by smtp.gmail.com with ESMTPSA id j1-v6sm28935626pfh.95.2018.05.22.17.18.33 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 22 May 2018 17:18:35 -0700 (PDT) From: Michael Clark To: qemu-devel@nongnu.org Date: Wed, 23 May 2018 12:15:17 +1200 Message-Id: <1527034517-7851-31-git-send-email-mjc@sifive.com> X-Mailer: git-send-email 2.7.0 In-Reply-To: <1527034517-7851-1-git-send-email-mjc@sifive.com> References: <1527034517-7851-1-git-send-email-mjc@sifive.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2607:f8b0:400e:c05::242 Subject: [Qemu-devel] [PATCH v1 30/30] RISC-V: Support separate firmware and kernel payload X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: patches@groups.riscv.org, Alistair Francis , Michael Clark , Palmer Dabbelt Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" 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. The board is also changed to use the firmware address from the loaded firmware or combined firmware+kernel. This is normally equal to the DRAM base address of 0x8000_0000, however now it is possible to boot firmware at different load addresses because the reset code jumps to the actual firmware entry address. Cc: Palmer Dabbelt Cc: Alistair Francis Signed-off-by: Michael Clark --- hw/riscv/Makefile.objs | 1 + hw/riscv/boot.c | 172 ++++++++++++++++++++++++++++++++++++++++++++++++ hw/riscv/virt.c | 67 +++---------------- include/hw/riscv/boot.h | 30 +++++++++ 4 files changed, 213 insertions(+), 57 deletions(-) create mode 100644 hw/riscv/boot.c create mode 100644 include/hw/riscv/boot.h diff --git a/hw/riscv/Makefile.objs b/hw/riscv/Makefile.objs index 1dde01d39dcc..d36b004ab0f9 100644 --- a/hw/riscv/Makefile.objs +++ b/hw/riscv/Makefile.objs @@ -1,3 +1,4 @@ +obj-y += boot.o obj-y += riscv_htif.o obj-y += riscv_hart.o obj-y += sifive_e.o diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c new file mode 100644 index 000000000000..cf4e5d594638 --- /dev/null +++ b/hw/riscv/boot.c @@ -0,0 +1,172 @@ +/* + * QEMU RISCV firmware and kernel loader + * + * Copyright (c) 2017-2018 SiFive, Inc. + * + * Holds the state of a heterogenous array of RISC-V harts + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "hw/loader.h" +#include "hw/boards.h" +#include "sysemu/device_tree.h" +#include "elf.h" +#include "hw/riscv/boot.h" + +#define RISCV_BOOT_DEBUG 0 + +#define boot_debug(fs, ...) \ + if (RISCV_BOOT_DEBUG) { \ + fprintf(stderr, "boot: %s: "fs, __func__, ##__VA_ARGS__); \ + } + +static uint64_t kernel_offset; + +static uint64_t kernel_translate(void *opaque, uint64_t addr) +{ + /* mask kernel virtual address and offset by load address */ + if (kernel_offset) { + return (addr & 0x7fffffff) + kernel_offset; + } else { + return addr; + } +} + +hwaddr riscv_load_firmware(const char *filename) +{ + uint64_t firmware_entry, firmware_start, firmware_end; + + if (load_elf(filename, NULL, NULL, + &firmware_entry, &firmware_start, &firmware_end, + 0, EM_RISCV, 1, 0) < 0) { + error_report("riscv_boot: could not load firmware '%s'", filename); + exit(1); + } + + /* align kernel load address to the megapage after the firmware */ +#if defined(TARGET_RISCV32) + kernel_offset = (firmware_end + 0x3fffff) & ~0x3fffff; +#else + kernel_offset = (firmware_end + 0x1fffff) & ~0x1fffff; +#endif + + boot_debug("entry=0x" TARGET_FMT_plx " start=0x" TARGET_FMT_plx " " + "end=0x" TARGET_FMT_plx " kernel_offset=0x" TARGET_FMT_plx "\n", + firmware_entry, firmware_start, firmware_end, kernel_offset); + + return firmware_entry; +} + +hwaddr riscv_load_kernel(const char *filename, void *fdt) +{ + uint64_t kernel_entry, kernel_start, kernel_end; + + if (load_elf(filename, kernel_translate, NULL, + &kernel_entry, &kernel_start, &kernel_end, + 0, EM_RISCV, 1, 0) < 0) { + error_report("riscv_boot: could not load kernel '%s'", filename); + exit(1); + } + + boot_debug("entry=0x" TARGET_FMT_plx " start=0x" TARGET_FMT_plx " " + "end=0x" TARGET_FMT_plx "\n", kernel_entry, kernel_start, + kernel_end); + + /* + * pass kernel load address via device-tree to firmware + * + * BBL reads the kernel address from device-tree + */ + if (fdt) { + qemu_fdt_setprop_cells(fdt, "/chosen", "riscv,kernel-end", + kernel_end >> 32, kernel_end); + qemu_fdt_setprop_cells(fdt, "/chosen", "riscv,kernel-start", + kernel_start >> 32, kernel_start); + } + + return kernel_entry; +} + +void riscv_load_initrd(const char *filename, uint64_t mem_size, + hwaddr firmware_entry, void *fdt) +{ + uint64_t start, size; + + /* We want to put the initrd far enough into RAM that when the + * kernel is uncompressed it will not clobber the initrd. However + * on boards without much RAM we must ensure that we still leave + * enough room for a decent sized initrd, and on boards with large + * amounts of RAM we must avoid the initrd being so far up in RAM + * that it is outside lowmem and inaccessible to the kernel. + * So for boards with less than 256MB of RAM we put the initrd + * halfway into RAM, and for boards with 256MB of RAM or more we put + * the initrd at 128MB. + */ + start = firmware_entry + MIN(mem_size / 2, 128 * 1024 * 1024); + + size = load_ramdisk(filename, start, mem_size - start); + if (size == -1) { + size = load_image_targphys(filename, start, mem_size - start); + if (size == -1) { + error_report("riscv_boot: could not load ramdisk '%s'", filename); + exit(1); + } + } + + boot_debug("start=0x" TARGET_FMT_plx " end=0x" TARGET_FMT_plx "\n", + start, start + size); + + /* + * pass initrd load address via device-tree to kernel + * + * linux-kernel reads the initrd address from device-tree + */ + if (fdt) { + qemu_fdt_setprop_cells(fdt, "/chosen", "linux,initrd-end", + (start + size) >> 32, start + size); + qemu_fdt_setprop_cells(fdt, "/chosen", "linux,initrd-start", + start >> 32, start); + } +} + +hwaddr riscv_load_firmware_kernel_initrd(MachineState *machine, void *fdt) +{ + hwaddr firmware_entry = 0; + + /* load firmware e.g. -bios bbl */ + if (machine->firmware) { + firmware_entry = riscv_load_firmware(machine->firmware); + } + + /* load combined bbl+kernel or separate kernel */ + if (machine->kernel_filename) { + if (machine->firmware) { + /* load separate bios and kernel e.g. -bios bbl -kernel vmlinux */ + riscv_load_kernel(machine->kernel_filename, fdt); + } else { + /* load traditional combined bbl+kernel e.g. -kernel bbl_vmlimux */ + firmware_entry = riscv_load_kernel(machine->kernel_filename, NULL); + } + if (machine->initrd_filename) { + /* load separate initrd */ + riscv_load_initrd(machine->initrd_filename, machine->ram_size, + firmware_entry, fdt); + } + } + + return firmware_entry; +} diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index c889aa3cd269..984ddf0635fd 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -33,6 +33,7 @@ #include "hw/riscv/sifive_plic.h" #include "hw/riscv/sifive_clint.h" #include "hw/riscv/sifive_test.h" +#include "hw/riscv/boot.h" #include "hw/riscv/virt.h" #include "chardev/char.h" #include "sysemu/arch_init.h" @@ -56,47 +57,6 @@ static const struct MemmapEntry { [VIRT_DRAM] = { 0x80000000, 0x0 }, }; -static uint64_t load_kernel(const char *kernel_filename) -{ - uint64_t kernel_entry, kernel_high; - - if (load_elf(kernel_filename, NULL, NULL, - &kernel_entry, NULL, &kernel_high, - 0, EM_RISCV, 1, 0) < 0) { - error_report("qemu: could not load kernel '%s'", kernel_filename); - exit(1); - } - return kernel_entry; -} - -static hwaddr load_initrd(const char *filename, uint64_t mem_size, - uint64_t kernel_entry, hwaddr *start) -{ - int size; - - /* We want to put the initrd far enough into RAM that when the - * kernel is uncompressed it will not clobber the initrd. However - * on boards without much RAM we must ensure that we still leave - * enough room for a decent sized initrd, and on boards with large - * amounts of RAM we must avoid the initrd being so far up in RAM - * that it is outside lowmem and inaccessible to the kernel. - * So for boards with less than 256MB of RAM we put the initrd - * halfway into RAM, and for boards with 256MB of RAM or more we put - * the initrd at 128MB. - */ - *start = kernel_entry + MIN(mem_size / 2, 128 * 1024 * 1024); - - size = load_ramdisk(filename, *start, mem_size - *start); - if (size == -1) { - size = load_image_targphys(filename, *start, mem_size - *start); - if (size == -1) { - error_report("qemu: could not load ramdisk '%s'", filename); - exit(1); - } - } - return *start + size; -} - static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap, uint64_t mem_size, const char *cmdline) { @@ -273,6 +233,7 @@ static void riscv_virt_board_init(MachineState *machine) size_t plic_hart_config_len; int i; void *fdt; + hwaddr firmware_entry; /* Initialize SOC */ object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY); @@ -300,20 +261,12 @@ static void riscv_virt_board_init(MachineState *machine) memory_region_add_subregion(system_memory, memmap[VIRT_MROM].base, mask_rom); - if (machine->kernel_filename) { - uint64_t kernel_entry = load_kernel(machine->kernel_filename); - - if (machine->initrd_filename) { - hwaddr start; - hwaddr end = load_initrd(machine->initrd_filename, - machine->ram_size, kernel_entry, - &start); - qemu_fdt_setprop_cell(fdt, "/chosen", - "linux,initrd-start", start); - qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", - end); - } - } + /* + * combined firmware and kernel: -kernel bbl_vmlimux + * separate firmware and kernel: -bios bbl -kernel vmlinux + * firmware, kernel and ramdisk: -bios bbl -kernel vmlinux -initrd initramfs + */ + firmware_entry = riscv_load_firmware_kernel_initrd(machine, fdt); /* reset vector */ uint32_t reset_vec[8] = { @@ -327,8 +280,8 @@ static void riscv_virt_board_init(MachineState *machine) #endif 0x00028067, /* jr t0 */ 0x00000000, - memmap[VIRT_DRAM].base, /* start: .dword memmap[VIRT_DRAM].base */ - 0x00000000, + firmware_entry, /* .word firmware_entry */ + firmware_entry >> 32, /* dtb: */ }; diff --git a/include/hw/riscv/boot.h b/include/hw/riscv/boot.h new file mode 100644 index 000000000000..aa30bf1c45b2 --- /dev/null +++ b/include/hw/riscv/boot.h @@ -0,0 +1,30 @@ +/* + * QEMU RISCV firmware and kernel loader interface + * + * Copyright (c) 2017-2018 SiFive, Inc. + * + * Holds the state of a heterogenous array of RISC-V harts + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef HW_RISCV_BOOT_H +#define HW_RISCV_BOOT_H + +hwaddr riscv_load_firmware(const char *filename); +hwaddr riscv_load_kernel(const char *filename, void *fdt); +void riscv_load_initrd(const char *filename, uint64_t mem_size, + hwaddr firmware_entry, void *fdt); +hwaddr riscv_load_firmware_kernel_initrd(MachineState *machine, void *fdt); + +#endif