Signed-off-by: Sagar Karandikar <sag...@eecs.berkeley.edu> --- configure | 6 + default-configs/riscv32-softmmu.mak | 38 ++++++ default-configs/riscv64-softmmu.mak | 38 ++++++ hw/riscv/Makefile.objs | 2 + hw/riscv/riscv_board.c | 264 ++++++++++++++++++++++++++++++++++++ hw/riscv/riscv_int.c | 67 +++++++++ 6 files changed, 415 insertions(+) create mode 100644 default-configs/riscv32-softmmu.mak create mode 100644 default-configs/riscv64-softmmu.mak create mode 100644 hw/riscv/riscv_board.c create mode 100644 hw/riscv/riscv_int.c
diff --git a/configure b/configure index 8fa62ad..e3381b8 100755 --- a/configure +++ b/configure @@ -5667,6 +5667,12 @@ case "$target_name" in TARGET_BASE_ARCH=mips echo "TARGET_ABI_MIPSN64=y" >> $config_target_mak ;; + riscv32) + TARGET_BASE_ARCH=riscv + ;; + riscv64) + TARGET_BASE_ARCH=riscv + ;; moxie) ;; or32) diff --git a/default-configs/riscv32-softmmu.mak b/default-configs/riscv32-softmmu.mak new file mode 100644 index 0000000..c8b7fa1 --- /dev/null +++ b/default-configs/riscv32-softmmu.mak @@ -0,0 +1,38 @@ +# Default configuration for riscv-softmmu + +#include pci.mak +#include sound.mak +#include usb.mak +#CONFIG_ESP=y +#CONFIG_VGA=y +#CONFIG_VGA_PCI=y +#CONFIG_VGA_ISA=y +#CONFIG_VGA_ISA_MM=y +#CONFIG_VGA_CIRRUS=y +#CONFIG_VMWARE_VGA=y +CONFIG_SERIAL=y +#CONFIG_PARALLEL=y +#CONFIG_I8254=y +#CONFIG_PCSPK=y +#CONFIG_PCKBD=y +#CONFIG_FDC=y +#CONFIG_ACPI=y +#CONFIG_APM=y +#CONFIG_I8257=y +#CONFIG_PIIX4=y +#CONFIG_IDE_ISA=y +#CONFIG_IDE_PIIX=y +#CONFIG_NE2000_ISA=y +#CONFIG_RC4030=y +#CONFIG_DP8393X=y +#CONFIG_DS1225Y=y +#CONFIG_MIPSNET=y +#CONFIG_PFLASH_CFI01=y +#CONFIG_G364FB=y +CONFIG_I8259=y +#CONFIG_JAZZ_LED=y +#CONFIG_MC146818RTC=y +#CONFIG_VT82C686=y +#CONFIG_ISA_TESTDEV=y +#CONFIG_EMPTY_SLOT=y +CONFIG_VIRTIO=y diff --git a/default-configs/riscv64-softmmu.mak b/default-configs/riscv64-softmmu.mak new file mode 100644 index 0000000..c8b7fa1 --- /dev/null +++ b/default-configs/riscv64-softmmu.mak @@ -0,0 +1,38 @@ +# Default configuration for riscv-softmmu + +#include pci.mak +#include sound.mak +#include usb.mak +#CONFIG_ESP=y +#CONFIG_VGA=y +#CONFIG_VGA_PCI=y +#CONFIG_VGA_ISA=y +#CONFIG_VGA_ISA_MM=y +#CONFIG_VGA_CIRRUS=y +#CONFIG_VMWARE_VGA=y +CONFIG_SERIAL=y +#CONFIG_PARALLEL=y +#CONFIG_I8254=y +#CONFIG_PCSPK=y +#CONFIG_PCKBD=y +#CONFIG_FDC=y +#CONFIG_ACPI=y +#CONFIG_APM=y +#CONFIG_I8257=y +#CONFIG_PIIX4=y +#CONFIG_IDE_ISA=y +#CONFIG_IDE_PIIX=y +#CONFIG_NE2000_ISA=y +#CONFIG_RC4030=y +#CONFIG_DP8393X=y +#CONFIG_DS1225Y=y +#CONFIG_MIPSNET=y +#CONFIG_PFLASH_CFI01=y +#CONFIG_G364FB=y +CONFIG_I8259=y +#CONFIG_JAZZ_LED=y +#CONFIG_MC146818RTC=y +#CONFIG_VT82C686=y +#CONFIG_ISA_TESTDEV=y +#CONFIG_EMPTY_SLOT=y +CONFIG_VIRTIO=y diff --git a/hw/riscv/Makefile.objs b/hw/riscv/Makefile.objs index d830e5d..de6017e 100644 --- a/hw/riscv/Makefile.objs +++ b/hw/riscv/Makefile.objs @@ -1,3 +1,5 @@ obj-y += riscv_rtc.o +obj-y += riscv_int.o obj-y += htif/elf_symb.o obj-y += htif/htif.o +obj-y += riscv_board.o diff --git a/hw/riscv/riscv_board.c b/hw/riscv/riscv_board.c new file mode 100644 index 0000000..1c136e2 --- /dev/null +++ b/hw/riscv/riscv_board.c @@ -0,0 +1,264 @@ +/* + * QEMU RISC-V Generic Board Support + * + * Author: Sagar Karandikar, sag...@eecs.berkeley.edu + * + * This provides a RISC-V Board with the following devices: + * + * 0) HTIF Test Pass/Fail Reporting (no syscall proxy) + * 1) HTIF Console + * + * These are created by htif_mm_init below. + * + * This board currently uses a hardcoded devicetree that indicates one hart. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "hw/char/serial.h" +#include "hw/riscv/htif/htif.h" +#include "hw/riscv/riscv_rtc.h" +#include "hw/boards.h" +#include "hw/riscv/cpudevs.h" +#include "sysemu/char.h" +#include "sysemu/arch_init.h" +#include "qemu/log.h" +#include "hw/loader.h" +#include "elf.h" +#include "exec/address-spaces.h" +#include "hw/sysbus.h" /* SysBusDevice */ +#include "qemu/host-utils.h" +#include "sysemu/qtest.h" +#include "qemu/error-report.h" +#include "hw/empty_slot.h" +#include "qemu/error-report.h" +#include "sysemu/block-backend.h" + +#define TYPE_RISCV_BOARD "riscv" +#define RISCV_BOARD(obj) OBJECT_CHECK(BoardState, (obj), TYPE_RISCV_BOARD) + +typedef struct { + SysBusDevice parent_obj; +} BoardState; + +static struct _loaderparams { + int ram_size; + const char *kernel_filename; + const char *kernel_cmdline; + const char *initrd_filename; +} loaderparams; + +uint64_t identity_translate(void *opaque, uint64_t addr) +{ + return addr; +} + +static int64_t load_kernel(void) +{ + int64_t kernel_entry, kernel_high; + int big_endian; + big_endian = 0; + + if (load_elf(loaderparams.kernel_filename, identity_translate, NULL, + (uint64_t *)&kernel_entry, NULL, (uint64_t *)&kernel_high, + big_endian, ELF_MACHINE, 1, 0) < 0) { + fprintf(stderr, "qemu: could not load kernel '%s'\n", + loaderparams.kernel_filename); + exit(1); + } + return kernel_entry; +} + +static void main_cpu_reset(void *opaque) +{ + RISCVCPU *cpu = opaque; + cpu_reset(CPU(cpu)); +} + +static void riscv_board_init(MachineState *args) +{ + ram_addr_t ram_size = args->ram_size; + const char *cpu_model = args->cpu_model; + const char *kernel_filename = args->kernel_filename; + const char *kernel_cmdline = args->kernel_cmdline; + const char *initrd_filename = args->initrd_filename; + MemoryRegion *system_memory = get_system_memory(); + MemoryRegion *main_mem = g_new(MemoryRegion, 1); + RISCVCPU *cpu; + CPURISCVState *env; + int i; + DeviceState *dev = qdev_create(NULL, TYPE_RISCV_BOARD); + object_property_set_bool(OBJECT(dev), true, "realized", NULL); + + /* Make sure the first 3 serial ports are associated with a device. */ + for (i = 0; i < 3; i++) { + if (!serial_hds[i]) { + char label[32]; + snprintf(label, sizeof(label), "serial%d", i); + serial_hds[i] = qemu_chr_new(label, "null", NULL); + } + } + + /* init CPUs */ + if (cpu_model == NULL) { + cpu_model = "riscv"; + } + + for (i = 0; i < smp_cpus; i++) { + cpu = cpu_riscv_init(cpu_model); + if (cpu == NULL) { + fprintf(stderr, "Unable to find CPU definition\n"); + exit(1); + } + env = &cpu->env; + + /* Init internal devices */ + cpu_riscv_irq_init_cpu(env); + cpu_riscv_clock_init(env); + qemu_register_reset(main_cpu_reset, cpu); + } + cpu = RISCV_CPU(first_cpu); + env = &cpu->env; + + /* register system main memory (actual RAM) */ + memory_region_init_ram(main_mem, NULL, "riscv_board.ram", 2147483648L + + ram_size, &error_fatal); + /* for phys mem size check in page table walk */ + env->memsize = ram_size; + vmstate_register_ram_global(main_mem); + memory_region_add_subregion(system_memory, 0x0, main_mem); + + if (kernel_filename) { + loaderparams.ram_size = ram_size; + loaderparams.kernel_filename = kernel_filename; + loaderparams.kernel_cmdline = kernel_cmdline; + loaderparams.initrd_filename = initrd_filename; + load_kernel(); + } + + uint32_t reset_vec[8] = { + 0x297 + 0x80000000 - 0x1000, /* reset vector */ + 0x00028067, /* jump to DRAM_BASE */ + 0x00000000, /* reserved */ + 0x0, /* config string pointer */ + 0, 0, 0, 0 /* trap vector */ + }; + reset_vec[3] = 0x1000 + sizeof(reset_vec); /* config string pointer */ + + /* part one of config string - before memory size specified */ + const char *config_string1 = "platform {\n" + " vendor ucb;\n" + " arch spike;\n" + "};\n" + "rtc {\n" + " addr 0x" "40000000" ";\n" + "};\n" + "ram {\n" + " 0 {\n" + " addr 0x" "80000000" ";\n" + " size 0x"; + + + /* part two of config string - after memory size specified */ + const char *config_string2 = ";\n" + " };\n" + "};\n" + "core {\n" + " 0" " {\n" + " " "0 {\n" + " isa " "rv64imafd" ";\n" + " timecmp 0x" "40000008" ";\n" + " ipi 0x" "40001000" ";\n" + " };\n" + " };\n" + "};\n"; + + /* build config string with supplied memory size */ + uint64_t rsz = ram_size; + char *ramsize_as_hex_str = malloc(17); + sprintf(ramsize_as_hex_str, "%016lx", rsz); + char *config_string = malloc(strlen(config_string1) + + strlen(ramsize_as_hex_str) + + strlen(config_string2) + 1); + config_string[0] = 0; + strcat(config_string, config_string1); + strcat(config_string, ramsize_as_hex_str); + strcat(config_string, config_string2); + + /* copy in the reset vec and configstring */ + int q; + for (q = 0; q < sizeof(reset_vec) / sizeof(reset_vec[0]); q++) { + stl_p(memory_region_get_ram_ptr(main_mem) + 0x1000 + q * 4, + reset_vec[q]); + } + + int confstrlen = strlen(config_string); + for (q = 0; q < confstrlen; q++) { + stb_p(memory_region_get_ram_ptr(main_mem) + reset_vec[3] + q, + config_string[q]); + } + + /* add memory mapped htif registers at location specified in the symbol + table of the elf being loaded (thus kernel_filename is passed to the + init rather than an address) */ + htif_mm_init(system_memory, kernel_filename, env->irq[4], main_mem, + env, serial_hds[0]); + + /* timer device at 0x40000000, as specified in the config string above */ + timer_mm_init(system_memory, 0x40000000, env); + + /* TODO: VIRTIO */ +} + +static int riscv_board_sysbus_device_init(SysBusDevice *sysbusdev) +{ + return 0; +} + +static void riscv_board_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + k->init = riscv_board_sysbus_device_init; +} + +static const TypeInfo riscv_board_device = { + .name = TYPE_RISCV_BOARD, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(BoardState), + .class_init = riscv_board_class_init, +}; + +static void riscv_board_machine_init(MachineClass *mc) +{ + mc->desc = "RISC-V Generic Board"; + mc->init = riscv_board_init; + mc->max_cpus = 1; + mc->is_default = 1; +} + +DEFINE_MACHINE("riscv", riscv_board_machine_init) + +static void riscv_board_register_types(void) +{ + type_register_static(&riscv_board_device); +} + +type_init(riscv_board_register_types); diff --git a/hw/riscv/riscv_int.c b/hw/riscv/riscv_int.c new file mode 100644 index 0000000..9eaf221 --- /dev/null +++ b/hw/riscv/riscv_int.c @@ -0,0 +1,67 @@ +/* + * QEMU RISC-V - QEMU IRQ Support + * + * Author: Sagar Karandikar, sag...@eecs.berkeley.edu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "hw/riscv/cpudevs.h" +#include "cpu.h" + +static void cpu_riscv_irq_request(void *opaque, int irq, int level) +{ + /* These are not the same irq numbers visible to the emulated processor. */ + RISCVCPU *cpu = opaque; + CPURISCVState *env = &cpu->env; + CPUState *cs = CPU(cpu); + + /* current irqs: + 4: Host Interrupt. mfromhost should have a nonzero value + 3: Machine Timer. MIP_MTIP should have already been set + 2, 1, 0: Interrupts triggered by the CPU. At least one of + MIP_STIP, MIP_SSIP, MIP_MSIP should already be set */ + if (unlikely(!(irq < 5 && irq >= 0))) { + printf("IRQNO: %d\n", irq); + fprintf(stderr, "Unused IRQ was raised.\n"); + exit(1); + } + + if (level) { + cpu_interrupt(cs, CPU_INTERRUPT_HARD); + } else { + if (!env->csr[CSR_MIP] && !env->mfromhost) { + /* no interrupts pending, no host interrupt for HTIF, reset */ + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); + } + } +} + +void cpu_riscv_irq_init_cpu(CPURISCVState *env) +{ + qemu_irq *qi; + int i; + + qi = qemu_allocate_irqs(cpu_riscv_irq_request, riscv_env_get_cpu(env), 8); + for (i = 0; i < 8; i++) { + env->irq[i] = qi[i]; + } +} -- 2.9.3