Currently, the fdt is copied to the ROM after the reset vector. The firmware has to copy it to DRAM. Instead of this, directly copy the device tree to a pre-computed dram address. The device tree load address should be as far as possible from kernel and initrd images. That's why it is kept at the end of the DRAM or 4GB whichever is lesser. Signed-off-by: Atish Patra <atish.patra@wdc.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Bin Meng <bin.meng@windriver.com> Tested-by: Bin Meng <bin.meng@windriver.com> Message-Id: <20200701183949.398134-3-atish.patra@wdc.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
239 lines
8.9 KiB
C
239 lines
8.9 KiB
C
/*
|
|
* QEMU RISC-V Spike Board
|
|
*
|
|
* Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
|
|
* Copyright (c) 2017-2018 SiFive, Inc.
|
|
*
|
|
* This provides a RISC-V Board with the following devices:
|
|
*
|
|
* 0) HTIF Console and Poweroff
|
|
* 1) CLINT (Timer and IPI)
|
|
* 2) PLIC (Platform Level Interrupt Controller)
|
|
*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "qemu/log.h"
|
|
#include "qemu/error-report.h"
|
|
#include "qapi/error.h"
|
|
#include "hw/boards.h"
|
|
#include "hw/loader.h"
|
|
#include "hw/sysbus.h"
|
|
#include "target/riscv/cpu.h"
|
|
#include "hw/riscv/riscv_htif.h"
|
|
#include "hw/riscv/riscv_hart.h"
|
|
#include "hw/riscv/sifive_clint.h"
|
|
#include "hw/riscv/spike.h"
|
|
#include "hw/riscv/boot.h"
|
|
#include "chardev/char.h"
|
|
#include "sysemu/arch_init.h"
|
|
#include "sysemu/device_tree.h"
|
|
#include "sysemu/qtest.h"
|
|
#include "sysemu/sysemu.h"
|
|
|
|
#if defined(TARGET_RISCV32)
|
|
# define BIOS_FILENAME "opensbi-riscv32-spike-fw_jump.elf"
|
|
#else
|
|
# define BIOS_FILENAME "opensbi-riscv64-spike-fw_jump.elf"
|
|
#endif
|
|
|
|
static const struct MemmapEntry {
|
|
hwaddr base;
|
|
hwaddr size;
|
|
} spike_memmap[] = {
|
|
[SPIKE_MROM] = { 0x1000, 0x11000 },
|
|
[SPIKE_CLINT] = { 0x2000000, 0x10000 },
|
|
[SPIKE_DRAM] = { 0x80000000, 0x0 },
|
|
};
|
|
|
|
static void create_fdt(SpikeState *s, const struct MemmapEntry *memmap,
|
|
uint64_t mem_size, const char *cmdline)
|
|
{
|
|
void *fdt;
|
|
int cpu;
|
|
uint32_t *cells;
|
|
char *nodename;
|
|
|
|
fdt = s->fdt = create_device_tree(&s->fdt_size);
|
|
if (!fdt) {
|
|
error_report("create_device_tree() failed");
|
|
exit(1);
|
|
}
|
|
|
|
qemu_fdt_setprop_string(fdt, "/", "model", "ucbbar,spike-bare,qemu");
|
|
qemu_fdt_setprop_string(fdt, "/", "compatible", "ucbbar,spike-bare-dev");
|
|
qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2);
|
|
qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
|
|
|
|
qemu_fdt_add_subnode(fdt, "/htif");
|
|
qemu_fdt_setprop_string(fdt, "/htif", "compatible", "ucb,htif0");
|
|
|
|
qemu_fdt_add_subnode(fdt, "/soc");
|
|
qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
|
|
qemu_fdt_setprop_string(fdt, "/soc", "compatible", "simple-bus");
|
|
qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2);
|
|
qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
|
|
|
|
nodename = g_strdup_printf("/memory@%lx",
|
|
(long)memmap[SPIKE_DRAM].base);
|
|
qemu_fdt_add_subnode(fdt, nodename);
|
|
qemu_fdt_setprop_cells(fdt, nodename, "reg",
|
|
memmap[SPIKE_DRAM].base >> 32, memmap[SPIKE_DRAM].base,
|
|
mem_size >> 32, mem_size);
|
|
qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory");
|
|
g_free(nodename);
|
|
|
|
qemu_fdt_add_subnode(fdt, "/cpus");
|
|
qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency",
|
|
SIFIVE_CLINT_TIMEBASE_FREQ);
|
|
qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0);
|
|
qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1);
|
|
|
|
for (cpu = s->soc.num_harts - 1; cpu >= 0; cpu--) {
|
|
nodename = g_strdup_printf("/cpus/cpu@%d", cpu);
|
|
char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
|
|
char *isa = riscv_isa_string(&s->soc.harts[cpu]);
|
|
qemu_fdt_add_subnode(fdt, nodename);
|
|
#if defined(TARGET_RISCV32)
|
|
qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv32");
|
|
#else
|
|
qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv48");
|
|
#endif
|
|
qemu_fdt_setprop_string(fdt, nodename, "riscv,isa", isa);
|
|
qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv");
|
|
qemu_fdt_setprop_string(fdt, nodename, "status", "okay");
|
|
qemu_fdt_setprop_cell(fdt, nodename, "reg", cpu);
|
|
qemu_fdt_setprop_string(fdt, nodename, "device_type", "cpu");
|
|
qemu_fdt_add_subnode(fdt, intc);
|
|
qemu_fdt_setprop_cell(fdt, intc, "phandle", 1);
|
|
qemu_fdt_setprop_string(fdt, intc, "compatible", "riscv,cpu-intc");
|
|
qemu_fdt_setprop(fdt, intc, "interrupt-controller", NULL, 0);
|
|
qemu_fdt_setprop_cell(fdt, intc, "#interrupt-cells", 1);
|
|
g_free(isa);
|
|
g_free(intc);
|
|
g_free(nodename);
|
|
}
|
|
|
|
cells = g_new0(uint32_t, s->soc.num_harts * 4);
|
|
for (cpu = 0; cpu < s->soc.num_harts; cpu++) {
|
|
nodename =
|
|
g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
|
|
uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename);
|
|
cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
|
|
cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT);
|
|
cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle);
|
|
cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER);
|
|
g_free(nodename);
|
|
}
|
|
nodename = g_strdup_printf("/soc/clint@%lx",
|
|
(long)memmap[SPIKE_CLINT].base);
|
|
qemu_fdt_add_subnode(fdt, nodename);
|
|
qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,clint0");
|
|
qemu_fdt_setprop_cells(fdt, nodename, "reg",
|
|
0x0, memmap[SPIKE_CLINT].base,
|
|
0x0, memmap[SPIKE_CLINT].size);
|
|
qemu_fdt_setprop(fdt, nodename, "interrupts-extended",
|
|
cells, s->soc.num_harts * sizeof(uint32_t) * 4);
|
|
g_free(cells);
|
|
g_free(nodename);
|
|
|
|
if (cmdline) {
|
|
qemu_fdt_add_subnode(fdt, "/chosen");
|
|
qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
|
|
}
|
|
}
|
|
|
|
static void spike_board_init(MachineState *machine)
|
|
{
|
|
const struct MemmapEntry *memmap = spike_memmap;
|
|
|
|
SpikeState *s = g_new0(SpikeState, 1);
|
|
MemoryRegion *system_memory = get_system_memory();
|
|
MemoryRegion *main_mem = g_new(MemoryRegion, 1);
|
|
MemoryRegion *mask_rom = g_new(MemoryRegion, 1);
|
|
unsigned int smp_cpus = machine->smp.cpus;
|
|
uint32_t fdt_load_addr;
|
|
|
|
/* Initialize SOC */
|
|
object_initialize_child(OBJECT(machine), "soc", &s->soc,
|
|
TYPE_RISCV_HART_ARRAY);
|
|
object_property_set_str(OBJECT(&s->soc), "cpu-type", machine->cpu_type,
|
|
&error_abort);
|
|
object_property_set_int(OBJECT(&s->soc), "num-harts", smp_cpus,
|
|
&error_abort);
|
|
sysbus_realize(SYS_BUS_DEVICE(&s->soc), &error_abort);
|
|
|
|
/* register system main memory (actual RAM) */
|
|
memory_region_init_ram(main_mem, NULL, "riscv.spike.ram",
|
|
machine->ram_size, &error_fatal);
|
|
memory_region_add_subregion(system_memory, memmap[SPIKE_DRAM].base,
|
|
main_mem);
|
|
|
|
/* create device tree */
|
|
create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline);
|
|
|
|
/* boot rom */
|
|
memory_region_init_rom(mask_rom, NULL, "riscv.spike.mrom",
|
|
memmap[SPIKE_MROM].size, &error_fatal);
|
|
memory_region_add_subregion(system_memory, memmap[SPIKE_MROM].base,
|
|
mask_rom);
|
|
|
|
riscv_find_and_load_firmware(machine, BIOS_FILENAME,
|
|
memmap[SPIKE_DRAM].base,
|
|
htif_symbol_callback);
|
|
|
|
if (machine->kernel_filename) {
|
|
uint64_t kernel_entry = riscv_load_kernel(machine->kernel_filename,
|
|
htif_symbol_callback);
|
|
|
|
if (machine->initrd_filename) {
|
|
hwaddr start;
|
|
hwaddr end = riscv_load_initrd(machine->initrd_filename,
|
|
machine->ram_size, kernel_entry,
|
|
&start);
|
|
qemu_fdt_setprop_cell(s->fdt, "/chosen",
|
|
"linux,initrd-start", start);
|
|
qemu_fdt_setprop_cell(s->fdt, "/chosen", "linux,initrd-end",
|
|
end);
|
|
}
|
|
}
|
|
|
|
/* Compute the fdt load address in dram */
|
|
fdt_load_addr = riscv_load_fdt(memmap[SPIKE_DRAM].base,
|
|
machine->ram_size, s->fdt);
|
|
/* load the reset vector */
|
|
riscv_setup_rom_reset_vec(memmap[SPIKE_DRAM].base, memmap[SPIKE_MROM].base,
|
|
memmap[SPIKE_MROM].size,
|
|
fdt_load_addr, s->fdt);
|
|
|
|
/* initialize HTIF using symbols found in load_kernel */
|
|
htif_mm_init(system_memory, mask_rom, &s->soc.harts[0].env, serial_hd(0));
|
|
|
|
/* Core Local Interruptor (timer and IPI) */
|
|
sifive_clint_create(memmap[SPIKE_CLINT].base, memmap[SPIKE_CLINT].size,
|
|
smp_cpus, SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE,
|
|
false);
|
|
}
|
|
|
|
static void spike_machine_init(MachineClass *mc)
|
|
{
|
|
mc->desc = "RISC-V Spike Board";
|
|
mc->init = spike_board_init;
|
|
mc->max_cpus = 8;
|
|
mc->is_default = true;
|
|
mc->default_cpu_type = SPIKE_V1_10_0_CPU;
|
|
}
|
|
|
|
DEFINE_MACHINE("spike", spike_machine_init)
|