diff options
Diffstat (limited to 'hw')
46 files changed, 3208 insertions, 349 deletions
diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c index 2cb990997e..101f32cf66 100644 --- a/hw/block/dataplane/virtio-blk.c +++ b/hw/block/dataplane/virtio-blk.c @@ -34,6 +34,7 @@ struct VirtIOBlockDataPlane { VirtIODevice *vdev; QEMUBH *bh; /* bh for guest notification */ unsigned long *batch_notify_vqs; + bool batch_notifications; /* Note that these EventNotifiers are assigned by value. This is * fine as long as you do not call event_notifier_cleanup on them @@ -47,8 +48,12 @@ struct VirtIOBlockDataPlane { /* Raise an interrupt to signal guest, if necessary */ void virtio_blk_data_plane_notify(VirtIOBlockDataPlane *s, VirtQueue *vq) { - set_bit(virtio_get_queue_index(vq), s->batch_notify_vqs); - qemu_bh_schedule(s->bh); + if (s->batch_notifications) { + set_bit(virtio_get_queue_index(vq), s->batch_notify_vqs); + qemu_bh_schedule(s->bh); + } else { + virtio_notify_irqfd(s->vdev, vq); + } } static void notify_guest_bh(void *opaque) @@ -177,6 +182,12 @@ int virtio_blk_data_plane_start(VirtIODevice *vdev) s->starting = true; + if (!virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { + s->batch_notifications = true; + } else { + s->batch_notifications = false; + } + /* Set up guest notifier (irq) */ r = k->set_guest_notifiers(qbus->parent, nvqs, true); if (r != 0) { @@ -229,6 +240,22 @@ int virtio_blk_data_plane_start(VirtIODevice *vdev) return -ENOSYS; } +/* Stop notifications for new requests from guest. + * + * Context: BH in IOThread + */ +static void virtio_blk_data_plane_stop_bh(void *opaque) +{ + VirtIOBlockDataPlane *s = opaque; + unsigned i; + + for (i = 0; i < s->conf->num_queues; i++) { + VirtQueue *vq = virtio_get_queue(s->vdev, i); + + virtio_queue_aio_set_host_notifier_handler(vq, s->ctx, NULL); + } +} + /* Context: QEMU global mutex held */ void virtio_blk_data_plane_stop(VirtIODevice *vdev) { @@ -253,13 +280,7 @@ void virtio_blk_data_plane_stop(VirtIODevice *vdev) trace_virtio_blk_data_plane_stop(s); aio_context_acquire(s->ctx); - - /* Stop notifications for new requests from guest */ - for (i = 0; i < nvqs; i++) { - VirtQueue *vq = virtio_get_queue(s->vdev, i); - - virtio_queue_aio_set_host_notifier_handler(vq, s->ctx, NULL); - } + aio_wait_bh_oneshot(s->ctx, virtio_blk_data_plane_stop_bh, s); /* Drain and switch bs back to the QEMU main loop */ blk_set_aio_context(s->conf->conf.blk, qemu_get_aio_context()); diff --git a/hw/char/sclpconsole-lm.c b/hw/char/sclpconsole-lm.c index c500bdaf29..dbc91a1e5b 100644 --- a/hw/char/sclpconsole-lm.c +++ b/hw/char/sclpconsole-lm.c @@ -102,12 +102,12 @@ static bool can_handle_event(uint8_t type) return type == SCLP_EVENT_MESSAGE || type == SCLP_EVENT_PMSGCMD; } -static unsigned int send_mask(void) +static sccb_mask_t send_mask(void) { return SCLP_EVENT_MASK_OP_CMD | SCLP_EVENT_MASK_PMSGCMD; } -static unsigned int receive_mask(void) +static sccb_mask_t receive_mask(void) { return SCLP_EVENT_MASK_MSG | SCLP_EVENT_MASK_PMSGCMD; } @@ -318,11 +318,6 @@ static int console_init(SCLPEvent *event) return 0; } -static int console_exit(SCLPEvent *event) -{ - return 0; -} - static void console_reset(DeviceState *dev) { SCLPEvent *event = SCLP_EVENT(dev); @@ -349,7 +344,6 @@ static void console_class_init(ObjectClass *klass, void *data) dc->reset = console_reset; dc->vmsd = &vmstate_sclplmconsole; ec->init = console_init; - ec->exit = console_exit; ec->get_send_mask = send_mask; ec->get_receive_mask = receive_mask; ec->can_handle_event = can_handle_event; diff --git a/hw/char/sclpconsole.c b/hw/char/sclpconsole.c index d0265dfa7a..1fa16e9055 100644 --- a/hw/char/sclpconsole.c +++ b/hw/char/sclpconsole.c @@ -83,12 +83,12 @@ static bool can_handle_event(uint8_t type) return type == SCLP_EVENT_ASCII_CONSOLE_DATA; } -static unsigned int send_mask(void) +static sccb_mask_t send_mask(void) { return SCLP_EVENT_MASK_MSG_ASCII; } -static unsigned int receive_mask(void) +static sccb_mask_t receive_mask(void) { return SCLP_EVENT_MASK_MSG_ASCII; } @@ -246,11 +246,6 @@ static void console_reset(DeviceState *dev) scon->notify = false; } -static int console_exit(SCLPEvent *event) -{ - return 0; -} - static Property console_properties[] = { DEFINE_PROP_CHR("chardev", SCLPConsole, chr), DEFINE_PROP_END_OF_LIST(), @@ -265,7 +260,6 @@ static void console_class_init(ObjectClass *klass, void *data) dc->reset = console_reset; dc->vmsd = &vmstate_sclpconsole; ec->init = console_init; - ec->exit = console_exit; ec->get_send_mask = send_mask; ec->get_receive_mask = receive_mask; ec->can_handle_event = can_handle_event; diff --git a/hw/core/loader.c b/hw/core/loader.c index 76b244c508..06bdbca537 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -450,6 +450,20 @@ int load_elf_ram(const char *filename, int clear_lsb, int data_swab, AddressSpace *as, bool load_rom) { + return load_elf_ram_sym(filename, translate_fn, translate_opaque, + pentry, lowaddr, highaddr, big_endian, + elf_machine, clear_lsb, data_swab, as, + load_rom, NULL); +} + +/* return < 0 if error, otherwise the number of bytes loaded in memory */ +int load_elf_ram_sym(const char *filename, + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, uint64_t *pentry, + uint64_t *lowaddr, uint64_t *highaddr, int big_endian, + int elf_machine, int clear_lsb, int data_swab, + AddressSpace *as, bool load_rom, symbol_fn_t sym_cb) +{ int fd, data_order, target_data_order, must_swab, ret = ELF_LOAD_FAILED; uint8_t e_ident[EI_NIDENT]; @@ -488,11 +502,11 @@ int load_elf_ram(const char *filename, if (e_ident[EI_CLASS] == ELFCLASS64) { ret = load_elf64(filename, fd, translate_fn, translate_opaque, must_swab, pentry, lowaddr, highaddr, elf_machine, clear_lsb, - data_swab, as, load_rom); + data_swab, as, load_rom, sym_cb); } else { ret = load_elf32(filename, fd, translate_fn, translate_opaque, must_swab, pentry, lowaddr, highaddr, elf_machine, clear_lsb, - data_swab, as, load_rom); + data_swab, as, load_rom, sym_cb); } fail: diff --git a/hw/display/g364fb.c b/hw/display/g364fb.c index 819f8be05d..3d75394e77 100644 --- a/hw/display/g364fb.c +++ b/hw/display/g364fb.c @@ -207,6 +207,7 @@ done: if (xmax || ymax) { dpy_gfx_update(s->con, xmin, ymin, xmax - xmin + 1, ymax - ymin + 1); } + g_free(snap); } static void g364fb_draw_blank(G364State *s) diff --git a/hw/i386/multiboot.c b/hw/i386/multiboot.c index 46d9c68bf5..b9064264d8 100644 --- a/hw/i386/multiboot.c +++ b/hw/i386/multiboot.c @@ -31,12 +31,13 @@ #include "hw/loader.h" #include "elf.h" #include "sysemu/sysemu.h" +#include "qemu/error-report.h" /* Show multiboot debug output */ //#define DEBUG_MULTIBOOT #ifdef DEBUG_MULTIBOOT -#define mb_debug(a...) fprintf(stderr, ## a) +#define mb_debug(a...) error_report(a) #else #define mb_debug(a...) #endif @@ -137,7 +138,7 @@ static void mb_add_mod(MultibootState *s, stl_p(p + MB_MOD_END, end); stl_p(p + MB_MOD_CMDLINE, cmdline_phys); - mb_debug("mod%02d: "TARGET_FMT_plx" - "TARGET_FMT_plx"\n", + mb_debug("mod%02d: "TARGET_FMT_plx" - "TARGET_FMT_plx, s->mb_mods_count, start, end); s->mb_mods_count++; @@ -179,12 +180,12 @@ int load_multiboot(FWCfgState *fw_cfg, if (!is_multiboot) return 0; /* no multiboot */ - mb_debug("qemu: I believe we found a multiboot image!\n"); + mb_debug("qemu: I believe we found a multiboot image!"); memset(bootinfo, 0, sizeof(bootinfo)); memset(&mbs, 0, sizeof(mbs)); if (flags & 0x00000004) { /* MULTIBOOT_HEADER_HAS_VBE */ - fprintf(stderr, "qemu: multiboot knows VBE. we don't.\n"); + error_report("qemu: multiboot knows VBE. we don't."); } if (!(flags & 0x00010000)) { /* MULTIBOOT_HEADER_HAS_ADDR */ uint64_t elf_entry; @@ -193,7 +194,7 @@ int load_multiboot(FWCfgState *fw_cfg, fclose(f); if (((struct elf64_hdr*)header)->e_machine == EM_X86_64) { - fprintf(stderr, "Cannot load x86-64 image, give a 32bit one.\n"); + error_report("Cannot load x86-64 image, give a 32bit one."); exit(1); } @@ -201,7 +202,7 @@ int load_multiboot(FWCfgState *fw_cfg, &elf_low, &elf_high, 0, I386_ELF_MACHINE, 0, 0); if (kernel_size < 0) { - fprintf(stderr, "Error while loading elf kernel\n"); + error_report("Error while loading elf kernel"); exit(1); } mh_load_addr = elf_low; @@ -210,12 +211,13 @@ int load_multiboot(FWCfgState *fw_cfg, mbs.mb_buf = g_malloc(mb_kernel_size); if (rom_copy(mbs.mb_buf, mh_load_addr, mb_kernel_size) != mb_kernel_size) { - fprintf(stderr, "Error while fetching elf kernel from rom\n"); + error_report("Error while fetching elf kernel from rom"); exit(1); } - mb_debug("qemu: loading multiboot-elf kernel (%#x bytes) with entry %#zx\n", - mb_kernel_size, (size_t)mh_entry_addr); + mb_debug("qemu: loading multiboot-elf kernel " + "(%#x bytes) with entry %#zx", + mb_kernel_size, (size_t)mh_entry_addr); } else { /* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_ADDR. */ uint32_t mh_header_addr = ldl_p(header+i+12); @@ -224,7 +226,7 @@ int load_multiboot(FWCfgState *fw_cfg, mh_load_addr = ldl_p(header+i+16); if (mh_header_addr < mh_load_addr) { - fprintf(stderr, "invalid mh_load_addr address\n"); + error_report("invalid load_addr address"); exit(1); } @@ -233,43 +235,39 @@ int load_multiboot(FWCfgState *fw_cfg, mh_entry_addr = ldl_p(header+i+28); if (mh_load_end_addr) { - if (mh_bss_end_addr < mh_load_addr) { - fprintf(stderr, "invalid mh_bss_end_addr address\n"); - exit(1); - } - mb_kernel_size = mh_bss_end_addr - mh_load_addr; - if (mh_load_end_addr < mh_load_addr) { - fprintf(stderr, "invalid mh_load_end_addr address\n"); + error_report("invalid load_end_addr address"); exit(1); } mb_load_size = mh_load_end_addr - mh_load_addr; } else { if (kernel_file_size < mb_kernel_text_offset) { - fprintf(stderr, "invalid kernel_file_size\n"); + error_report("invalid kernel_file_size"); exit(1); } - mb_kernel_size = kernel_file_size - mb_kernel_text_offset; - mb_load_size = mb_kernel_size; + mb_load_size = kernel_file_size - mb_kernel_text_offset; + } + if (mh_bss_end_addr) { + if (mh_bss_end_addr < (mh_load_addr + mb_load_size)) { + error_report("invalid bss_end_addr address"); + exit(1); + } + mb_kernel_size = mh_bss_end_addr - mh_load_addr; + } else { + mb_kernel_size = mb_load_size; } - /* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_VBE. - uint32_t mh_mode_type = ldl_p(header+i+32); - uint32_t mh_width = ldl_p(header+i+36); - uint32_t mh_height = ldl_p(header+i+40); - uint32_t mh_depth = ldl_p(header+i+44); */ - - mb_debug("multiboot: mh_header_addr = %#x\n", mh_header_addr); - mb_debug("multiboot: mh_load_addr = %#x\n", mh_load_addr); - mb_debug("multiboot: mh_load_end_addr = %#x\n", mh_load_end_addr); - mb_debug("multiboot: mh_bss_end_addr = %#x\n", mh_bss_end_addr); - mb_debug("qemu: loading multiboot kernel (%#x bytes) at %#x\n", + mb_debug("multiboot: header_addr = %#x", mh_header_addr); + mb_debug("multiboot: load_addr = %#x", mh_load_addr); + mb_debug("multiboot: load_end_addr = %#x", mh_load_end_addr); + mb_debug("multiboot: bss_end_addr = %#x", mh_bss_end_addr); + mb_debug("qemu: loading multiboot kernel (%#x bytes) at %#x", mb_load_size, mh_load_addr); mbs.mb_buf = g_malloc(mb_kernel_size); fseek(f, mb_kernel_text_offset, SEEK_SET); if (fread(mbs.mb_buf, 1, mb_load_size, f) != mb_load_size) { - fprintf(stderr, "fread() failed\n"); + error_report("fread() failed"); exit(1); } memset(mbs.mb_buf + mb_load_size, 0, mb_kernel_size - mb_load_size); @@ -323,10 +321,10 @@ int load_multiboot(FWCfgState *fw_cfg, hwaddr c = mb_add_cmdline(&mbs, tmpbuf); if ((next_space = strchr(tmpbuf, ' '))) *next_space = '\0'; - mb_debug("multiboot loading module: %s\n", tmpbuf); + mb_debug("multiboot loading module: %s", tmpbuf); mb_mod_length = get_image_size(tmpbuf); if (mb_mod_length < 0) { - fprintf(stderr, "Failed to open file '%s'\n", tmpbuf); + error_report("Failed to open file '%s'", tmpbuf); exit(1); } @@ -337,7 +335,7 @@ int load_multiboot(FWCfgState *fw_cfg, mb_add_mod(&mbs, mbs.mb_buf_phys + offs, mbs.mb_buf_phys + offs + mb_mod_length, c); - mb_debug("mod_start: %p\nmod_end: %p\n cmdline: "TARGET_FMT_plx"\n", + mb_debug("mod_start: %p\nmod_end: %p\n cmdline: "TARGET_FMT_plx, (char *)mbs.mb_buf + offs, (char *)mbs.mb_buf + offs + mb_mod_length, c); initrd_filename = next_initrd+1; @@ -365,10 +363,11 @@ int load_multiboot(FWCfgState *fw_cfg, stl_p(bootinfo + MBI_BOOT_DEVICE, 0x8000ffff); /* XXX: use the -boot switch? */ stl_p(bootinfo + MBI_MMAP_ADDR, ADDR_E820_MAP); - mb_debug("multiboot: mh_entry_addr = %#x\n", mh_entry_addr); - mb_debug(" mb_buf_phys = "TARGET_FMT_plx"\n", mbs.mb_buf_phys); - mb_debug(" mod_start = "TARGET_FMT_plx"\n", mbs.mb_buf_phys + mbs.offset_mods); - mb_debug(" mb_mods_count = %d\n", mbs.mb_mods_count); + mb_debug("multiboot: entry_addr = %#x", mh_entry_addr); + mb_debug(" mb_buf_phys = "TARGET_FMT_plx, mbs.mb_buf_phys); + mb_debug(" mod_start = "TARGET_FMT_plx, + mbs.mb_buf_phys + mbs.offset_mods); + mb_debug(" mb_mods_count = %d", mbs.mb_mods_count); /* save bootinfo off the stack */ mb_bootinfo_data = g_memdup(bootinfo, sizeof(bootinfo)); diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 94cfd40ef2..35fcb6efdf 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1636,23 +1636,6 @@ void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus) rom_reset_order_override(); } -void pc_pci_device_init(PCIBus *pci_bus) -{ - int max_bus; - int bus; - - /* Note: if=scsi is deprecated with PC machine types */ - max_bus = drive_get_max_bus(IF_SCSI); - for (bus = 0; bus <= max_bus; bus++) { - pci_create_simple(pci_bus, -1, "lsi53c895a"); - /* - * By not creating frontends here, we make - * scsi_legacy_handle_cmdline() create them, and warn that - * this usage is deprecated. - */ - } -} - void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name) { DeviceState *dev; diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 456dc9e9f0..8658bcba63 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -295,10 +295,6 @@ static void pc_init1(MachineState *machine, PC_MACHINE_ACPI_DEVICE_PROP, &error_abort); } - if (pcmc->pci_enabled) { - pc_pci_device_init(pci_bus); - } - if (pcms->acpi_nvdimm_state.is_enabled) { nvdimm_init_acpi_state(&pcms->acpi_nvdimm_state, system_io, pcms->fw_cfg, OBJECT(pcms)); diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index aba7541a82..0c0bc48137 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -273,9 +273,6 @@ static void pc_q35_init(MachineState *machine) /* the rest devices to which pci devfn is automatically assigned */ pc_vga_init(isa_bus, host_bus); pc_nic_init(isa_bus, host_bus); - if (pcmc->pci_enabled) { - pc_pci_device_init(host_bus); - } if (pcms->acpi_nvdimm_state.is_enabled) { nvdimm_init_acpi_state(&pcms->acpi_nvdimm_state, system_io, diff --git a/hw/ide/core.c b/hw/ide/core.c index 257b429381..139c843514 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -1087,15 +1087,7 @@ static void ide_flush_cache(IDEState *s) s->status |= BUSY_STAT; ide_set_retry(s); block_acct_start(blk_get_stats(s->blk), &s->acct, 0, BLOCK_ACCT_FLUSH); - - if (blk_bs(s->blk)) { - s->pio_aiocb = blk_aio_flush(s->blk, ide_flush_cb, s); - } else { - /* XXX blk_aio_flush() crashes when blk_bs(blk) is NULL, remove this - * temporary workaround when blk_aio_*() functions handle NULL blk_bs. - */ - ide_flush_cb(s, 0); - } + s->pio_aiocb = blk_aio_flush(s->blk, ide_flush_cb, s); } static void ide_cfata_metadata_inquiry(IDEState *s) diff --git a/hw/intc/openpic_kvm.c b/hw/intc/openpic_kvm.c index f1a59e5a85..928bc04a4e 100644 --- a/hw/intc/openpic_kvm.c +++ b/hw/intc/openpic_kvm.c @@ -125,10 +125,6 @@ static void kvm_openpic_region_add(MemoryListener *listener, uint64_t reg_base; int ret; - if (section->fv != address_space_to_flatview(&address_space_memory)) { - abort(); - } - /* Ignore events on regions that are not us */ if (section->mr != &opp->mem) { return; diff --git a/hw/misc/trace-events b/hw/misc/trace-events index eb5ffcc0a8..562d9ed005 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -92,8 +92,8 @@ tz_ppc_cfg_sec_resp(int level) "TZ PPC: cfg_sec_resp = %d" tz_ppc_irq_enable(int level) "TZ PPC: int_enable = %d" tz_ppc_irq_clear(int level) "TZ PPC: int_clear = %d" tz_ppc_update_irq(int level) "TZ PPC: setting irq line to %d" -tz_ppc_read_blocked(int n, hwaddr offset, bool secure, bool user) "TZ PPC: port %d offset 0x%" HWADDR_PRIx " read (secure %d user %d) blocked" -tz_ppc_write_blocked(int n, hwaddr offset, bool secure, bool user) "TZ PPC: port %d offset 0x%" HWADDR_PRIx " write (secure %d user %d) blocked" +tz_ppc_read_blocked(int n, uint64_t offset, bool secure, bool user) "TZ PPC: port %d offset 0x%" PRIx64 " read (secure %d user %d) blocked" +tz_ppc_write_blocked(int n, uint64_t offset, bool secure, bool user) "TZ PPC: port %d offset 0x%" PRIx64 " write (secure %d user %d) blocked" # hw/misc/iotkit-secctl.c iotkit_secctl_s_read(uint32_t offset, uint64_t data, unsigned size) "IoTKit SecCtl S regs read: offset 0x%x data 0x%" PRIx64 " size %u" diff --git a/hw/riscv/Makefile.objs b/hw/riscv/Makefile.objs new file mode 100644 index 0000000000..1dde01d39d --- /dev/null +++ b/hw/riscv/Makefile.objs @@ -0,0 +1,11 @@ +obj-y += riscv_htif.o +obj-y += riscv_hart.o +obj-y += sifive_e.o +obj-y += sifive_clint.o +obj-y += sifive_prci.o +obj-y += sifive_plic.o +obj-y += sifive_test.o +obj-y += sifive_u.o +obj-y += sifive_uart.o +obj-y += spike.o +obj-y += virt.o diff --git a/hw/riscv/riscv_hart.c b/hw/riscv/riscv_hart.c new file mode 100644 index 0000000000..14e3c186fe --- /dev/null +++ b/hw/riscv/riscv_hart.c @@ -0,0 +1,89 @@ +/* + * QEMU RISCV Hart Array + * + * Copyright (c) 2017 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 <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/sysbus.h" +#include "target/riscv/cpu.h" +#include "hw/riscv/riscv_hart.h" + +static Property riscv_harts_props[] = { + DEFINE_PROP_UINT32("num-harts", RISCVHartArrayState, num_harts, 1), + DEFINE_PROP_STRING("cpu-type", RISCVHartArrayState, cpu_type), + DEFINE_PROP_END_OF_LIST(), +}; + +static void riscv_harts_cpu_reset(void *opaque) +{ + RISCVCPU *cpu = opaque; + cpu_reset(CPU(cpu)); +} + +static void riscv_harts_realize(DeviceState *dev, Error **errp) +{ + RISCVHartArrayState *s = RISCV_HART_ARRAY(dev); + Error *err = NULL; + int n; + + s->harts = g_new0(RISCVCPU, s->num_harts); + + for (n = 0; n < s->num_harts; n++) { + + object_initialize(&s->harts[n], sizeof(RISCVCPU), s->cpu_type); + s->harts[n].env.mhartid = n; + object_property_add_child(OBJECT(s), "harts[*]", OBJECT(&s->harts[n]), + &error_abort); + qemu_register_reset(riscv_harts_cpu_reset, &s->harts[n]); + object_property_set_bool(OBJECT(&s->harts[n]), true, + "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + } +} + +static void riscv_harts_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->props = riscv_harts_props; + dc->realize = riscv_harts_realize; +} + +static void riscv_harts_init(Object *obj) +{ + /* RISCVHartArrayState *s = SIFIVE_COREPLEX(obj); */ +} + +static const TypeInfo riscv_harts_info = { + .name = TYPE_RISCV_HART_ARRAY, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(RISCVHartArrayState), + .instance_init = riscv_harts_init, + .class_init = riscv_harts_class_init, +}; + +static void riscv_harts_register_types(void) +{ + type_register_static(&riscv_harts_info); +} + +type_init(riscv_harts_register_types) diff --git a/hw/riscv/riscv_htif.c b/hw/riscv/riscv_htif.c new file mode 100644 index 0000000000..3e17f30251 --- /dev/null +++ b/hw/riscv/riscv_htif.c @@ -0,0 +1,258 @@ +/* + * QEMU RISC-V Host Target Interface (HTIF) Emulation + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * Copyright (c) 2017-2018 SiFive, Inc. + * + * This provides HTIF device emulation for QEMU. At the moment this allows + * for identical copies of bbl/linux to run on both spike and QEMU. + * + * 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 "qapi/error.h" +#include "qemu/log.h" +#include "hw/sysbus.h" +#include "hw/char/serial.h" +#include "chardev/char.h" +#include "chardev/char-fe.h" +#include "hw/riscv/riscv_htif.h" +#include "qemu/timer.h" +#include "exec/address-spaces.h" +#include "qemu/error-report.h" + +#define RISCV_DEBUG_HTIF 0 +#define HTIF_DEBUG(fmt, ...) \ + do { \ + if (RISCV_DEBUG_HTIF) { \ + qemu_log_mask(LOG_TRACE, "%s: " fmt "\n", __func__, ##__VA_ARGS__);\ + } \ + } while (0) + +static uint64_t fromhost_addr, tohost_addr; + +void htif_symbol_callback(const char *st_name, int st_info, uint64_t st_value, + uint64_t st_size) +{ + if (strcmp("fromhost", st_name) == 0) { + fromhost_addr = st_value; + if (st_size != 8) { + error_report("HTIF fromhost must be 8 bytes"); + exit(1); + } + } else if (strcmp("tohost", st_name) == 0) { + tohost_addr = st_value; + if (st_size != 8) { + error_report("HTIF tohost must be 8 bytes"); + exit(1); + } + } +} + +/* + * Called by the char dev to see if HTIF is ready to accept input. + */ +static int htif_can_recv(void *opaque) +{ + return 1; +} + +/* + * Called by the char dev to supply input to HTIF console. + * We assume that we will receive one character at a time. + */ +static void htif_recv(void *opaque, const uint8_t *buf, int size) +{ + HTIFState *htifstate = opaque; + + if (size != 1) { + return; + } + + /* TODO - we need to check whether mfromhost is zero which indicates + the device is ready to receive. The current implementation + will drop characters */ + + uint64_t val_written = htifstate->pending_read; + uint64_t resp = 0x100 | *buf; + + htifstate->env->mfromhost = (val_written >> 48 << 48) | (resp << 16 >> 16); +} + +/* + * Called by the char dev to supply special events to the HTIF console. + * Not used for HTIF. + */ +static void htif_event(void *opaque, int event) +{ + +} + +static int htif_be_change(void *opaque) +{ + HTIFState *s = opaque; + + qemu_chr_fe_set_handlers(&s->chr, htif_can_recv, htif_recv, htif_event, + htif_be_change, s, NULL, true); + + return 0; +} + +static void htif_handle_tohost_write(HTIFState *htifstate, uint64_t val_written) +{ + uint8_t device = val_written >> 56; + uint8_t cmd = val_written >> 48; + uint64_t payload = val_written & 0xFFFFFFFFFFFFULL; + int resp = 0; + + HTIF_DEBUG("mtohost write: device: %d cmd: %d what: %02" PRIx64 + " -payload: %016" PRIx64 "\n", device, cmd, payload & 0xFF, payload); + + /* + * Currently, there is a fixed mapping of devices: + * 0: riscv-tests Pass/Fail Reporting Only (no syscall proxy) + * 1: Console + */ + if (unlikely(device == 0x0)) { + /* frontend syscall handler, shutdown and exit code support */ + if (cmd == 0x0) { + if (payload & 0x1) { + /* exit code */ + int exit_code = payload >> 1; + exit(exit_code); + } else { + qemu_log_mask(LOG_UNIMP, "pk syscall proxy not supported\n"); + } + } else { + qemu_log("HTIF device %d: unknown command\n", device); + } + } else if (likely(device == 0x1)) { + /* HTIF Console */ + if (cmd == 0x0) { + /* this should be a queue, but not yet implemented as such */ + htifstate->pending_read = val_written; + htifstate->env->mtohost = 0; /* clear to indicate we read */ + return; + } else if (cmd == 0x1) { + qemu_chr_fe_write(&htifstate->chr, (uint8_t *)&payload, 1); + resp = 0x100 | (uint8_t)payload; + } else { + qemu_log("HTIF device %d: unknown command\n", device); + } + } else { + qemu_log("HTIF unknown device or command\n"); + HTIF_DEBUG("device: %d cmd: %d what: %02" PRIx64 + " payload: %016" PRIx64, device, cmd, payload & 0xFF, payload); + } + /* + * - latest bbl does not set fromhost to 0 if there is a value in tohost + * - with this code enabled, qemu hangs waiting for fromhost to go to 0 + * - with this code disabled, qemu works with bbl priv v1.9.1 and v1.10 + * - HTIF needs protocol documentation and a more complete state machine + + while (!htifstate->fromhost_inprogress && + htifstate->env->mfromhost != 0x0) { + } + */ + htifstate->env->mfromhost = (val_written >> 48 << 48) | (resp << 16 >> 16); + htifstate->env->mtohost = 0; /* clear to indicate we read */ +} + +#define TOHOST_OFFSET1 (htifstate->tohost_offset) +#define TOHOST_OFFSET2 (htifstate->tohost_offset + 4) +#define FROMHOST_OFFSET1 (htifstate->fromhost_offset) +#define FROMHOST_OFFSET2 (htifstate->fromhost_offset + 4) + +/* CPU wants to read an HTIF register */ +static uint64_t htif_mm_read(void *opaque, hwaddr addr, unsigned size) +{ + HTIFState *htifstate = opaque; + if (addr == TOHOST_OFFSET1) { + return htifstate->env->mtohost & 0xFFFFFFFF; + } else if (addr == TOHOST_OFFSET2) { + return (htifstate->env->mtohost >> 32) & 0xFFFFFFFF; + } else if (addr == FROMHOST_OFFSET1) { + return htifstate->env->mfromhost & 0xFFFFFFFF; + } else if (addr == FROMHOST_OFFSET2) { + return (htifstate->env->mfromhost >> 32) & 0xFFFFFFFF; + } else { + qemu_log("Invalid htif read: address %016" PRIx64 "\n", + (uint64_t)addr); + return 0; + } +} + +/* CPU wrote to an HTIF register */ +static void htif_mm_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + HTIFState *htifstate = opaque; + if (addr == TOHOST_OFFSET1) { + if (htifstate->env->mtohost == 0x0) { + htifstate->allow_tohost = 1; + htifstate->env->mtohost = value & 0xFFFFFFFF; + } else { + htifstate->allow_tohost = 0; + } + } else if (addr == TOHOST_OFFSET2) { + if (htifstate->allow_tohost) { + htifstate->env->mtohost |= value << 32; + htif_handle_tohost_write(htifstate, htifstate->env->mtohost); + } + } else if (addr == FROMHOST_OFFSET1) { + htifstate->fromhost_inprogress = 1; + htifstate->env->mfromhost = value & 0xFFFFFFFF; + } else if (addr == FROMHOST_OFFSET2) { + htifstate->env->mfromhost |= value << 32; + htifstate->fromhost_inprogress = 0; + } else { + qemu_log("Invalid htif write: address %016" PRIx64 "\n", + (uint64_t)addr); + } +} + +static const MemoryRegionOps htif_mm_ops = { + .read = htif_mm_read, + .write = htif_mm_write, +}; + +HTIFState *htif_mm_init(MemoryRegion *address_space, MemoryRegion *main_mem, + CPURISCVState *env, Chardev *chr) +{ + uint64_t base = MIN(tohost_addr, fromhost_addr); + uint64_t size = MAX(tohost_addr + 8, fromhost_addr + 8) - base; + uint64_t tohost_offset = tohost_addr - base; + uint64_t fromhost_offset = fromhost_addr - base; + + HTIFState *s = g_malloc0(sizeof(HTIFState)); + s->address_space = address_space; + s->main_mem = main_mem; + s->main_mem_ram_ptr = memory_region_get_ram_ptr(main_mem); + s->env = env; + s->tohost_offset = tohost_offset; + s->fromhost_offset = fromhost_offset; + s->pending_read = 0; + s->allow_tohost = 0; + s->fromhost_inprogress = 0; + qemu_chr_fe_init(&s->chr, chr, &error_abort); + qemu_chr_fe_set_handlers(&s->chr, htif_can_recv, htif_recv, htif_event, + htif_be_change, s, NULL, true); + if (base) { + memory_region_init_io(&s->mmio, NULL, &htif_mm_ops, s, + TYPE_HTIF_UART, size); + memory_region_add_subregion(address_space, base, &s->mmio); + } + + return s; +} diff --git a/hw/riscv/sifive_clint.c b/hw/riscv/sifive_clint.c new file mode 100644 index 0000000000..4893453b70 --- /dev/null +++ b/hw/riscv/sifive_clint.c @@ -0,0 +1,254 @@ +/* + * SiFive CLINT (Core Local Interruptor) + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * Copyright (c) 2017 SiFive, Inc. + * + * This provides real-time clock, timer and interprocessor interrupts. + * + * 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/error-report.h" +#include "hw/sysbus.h" +#include "target/riscv/cpu.h" +#include "hw/riscv/sifive_clint.h" +#include "qemu/timer.h" + +/* See: riscv-pk/machine/sbi_entry.S and arch/riscv/kernel/time.c */ +#define TIMER_FREQ (10 * 1000 * 1000) + +static uint64_t cpu_riscv_read_rtc(void) +{ + return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), TIMER_FREQ, + NANOSECONDS_PER_SECOND); +} + +/* + * Called when timecmp is written to update the QEMU timer or immediately + * trigger timer interrupt if mtimecmp <= current timer value. + */ +static void sifive_clint_write_timecmp(RISCVCPU *cpu, uint64_t value) +{ + uint64_t next; + uint64_t diff; + + uint64_t rtc_r = cpu_riscv_read_rtc(); + + cpu->env.timecmp = value; + if (cpu->env.timecmp <= rtc_r) { + /* if we're setting an MTIMECMP value in the "past", + immediately raise the timer interrupt */ + riscv_set_local_interrupt(cpu, MIP_MTIP, 1); + return; + } + + /* otherwise, set up the future timer interrupt */ + riscv_set_local_interrupt(cpu, MIP_MTIP, 0); + diff = cpu->env.timecmp - rtc_r; + /* back to ns (note args switched in muldiv64) */ + next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + muldiv64(diff, NANOSECONDS_PER_SECOND, TIMER_FREQ); + timer_mod(cpu->env.timer, next); +} + +/* + * Callback used when the timer set using timer_mod expires. + * Should raise the timer interrupt line + */ +static void sifive_clint_timer_cb(void *opaque) +{ + RISCVCPU *cpu = opaque; + riscv_set_local_interrupt(cpu, MIP_MTIP, 1); +} + +/* CPU wants to read rtc or timecmp register */ +static uint64_t sifive_clint_read(void *opaque, hwaddr addr, unsigned size) +{ + SiFiveCLINTState *clint = opaque; + if (addr >= clint->sip_base && + addr < clint->sip_base + (clint->num_harts << 2)) { + size_t hartid = (addr - clint->sip_base) >> 2; + CPUState *cpu = qemu_get_cpu(hartid); + CPURISCVState *env = cpu ? cpu->env_ptr : NULL; + if (!env) { + error_report("clint: invalid timecmp hartid: %zu", hartid); + } else if ((addr & 0x3) == 0) { + return (env->mip & MIP_MSIP) > 0; + } else { + error_report("clint: invalid read: %08x", (uint32_t)addr); + return 0; + } + } else if (addr >= clint->timecmp_base && + addr < clint->timecmp_base + (clint->num_harts << 3)) { + size_t hartid = (addr - clint->timecmp_base) >> 3; + CPUState *cpu = qemu_get_cpu(hartid); + CPURISCVState *env = cpu ? cpu->env_ptr : NULL; + if (!env) { + error_report("clint: invalid timecmp hartid: %zu", hartid); + } else if ((addr & 0x7) == 0) { + /* timecmp_lo */ + uint64_t timecmp = env->timecmp; + return timecmp & 0xFFFFFFFF; + } else if ((addr & 0x7) == 4) { + /* timecmp_hi */ + uint64_t timecmp = env->timecmp; + return (timecmp >> 32) & 0xFFFFFFFF; + } else { + error_report("clint: invalid read: %08x", (uint32_t)addr); + return 0; + } + } else if (addr == clint->time_base) { + /* time_lo */ + return cpu_riscv_read_rtc() & 0xFFFFFFFF; + } else if (addr == clint->time_base + 4) { + /* time_hi */ + return (cpu_riscv_read_rtc() >> 32) & 0xFFFFFFFF; + } + + error_report("clint: invalid read: %08x", (uint32_t)addr); + return 0; +} + +/* CPU wrote to rtc or timecmp register */ +static void sifive_clint_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + SiFiveCLINTState *clint = opaque; + + if (addr >= clint->sip_base && + addr < clint->sip_base + (clint->num_harts << 2)) { + size_t hartid = (addr - clint->sip_base) >> 2; + CPUState *cpu = qemu_get_cpu(hartid); + CPURISCVState *env = cpu ? cpu->env_ptr : NULL; + if (!env) { + error_report("clint: invalid timecmp hartid: %zu", hartid); + } else if ((addr & 0x3) == 0) { + riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_MSIP, value != 0); + } else { + error_report("clint: invalid sip write: %08x", (uint32_t)addr); + } + return; + } else if (addr >= clint->timecmp_base && + addr < clint->timecmp_base + (clint->num_harts << 3)) { + size_t hartid = (addr - clint->timecmp_base) >> 3; + CPUState *cpu = qemu_get_cpu(hartid); + CPURISCVState *env = cpu ? cpu->env_ptr : NULL; + if (!env) { + error_report("clint: invalid timecmp hartid: %zu", hartid); + } else if ((addr & 0x7) == 0) { + /* timecmp_lo */ + uint64_t timecmp = env->timecmp; + sifive_clint_write_timecmp(RISCV_CPU(cpu), + timecmp << 32 | (value & 0xFFFFFFFF)); + return; + } else if ((addr & 0x7) == 4) { + /* timecmp_hi */ + uint64_t timecmp = env->timecmp; + sifive_clint_write_timecmp(RISCV_CPU(cpu), + value << 32 | (timecmp & 0xFFFFFFFF)); + } else { + error_report("clint: invalid timecmp write: %08x", (uint32_t)addr); + } + return; + } else if (addr == clint->time_base) { + /* time_lo */ + error_report("clint: time_lo write not implemented"); + return; + } else if (addr == clint->time_base + 4) { + /* time_hi */ + error_report("clint: time_hi write not implemented"); + return; + } + + error_report("clint: invalid write: %08x", (uint32_t)addr); +} + +static const MemoryRegionOps sifive_clint_ops = { + .read = sifive_clint_read, + .write = sifive_clint_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4 + } +}; + +static Property sifive_clint_properties[] = { + DEFINE_PROP_UINT32("num-harts", SiFiveCLINTState, num_harts, 0), + DEFINE_PROP_UINT32("sip-base", SiFiveCLINTState, sip_base, 0), + DEFINE_PROP_UINT32("timecmp-base", SiFiveCLINTState, timecmp_base, 0), + DEFINE_PROP_UINT32("time-base", SiFiveCLINTState, time_base, 0), + DEFINE_PROP_UINT32("aperture-size", SiFiveCLINTState, aperture_size, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void sifive_clint_realize(DeviceState *dev, Error **errp) +{ + SiFiveCLINTState *s = SIFIVE_CLINT(dev); + memory_region_init_io(&s->mmio, OBJECT(dev), &sifive_clint_ops, s, + TYPE_SIFIVE_CLINT, s->aperture_size); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio); +} + +static void sifive_clint_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + dc->realize = sifive_clint_realize; + dc->props = sifive_clint_properties; +} + +static const TypeInfo sifive_clint_info = { + .name = TYPE_SIFIVE_CLINT, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SiFiveCLINTState), + .class_init = sifive_clint_class_init, +}; + +static void sifive_clint_register_types(void) +{ + type_register_static(&sifive_clint_info); +} + +type_init(sifive_clint_register_types) + + +/* + * Create CLINT device. + */ +DeviceState *sifive_clint_create(hwaddr addr, hwaddr size, uint32_t num_harts, + uint32_t sip_base, uint32_t timecmp_base, uint32_t time_base) +{ + int i; + for (i = 0; i < num_harts; i++) { + CPUState *cpu = qemu_get_cpu(i); + CPURISCVState *env = cpu ? cpu->env_ptr : NULL; + if (!env) { + continue; + } + env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + &sifive_clint_timer_cb, cpu); + env->timecmp = 0; + } + + DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_CLINT); + qdev_prop_set_uint32(dev, "num-harts", num_harts); + qdev_prop_set_uint32(dev, "sip-base", sip_base); + qdev_prop_set_uint32(dev, "timecmp-base", timecmp_base); + qdev_prop_set_uint32(dev, "time-base", time_base); + qdev_prop_set_uint32(dev, "aperture-size", size); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); + return dev; +} diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c new file mode 100644 index 0000000000..19eca36ff4 --- /dev/null +++ b/hw/riscv/sifive_e.c @@ -0,0 +1,234 @@ +/* + * QEMU RISC-V Board Compatible with SiFive Freedom E SDK + * + * Copyright (c) 2017 SiFive, Inc. + * + * Provides a board compatible with the SiFive Freedom E SDK: + * + * 0) UART + * 1) CLINT (Core Level Interruptor) + * 2) PLIC (Platform Level Interrupt Controller) + * 3) PRCI (Power, Reset, Clock, Interrupt) + * 4) Registers emulated as RAM: AON, GPIO, QSPI, PWM + * 5) Flash memory emulated as RAM + * + * The Mask ROM reset vector jumps to the flash payload at 0x2040_0000. + * The OTP ROM and Flash boot code will be emulated in a future version. + * + * 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/hw.h" +#include "hw/boards.h" +#include "hw/loader.h" +#include "hw/sysbus.h" +#include "hw/char/serial.h" +#include "target/riscv/cpu.h" +#include "hw/riscv/riscv_hart.h" +#include "hw/riscv/sifive_plic.h" +#include "hw/riscv/sifive_clint.h" +#include "hw/riscv/sifive_prci.h" +#include "hw/riscv/sifive_uart.h" +#include "hw/riscv/sifive_e.h" +#include "chardev/char.h" +#include "sysemu/arch_init.h" +#include "exec/address-spaces.h" +#include "elf.h" + +static const struct MemmapEntry { + hwaddr base; + hwaddr size; +} sifive_e_memmap[] = { + [SIFIVE_E_DEBUG] = { 0x0, 0x100 }, + [SIFIVE_E_MROM] = { 0x1000, 0x2000 }, + [SIFIVE_E_OTP] = { 0x20000, 0x2000 }, + [SIFIVE_E_CLINT] = { 0x2000000, 0x10000 }, + [SIFIVE_E_PLIC] = { 0xc000000, 0x4000000 }, + [SIFIVE_E_AON] = { 0x10000000, 0x8000 }, + [SIFIVE_E_PRCI] = { 0x10008000, 0x8000 }, + [SIFIVE_E_OTP_CTRL] = { 0x10010000, 0x1000 }, + [SIFIVE_E_GPIO0] = { 0x10012000, 0x1000 }, + [SIFIVE_E_UART0] = { 0x10013000, 0x1000 }, + [SIFIVE_E_QSPI0] = { 0x10014000, 0x1000 }, + [SIFIVE_E_PWM0] = { 0x10015000, 0x1000 }, + [SIFIVE_E_UART1] = { 0x10023000, 0x1000 }, + [SIFIVE_E_QSPI1] = { 0x10024000, 0x1000 }, + [SIFIVE_E_PWM1] = { 0x10025000, 0x1000 }, + [SIFIVE_E_QSPI2] = { 0x10034000, 0x1000 }, + [SIFIVE_E_PWM2] = { 0x10035000, 0x1000 }, + [SIFIVE_E_XIP] = { 0x20000000, 0x20000000 }, + [SIFIVE_E_DTIM] = { 0x80000000, 0x4000 } +}; + +static void copy_le32_to_phys(hwaddr pa, uint32_t *rom, size_t len) +{ + int i; + for (i = 0; i < (len >> 2); i++) { + stl_phys(&address_space_memory, pa + (i << 2), rom[i]); + } +} + +static uint64_t identity_translate(void *opaque, uint64_t addr) +{ + return addr; +} + +static uint64_t load_kernel(const char *kernel_filename) +{ + uint64_t kernel_entry, kernel_high; + + if (load_elf(kernel_filename, identity_translate, NULL, + &kernel_entry, NULL, &kernel_high, + 0, ELF_MACHINE, 1, 0) < 0) { + error_report("qemu: could not load kernel '%s'", kernel_filename); + exit(1); + } + return kernel_entry; +} + +static void sifive_mmio_emulate(MemoryRegion *parent, const char *name, + uintptr_t offset, uintptr_t length) +{ + MemoryRegion *mock_mmio = g_new(MemoryRegion, 1); + memory_region_init_ram(mock_mmio, NULL, name, length, &error_fatal); + memory_region_add_subregion(parent, offset, mock_mmio); +} + +static void riscv_sifive_e_init(MachineState *machine) +{ + const struct MemmapEntry *memmap = sifive_e_memmap; + + SiFiveEState *s = g_new0(SiFiveEState, 1); + MemoryRegion *sys_mem = get_system_memory(); + MemoryRegion *main_mem = g_new(MemoryRegion, 1); + MemoryRegion *mask_rom = g_new(MemoryRegion, 1); + MemoryRegion *xip_mem = g_new(MemoryRegion, 1); + + /* Initialize SOC */ + object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY); + object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc), + &error_abort); + object_property_set_str(OBJECT(&s->soc), SIFIVE_E_CPU, "cpu-type", + &error_abort); + object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts", + &error_abort); + object_property_set_bool(OBJECT(&s->soc), true, "realized", + &error_abort); + + /* Data Tightly Integrated Memory */ + memory_region_init_ram(main_mem, NULL, "riscv.sifive.e.ram", + memmap[SIFIVE_E_DTIM].size, &error_fatal); + memory_region_add_subregion(sys_mem, + memmap[SIFIVE_E_DTIM].base, main_mem); + + /* Mask ROM */ + memory_region_init_ram(mask_rom, NULL, "riscv.sifive.e.mrom", + memmap[SIFIVE_E_MROM].size, &error_fatal); + memory_region_add_subregion(sys_mem, + memmap[SIFIVE_E_MROM].base, mask_rom); + + /* MMIO */ + s->plic = sifive_plic_create(memmap[SIFIVE_E_PLIC].base, + (char *)SIFIVE_E_PLIC_HART_CONFIG, + SIFIVE_E_PLIC_NUM_SOURCES, + SIFIVE_E_PLIC_NUM_PRIORITIES, + SIFIVE_E_PLIC_PRIORITY_BASE, + SIFIVE_E_PLIC_PENDING_BASE, + SIFIVE_E_PLIC_ENABLE_BASE, + SIFIVE_E_PLIC_ENABLE_STRIDE, + SIFIVE_E_PLIC_CONTEXT_BASE, + SIFIVE_E_PLIC_CONTEXT_STRIDE, + memmap[SIFIVE_E_PLIC].size); + sifive_clint_create(memmap[SIFIVE_E_CLINT].base, + memmap[SIFIVE_E_CLINT].size, smp_cpus, + SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE); + sifive_mmio_emulate(sys_mem, "riscv.sifive.e.aon", + memmap[SIFIVE_E_AON].base, memmap[SIFIVE_E_AON].size); + sifive_prci_create(memmap[SIFIVE_E_PRCI].base); + sifive_mmio_emulate(sys_mem, "riscv.sifive.e.gpio0", + memmap[SIFIVE_E_GPIO0].base, memmap[SIFIVE_E_GPIO0].size); + sifive_uart_create(sys_mem, memmap[SIFIVE_E_UART0].base, + serial_hds[0], SIFIVE_PLIC(s->plic)->irqs[SIFIVE_E_UART0_IRQ]); + sifive_mmio_emulate(sys_mem, "riscv.sifive.e.qspi0", + memmap[SIFIVE_E_QSPI0].base, memmap[SIFIVE_E_QSPI0].size); + sifive_mmio_emulate(sys_mem, "riscv.sifive.e.pwm0", + memmap[SIFIVE_E_PWM0].base, memmap[SIFIVE_E_PWM0].size); + /* sifive_uart_create(sys_mem, memmap[SIFIVE_E_UART1].base, + serial_hds[1], SIFIVE_PLIC(s->plic)->irqs[SIFIVE_E_UART1_IRQ]); */ + sifive_mmio_emulate(sys_mem, "riscv.sifive.e.qspi1", + memmap[SIFIVE_E_QSPI1].base, memmap[SIFIVE_E_QSPI1].size); + sifive_mmio_emulate(sys_mem, "riscv.sifive.e.pwm1", + memmap[SIFIVE_E_PWM1].base, memmap[SIFIVE_E_PWM1].size); + sifive_mmio_emulate(sys_mem, "riscv.sifive.e.qspi2", + memmap[SIFIVE_E_QSPI2].base, memmap[SIFIVE_E_QSPI2].size); + sifive_mmio_emulate(sys_mem, "riscv.sifive.e.pwm2", + memmap[SIFIVE_E_PWM2].base, memmap[SIFIVE_E_PWM2].size); + + /* Flash memory */ + memory_region_init_ram(xip_mem, NULL, "riscv.sifive.e.xip", + memmap[SIFIVE_E_XIP].size, &error_fatal); + memory_region_set_readonly(xip_mem, true); + memory_region_add_subregion(sys_mem, memmap[SIFIVE_E_XIP].base, xip_mem); + + /* Mask ROM reset vector */ + uint32_t reset_vec[2] = { + 0x204002b7, /* 0x1000: lui t0,0x20400 */ + 0x00028067, /* 0x1004: jr t0 */ + }; + + /* copy in the reset vector */ + copy_le32_to_phys(memmap[SIFIVE_E_MROM].base, reset_vec, sizeof(reset_vec)); + memory_region_set_readonly(mask_rom, true); + + if (machine->kernel_filename) { + load_kernel(machine->kernel_filename); + } +} + +static int riscv_sifive_e_sysbus_device_init(SysBusDevice *sysbusdev) +{ + return 0; +} + +static void riscv_sifive_e_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + k->init = riscv_sifive_e_sysbus_device_init; +} + +static const TypeInfo riscv_sifive_e_device = { + .name = TYPE_SIFIVE_E, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SiFiveEState), + .class_init = riscv_sifive_e_class_init, +}; + +static void riscv_sifive_e_machine_init(MachineClass *mc) +{ + mc->desc = "RISC-V Board compatible with SiFive E SDK"; + mc->init = riscv_sifive_e_init; + mc->max_cpus = 1; +} + +DEFINE_MACHINE("sifive_e", riscv_sifive_e_machine_init) + +static void riscv_sifive_e_register_types(void) +{ + type_register_static(&riscv_sifive_e_device); +} + +type_init(riscv_sifive_e_register_types); diff --git a/hw/riscv/sifive_plic.c b/hw/riscv/sifive_plic.c new file mode 100644 index 0000000000..874de2ebaf --- /dev/null +++ b/hw/riscv/sifive_plic.c @@ -0,0 +1,505 @@ +/* + * SiFive PLIC (Platform Level Interrupt Controller) + * + * Copyright (c) 2017 SiFive, Inc. + * + * This provides a parameterizable interrupt controller based on SiFive's PLIC. + * + * 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 "hw/sysbus.h" +#include "target/riscv/cpu.h" +#include "hw/riscv/sifive_plic.h" + +#define RISCV_DEBUG_PLIC 0 + +static PLICMode char_to_mode(char c) +{ + switch (c) { + case 'U': return PLICMode_U; + case 'S': return PLICMode_S; + case 'H': return PLICMode_H; + case 'M': return PLICMode_M; + default: + error_report("plic: invalid mode '%c'", c); + exit(1); + } +} + +static char mode_to_char(PLICMode m) +{ + switch (m) { + case PLICMode_U: return 'U'; + case PLICMode_S: return 'S'; + case PLICMode_H: return 'H'; + case PLICMode_M: return 'M'; + default: return '?'; + } +} + +static void sifive_plic_print_state(SiFivePLICState *plic) +{ + int i; + int addrid; + + /* pending */ + qemu_log("pending : "); + for (i = plic->bitfield_words - 1; i >= 0; i--) { + qemu_log("%08x", plic->pending[i]); + } + qemu_log("\n"); + + /* pending */ + qemu_log("claimed : "); + for (i = plic->bitfield_words - 1; i >= 0; i--) { + qemu_log("%08x", plic->claimed[i]); + } + qemu_log("\n"); + + for (addrid = 0; addrid < plic->num_addrs; addrid++) { + qemu_log("hart%d-%c enable: ", + plic->addr_config[addrid].hartid, + mode_to_char(plic->addr_config[addrid].mode)); + for (i = plic->bitfield_words - 1; i >= 0; i--) { + qemu_log("%08x", plic->enable[addrid * plic->bitfield_words + i]); + } + qemu_log("\n"); + } +} + +static +void sifive_plic_set_pending(SiFivePLICState *plic, int irq, bool pending) +{ + qemu_mutex_lock(&plic->lock); + uint32_t word = irq >> 5; + if (pending) { + plic->pending[word] |= (1 << (irq & 31)); + } else { + plic->pending[word] &= ~(1 << (irq & 31)); + } + qemu_mutex_unlock(&plic->lock); +} + +static +void sifive_plic_set_claimed(SiFivePLICState *plic, int irq, bool claimed) +{ + qemu_mutex_lock(&plic->lock); + uint32_t word = irq >> 5; + if (claimed) { + plic->claimed[word] |= (1 << (irq & 31)); + } else { + plic->claimed[word] &= ~(1 << (irq & 31)); + } + qemu_mutex_unlock(&plic->lock); +} + +static +int sifive_plic_num_irqs_pending(SiFivePLICState *plic, uint32_t addrid) +{ + int i, j, count = 0; + for (i = 0; i < plic->bitfield_words; i++) { + uint32_t pending_enabled_not_claimed = + (plic->pending[i] & ~plic->claimed[i]) & + plic->enable[addrid * plic->bitfield_words + i]; + if (!pending_enabled_not_claimed) { + continue; + } + for (j = 0; j < 32; j++) { + int irq = (i << 5) + j; + uint32_t prio = plic->source_priority[irq]; + int enabled = pending_enabled_not_claimed & (1 << j); + if (enabled && prio > plic->target_priority[addrid]) { + count++; + } + } + } + return count; +} + +static void sifive_plic_update(SiFivePLICState *plic) +{ + int addrid; + + /* raise irq on harts where this irq is enabled */ + for (addrid = 0; addrid < plic->num_addrs; addrid++) { + uint32_t hartid = plic->addr_config[addrid].hartid; + PLICMode mode = plic->addr_config[addrid].mode; + CPUState *cpu = qemu_get_cpu(hartid); + CPURISCVState *env = cpu ? cpu->env_ptr : NULL; + if (!env) { + continue; + } + int level = sifive_plic_num_irqs_pending(plic, addrid) > 0; + switch (mode) { + case PLICMode_M: + riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_MEIP, level); + break; + case PLICMode_S: + riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_SEIP, level); + break; + default: + break; + } + } + + if (RISCV_DEBUG_PLIC) { + sifive_plic_print_state(plic); + } +} + +void sifive_plic_raise_irq(SiFivePLICState *plic, uint32_t irq) +{ + sifive_plic_set_pending(plic, irq, true); + sifive_plic_update(plic); +} + +void sifive_plic_lower_irq(SiFivePLICState *plic, uint32_t irq) +{ + sifive_plic_set_pending(plic, irq, false); + sifive_plic_update(plic); +} + +static uint32_t sifive_plic_claim(SiFivePLICState *plic, uint32_t addrid) +{ + int i, j; + for (i = 0; i < plic->bitfield_words; i++) { + uint32_t pending_enabled_not_claimed = + (plic->pending[i] & ~plic->claimed[i]) & + plic->enable[addrid * plic->bitfield_words + i]; + if (!pending_enabled_not_claimed) { + continue; + } + for (j = 0; j < 32; j++) { + int irq = (i << 5) + j; + uint32_t prio = plic->source_priority[irq]; + int enabled = pending_enabled_not_claimed & (1 << j); + if (enabled && prio > plic->target_priority[addrid]) { + sifive_plic_set_pending(plic, irq, false); + sifive_plic_set_claimed(plic, irq, true); + return irq; + } + } + } + return 0; +} + +static uint64_t sifive_plic_read(void *opaque, hwaddr addr, unsigned size) +{ + SiFivePLICState *plic = opaque; + + /* writes must be 4 byte words */ + if ((addr & 0x3) != 0) { + goto err; + } + + if (addr >= plic->priority_base && /* 4 bytes per source */ + addr < plic->priority_base + (plic->num_sources << 2)) + { + uint32_t irq = (addr - plic->priority_base) >> 2; + if (RISCV_DEBUG_PLIC) { + qemu_log("plic: read priority: irq=%d priority=%d\n", + irq, plic->source_priority[irq]); + } + return plic->source_priority[irq]; + } else if (addr >= plic->pending_base && /* 1 bit per source */ + addr < plic->pending_base + (plic->num_sources >> 3)) + { + uint32_t word = (addr - plic->priority_base) >> 2; + if (RISCV_DEBUG_PLIC) { + qemu_log("plic: read pending: word=%d value=%d\n", + word, plic->pending[word]); + } + return plic->pending[word]; + } else if (addr >= plic->enable_base && /* 1 bit per source */ + addr < plic->enable_base + plic->num_addrs * plic->enable_stride) + { + uint32_t addrid = (addr - plic->enable_base) / plic->enable_stride; + uint32_t wordid = (addr & (plic->enable_stride - 1)) >> 2; + if (wordid < plic->bitfield_words) { + if (RISCV_DEBUG_PLIC) { + qemu_log("plic: read enable: hart%d-%c word=%d value=%x\n", + plic->addr_config[addrid].hartid, + mode_to_char(plic->addr_config[addrid].mode), wordid, + plic->enable[addrid * plic->bitfield_words + wordid]); + } + return plic->enable[addrid * plic->bitfield_words + wordid]; + } + } else if (addr >= plic->context_base && /* 1 bit per source */ + addr < plic->context_base + plic->num_addrs * plic->context_stride) + { + uint32_t addrid = (addr - plic->context_base) / plic->context_stride; + uint32_t contextid = (addr & (plic->context_stride - 1)); + if (contextid == 0) { + if (RISCV_DEBUG_PLIC) { + qemu_log("plic: read priority: hart%d-%c priority=%x\n", + plic->addr_config[addrid].hartid, + mode_to_char(plic->addr_config[addrid].mode), + plic->target_priority[addrid]); + } + return plic->target_priority[addrid]; + } else if (contextid == 4) { + uint32_t value = sifive_plic_claim(plic, addrid); + if (RISCV_DEBUG_PLIC) { + qemu_log("plic: read claim: hart%d-%c irq=%x\n", + plic->addr_config[addrid].hartid, + mode_to_char(plic->addr_config[addrid].mode), + value); + sifive_plic_print_state(plic); + } + return value; + } + } + +err: + error_report("plic: invalid register read: %08x", (uint32_t)addr); + return 0; +} + +static void sifive_plic_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + SiFivePLICState *plic = opaque; + + /* writes must be 4 byte words */ + if ((addr & 0x3) != 0) { + goto err; + } + + if (addr >= plic->priority_base && /* 4 bytes per source */ + addr < plic->priority_base + (plic->num_sources << 2)) + { + uint32_t irq = (addr - plic->priority_base) >> 2; + plic->source_priority[irq] = value & 7; + if (RISCV_DEBUG_PLIC) { + qemu_log("plic: write priority: irq=%d priority=%d\n", + irq, plic->source_priority[irq]); + } + return; + } else if (addr >= plic->pending_base && /* 1 bit per source */ + addr < plic->pending_base + (plic->num_sources >> 3)) + { + error_report("plic: invalid pending write: %08x", (uint32_t)addr); + return; + } else if (addr >= plic->enable_base && /* 1 bit per source */ + addr < plic->enable_base + plic->num_addrs * plic->enable_stride) + { + uint32_t addrid = (addr - plic->enable_base) / plic->enable_stride; + uint32_t wordid = (addr & (plic->enable_stride - 1)) >> 2; + if (wordid < plic->bitfield_words) { + plic->enable[addrid * plic->bitfield_words + wordid] = value; + if (RISCV_DEBUG_PLIC) { + qemu_log("plic: write enable: hart%d-%c word=%d value=%x\n", + plic->addr_config[addrid].hartid, + mode_to_char(plic->addr_config[addrid].mode), wordid, + plic->enable[addrid * plic->bitfield_words + wordid]); + } + return; + } + } else if (addr >= plic->context_base && /* 4 bytes per reg */ + addr < plic->context_base + plic->num_addrs * plic->context_stride) + { + uint32_t addrid = (addr - plic->context_base) / plic->context_stride; + uint32_t contextid = (addr & (plic->context_stride - 1)); + if (contextid == 0) { + if (RISCV_DEBUG_PLIC) { + qemu_log("plic: write priority: hart%d-%c priority=%x\n", + plic->addr_config[addrid].hartid, + mode_to_char(plic->addr_config[addrid].mode), + plic->target_priority[addrid]); + } + if (value <= plic->num_priorities) { + plic->target_priority[addrid] = value; + sifive_plic_update(plic); + } + return; + } else if (contextid == 4) { + if (RISCV_DEBUG_PLIC) { + qemu_log("plic: write claim: hart%d-%c irq=%x\n", + plic->addr_config[addrid].hartid, + mode_to_char(plic->addr_config[addrid].mode), + (uint32_t)value); + } + if (value < plic->num_sources) { + sifive_plic_set_claimed(plic, value, false); + sifive_plic_update(plic); + } + return; + } + } + +err: + error_report("plic: invalid register write: %08x", (uint32_t)addr); +} + +static const MemoryRegionOps sifive_plic_ops = { + .read = sifive_plic_read, + .write = sifive_plic_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4 + } +}; + +static Property sifive_plic_properties[] = { + DEFINE_PROP_STRING("hart-config", SiFivePLICState, hart_config), + DEFINE_PROP_UINT32("num-sources", SiFivePLICState, num_sources, 0), + DEFINE_PROP_UINT32("num-priorities", SiFivePLICState, num_priorities, 0), + DEFINE_PROP_UINT32("priority-base", SiFivePLICState, priority_base, 0), + DEFINE_PROP_UINT32("pending-base", SiFivePLICState, pending_base, 0), + DEFINE_PROP_UINT32("enable-base", SiFivePLICState, enable_base, 0), + DEFINE_PROP_UINT32("enable-stride", SiFivePLICState, enable_stride, 0), + DEFINE_PROP_UINT32("context-base", SiFivePLICState, context_base, 0), + DEFINE_PROP_UINT32("context-stride", SiFivePLICState, context_stride, 0), + DEFINE_PROP_UINT32("aperture-size", SiFivePLICState, aperture_size, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +/* + * parse PLIC hart/mode address offset config + * + * "M" 1 hart with M mode + * "MS,MS" 2 harts, 0-1 with M and S mode + * "M,MS,MS,MS,MS" 5 harts, 0 with M mode, 1-5 with M and S mode + */ +static void parse_hart_config(SiFivePLICState *plic) +{ + int addrid, hartid, modes; + const char *p; + char c; + + /* count and validate hart/mode combinations */ + addrid = 0, hartid = 0, modes = 0; + p = plic->hart_config; + while ((c = *p++)) { + if (c == ',') { + addrid += __builtin_popcount(modes); + modes = 0; + hartid++; + } else { + int m = 1 << char_to_mode(c); + if (modes == (modes | m)) { + error_report("plic: duplicate mode '%c' in config: %s", + c, plic->hart_config); + exit(1); + } + modes |= m; + } + } + if (modes) { + addrid += __builtin_popcount(modes); + } + hartid++; + + /* store hart/mode combinations */ + plic->num_addrs = addrid; + plic->addr_config = g_new(PLICAddr, plic->num_addrs); + addrid = 0, hartid = 0; + p = plic->hart_config; + while ((c = *p++)) { + if (c == ',') { + hartid++; + } else { + plic->addr_config[addrid].addrid = addrid; + plic->addr_config[addrid].hartid = hartid; + plic->addr_config[addrid].mode = char_to_mode(c); + addrid++; + } + } +} + +static void sifive_plic_irq_request(void *opaque, int irq, int level) +{ + SiFivePLICState *plic = opaque; + if (RISCV_DEBUG_PLIC) { + qemu_log("sifive_plic_irq_request: irq=%d level=%d\n", irq, level); + } + sifive_plic_set_pending(plic, irq, level > 0); + sifive_plic_update(plic); +} + +static void sifive_plic_realize(DeviceState *dev, Error **errp) +{ + SiFivePLICState *plic = SIFIVE_PLIC(dev); + int i; + + memory_region_init_io(&plic->mmio, OBJECT(dev), &sifive_plic_ops, plic, + TYPE_SIFIVE_PLIC, plic->aperture_size); + parse_hart_config(plic); + qemu_mutex_init(&plic->lock); + plic->bitfield_words = (plic->num_sources + 31) >> 5; + plic->source_priority = g_new0(uint32_t, plic->num_sources); + plic->target_priority = g_new(uint32_t, plic->num_addrs); + plic->pending = g_new0(uint32_t, plic->bitfield_words); + plic->claimed = g_new0(uint32_t, plic->bitfield_words); + plic->enable = g_new0(uint32_t, plic->bitfield_words * plic->num_addrs); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &plic->mmio); + plic->irqs = g_new0(qemu_irq, plic->num_sources + 1); + for (i = 0; i <= plic->num_sources; i++) { + plic->irqs[i] = qemu_allocate_irq(sifive_plic_irq_request, plic, i); + } +} + +static void sifive_plic_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->props = sifive_plic_properties; + dc->realize = sifive_plic_realize; +} + +static const TypeInfo sifive_plic_info = { + .name = TYPE_SIFIVE_PLIC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SiFivePLICState), + .class_init = sifive_plic_class_init, +}; + +static void sifive_plic_register_types(void) +{ + type_register_static(&sifive_plic_info); +} + +type_init(sifive_plic_register_types) + +/* + * Create PLIC device. + */ +DeviceState *sifive_plic_create(hwaddr addr, char *hart_config, + uint32_t num_sources, uint32_t num_priorities, + uint32_t priority_base, uint32_t pending_base, + uint32_t enable_base, uint32_t enable_stride, + uint32_t context_base, uint32_t context_stride, + uint32_t aperture_size) +{ + DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_PLIC); + assert(enable_stride == (enable_stride & -enable_stride)); + assert(context_stride == (context_stride & -context_stride)); + qdev_prop_set_string(dev, "hart-config", hart_config); + qdev_prop_set_uint32(dev, "num-sources", num_sources); + qdev_prop_set_uint32(dev, "num-priorities", num_priorities); + qdev_prop_set_uint32(dev, "priority-base", priority_base); + qdev_prop_set_uint32(dev, "pending-base", pending_base); + qdev_prop_set_uint32(dev, "enable-base", enable_base); + qdev_prop_set_uint32(dev, "enable-stride", enable_stride); + qdev_prop_set_uint32(dev, "context-base", context_base); + qdev_prop_set_uint32(dev, "context-stride", context_stride); + qdev_prop_set_uint32(dev, "aperture-size", aperture_size); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); + return dev; +} diff --git a/hw/riscv/sifive_prci.c b/hw/riscv/sifive_prci.c new file mode 100644 index 0000000000..0910ea32c1 --- /dev/null +++ b/hw/riscv/sifive_prci.c @@ -0,0 +1,89 @@ +/* + * QEMU SiFive PRCI (Power, Reset, Clock, Interrupt) + * + * Copyright (c) 2017 SiFive, Inc. + * + * Simple model of the PRCI to emulate register reads made by the SDK BSP + * + * 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 "hw/sysbus.h" +#include "target/riscv/cpu.h" +#include "hw/riscv/sifive_prci.h" + +/* currently implements enough to mock freedom-e-sdk BSP clock programming */ + +static uint64_t sifive_prci_read(void *opaque, hwaddr addr, unsigned int size) +{ + if (addr == 0 /* PRCI_HFROSCCFG */) { + return 1 << 31; /* ROSC_RDY */ + } + if (addr == 8 /* PRCI_PLLCFG */) { + return 1 << 31; /* PLL_LOCK */ + } + hw_error("%s: read: addr=0x%x\n", __func__, (int)addr); + return 0; +} + +static void sifive_prci_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + /* discard writes */ +} + +static const MemoryRegionOps sifive_prci_ops = { + .read = sifive_prci_read, + .write = sifive_prci_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4 + } +}; + +static void sifive_prci_init(Object *obj) +{ + SiFivePRCIState *s = SIFIVE_PRCI(obj); + + memory_region_init_io(&s->mmio, obj, &sifive_prci_ops, s, + TYPE_SIFIVE_PRCI, 0x8000); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); +} + +static const TypeInfo sifive_prci_info = { + .name = TYPE_SIFIVE_PRCI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SiFivePRCIState), + .instance_init = sifive_prci_init, +}; + +static void sifive_prci_register_types(void) +{ + type_register_static(&sifive_prci_info); +} + +type_init(sifive_prci_register_types) + + +/* + * Create PRCI device. + */ +DeviceState *sifive_prci_create(hwaddr addr) +{ + DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_PRCI); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); + return dev; +} diff --git a/hw/riscv/sifive_test.c b/hw/riscv/sifive_test.c new file mode 100644 index 0000000000..8abd2cd525 --- /dev/null +++ b/hw/riscv/sifive_test.c @@ -0,0 +1,93 @@ +/* + * QEMU SiFive Test Finisher + * + * Copyright (c) 2018 SiFive, Inc. + * + * Test finisher memory mapped device used to exit simulation + * + * 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 "hw/sysbus.h" +#include "target/riscv/cpu.h" +#include "hw/riscv/sifive_test.h" + +static uint64_t sifive_test_read(void *opaque, hwaddr addr, unsigned int size) +{ + return 0; +} + +static void sifive_test_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + if (addr == 0) { + int status = val64 & 0xffff; + int code = (val64 >> 16) & 0xffff; + switch (status) { + case FINISHER_FAIL: + exit(code); + case FINISHER_PASS: + exit(0); + default: + break; + } + } + hw_error("%s: write: addr=0x%x val=0x%016" PRIx64 "\n", + __func__, (int)addr, val64); +} + +static const MemoryRegionOps sifive_test_ops = { + .read = sifive_test_read, + .write = sifive_test_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4 + } +}; + +static void sifive_test_init(Object *obj) +{ + SiFiveTestState *s = SIFIVE_TEST(obj); + + memory_region_init_io(&s->mmio, obj, &sifive_test_ops, s, + TYPE_SIFIVE_TEST, 0x1000); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); +} + +static const TypeInfo sifive_test_info = { + .name = TYPE_SIFIVE_TEST, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SiFiveTestState), + .instance_init = sifive_test_init, +}; + +static void sifive_test_register_types(void) +{ + type_register_static(&sifive_test_info); +} + +type_init(sifive_test_register_types) + + +/* + * Create Test device. + */ +DeviceState *sifive_test_create(hwaddr addr) +{ + DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_TEST); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); + return dev; +} diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c new file mode 100644 index 0000000000..1c2deefa6c --- /dev/null +++ b/hw/riscv/sifive_u.c @@ -0,0 +1,339 @@ +/* + * QEMU RISC-V Board Compatible with SiFive Freedom U SDK + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * Copyright (c) 2017 SiFive, Inc. + * + * Provides a board compatible with the SiFive Freedom U SDK: + * + * 0) UART + * 1) CLINT (Core Level Interruptor) + * 2) PLIC (Platform Level Interrupt Controller) + * + * This board currently uses a hardcoded devicetree that indicates one hart. + * + * 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/hw.h" +#include "hw/boards.h" +#include "hw/loader.h" +#include "hw/sysbus.h" +#include "hw/char/serial.h" +#include "target/riscv/cpu.h" +#include "hw/riscv/riscv_hart.h" +#include "hw/riscv/sifive_plic.h" +#include "hw/riscv/sifive_clint.h" +#include "hw/riscv/sifive_uart.h" +#include "hw/riscv/sifive_prci.h" +#include "hw/riscv/sifive_u.h" +#include "chardev/char.h" +#include "sysemu/arch_init.h" +#include "sysemu/device_tree.h" +#include "exec/address-spaces.h" +#include "elf.h" + +static const struct MemmapEntry { + hwaddr base; + hwaddr size; +} sifive_u_memmap[] = { + [SIFIVE_U_DEBUG] = { 0x0, 0x100 }, + [SIFIVE_U_MROM] = { 0x1000, 0x2000 }, + [SIFIVE_U_CLINT] = { 0x2000000, 0x10000 }, + [SIFIVE_U_PLIC] = { 0xc000000, 0x4000000 }, + [SIFIVE_U_UART0] = { 0x10013000, 0x1000 }, + [SIFIVE_U_UART1] = { 0x10023000, 0x1000 }, + [SIFIVE_U_DRAM] = { 0x80000000, 0x0 }, +}; + +static void copy_le32_to_phys(hwaddr pa, uint32_t *rom, size_t len) +{ + int i; + for (i = 0; i < (len >> 2); i++) { + stl_phys(&address_space_memory, pa + (i << 2), rom[i]); + } +} + +static uint64_t identity_translate(void *opaque, uint64_t addr) +{ + return addr; +} + +static uint64_t load_kernel(const char *kernel_filename) +{ + uint64_t kernel_entry, kernel_high; + + if (load_elf(kernel_filename, identity_translate, NULL, + &kernel_entry, NULL, &kernel_high, + 0, ELF_MACHINE, 1, 0) < 0) { + error_report("qemu: could not load kernel '%s'", kernel_filename); + exit(1); + } + return kernel_entry; +} + +static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, + uint64_t mem_size, const char *cmdline) +{ + void *fdt; + int cpu; + uint32_t *cells; + char *nodename; + uint32_t plic_phandle; + + 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, "/soc"); + qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0); + qemu_fdt_setprop_string(fdt, "/soc", "compatible", "ucbbar,spike-bare-soc"); + 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[SIFIVE_U_DRAM].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + memmap[SIFIVE_U_DRAM].base >> 32, memmap[SIFIVE_U_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", 10000000); + 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); + qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", 1000000000); + qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv48"); + 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_cell(fdt, intc, "linux,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[SIFIVE_U_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[SIFIVE_U_CLINT].base, + 0x0, memmap[SIFIVE_U_CLINT].size); + qemu_fdt_setprop(fdt, nodename, "interrupts-extended", + cells, s->soc.num_harts * sizeof(uint32_t) * 4); + g_free(cells); + 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_EXT); + cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle); + cells[cpu * 4 + 3] = cpu_to_be32(IRQ_S_EXT); + g_free(nodename); + } + nodename = g_strdup_printf("/soc/interrupt-controller@%lx", + (long)memmap[SIFIVE_U_PLIC].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", 1); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,plic0"); + qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0); + qemu_fdt_setprop(fdt, nodename, "interrupts-extended", + cells, s->soc.num_harts * sizeof(uint32_t) * 4); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[SIFIVE_U_PLIC].base, + 0x0, memmap[SIFIVE_U_PLIC].size); + qemu_fdt_setprop_string(fdt, nodename, "reg-names", "control"); + qemu_fdt_setprop_cell(fdt, nodename, "riscv,max-priority", 7); + qemu_fdt_setprop_cell(fdt, nodename, "riscv,ndev", 4); + qemu_fdt_setprop_cells(fdt, nodename, "phandle", 2); + qemu_fdt_setprop_cells(fdt, nodename, "linux,phandle", 2); + plic_phandle = qemu_fdt_get_phandle(fdt, nodename); + g_free(cells); + g_free(nodename); + + nodename = g_strdup_printf("/uart@%lx", + (long)memmap[SIFIVE_U_UART0].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "sifive,uart0"); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[SIFIVE_U_UART0].base, + 0x0, memmap[SIFIVE_U_UART0].size); + qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent", plic_phandle); + qemu_fdt_setprop_cells(fdt, nodename, "interrupts", 1); + + qemu_fdt_add_subnode(fdt, "/chosen"); + qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", nodename); + qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline); + g_free(nodename); +} + +static void riscv_sifive_u_init(MachineState *machine) +{ + const struct MemmapEntry *memmap = sifive_u_memmap; + + SiFiveUState *s = g_new0(SiFiveUState, 1); + MemoryRegion *sys_memory = get_system_memory(); + MemoryRegion *main_mem = g_new(MemoryRegion, 1); + MemoryRegion *boot_rom = g_new(MemoryRegion, 1); + + /* Initialize SOC */ + object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY); + object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc), + &error_abort); + object_property_set_str(OBJECT(&s->soc), SIFIVE_U_CPU, "cpu-type", + &error_abort); + object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts", + &error_abort); + object_property_set_bool(OBJECT(&s->soc), true, "realized", + &error_abort); + + /* register RAM */ + memory_region_init_ram(main_mem, NULL, "riscv.sifive.u.ram", + machine->ram_size, &error_fatal); + memory_region_add_subregion(sys_memory, memmap[SIFIVE_U_DRAM].base, + main_mem); + + /* create device tree */ + create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline); + + /* boot rom */ + memory_region_init_ram(boot_rom, NULL, "riscv.sifive.u.mrom", + memmap[SIFIVE_U_MROM].base, &error_fatal); + memory_region_set_readonly(boot_rom, true); + memory_region_add_subregion(sys_memory, 0x0, boot_rom); + + if (machine->kernel_filename) { + load_kernel(machine->kernel_filename); + } + + /* reset vector */ + uint32_t reset_vec[8] = { + 0x00000297, /* 1: auipc t0, %pcrel_hi(dtb) */ + 0x02028593, /* addi a1, t0, %pcrel_lo(1b) */ + 0xf1402573, /* csrr a0, mhartid */ +#if defined(TARGET_RISCV32) + 0x0182a283, /* lw t0, 24(t0) */ +#elif defined(TARGET_RISCV64) + 0x0182b283, /* ld t0, 24(t0) */ +#endif + 0x00028067, /* jr t0 */ + 0x00000000, + memmap[SIFIVE_U_DRAM].base, /* start: .dword DRAM_BASE */ + 0x00000000, + /* dtb: */ + }; + + /* copy in the reset vector */ + copy_le32_to_phys(memmap[SIFIVE_U_MROM].base, reset_vec, sizeof(reset_vec)); + + /* copy in the device tree */ + qemu_fdt_dumpdtb(s->fdt, s->fdt_size); + cpu_physical_memory_write(memmap[SIFIVE_U_MROM].base + + sizeof(reset_vec), s->fdt, s->fdt_size); + + /* MMIO */ + s->plic = sifive_plic_create(memmap[SIFIVE_U_PLIC].base, + (char *)SIFIVE_U_PLIC_HART_CONFIG, + SIFIVE_U_PLIC_NUM_SOURCES, + SIFIVE_U_PLIC_NUM_PRIORITIES, + SIFIVE_U_PLIC_PRIORITY_BASE, + SIFIVE_U_PLIC_PENDING_BASE, + SIFIVE_U_PLIC_ENABLE_BASE, + SIFIVE_U_PLIC_ENABLE_STRIDE, + SIFIVE_U_PLIC_CONTEXT_BASE, + SIFIVE_U_PLIC_CONTEXT_STRIDE, + memmap[SIFIVE_U_PLIC].size); + sifive_uart_create(sys_memory, memmap[SIFIVE_U_UART0].base, + serial_hds[0], SIFIVE_PLIC(s->plic)->irqs[SIFIVE_U_UART0_IRQ]); + /* sifive_uart_create(sys_memory, memmap[SIFIVE_U_UART1].base, + serial_hds[1], SIFIVE_PLIC(s->plic)->irqs[SIFIVE_U_UART1_IRQ]); */ + sifive_clint_create(memmap[SIFIVE_U_CLINT].base, + memmap[SIFIVE_U_CLINT].size, smp_cpus, + SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE); +} + +static int riscv_sifive_u_sysbus_device_init(SysBusDevice *sysbusdev) +{ + return 0; +} + +static void riscv_sifive_u_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + k->init = riscv_sifive_u_sysbus_device_init; +} + +static const TypeInfo riscv_sifive_u_device = { + .name = TYPE_SIFIVE_U, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SiFiveUState), + .class_init = riscv_sifive_u_class_init, +}; + +static void riscv_sifive_u_register_types(void) +{ + type_register_static(&riscv_sifive_u_device); +} + +type_init(riscv_sifive_u_register_types); + +static void riscv_sifive_u_machine_init(MachineClass *mc) +{ + mc->desc = "RISC-V Board compatible with SiFive U SDK"; + mc->init = riscv_sifive_u_init; + mc->max_cpus = 1; +} + +DEFINE_MACHINE("sifive_u", riscv_sifive_u_machine_init) diff --git a/hw/riscv/sifive_uart.c b/hw/riscv/sifive_uart.c new file mode 100644 index 0000000000..b0c3798cf2 --- /dev/null +++ b/hw/riscv/sifive_uart.c @@ -0,0 +1,176 @@ +/* + * QEMU model of the UART on the SiFive E300 and U500 series SOCs. + * + * Copyright (c) 2016 Stefan O'Rear + * + * 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 "qapi/error.h" +#include "hw/sysbus.h" +#include "chardev/char.h" +#include "chardev/char-fe.h" +#include "target/riscv/cpu.h" +#include "hw/riscv/sifive_uart.h" + +/* + * Not yet implemented: + * + * Transmit FIFO using "qemu/fifo8.h" + * SIFIVE_UART_IE_TXWM interrupts + * SIFIVE_UART_IE_RXWM interrupts must honor fifo watermark + * Rx FIFO watermark interrupt trigger threshold + * Tx FIFO watermark interrupt trigger threshold. + */ + +static void update_irq(SiFiveUARTState *s) +{ + int cond = 0; + if ((s->ie & SIFIVE_UART_IE_RXWM) && s->rx_fifo_len) { + cond = 1; + } + if (cond) { + qemu_irq_raise(s->irq); + } else { + qemu_irq_lower(s->irq); + } +} + +static uint64_t +uart_read(void *opaque, hwaddr addr, unsigned int size) +{ + SiFiveUARTState *s = opaque; + unsigned char r; + switch (addr) { + case SIFIVE_UART_RXFIFO: + if (s->rx_fifo_len) { + r = s->rx_fifo[0]; + memmove(s->rx_fifo, s->rx_fifo + 1, s->rx_fifo_len - 1); + s->rx_fifo_len--; + qemu_chr_fe_accept_input(&s->chr); + update_irq(s); + return r; + } + return 0x80000000; + + case SIFIVE_UART_TXFIFO: + return 0; /* Should check tx fifo */ + case SIFIVE_UART_IE: + return s->ie; + case SIFIVE_UART_IP: + return s->rx_fifo_len ? SIFIVE_UART_IP_RXWM : 0; + case SIFIVE_UART_TXCTRL: + return s->txctrl; + case SIFIVE_UART_RXCTRL: + return s->rxctrl; + case SIFIVE_UART_DIV: + return s->div; + } + + hw_error("%s: bad read: addr=0x%x\n", + __func__, (int)addr); + return 0; +} + +static void +uart_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + SiFiveUARTState *s = opaque; + uint32_t value = val64; + unsigned char ch = value; + + switch (addr) { + case SIFIVE_UART_TXFIFO: + qemu_chr_fe_write(&s->chr, &ch, 1); + return; + case SIFIVE_UART_IE: + s->ie = val64; + update_irq(s); + return; + case SIFIVE_UART_TXCTRL: + s->txctrl = val64; + return; + case SIFIVE_UART_RXCTRL: + s->rxctrl = val64; + return; + case SIFIVE_UART_DIV: + s->div = val64; + return; + } + hw_error("%s: bad write: addr=0x%x v=0x%x\n", + __func__, (int)addr, (int)value); +} + +static const MemoryRegionOps uart_ops = { + .read = uart_read, + .write = uart_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4 + } +}; + +static void uart_rx(void *opaque, const uint8_t *buf, int size) +{ + SiFiveUARTState *s = opaque; + + /* Got a byte. */ + if (s->rx_fifo_len >= sizeof(s->rx_fifo)) { + printf("WARNING: UART dropped char.\n"); + return; + } + s->rx_fifo[s->rx_fifo_len++] = *buf; + + update_irq(s); +} + +static int uart_can_rx(void *opaque) +{ + SiFiveUARTState *s = opaque; + + return s->rx_fifo_len < sizeof(s->rx_fifo); +} + +static void uart_event(void *opaque, int event) +{ +} + +static int uart_be_change(void *opaque) +{ + SiFiveUARTState *s = opaque; + + qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event, + uart_be_change, s, NULL, true); + + return 0; +} + +/* + * Create UART device. + */ +SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space, hwaddr base, + Chardev *chr, qemu_irq irq) +{ + SiFiveUARTState *s = g_malloc0(sizeof(SiFiveUARTState)); + s->irq = irq; + qemu_chr_fe_init(&s->chr, chr, &error_abort); + qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event, + uart_be_change, s, NULL, true); + memory_region_init_io(&s->mmio, NULL, &uart_ops, s, + TYPE_SIFIVE_UART, SIFIVE_UART_MAX); + memory_region_add_subregion(address_space, base, &s->mmio); + return s; +} diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c new file mode 100644 index 0000000000..2d1f114d40 --- /dev/null +++ b/hw/riscv/spike.c @@ -0,0 +1,376 @@ +/* + * 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/hw.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 "chardev/char.h" +#include "sysemu/arch_init.h" +#include "sysemu/device_tree.h" +#include "exec/address-spaces.h" +#include "elf.h" + +static const struct MemmapEntry { + hwaddr base; + hwaddr size; +} spike_memmap[] = { + [SPIKE_MROM] = { 0x1000, 0x2000 }, + [SPIKE_CLINT] = { 0x2000000, 0x10000 }, + [SPIKE_DRAM] = { 0x80000000, 0x0 }, +}; + +static void copy_le32_to_phys(hwaddr pa, uint32_t *rom, size_t len) +{ + int i; + for (i = 0; i < (len >> 2); i++) { + stl_phys(&address_space_memory, pa + (i << 2), rom[i]); + } +} + +static uint64_t identity_translate(void *opaque, uint64_t addr) +{ + return addr; +} + +static uint64_t load_kernel(const char *kernel_filename) +{ + uint64_t kernel_entry, kernel_high; + + if (load_elf_ram_sym(kernel_filename, identity_translate, NULL, + &kernel_entry, NULL, &kernel_high, 0, ELF_MACHINE, 1, 0, + NULL, true, htif_symbol_callback) < 0) { + error_report("qemu: could not load kernel '%s'", kernel_filename); + exit(1); + } + return kernel_entry; +} + +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", "ucbbar,spike-bare-soc"); + 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", 10000000); + 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); + qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", 1000000000); + qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv48"); + 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_cell(fdt, intc, "linux,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); + + qemu_fdt_add_subnode(fdt, "/chosen"); + qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline); + } + +static void spike_v1_10_0_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 *boot_rom = g_new(MemoryRegion, 1); + + /* Initialize SOC */ + object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY); + object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc), + &error_abort); + object_property_set_str(OBJECT(&s->soc), SPIKE_V1_10_0_CPU, "cpu-type", + &error_abort); + object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts", + &error_abort); + object_property_set_bool(OBJECT(&s->soc), true, "realized", + &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_ram(boot_rom, NULL, "riscv.spike.bootrom", + s->fdt_size + 0x2000, &error_fatal); + memory_region_add_subregion(system_memory, 0x0, boot_rom); + + if (machine->kernel_filename) { + load_kernel(machine->kernel_filename); + } + + /* reset vector */ + uint32_t reset_vec[8] = { + 0x00000297, /* 1: auipc t0, %pcrel_hi(dtb) */ + 0x02028593, /* addi a1, t0, %pcrel_lo(1b) */ + 0xf1402573, /* csrr a0, mhartid */ +#if defined(TARGET_RISCV32) + 0x0182a283, /* lw t0, 24(t0) */ +#elif defined(TARGET_RISCV64) + 0x0182b283, /* ld t0, 24(t0) */ +#endif + 0x00028067, /* jr t0 */ + 0x00000000, + memmap[SPIKE_DRAM].base, /* start: .dword DRAM_BASE */ + 0x00000000, + /* dtb: */ + }; + + /* copy in the reset vector */ + copy_le32_to_phys(memmap[SPIKE_MROM].base, reset_vec, sizeof(reset_vec)); + + /* copy in the device tree */ + qemu_fdt_dumpdtb(s->fdt, s->fdt_size); + cpu_physical_memory_write(memmap[SPIKE_MROM].base + sizeof(reset_vec), + s->fdt, s->fdt_size); + + /* initialize HTIF using symbols found in load_kernel */ + htif_mm_init(system_memory, boot_rom, &s->soc.harts[0].env, serial_hds[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); +} + +static void spike_v1_09_1_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 *boot_rom = g_new(MemoryRegion, 1); + + /* Initialize SOC */ + object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY); + object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc), + &error_abort); + object_property_set_str(OBJECT(&s->soc), SPIKE_V1_09_1_CPU, "cpu-type", + &error_abort); + object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts", + &error_abort); + object_property_set_bool(OBJECT(&s->soc), true, "realized", + &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); + + /* boot rom */ + memory_region_init_ram(boot_rom, NULL, "riscv.spike.bootrom", + 0x40000, &error_fatal); + memory_region_add_subregion(system_memory, 0x0, boot_rom); + + if (machine->kernel_filename) { + load_kernel(machine->kernel_filename); + } + + /* reset vector */ + uint32_t reset_vec[8] = { + 0x297 + memmap[SPIKE_DRAM].base - memmap[SPIKE_MROM].base, /* lui */ + 0x00028067, /* jump to DRAM_BASE */ + 0x00000000, /* reserved */ + memmap[SPIKE_MROM].base + sizeof(reset_vec), /* config string pointer */ + 0, 0, 0, 0 /* trap vector */ + }; + + /* part one of config string - before memory size specified */ + const char *config_string_tmpl = + "platform {\n" + " vendor ucb;\n" + " arch spike;\n" + "};\n" + "rtc {\n" + " addr 0x%" PRIx64 "x;\n" + "};\n" + "ram {\n" + " 0 {\n" + " addr 0x%" PRIx64 "x;\n" + " size 0x%" PRIx64 "x;\n" + " };\n" + "};\n" + "core {\n" + " 0" " {\n" + " " "0 {\n" + " isa %s;\n" + " timecmp 0x%" PRIx64 "x;\n" + " ipi 0x%" PRIx64 "x;\n" + " };\n" + " };\n" + "};\n"; + + /* build config string with supplied memory size */ + char *isa = riscv_isa_string(&s->soc.harts[0]); + size_t config_string_size = strlen(config_string_tmpl) + 48; + char *config_string = malloc(config_string_size); + snprintf(config_string, config_string_size, config_string_tmpl, + (uint64_t)memmap[SPIKE_CLINT].base + SIFIVE_TIME_BASE, + (uint64_t)memmap[SPIKE_DRAM].base, + (uint64_t)ram_size, isa, + (uint64_t)memmap[SPIKE_CLINT].base + SIFIVE_TIMECMP_BASE, + (uint64_t)memmap[SPIKE_CLINT].base + SIFIVE_SIP_BASE); + g_free(isa); + size_t config_string_len = strlen(config_string); + + /* copy in the reset vector */ + copy_le32_to_phys(memmap[SPIKE_MROM].base, reset_vec, sizeof(reset_vec)); + + /* copy in the config string */ + cpu_physical_memory_write(memmap[SPIKE_MROM].base + sizeof(reset_vec), + config_string, config_string_len); + + /* initialize HTIF using symbols found in load_kernel */ + htif_mm_init(system_memory, boot_rom, &s->soc.harts[0].env, serial_hds[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); +} + +static const TypeInfo spike_v_1_09_1_device = { + .name = TYPE_RISCV_SPIKE_V1_09_1_BOARD, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SpikeState), +}; + +static const TypeInfo spike_v_1_10_0_device = { + .name = TYPE_RISCV_SPIKE_V1_10_0_BOARD, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SpikeState), +}; + +static void spike_v1_09_1_machine_init(MachineClass *mc) +{ + mc->desc = "RISC-V Spike Board (Privileged ISA v1.9.1)"; + mc->init = spike_v1_09_1_board_init; + mc->max_cpus = 1; +} + +static void spike_v1_10_0_machine_init(MachineClass *mc) +{ + mc->desc = "RISC-V Spike Board (Privileged ISA v1.10)"; + mc->init = spike_v1_10_0_board_init; + mc->max_cpus = 1; + mc->is_default = 1; +} + +DEFINE_MACHINE("spike_v1.9.1", spike_v1_09_1_machine_init) +DEFINE_MACHINE("spike_v1.10", spike_v1_10_0_machine_init) + +static void riscv_spike_board_register_types(void) +{ + type_register_static(&spike_v_1_09_1_device); + type_register_static(&spike_v_1_10_0_device); +} + +type_init(riscv_spike_board_register_types); diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c new file mode 100644 index 0000000000..e2c214e86a --- /dev/null +++ b/hw/riscv/virt.c @@ -0,0 +1,420 @@ +/* + * QEMU RISC-V VirtIO Board + * + * Copyright (c) 2017 SiFive, Inc. + * + * RISC-V machine with 16550a UART and VirtIO MMIO + * + * 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/hw.h" +#include "hw/boards.h" +#include "hw/loader.h" +#include "hw/sysbus.h" +#include "hw/char/serial.h" +#include "target/riscv/cpu.h" +#include "hw/riscv/riscv_htif.h" +#include "hw/riscv/riscv_hart.h" +#include "hw/riscv/sifive_plic.h" +#include "hw/riscv/sifive_clint.h" +#include "hw/riscv/sifive_test.h" +#include "hw/riscv/virt.h" +#include "chardev/char.h" +#include "sysemu/arch_init.h" +#include "sysemu/device_tree.h" +#include "exec/address-spaces.h" +#include "elf.h" + +static const struct MemmapEntry { + hwaddr base; + hwaddr size; +} virt_memmap[] = { + [VIRT_DEBUG] = { 0x0, 0x100 }, + [VIRT_MROM] = { 0x1000, 0x2000 }, + [VIRT_TEST] = { 0x4000, 0x1000 }, + [VIRT_CLINT] = { 0x2000000, 0x10000 }, + [VIRT_PLIC] = { 0xc000000, 0x4000000 }, + [VIRT_UART0] = { 0x10000000, 0x100 }, + [VIRT_VIRTIO] = { 0x10001000, 0x1000 }, + [VIRT_DRAM] = { 0x80000000, 0x0 }, +}; + +static void copy_le32_to_phys(hwaddr pa, uint32_t *rom, size_t len) +{ + int i; + for (i = 0; i < (len >> 2); i++) { + stl_phys(&address_space_memory, pa + (i << 2), rom[i]); + } +} + +static uint64_t identity_translate(void *opaque, uint64_t addr) +{ + return addr; +} + +static uint64_t load_kernel(const char *kernel_filename) +{ + uint64_t kernel_entry, kernel_high; + + if (load_elf(kernel_filename, identity_translate, NULL, + &kernel_entry, NULL, &kernel_high, + 0, ELF_MACHINE, 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) +{ + void *fdt; + int cpu; + uint32_t *cells; + char *nodename; + uint32_t plic_phandle, phandle = 1; + int i; + + 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", "riscv-virtio,qemu"); + qemu_fdt_setprop_string(fdt, "/", "compatible", "riscv-virtio"); + qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2); + qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2); + + qemu_fdt_add_subnode(fdt, "/soc"); + qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0); + qemu_fdt_setprop_string(fdt, "/soc", "compatible", "riscv-virtio-soc"); + 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[VIRT_DRAM].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + memmap[VIRT_DRAM].base >> 32, memmap[VIRT_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", 10000000); + 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--) { + int cpu_phandle = phandle++; + 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); + qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", 1000000000); + qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv48"); + 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", cpu_phandle); + qemu_fdt_setprop_cell(fdt, intc, "linux,phandle", cpu_phandle); + 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[VIRT_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[VIRT_CLINT].base, + 0x0, memmap[VIRT_CLINT].size); + qemu_fdt_setprop(fdt, nodename, "interrupts-extended", + cells, s->soc.num_harts * sizeof(uint32_t) * 4); + g_free(cells); + g_free(nodename); + + plic_phandle = phandle++; + 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_EXT); + cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle); + cells[cpu * 4 + 3] = cpu_to_be32(IRQ_S_EXT); + g_free(nodename); + } + nodename = g_strdup_printf("/soc/interrupt-controller@%lx", + (long)memmap[VIRT_PLIC].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", 1); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,plic0"); + qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0); + qemu_fdt_setprop(fdt, nodename, "interrupts-extended", + cells, s->soc.num_harts * sizeof(uint32_t) * 4); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[VIRT_PLIC].base, + 0x0, memmap[VIRT_PLIC].size); + qemu_fdt_setprop_string(fdt, nodename, "reg-names", "control"); + qemu_fdt_setprop_cell(fdt, nodename, "riscv,max-priority", 7); + qemu_fdt_setprop_cell(fdt, nodename, "riscv,ndev", VIRTIO_NDEV); + qemu_fdt_setprop_cells(fdt, nodename, "phandle", plic_phandle); + qemu_fdt_setprop_cells(fdt, nodename, "linux,phandle", plic_phandle); + plic_phandle = qemu_fdt_get_phandle(fdt, nodename); + g_free(cells); + g_free(nodename); + + for (i = 0; i < VIRTIO_COUNT; i++) { + nodename = g_strdup_printf("/virtio_mmio@%lx", + (long)(memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size)); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "virtio,mmio"); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size, + 0x0, memmap[VIRT_VIRTIO].size); + qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent", plic_phandle); + qemu_fdt_setprop_cells(fdt, nodename, "interrupts", VIRTIO_IRQ + i); + g_free(nodename); + } + + nodename = g_strdup_printf("/test@%lx", + (long)memmap[VIRT_TEST].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "sifive,test0"); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[VIRT_TEST].base, + 0x0, memmap[VIRT_TEST].size); + + nodename = g_strdup_printf("/uart@%lx", + (long)memmap[VIRT_UART0].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "ns16550a"); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[VIRT_UART0].base, + 0x0, memmap[VIRT_UART0].size); + qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", 3686400); + qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent", plic_phandle); + qemu_fdt_setprop_cells(fdt, nodename, "interrupts", UART0_IRQ); + + qemu_fdt_add_subnode(fdt, "/chosen"); + qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", nodename); + qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline); + g_free(nodename); + + return fdt; +} + +static void riscv_virt_board_init(MachineState *machine) +{ + const struct MemmapEntry *memmap = virt_memmap; + + RISCVVirtState *s = g_new0(RISCVVirtState, 1); + MemoryRegion *system_memory = get_system_memory(); + MemoryRegion *main_mem = g_new(MemoryRegion, 1); + MemoryRegion *boot_rom = g_new(MemoryRegion, 1); + char *plic_hart_config; + size_t plic_hart_config_len; + int i; + void *fdt; + + /* Initialize SOC */ + object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY); + object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc), + &error_abort); + object_property_set_str(OBJECT(&s->soc), VIRT_CPU, "cpu-type", + &error_abort); + object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts", + &error_abort); + object_property_set_bool(OBJECT(&s->soc), true, "realized", + &error_abort); + + /* register system main memory (actual RAM) */ + memory_region_init_ram(main_mem, NULL, "riscv_virt_board.ram", + machine->ram_size, &error_fatal); + memory_region_add_subregion(system_memory, memmap[VIRT_DRAM].base, + main_mem); + + /* create device tree */ + fdt = create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline); + + /* boot rom */ + memory_region_init_ram(boot_rom, NULL, "riscv_virt_board.bootrom", + s->fdt_size + 0x2000, &error_fatal); + memory_region_add_subregion(system_memory, 0x0, boot_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); + } + } + + /* reset vector */ + uint32_t reset_vec[8] = { + 0x00000297, /* 1: auipc t0, %pcrel_hi(dtb) */ + 0x02028593, /* addi a1, t0, %pcrel_lo(1b) */ + 0xf1402573, /* csrr a0, mhartid */ +#if defined(TARGET_RISCV32) + 0x0182a283, /* lw t0, 24(t0) */ +#elif defined(TARGET_RISCV64) + 0x0182b283, /* ld t0, 24(t0) */ +#endif + 0x00028067, /* jr t0 */ + 0x00000000, + memmap[VIRT_DRAM].base, /* start: .dword memmap[VIRT_DRAM].base */ + 0x00000000, + /* dtb: */ + }; + + /* copy in the reset vector */ + copy_le32_to_phys(ROM_BASE, reset_vec, sizeof(reset_vec)); + + /* copy in the device tree */ + qemu_fdt_dumpdtb(s->fdt, s->fdt_size); + cpu_physical_memory_write(ROM_BASE + sizeof(reset_vec), + s->fdt, s->fdt_size); + + /* create PLIC hart topology configuration string */ + plic_hart_config_len = (strlen(VIRT_PLIC_HART_CONFIG) + 1) * smp_cpus; + plic_hart_config = g_malloc0(plic_hart_config_len); + for (i = 0; i < smp_cpus; i++) { + if (i != 0) { + strncat(plic_hart_config, ",", plic_hart_config_len); + } + strncat(plic_hart_config, VIRT_PLIC_HART_CONFIG, plic_hart_config_len); + plic_hart_config_len -= (strlen(VIRT_PLIC_HART_CONFIG) + 1); + } + + /* MMIO */ + s->plic = sifive_plic_create(memmap[VIRT_PLIC].base, + plic_hart_config, + VIRT_PLIC_NUM_SOURCES, + VIRT_PLIC_NUM_PRIORITIES, + VIRT_PLIC_PRIORITY_BASE, + VIRT_PLIC_PENDING_BASE, + VIRT_PLIC_ENABLE_BASE, + VIRT_PLIC_ENABLE_STRIDE, + VIRT_PLIC_CONTEXT_BASE, + VIRT_PLIC_CONTEXT_STRIDE, + memmap[VIRT_PLIC].size); + sifive_clint_create(memmap[VIRT_CLINT].base, + memmap[VIRT_CLINT].size, smp_cpus, + SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE); + sifive_test_create(memmap[VIRT_TEST].base); + + for (i = 0; i < VIRTIO_COUNT; i++) { + sysbus_create_simple("virtio-mmio", + memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size, + SIFIVE_PLIC(s->plic)->irqs[VIRTIO_IRQ + i]); + } + + serial_mm_init(system_memory, memmap[VIRT_UART0].base, + 0, SIFIVE_PLIC(s->plic)->irqs[UART0_IRQ], 399193, + serial_hds[0], DEVICE_LITTLE_ENDIAN); +} + +static int riscv_virt_board_sysbus_device_init(SysBusDevice *sysbusdev) +{ + return 0; +} + +static void riscv_virt_board_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + k->init = riscv_virt_board_sysbus_device_init; +} + +static const TypeInfo riscv_virt_board_device = { + .name = TYPE_RISCV_VIRT_BOARD, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(RISCVVirtState), + .class_init = riscv_virt_board_class_init, +}; + +static void riscv_virt_board_machine_init(MachineClass *mc) +{ + mc->desc = "RISC-V VirtIO Board (Privileged spec v1.10)"; + mc->init = riscv_virt_board_init; + mc->max_cpus = 8; /* hardcoded limit in BBL */ +} + +DEFINE_MACHINE("virt", riscv_virt_board_machine_init) + +static void riscv_virt_board_register_types(void) +{ + type_register_static(&riscv_virt_board_device); +} + +type_init(riscv_virt_board_register_types); diff --git a/hw/s390x/event-facility.c b/hw/s390x/event-facility.c index 155a69467b..9c24bc6f7c 100644 --- a/hw/s390x/event-facility.c +++ b/hw/s390x/event-facility.c @@ -29,8 +29,17 @@ typedef struct SCLPEventsBus { struct SCLPEventFacility { SysBusDevice parent_obj; SCLPEventsBus sbus; - /* guest' receive mask */ - unsigned int receive_mask; + /* guest's receive mask */ + sccb_mask_t receive_mask; + /* + * when false, we keep the same broken, backwards compatible behaviour as + * before, allowing only masks of size exactly 4; when true, we implement + * the architecture correctly, allowing all valid mask sizes. Needed for + * migration toward older versions. + */ + bool allow_all_mask_sizes; + /* length of the receive mask */ + uint16_t mask_length; }; /* return true if any child has event pending set */ @@ -52,9 +61,9 @@ static bool event_pending(SCLPEventFacility *ef) return false; } -static unsigned int get_host_send_mask(SCLPEventFacility *ef) +static sccb_mask_t get_host_send_mask(SCLPEventFacility *ef) { - unsigned int mask; + sccb_mask_t mask; BusChild *kid; SCLPEventClass *child; @@ -68,9 +77,9 @@ static unsigned int get_host_send_mask(SCLPEventFacility *ef) return mask; } -static unsigned int get_host_receive_mask(SCLPEventFacility *ef) +static sccb_mask_t get_host_receive_mask(SCLPEventFacility *ef) { - unsigned int mask; + sccb_mask_t mask; BusChild *kid; SCLPEventClass *child; @@ -180,7 +189,7 @@ out: } static uint16_t handle_sccb_read_events(SCLPEventFacility *ef, SCCB *sccb, - unsigned int mask) + sccb_mask_t mask) { uint16_t rc; int slen; @@ -220,10 +229,21 @@ static uint16_t handle_sccb_read_events(SCLPEventFacility *ef, SCCB *sccb, return rc; } +/* copy up to src_len bytes and fill the rest of dst with zeroes */ +static void copy_mask(uint8_t *dst, uint8_t *src, uint16_t dst_len, + uint16_t src_len) +{ + int i; + + for (i = 0; i < dst_len; i++) { + dst[i] = i < src_len ? src[i] : 0; + } +} + static void read_event_data(SCLPEventFacility *ef, SCCB *sccb) { - unsigned int sclp_active_selection_mask; - unsigned int sclp_cp_receive_mask; + sccb_mask_t sclp_active_selection_mask; + sccb_mask_t sclp_cp_receive_mask; ReadEventData *red = (ReadEventData *) sccb; @@ -240,7 +260,9 @@ static void read_event_data(SCLPEventFacility *ef, SCCB *sccb) sclp_active_selection_mask = sclp_cp_receive_mask; break; case SCLP_SELECTIVE_READ: - sclp_active_selection_mask = be32_to_cpu(red->mask); + copy_mask((uint8_t *)&sclp_active_selection_mask, (uint8_t *)&red->mask, + sizeof(sclp_active_selection_mask), ef->mask_length); + sclp_active_selection_mask = be32_to_cpu(sclp_active_selection_mask); if (!sclp_cp_receive_mask || (sclp_active_selection_mask & ~sclp_cp_receive_mask)) { sccb->h.response_code = @@ -259,24 +281,14 @@ out: return; } -/* copy up to dst_len bytes and fill the rest of dst with zeroes */ -static void copy_mask(uint8_t *dst, uint8_t *src, uint16_t dst_len, - uint16_t src_len) -{ - int i; - - for (i = 0; i < dst_len; i++) { - dst[i] = i < src_len ? src[i] : 0; - } -} - static void write_event_mask(SCLPEventFacility *ef, SCCB *sccb) { WriteEventMask *we_mask = (WriteEventMask *) sccb; uint16_t mask_length = be16_to_cpu(we_mask->mask_length); - uint32_t tmp_mask; + sccb_mask_t tmp_mask; - if (!mask_length || (mask_length > SCLP_EVENT_MASK_LEN_MAX)) { + if (!mask_length || (mask_length > SCLP_EVENT_MASK_LEN_MAX) || + ((mask_length != 4) && !ef->allow_all_mask_sizes)) { sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_MASK_LENGTH); goto out; } @@ -301,6 +313,7 @@ static void write_event_mask(SCLPEventFacility *ef, SCCB *sccb) mask_length, sizeof(tmp_mask)); sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION); + ef->mask_length = mask_length; out: return; @@ -356,6 +369,24 @@ static void command_handler(SCLPEventFacility *ef, SCCB *sccb, uint64_t code) } } +static bool vmstate_event_facility_mask_length_needed(void *opaque) +{ + SCLPEventFacility *ef = opaque; + + return ef->allow_all_mask_sizes; +} + +static const VMStateDescription vmstate_event_facility_mask_length = { + .name = "vmstate-event-facility/mask_length", + .version_id = 0, + .minimum_version_id = 0, + .needed = vmstate_event_facility_mask_length_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT16(mask_length, SCLPEventFacility), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_event_facility = { .name = "vmstate-event-facility", .version_id = 0, @@ -363,15 +394,39 @@ static const VMStateDescription vmstate_event_facility = { .fields = (VMStateField[]) { VMSTATE_UINT32(receive_mask, SCLPEventFacility), VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * []) { + &vmstate_event_facility_mask_length, + NULL } }; +static void sclp_event_set_allow_all_mask_sizes(Object *obj, bool value, + Error **errp) +{ + SCLPEventFacility *ef = (SCLPEventFacility *)obj; + + ef->allow_all_mask_sizes = value; +} + +static bool sclp_event_get_allow_all_mask_sizes(Object *obj, Error **e) +{ + SCLPEventFacility *ef = (SCLPEventFacility *)obj; + + return ef->allow_all_mask_sizes; +} + static void init_event_facility(Object *obj) { SCLPEventFacility *event_facility = EVENT_FACILITY(obj); DeviceState *sdev = DEVICE(obj); Object *new; + event_facility->mask_length = 4; + event_facility->allow_all_mask_sizes = true; + object_property_add_bool(obj, "allow_all_mask_sizes", + sclp_event_get_allow_all_mask_sizes, + sclp_event_set_allow_all_mask_sizes, NULL); /* Spawn a new bus for SCLP events */ qbus_create_inplace(&event_facility->sbus, sizeof(event_facility->sbus), TYPE_SCLP_EVENTS_BUS, sdev, NULL); @@ -431,26 +486,12 @@ static void event_realize(DeviceState *qdev, Error **errp) } } -static void event_unrealize(DeviceState *qdev, Error **errp) -{ - SCLPEvent *event = SCLP_EVENT(qdev); - SCLPEventClass *child = SCLP_EVENT_GET_CLASS(event); - if (child->exit) { - int rc = child->exit(event); - if (rc < 0) { - error_setg(errp, "SCLP event exit failed."); - return; - } - } -} - static void event_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->bus_type = TYPE_SCLP_EVENTS_BUS; dc->realize = event_realize; - dc->unrealize = event_unrealize; } static const TypeInfo sclp_event_type_info = { diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index 798e99aadf..fdeaec3a58 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -234,7 +234,7 @@ static void s390_ipl_set_boot_menu(S390IPLState *ipl) if (!get_boot_device(0)) { if (boot_menu) { error_report("boot menu requires a bootindex to be specified for " - "the IPL device."); + "the IPL device"); } return; } @@ -250,7 +250,9 @@ static void s390_ipl_set_boot_menu(S390IPLState *ipl) case S390_IPL_TYPE_QEMU_SCSI: break; default: - error_report("boot menu is not supported for this device type."); + if (boot_menu) { + error_report("boot menu is not supported for this device type"); + } return; } @@ -263,13 +265,13 @@ static void s390_ipl_set_boot_menu(S390IPLState *ipl) tmp = qemu_opt_get(opts, "splash-time"); if (tmp && qemu_strtoul(tmp, NULL, 10, &splash_time)) { - error_report("splash-time is invalid, forcing it to 0."); + error_report("splash-time is invalid, forcing it to 0"); *timeout = 0; return; } if (splash_time > 0xffffffff) { - error_report("splash-time is too large, forcing it to max value."); + error_report("splash-time is too large, forcing it to max value"); *timeout = 0xffffffff; return; } @@ -380,7 +382,8 @@ static int load_netboot_image(Error **errp) netboot_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, ipl->netboot_fw); if (netboot_filename == NULL) { - error_setg(errp, "Could not find network bootloader"); + error_setg(errp, "Could not find network bootloader '%s'", + ipl->netboot_fw); goto unref_mr; } @@ -489,7 +492,7 @@ void s390_ipl_prepare_cpu(S390CPU *cpu) if (ipl->netboot) { if (load_netboot_image(&err) < 0) { error_report_err(err); - vm_stop(RUN_STATE_INTERNAL_ERROR); + exit(1); } ipl->qipl.netboot_start_addr = cpu_to_be64(ipl->start_addr); } diff --git a/hw/s390x/s390-ccw.c b/hw/s390x/s390-ccw.c index 7fc1c603c0..214c940593 100644 --- a/hw/s390x/s390-ccw.c +++ b/hw/s390x/s390-ccw.c @@ -48,7 +48,7 @@ static void s390_ccw_get_dev_info(S390CCWDevice *cdev, return; } - cdev->mdevid = g_strdup(basename(dev_path)); + cdev->mdevid = g_path_get_basename(dev_path); tmp = basename(dirname(dev_path)); if (sscanf(tmp, "%2x.%1x.%4x", &cssid, &ssid, &devid) != 3) { diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 4d0c3deba6..864145a7c6 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -27,6 +27,7 @@ #include "s390-pci-bus.h" #include "hw/s390x/storage-keys.h" #include "hw/s390x/storage-attributes.h" +#include "hw/s390x/event-facility.h" #include "hw/compat.h" #include "ipl.h" #include "hw/s390x/s390-virtio-ccw.h" @@ -254,8 +255,10 @@ static void s390_init_ipl_dev(const char *kernel_filename, } qdev_prop_set_string(dev, "cmdline", kernel_cmdline); qdev_prop_set_string(dev, "firmware", firmware); - qdev_prop_set_string(dev, "netboot_fw", netboot_fw); qdev_prop_set_bit(dev, "enforce_bios", enforce_bios); + if (!strlen(object_property_get_str(new, "netboot_fw", &error_abort))) { + qdev_prop_set_string(dev, "netboot_fw", netboot_fw); + } object_property_add_child(qdev_get_machine(), TYPE_S390_IPL, new, NULL); object_unref(new); @@ -388,12 +391,14 @@ static void s390_machine_device_unplug_request(HotplugHandler *hotplug_dev, } } -static CpuInstanceProperties s390_cpu_index_to_props(MachineState *machine, +static CpuInstanceProperties s390_cpu_index_to_props(MachineState *ms, unsigned cpu_index) { - g_assert(machine->possible_cpus && cpu_index < machine->possible_cpus->len); + MachineClass *mc = MACHINE_GET_CLASS(ms); + const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms); - return machine->possible_cpus->cpus[cpu_index].props; + assert(cpu_index < possible_cpus->len); + return possible_cpus->cpus[cpu_index].props; } static const CPUArchIdList *s390_possible_cpu_arch_ids(MachineState *ms) @@ -664,7 +669,12 @@ bool css_migration_enabled(void) type_init(ccw_machine_register_##suffix) #define CCW_COMPAT_2_11 \ - HW_COMPAT_2_11 + HW_COMPAT_2_11 \ + {\ + .driver = TYPE_SCLP_EVENT_FACILITY,\ + .property = "allow_all_mask_sizes",\ + .value = "off",\ + }, #define CCW_COMPAT_2_10 \ HW_COMPAT_2_10 diff --git a/hw/s390x/sclpcpu.c b/hw/s390x/sclpcpu.c index 3ee890b392..50c021b9c2 100644 --- a/hw/s390x/sclpcpu.c +++ b/hw/s390x/sclpcpu.c @@ -37,12 +37,12 @@ void raise_irq_cpu_hotplug(void) sclp_service_interrupt(0); } -static unsigned int send_mask(void) +static sccb_mask_t send_mask(void) { return SCLP_EVENT_MASK_CONFIG_MGT_DATA; } -static unsigned int receive_mask(void) +static sccb_mask_t receive_mask(void) { return 0; } diff --git a/hw/s390x/sclpquiesce.c b/hw/s390x/sclpquiesce.c index 02416435a1..1c8f5c9393 100644 --- a/hw/s390x/sclpquiesce.c +++ b/hw/s390x/sclpquiesce.c @@ -28,12 +28,12 @@ static bool can_handle_event(uint8_t type) return type == SCLP_EVENT_SIGNAL_QUIESCE; } -static unsigned int send_mask(void) +static sccb_mask_t send_mask(void) { return SCLP_EVENT_MASK_SIGNAL_QUIESCE; } -static unsigned int receive_mask(void) +static sccb_mask_t receive_mask(void) { return 0; } diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 8f7fbc2ab7..e51fbefd23 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -752,7 +752,7 @@ out_err: g_free(sch); } -static int virtio_ccw_exit(VirtioCcwDevice *dev) +static void virtio_ccw_unrealize(VirtioCcwDevice *dev, Error **errp) { CcwDevice *ccw_dev = CCW_DEVICE(dev); SubchDev *sch = ccw_dev->sch; @@ -760,12 +760,12 @@ static int virtio_ccw_exit(VirtioCcwDevice *dev) if (sch) { css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); g_free(sch); + ccw_dev->sch = NULL; } if (dev->indicators) { release_indicator(&dev->routes.adapter, dev->indicators); dev->indicators = NULL; } - return 0; } static void virtio_ccw_net_realize(VirtioCcwDevice *ccw_dev, Error **errp) @@ -1344,7 +1344,7 @@ static void virtio_ccw_net_class_init(ObjectClass *klass, void *data) VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); k->realize = virtio_ccw_net_realize; - k->exit = virtio_ccw_exit; + k->unrealize = virtio_ccw_unrealize; dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_net_properties; set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); @@ -1372,7 +1372,7 @@ static void virtio_ccw_blk_class_init(ObjectClass *klass, void *data) VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); k->realize = virtio_ccw_blk_realize; - k->exit = virtio_ccw_exit; + k->unrealize = virtio_ccw_unrealize; dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_blk_properties; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); @@ -1400,7 +1400,7 @@ static void virtio_ccw_serial_class_init(ObjectClass *klass, void *data) VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); k->realize = virtio_ccw_serial_realize; - k->exit = virtio_ccw_exit; + k->unrealize = virtio_ccw_unrealize; dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_serial_properties; set_bit(DEVICE_CATEGORY_INPUT, dc->categories); @@ -1428,7 +1428,7 @@ static void virtio_ccw_balloon_class_init(ObjectClass *klass, void *data) VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); k->realize = virtio_ccw_balloon_realize; - k->exit = virtio_ccw_exit; + k->unrealize = virtio_ccw_unrealize; dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_balloon_properties; set_bit(DEVICE_CATEGORY_MISC, dc->categories); @@ -1456,7 +1456,7 @@ static void virtio_ccw_scsi_class_init(ObjectClass *klass, void *data) VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); k->realize = virtio_ccw_scsi_realize; - k->exit = virtio_ccw_exit; + k->unrealize = virtio_ccw_unrealize; dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_scsi_properties; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); @@ -1483,7 +1483,7 @@ static void vhost_ccw_scsi_class_init(ObjectClass *klass, void *data) VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); k->realize = vhost_ccw_scsi_realize; - k->exit = virtio_ccw_exit; + k->unrealize = virtio_ccw_unrealize; dc->reset = virtio_ccw_reset; dc->props = vhost_ccw_scsi_properties; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); @@ -1520,7 +1520,7 @@ static void virtio_ccw_rng_class_init(ObjectClass *klass, void *data) VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); k->realize = virtio_ccw_rng_realize; - k->exit = virtio_ccw_exit; + k->unrealize = virtio_ccw_unrealize; dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_rng_properties; set_bit(DEVICE_CATEGORY_MISC, dc->categories); @@ -1558,7 +1558,7 @@ static void virtio_ccw_crypto_class_init(ObjectClass *klass, void *data) VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); k->realize = virtio_ccw_crypto_realize; - k->exit = virtio_ccw_exit; + k->unrealize = virtio_ccw_unrealize; dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_crypto_properties; set_bit(DEVICE_CATEGORY_MISC, dc->categories); @@ -1596,7 +1596,7 @@ static void virtio_ccw_gpu_class_init(ObjectClass *klass, void *data) VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); k->realize = virtio_ccw_gpu_realize; - k->exit = virtio_ccw_exit; + k->unrealize = virtio_ccw_unrealize; dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_gpu_properties; dc->hotpluggable = false; @@ -1625,7 +1625,7 @@ static void virtio_ccw_input_class_init(ObjectClass *klass, void *data) VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); k->realize = virtio_ccw_input_realize; - k->exit = virtio_ccw_exit; + k->unrealize = virtio_ccw_unrealize; dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_input_properties; set_bit(DEVICE_CATEGORY_INPUT, dc->categories); @@ -1705,12 +1705,12 @@ static void virtio_ccw_busdev_realize(DeviceState *dev, Error **errp) virtio_ccw_device_realize(_dev, errp); } -static int virtio_ccw_busdev_exit(DeviceState *dev) +static void virtio_ccw_busdev_unrealize(DeviceState *dev, Error **errp) { VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev; VirtIOCCWDeviceClass *_info = VIRTIO_CCW_DEVICE_GET_CLASS(dev); - return _info->exit(_dev); + _info->unrealize(_dev, errp); } static void virtio_ccw_busdev_unplug(HotplugHandler *hotplug_dev, @@ -1728,7 +1728,7 @@ static void virtio_ccw_device_class_init(ObjectClass *klass, void *data) k->unplug = virtio_ccw_busdev_unplug; dc->realize = virtio_ccw_busdev_realize; - dc->exit = virtio_ccw_busdev_exit; + dc->unrealize = virtio_ccw_busdev_unrealize; dc->bus_type = TYPE_VIRTUAL_CSS_BUS; } @@ -1804,7 +1804,7 @@ static void virtio_ccw_9p_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - k->exit = virtio_ccw_exit; + k->unrealize = virtio_ccw_unrealize; k->realize = virtio_ccw_9p_realize; dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_9p_properties; @@ -1853,7 +1853,7 @@ static void vhost_vsock_ccw_class_init(ObjectClass *klass, void *data) VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); k->realize = vhost_vsock_ccw_realize; - k->exit = virtio_ccw_exit; + k->unrealize = virtio_ccw_unrealize; set_bit(DEVICE_CATEGORY_MISC, dc->categories); dc->props = vhost_vsock_ccw_properties; dc->reset = virtio_ccw_reset; diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h index 3905f3a3d6..2fc513001e 100644 --- a/hw/s390x/virtio-ccw.h +++ b/hw/s390x/virtio-ccw.h @@ -76,7 +76,7 @@ typedef struct VirtioCcwDevice VirtioCcwDevice; typedef struct VirtIOCCWDeviceClass { CCWDeviceClass parent_class; void (*realize)(VirtioCcwDevice *dev, Error **errp); - int (*exit)(VirtioCcwDevice *dev); + void (*unrealize)(VirtioCcwDevice *dev, Error **errp); } VirtIOCCWDeviceClass; /* Performance improves when virtqueue kick processing is decoupled from the diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c index 191505df5b..f3d4c4d230 100644 --- a/hw/scsi/lsi53c895a.c +++ b/hw/scsi/lsi53c895a.c @@ -2277,5 +2277,5 @@ void lsi53c895a_create(PCIBus *bus) { LSIState *s = LSI53C895A(pci_create_simple(bus, -1, "lsi53c895a")); - scsi_bus_legacy_handle_cmdline(&s->bus, false); + scsi_bus_legacy_handle_cmdline(&s->bus); } diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index b7bafbed6e..1eaeffc830 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -271,7 +271,7 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockBackend *blk, return SCSI_DEVICE(dev); } -void scsi_bus_legacy_handle_cmdline(SCSIBus *bus, bool deprecated) +void scsi_bus_legacy_handle_cmdline(SCSIBus *bus) { Location loc; DriveInfo *dinfo; @@ -284,59 +284,12 @@ void scsi_bus_legacy_handle_cmdline(SCSIBus *bus, bool deprecated) continue; } qemu_opts_loc_restore(dinfo->opts); - if (deprecated) { - /* Handling -drive not claimed by machine initialization */ - if (blk_get_attached_dev(blk_by_legacy_dinfo(dinfo))) { - continue; /* claimed */ - } - if (!dinfo->is_default) { - warn_report("bus=%d,unit=%d is deprecated with this" - " machine type", - bus->busnr, unit); - } - } scsi_bus_legacy_add_drive(bus, blk_by_legacy_dinfo(dinfo), unit, false, -1, false, NULL, &error_fatal); } loc_pop(&loc); } -static bool is_scsi_hba_with_legacy_magic(Object *obj) -{ - static const char *magic[] = { - "am53c974", "dc390", "esp", "lsi53c810", "lsi53c895a", - "megasas", "megasas-gen2", "mptsas1068", "spapr-vscsi", - "virtio-scsi-device", - NULL - }; - const char *typename = object_get_typename(obj); - int i; - - for (i = 0; magic[i]; i++) - if (!strcmp(typename, magic[i])) { - return true; - } - - return false; -} - -static int scsi_legacy_handle_cmdline_cb(Object *obj, void *opaque) -{ - SCSIBus *bus = (SCSIBus *)object_dynamic_cast(obj, TYPE_SCSI_BUS); - - if (bus && is_scsi_hba_with_legacy_magic(OBJECT(bus->qbus.parent))) { - scsi_bus_legacy_handle_cmdline(bus, true); - } - - return 0; -} - -void scsi_legacy_handle_cmdline(void) -{ - object_child_foreach_recursive(object_get_root(), - scsi_legacy_handle_cmdline_cb, NULL); -} - static int32_t scsi_invalid_field(SCSIRequest *req, uint8_t *buf) { scsi_req_build_sense(req, SENSE_CODE(INVALID_FIELD)); diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c index 360db53ac8..a9e49c7cb5 100644 --- a/hw/scsi/spapr_vscsi.c +++ b/hw/scsi/spapr_vscsi.c @@ -1215,8 +1215,7 @@ void spapr_vscsi_create(VIOsPAPRBus *bus) dev = qdev_create(&bus->bus, "spapr-vscsi"); qdev_init_nofail(dev); - scsi_bus_legacy_handle_cmdline(&VIO_SPAPR_VSCSI_DEVICE(dev)->bus, - false); + scsi_bus_legacy_handle_cmdline(&VIO_SPAPR_VSCSI_DEVICE(dev)->bus); } static int spapr_vscsi_devnode(VIOsPAPRDevice *dev, void *fdt, int node_off) diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c index 1c33322ba6..912e5005d8 100644 --- a/hw/scsi/virtio-scsi-dataplane.c +++ b/hw/scsi/virtio-scsi-dataplane.c @@ -107,9 +107,10 @@ static int virtio_scsi_vring_init(VirtIOSCSI *s, VirtQueue *vq, int n, return 0; } -/* assumes s->ctx held */ -static void virtio_scsi_clear_aio(VirtIOSCSI *s) +/* Context: BH in IOThread */ +static void virtio_scsi_dataplane_stop_bh(void *opaque) { + VirtIOSCSI *s = opaque; VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); int i; @@ -171,7 +172,7 @@ int virtio_scsi_dataplane_start(VirtIODevice *vdev) return 0; fail_vrings: - virtio_scsi_clear_aio(s); + aio_wait_bh_oneshot(s->ctx, virtio_scsi_dataplane_stop_bh, s); aio_context_release(s->ctx); for (i = 0; i < vs->conf.num_queues + 2; i++) { virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); @@ -207,7 +208,7 @@ void virtio_scsi_dataplane_stop(VirtIODevice *vdev) s->dataplane_stopping = true; aio_context_acquire(s->ctx); - virtio_scsi_clear_aio(s); + aio_wait_bh_oneshot(s->ctx, virtio_scsi_dataplane_stop_bh, s); aio_context_release(s->ctx); blk_drain_all(); /* ensure there are no in-flight requests */ diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index 61eb424bbc..0f5804b3b4 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -324,6 +324,7 @@ static void *sparc32_dma_init(hwaddr dma_base, esp = ESP_STATE(object_resolve_path_component(OBJECT(espdma), "esp")); sysbus_mmio_map(SYS_BUS_DEVICE(esp), 0, esp_base); + scsi_bus_legacy_handle_cmdline(&esp->esp.bus); ledma = SPARC32_LEDMA_DEVICE(object_resolve_path_component( OBJECT(dma), "ledma")); diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c index b5b8256360..d8917cb101 100644 --- a/hw/tpm/tpm_crb.c +++ b/hw/tpm/tpm_crb.c @@ -29,6 +29,7 @@ #include "sysemu/reset.h" #include "tpm_int.h" #include "tpm_util.h" +#include "trace.h" typedef struct CRBState { DeviceState parent_obj; @@ -44,14 +45,6 @@ typedef struct CRBState { #define CRB(obj) OBJECT_CHECK(CRBState, (obj), TYPE_TPM_CRB) -#define DEBUG_CRB 0 - -#define DPRINTF(fmt, ...) do { \ - if (DEBUG_CRB) { \ - printf(fmt, ## __VA_ARGS__); \ - } \ - } while (0) - #define CRB_INTF_TYPE_CRB_ACTIVE 0b1 #define CRB_INTF_VERSION_CRB 0b1 #define CRB_INTF_CAP_LOCALITY_0_ONLY 0b0 @@ -91,8 +84,8 @@ static uint64_t tpm_crb_mmio_read(void *opaque, hwaddr addr, unsigned offset = addr & 3; uint32_t val = *(uint32_t *)regs >> (8 * offset); - DPRINTF("CRB read 0x" TARGET_FMT_plx " len:%u val: 0x%" PRIx32 "\n", - addr, size, val); + trace_tpm_crb_mmio_read(addr, size, val); + return val; } @@ -100,8 +93,8 @@ static void tpm_crb_mmio_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { CRBState *s = CRB(opaque); - DPRINTF("CRB write 0x" TARGET_FMT_plx " len:%u val: 0x%" PRIx64 "\n", - addr, size, val); + + trace_tpm_crb_mmio_write(addr, size, val); switch (addr) { case A_CRB_CTRL_REQ: diff --git a/hw/tpm/tpm_emulator.c b/hw/tpm/tpm_emulator.c index f187a72c10..6418ef0831 100644 --- a/hw/tpm/tpm_emulator.c +++ b/hw/tpm/tpm_emulator.c @@ -40,14 +40,7 @@ #include "qapi/clone-visitor.h" #include "qapi/qapi-visit-tpm.h" #include "chardev/char-fe.h" - -#define DEBUG_TPM 0 - -#define DPRINTF(fmt, ...) do { \ - if (DEBUG_TPM) { \ - fprintf(stderr, "tpm-emulator:"fmt"\n", ## __VA_ARGS__); \ - } \ -} while (0) +#include "trace.h" #define TYPE_TPM_EMULATOR "tpm-emulator" #define TPM_EMULATOR(obj) \ @@ -152,13 +145,12 @@ static int tpm_emulator_set_locality(TPMEmulator *tpm_emu, uint8_t locty_number, { ptm_loc loc; - DPRINTF("%s : locality: 0x%x", __func__, locty_number); - if (tpm_emu->cur_locty_number == locty_number) { return 0; } - DPRINTF("setting locality : 0x%x", locty_number); + trace_tpm_emulator_set_locality(locty_number); + loc.u.req.loc = locty_number; if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_LOCALITY, &loc, sizeof(loc), sizeof(loc)) < 0) { @@ -184,7 +176,7 @@ static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd *cmd, { TPMEmulator *tpm_emu = TPM_EMULATOR(tb); - DPRINTF("processing TPM command"); + trace_tpm_emulator_handle_request(); if (tpm_emulator_set_locality(tpm_emu, cmd->locty, errp) < 0 || tpm_emulator_unix_tx_bufs(tpm_emu, cmd->in, cmd->in_len, @@ -196,7 +188,6 @@ static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd *cmd, static int tpm_emulator_probe_caps(TPMEmulator *tpm_emu) { - DPRINTF("%s", __func__); if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_CAPABILITY, &tpm_emu->caps, 0, sizeof(tpm_emu->caps)) < 0) { error_report("tpm-emulator: probing failed : %s", strerror(errno)); @@ -205,7 +196,7 @@ static int tpm_emulator_probe_caps(TPMEmulator *tpm_emu) tpm_emu->caps = be64_to_cpu(tpm_emu->caps); - DPRINTF("capabilities : 0x%"PRIx64, tpm_emu->caps); + trace_tpm_emulator_probe_caps(tpm_emu->caps); return 0; } @@ -294,7 +285,7 @@ static int tpm_emulator_set_buffer_size(TPMBackend *tb, *actual_size = be32_to_cpu(psbs.u.resp.buffersize); } - DPRINTF("buffer size: %u, min: %u, max: %u\n", + trace_tpm_emulator_set_buffer_size( be32_to_cpu(psbs.u.resp.buffersize), be32_to_cpu(psbs.u.resp.minsize), be32_to_cpu(psbs.u.resp.maxsize)); @@ -315,7 +306,7 @@ static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize) goto err_exit; } - DPRINTF("%s", __func__); + trace_tpm_emulator_startup_tpm(); if (tpm_emulator_ctrlcmd(tpm_emu, CMD_INIT, &init, sizeof(init), sizeof(init)) < 0) { error_report("tpm-emulator: could not send INIT: %s", @@ -349,7 +340,7 @@ static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb) strerror(errno)); return false; } - DPRINTF("got established flag: %0x", est.u.resp.bit); + trace_tpm_emulator_get_tpm_established_flag(est.u.resp.bit); tpm_emu->established_flag_cached = 1; tpm_emu->established_flag = (est.u.resp.bit != 0); @@ -396,7 +387,7 @@ static void tpm_emulator_cancel_cmd(TPMBackend *tb) ptm_res res; if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, PTM_CAP_CANCEL_TPM_CMD)) { - DPRINTF("Backend does not support CANCEL_TPM_CMD"); + trace_tpm_emulator_cancel_cmd_not_supt(); return; } @@ -522,8 +513,16 @@ static int tpm_emulator_handle_device_opts(TPMEmulator *tpm_emu, QemuOpts *opts) goto err; } - DPRINTF("TPM Version %s", tpm_emu->tpm_version == TPM_VERSION_1_2 ? "1.2" : - (tpm_emu->tpm_version == TPM_VERSION_2_0 ? "2.0" : "Unspecified")); + switch (tpm_emu->tpm_version) { + case TPM_VERSION_1_2: + trace_tpm_emulator_handle_device_opts_tpm12(); + break; + case TPM_VERSION_2_0: + trace_tpm_emulator_handle_device_opts_tpm2(); + break; + default: + trace_tpm_emulator_handle_device_opts_unspec(); + } if (tpm_emulator_probe_caps(tpm_emu) || tpm_emulator_check_caps(tpm_emu)) { @@ -533,7 +532,8 @@ static int tpm_emulator_handle_device_opts(TPMEmulator *tpm_emu, QemuOpts *opts) return tpm_emulator_block_migration(tpm_emu); err: - DPRINTF("Startup error"); + trace_tpm_emulator_handle_device_opts_startup_error(); + return -1; } @@ -574,7 +574,8 @@ static void tpm_emulator_inst_init(Object *obj) { TPMEmulator *tpm_emu = TPM_EMULATOR(obj); - DPRINTF("%s", __func__); + trace_tpm_emulator_inst_init(); + tpm_emu->options = g_new0(TPMEmulatorOptions, 1); tpm_emu->cur_locty_number = ~0; qemu_mutex_init(&tpm_emu->mutex); diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c index 211df3191c..479317ee50 100644 --- a/hw/tpm/tpm_passthrough.c +++ b/hw/tpm/tpm_passthrough.c @@ -32,14 +32,7 @@ #include "qapi/clone-visitor.h" #include "qapi/qapi-visit-tpm.h" #include "tpm_util.h" - -#define DEBUG_TPM 0 - -#define DPRINTF(fmt, ...) do { \ - if (DEBUG_TPM) { \ - fprintf(stderr, fmt, ## __VA_ARGS__); \ - } \ -} while (0) +#include "trace.h" #define TYPE_TPM_PASSTHROUGH "tpm-passthrough" #define TPM_PASSTHROUGH(obj) \ @@ -138,7 +131,7 @@ static void tpm_passthrough_handle_request(TPMBackend *tb, TPMBackendCmd *cmd, { TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); - DPRINTF("tpm_passthrough: processing command %p\n", cmd); + trace_tpm_passthrough_handle_request(cmd); tpm_passthrough_unix_tx_bufs(tpm_pt, cmd->in, cmd->in_len, cmd->out, cmd->out_len, &cmd->selftest_done, @@ -147,7 +140,7 @@ static void tpm_passthrough_handle_request(TPMBackend *tb, TPMBackendCmd *cmd, static void tpm_passthrough_reset(TPMBackend *tb) { - DPRINTF("tpm_passthrough: CALL TO TPM_RESET!\n"); + trace_tpm_passthrough_reset(); tpm_passthrough_cancel_cmd(tb); } diff --git a/hw/tpm/tpm_tis.c b/hw/tpm/tpm_tis.c index 834eef75fa..2ac7e74307 100644 --- a/hw/tpm/tpm_tis.c +++ b/hw/tpm/tpm_tis.c @@ -31,6 +31,7 @@ #include "sysemu/tpm_backend.h" #include "tpm_int.h" #include "tpm_util.h" +#include "trace.h" #define TPM_TIS_NUM_LOCALITIES 5 /* per spec */ #define TPM_TIS_LOCALITY_SHIFT 12 @@ -86,12 +87,6 @@ typedef struct TPMState { #define DEBUG_TIS 0 -#define DPRINTF(fmt, ...) do { \ - if (DEBUG_TIS) { \ - printf(fmt, ## __VA_ARGS__); \ - } \ -} while (0) - /* local prototypes */ static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr, @@ -107,19 +102,17 @@ static uint8_t tpm_tis_locality_from_addr(hwaddr addr) static void tpm_tis_show_buffer(const unsigned char *buffer, size_t buffer_size, const char *string) { -#ifdef DEBUG_TIS uint32_t len, i; len = MIN(tpm_cmd_get_size(buffer), buffer_size); - DPRINTF("tpm_tis: %s length = %d\n", string, len); + printf("tpm_tis: %s length = %d\n", string, len); for (i = 0; i < len; i++) { if (i && !(i % 16)) { - DPRINTF("\n"); + printf("\n"); } - DPRINTF("%.2X ", buffer[i]); + printf("%.2X ", buffer[i]); } - DPRINTF("\n"); -#endif + printf("\n"); } /* @@ -146,8 +139,10 @@ static void tpm_tis_sts_set(TPMLocality *l, uint32_t flags) */ static void tpm_tis_tpm_send(TPMState *s, uint8_t locty) { - tpm_tis_show_buffer(s->buffer, s->be_buffer_size, - "tpm_tis: To TPM"); + if (DEBUG_TIS) { + tpm_tis_show_buffer(s->buffer, s->be_buffer_size, + "tpm_tis: To TPM"); + } /* * rw_offset serves as length indicator for length of data; @@ -175,7 +170,7 @@ static void tpm_tis_raise_irq(TPMState *s, uint8_t locty, uint32_t irqmask) if ((s->loc[locty].inte & TPM_TIS_INT_ENABLED) && (s->loc[locty].inte & irqmask)) { - DPRINTF("tpm_tis: Raising IRQ for flag %08x\n", irqmask); + trace_tpm_tis_raise_irq(irqmask); qemu_irq_raise(s->irq); s->loc[locty].ints |= irqmask; } @@ -223,7 +218,7 @@ static void tpm_tis_new_active_locality(TPMState *s, uint8_t new_active_locty) s->active_locty = new_active_locty; - DPRINTF("tpm_tis: Active locality is now %d\n", s->active_locty); + trace_tpm_tis_new_active_locality(s->active_locty); if (TPM_TIS_IS_VALID_LOCTY(new_active_locty)) { /* set flags on the new active locality */ @@ -242,7 +237,7 @@ static void tpm_tis_abort(TPMState *s, uint8_t locty) { s->rw_offset = 0; - DPRINTF("tpm_tis: tis_abort: new active locality is %d\n", s->next_locty); + trace_tpm_tis_abort(s->next_locty); /* * Need to react differently depending on who's aborting now and @@ -310,8 +305,10 @@ static void tpm_tis_request_completed(TPMIf *ti, int ret) s->loc[locty].state = TPM_TIS_STATE_COMPLETION; s->rw_offset = 0; - tpm_tis_show_buffer(s->buffer, s->be_buffer_size, - "tpm_tis: From TPM"); + if (DEBUG_TIS) { + tpm_tis_show_buffer(s->buffer, s->be_buffer_size, + "tpm_tis: From TPM"); + } if (TPM_TIS_IS_VALID_LOCTY(s->next_locty)) { tpm_tis_abort(s, locty); @@ -339,8 +336,7 @@ static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty) tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID); tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID); } - DPRINTF("tpm_tis: tpm_tis_data_read byte 0x%02x [%d]\n", - ret, s->rw_offset - 1); + trace_tpm_tis_data_read(ret, s->rw_offset - 1); } return ret; @@ -364,29 +360,29 @@ static void tpm_tis_dump_state(void *opaque, hwaddr addr) hwaddr base = addr & ~0xfff; TPMState *s = opaque; - DPRINTF("tpm_tis: active locality : %d\n" - "tpm_tis: state of locality %d : %d\n" - "tpm_tis: register dump:\n", - s->active_locty, - locty, s->loc[locty].state); + printf("tpm_tis: active locality : %d\n" + "tpm_tis: state of locality %d : %d\n" + "tpm_tis: register dump:\n", + s->active_locty, + locty, s->loc[locty].state); for (idx = 0; regs[idx] != 0xfff; idx++) { - DPRINTF("tpm_tis: 0x%04x : 0x%08x\n", regs[idx], - (int)tpm_tis_mmio_read(opaque, base + regs[idx], 4)); + printf("tpm_tis: 0x%04x : 0x%08x\n", regs[idx], + (int)tpm_tis_mmio_read(opaque, base + regs[idx], 4)); } - DPRINTF("tpm_tis: r/w offset : %d\n" - "tpm_tis: result buffer : ", - s->rw_offset); + printf("tpm_tis: r/w offset : %d\n" + "tpm_tis: result buffer : ", + s->rw_offset); for (idx = 0; idx < MIN(tpm_cmd_get_size(&s->buffer), s->be_buffer_size); idx++) { - DPRINTF("%c%02x%s", - s->rw_offset == idx ? '>' : ' ', - s->buffer[idx], - ((idx & 0xf) == 0xf) ? "\ntpm_tis: " : ""); + printf("%c%02x%s", + s->rw_offset == idx ? '>' : ' ', + s->buffer[idx], + ((idx & 0xf) == 0xf) ? "\ntpm_tis: " : ""); } - DPRINTF("\n"); + printf("\n"); } #endif @@ -506,7 +502,7 @@ static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr, val >>= shift; } - DPRINTF("tpm_tis: read.%u(%08x) = %08x\n", size, (int)addr, (int)val); + trace_tpm_tis_mmio_read(size, addr, val); return val; } @@ -527,10 +523,10 @@ static void tpm_tis_mmio_write(void *opaque, hwaddr addr, uint16_t len; uint32_t mask = (size == 1) ? 0xff : ((size == 2) ? 0xffff : ~0); - DPRINTF("tpm_tis: write.%u(%08x) = %08x\n", size, (int)addr, (int)val); + trace_tpm_tis_mmio_write(size, addr, val); if (locty == 4) { - DPRINTF("tpm_tis: Access to locality 4 only allowed from hardware\n"); + trace_tpm_tis_mmio_write_locty4(); return; } @@ -560,20 +556,18 @@ static void tpm_tis_mmio_write(void *opaque, hwaddr addr, if ((val & TPM_TIS_ACCESS_ACTIVE_LOCALITY)) { /* give up locality if currently owned */ if (s->active_locty == locty) { - DPRINTF("tpm_tis: Releasing locality %d\n", locty); + trace_tpm_tis_mmio_write_release_locty(locty); uint8_t newlocty = TPM_TIS_NO_LOCALITY; /* anybody wants the locality ? */ for (c = TPM_TIS_NUM_LOCALITIES - 1; c >= 0; c--) { if ((s->loc[c].access & TPM_TIS_ACCESS_REQUEST_USE)) { - DPRINTF("tpm_tis: Locality %d requests use.\n", c); + trace_tpm_tis_mmio_write_locty_req_use(c); newlocty = c; break; } } - DPRINTF("tpm_tis: TPM_TIS_ACCESS_ACTIVE_LOCALITY: " - "Next active locality: %d\n", - newlocty); + trace_tpm_tis_mmio_write_next_locty(newlocty); if (TPM_TIS_IS_VALID_LOCTY(newlocty)) { set_new_locty = 0; @@ -627,10 +621,10 @@ static void tpm_tis_mmio_write(void *opaque, hwaddr addr, } s->loc[locty].access |= TPM_TIS_ACCESS_SEIZE; - DPRINTF("tpm_tis: TPM_TIS_ACCESS_SEIZE: " - "Locality %d seized from locality %d\n", - locty, s->active_locty); - DPRINTF("tpm_tis: TPM_TIS_ACCESS_SEIZE: Initiating abort.\n"); + + trace_tpm_tis_mmio_write_locty_seized(locty, s->active_locty); + trace_tpm_tis_mmio_write_init_abort(); + set_new_locty = 0; tpm_tis_prep_abort(s, s->active_locty, locty); break; @@ -677,7 +671,7 @@ static void tpm_tis_mmio_write(void *opaque, hwaddr addr, s->loc[locty].ints &= ~val; if (s->loc[locty].ints == 0) { qemu_irq_lower(s->irq); - DPRINTF("tpm_tis: Lowering IRQ\n"); + trace_tpm_tis_mmio_write_lowering_irq(); } } s->loc[locty].ints &= ~(val & TPM_TIS_INTERRUPTS_SUPPORTED); @@ -725,8 +719,7 @@ static void tpm_tis_mmio_write(void *opaque, hwaddr addr, case TPM_TIS_STATE_EXECUTION: case TPM_TIS_STATE_RECEPTION: /* abort currently running command */ - DPRINTF("tpm_tis: %s: Initiating abort.\n", - __func__); + trace_tpm_tis_mmio_write_init_abort(); tpm_tis_prep_abort(s, locty, locty); break; @@ -780,8 +773,7 @@ static void tpm_tis_mmio_write(void *opaque, hwaddr addr, s->loc[locty].state == TPM_TIS_STATE_COMPLETION) { /* drop the byte */ } else { - DPRINTF("tpm_tis: Data to send to TPM: %08x (size=%d)\n", - (int)val, size); + trace_tpm_tis_mmio_write_data2send(val, size); if (s->loc[locty].state == TPM_TIS_STATE_READY) { s->loc[locty].state = TPM_TIS_STATE_RECEPTION; tpm_tis_sts_set(&s->loc[locty], diff --git a/hw/tpm/tpm_util.c b/hw/tpm/tpm_util.c index 2de52a0f1b..ee41757ea2 100644 --- a/hw/tpm/tpm_util.c +++ b/hw/tpm/tpm_util.c @@ -28,14 +28,7 @@ #include "exec/memory.h" #include "sysemu/tpm_backend.h" #include "hw/qdev.h" - -#define DEBUG_TPM 0 - -#define DPRINTF(fmt, ...) do { \ - if (DEBUG_TPM) { \ - fprintf(stderr, "tpm-util:"fmt"\n", ## __VA_ARGS__); \ - } \ -} while (0) +#include "trace.h" /* tpm backend property */ @@ -279,10 +272,11 @@ int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version, if (be32_to_cpu(tpm_resp.hdr.len) != sizeof(tpm_resp) || be32_to_cpu(tpm_resp.len) != sizeof(uint32_t)) { - DPRINTF("tpm_resp->hdr.len = %u, expected = %zu\n", - be32_to_cpu(tpm_resp.hdr.len), sizeof(tpm_resp)); - DPRINTF("tpm_resp->len = %u, expected = %zu\n", - be32_to_cpu(tpm_resp.len), sizeof(uint32_t)); + trace_tpm_util_get_buffer_size_hdr_len( + be32_to_cpu(tpm_resp.hdr.len), + sizeof(tpm_resp)); + trace_tpm_util_get_buffer_size_len(be32_to_cpu(tpm_resp.len), + sizeof(uint32_t)); error_report("tpm_util: Got unexpected response to " "TPM_GetCapability; errcode: 0x%x", be32_to_cpu(tpm_resp.hdr.errcode)); @@ -327,10 +321,11 @@ int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version, if (be32_to_cpu(tpm2_resp.hdr.len) != sizeof(tpm2_resp) || be32_to_cpu(tpm2_resp.count) != 2) { - DPRINTF("tpm2_resp->hdr.len = %u, expected = %zu\n", - be32_to_cpu(tpm2_resp.hdr.len), sizeof(tpm2_resp)); - DPRINTF("tpm2_resp->len = %u, expected = %u\n", - be32_to_cpu(tpm2_resp.count), 2); + trace_tpm_util_get_buffer_size_hdr_len2( + be32_to_cpu(tpm2_resp.hdr.len), + sizeof(tpm2_resp)); + trace_tpm_util_get_buffer_size_len2( + be32_to_cpu(tpm2_resp.count), 2); error_report("tpm_util: Got unexpected response to " "TPM2_GetCapability; errcode: 0x%x", be32_to_cpu(tpm2_resp.hdr.errcode)); @@ -344,7 +339,7 @@ int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version, return -EFAULT; } - DPRINTF("buffersize of device: %zu\n", *buffersize); + trace_tpm_util_get_buffer_size(*buffersize); return 0; } diff --git a/hw/tpm/trace-events b/hw/tpm/trace-events new file mode 100644 index 0000000000..9a65384088 --- /dev/null +++ b/hw/tpm/trace-events @@ -0,0 +1,46 @@ +# See docs/devel/tracing.txt for syntax documentation. + +# hw/tpm/tpm_crb.c +tpm_crb_mmio_read(uint64_t addr, unsigned size, uint32_t val) "CRB read 0x" TARGET_FMT_plx " len:%u val: 0x%" PRIx32 +tpm_crb_mmio_write(uint64_t addr, unsigned size, uint32_t val) "CRB write 0x" TARGET_FMT_plx " len:%u val: 0x%" PRIx32 + +# hw/tpm/tpm_passthrough.c +tpm_passthrough_handle_request(void *cmd) "processing command %p" +tpm_passthrough_reset(void) "reset" + +# hw/tpm/tpm_util.c +tpm_util_get_buffer_size_hdr_len(uint32_t len, size_t expected) "tpm_resp->hdr.len = %u, expected = %zu" +tpm_util_get_buffer_size_len(uint32_t len, size_t expected) "tpm_resp->len = %u, expected = %zu" +tpm_util_get_buffer_size_hdr_len2(uint32_t len, size_t expected) "tpm2_resp->hdr.len = %u, expected = %zu" +tpm_util_get_buffer_size_len2(uint32_t len, size_t expected) "tpm2_resp->len = %u, expected = %zu" +tpm_util_get_buffer_size(size_t len) "buffersize of device: %zu" + +# hw/tpm/tpm_emulator.c +tpm_emulator_set_locality(uint8_t locty) "setting locality to %d" +tpm_emulator_handle_request(void) "processing TPM command" +tpm_emulator_probe_caps(uint64_t caps) "capabilities: 0x%"PRIx64 +tpm_emulator_set_buffer_size(uint32_t buffersize, uint32_t minsize, uint32_t maxsize) "buffer size: %u, min: %u, max: %u" +tpm_emulator_startup_tpm(void) "startup" +tpm_emulator_get_tpm_established_flag(uint8_t flag) "got established flag: %d" +tpm_emulator_cancel_cmd_not_supt(void) "Backend does not support CANCEL_TPM_CMD" +tpm_emulator_handle_device_opts_tpm12(void) "TPM Version 1.2" +tpm_emulator_handle_device_opts_tpm2(void) "TPM Version 2" +tpm_emulator_handle_device_opts_unspec(void) "TPM Version Unspecified" +tpm_emulator_handle_device_opts_startup_error(void) "Startup error" +tpm_emulator_inst_init(void) "" + +# hw/tpm/tpm_tis.c +tpm_tis_raise_irq(uint32_t irqmask) "Raising IRQ for flag 0x%08x" +tpm_tis_new_active_locality(uint8_t locty) "Active locality is now %d" +tpm_tis_abort(uint8_t locty) "New active locality is %d" +tpm_tis_data_read(uint32_t value, uint32_t off) "byte 0x%02x [%d]" +tpm_tis_mmio_read(unsigned size, uint32_t addr, uint32_t val) " read.%u(0x%08x) = 0x%08x" +tpm_tis_mmio_write(unsigned size, uint32_t addr, uint32_t val) "write.%u(0x%08x) = 0x%08x" +tpm_tis_mmio_write_locty4(void) "Access to locality 4 only allowed from hardware" +tpm_tis_mmio_write_release_locty(uint8_t locty) "Releasing locality %d" +tpm_tis_mmio_write_locty_req_use(uint8_t locty) "Locality %d requests use" +tpm_tis_mmio_write_next_locty(uint8_t locty) "Next active locality is %d" +tpm_tis_mmio_write_locty_seized(uint8_t locty, uint8_t active) "Locality %d seized from locality %d" +tpm_tis_mmio_write_init_abort(void) "Initiating abort" +tpm_tis_mmio_write_lowering_irq(void) "Lowering IRQ" +tpm_tis_mmio_write_data2send(uint32_t value, unsigned size) "Data to send to TPM: 0x%08x (size=%d)" diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index 16713f2c52..4e5855741a 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -6,8 +6,8 @@ * Xiao Feng Ren <renxiaof@linux.vnet.ibm.com> * Pierre Morel <pmorel@linux.vnet.ibm.com> * - * This work is licensed under the terms of the GNU GPL, version 2 or(at - * your option) any version. See the COPYING file in the top-level + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level * directory. */ diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 033cc8dea1..3ba3cbc146 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2807,7 +2807,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) return; } - vdev->vbasedev.name = g_strdup(basename(vdev->vbasedev.sysfsdev)); + vdev->vbasedev.name = g_path_get_basename(vdev->vbasedev.sysfsdev); vdev->vbasedev.ops = &vfio_pci_ops; vdev->vbasedev.type = VFIO_DEVICE_TYPE_PCI; vdev->vbasedev.dev = &vdev->pdev.qdev; diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c index 0d4bc0aae8..5c921c27ba 100644 --- a/hw/vfio/platform.c +++ b/hw/vfio/platform.c @@ -561,7 +561,7 @@ static int vfio_base_device_init(VFIODevice *vbasedev, Error **errp) /* @sysfsdev takes precedence over @host */ if (vbasedev->sysfsdev) { g_free(vbasedev->name); - vbasedev->name = g_strdup(basename(vbasedev->sysfsdev)); + vbasedev->name = g_path_get_basename(vbasedev->sysfsdev); } else { if (!vbasedev->name || strchr(vbasedev->name, '/')) { error_setg(errp, "wrong host device name"); |