diff options
82 files changed, 1698 insertions, 765 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index c1d1602..63223e1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1138,12 +1138,6 @@ S: Orphaned F: hw/mips/mipssim.c F: hw/net/mipsnet.c -R4000 -R: Aurelien Jarno <aurelien@aurel32.net> -R: Aleksandar Rikalo <aleksandar.rikalo@syrmia.com> -S: Obsolete -F: hw/mips/r4k.c - Fuloong 2E M: Huacai Chen <chenhc@lemote.com> M: Philippe Mathieu-Daudé <f4bug@amsat.org> @@ -1327,8 +1321,14 @@ L: qemu-riscv@nongnu.org S: Supported F: hw/riscv/microchip_pfsoc.c F: hw/char/mchp_pfsoc_mmuart.c +F: hw/misc/mchp_pfsoc_dmc.c +F: hw/misc/mchp_pfsoc_ioscb.c +F: hw/misc/mchp_pfsoc_sysreg.c F: include/hw/riscv/microchip_pfsoc.h F: include/hw/char/mchp_pfsoc_mmuart.h +F: include/hw/misc/mchp_pfsoc_dmc.h +F: include/hw/misc/mchp_pfsoc_ioscb.h +F: include/hw/misc/mchp_pfsoc_sysreg.h RX Machines ----------- @@ -1 +1 @@ -5.1.50 +5.1.90 diff --git a/block/vvfat.c b/block/vvfat.c index 5abb90e..54807f8 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -1437,7 +1437,7 @@ static void print_direntry(const direntry_t* direntry) for(i=0;i<11;i++) ADD_CHAR(direntry->name[i]); buffer[j] = 0; - fprintf(stderr,"%s attributes=0x%02x begin=%d size=%d\n", + fprintf(stderr, "%s attributes=0x%02x begin=%u size=%u\n", buffer, direntry->attributes, begin_of_direntry(direntry),le32_to_cpu(direntry->size)); @@ -1446,7 +1446,7 @@ static void print_direntry(const direntry_t* direntry) static void print_mapping(const mapping_t* mapping) { - fprintf(stderr, "mapping (%p): begin, end = %d, %d, dir_index = %d, " + fprintf(stderr, "mapping (%p): begin, end = %u, %u, dir_index = %u, " "first_mapping_index = %d, name = %s, mode = 0x%x, " , mapping, mapping->begin, mapping->end, mapping->dir_index, mapping->first_mapping_index, mapping->path, mapping->mode); @@ -1454,7 +1454,7 @@ static void print_mapping(const mapping_t* mapping) if (mapping->mode & MODE_DIRECTORY) fprintf(stderr, "parent_mapping_index = %d, first_dir_index = %d\n", mapping->info.dir.parent_mapping_index, mapping->info.dir.first_dir_index); else - fprintf(stderr, "offset = %d\n", mapping->info.file.offset); + fprintf(stderr, "offset = %u\n", mapping->info.file.offset); } #endif @@ -1588,7 +1588,7 @@ typedef struct commit_t { static void clear_commits(BDRVVVFATState* s) { int i; -DLOG(fprintf(stderr, "clear_commits (%d commits)\n", s->commits.next)); +DLOG(fprintf(stderr, "clear_commits (%u commits)\n", s->commits.next)); for (i = 0; i < s->commits.next; i++) { commit_t* commit = array_get(&(s->commits), i); assert(commit->path || commit->action == ACTION_WRITEOUT); @@ -2648,7 +2648,9 @@ static int handle_renames_and_mkdirs(BDRVVVFATState* s) fprintf(stderr, "handle_renames\n"); for (i = 0; i < s->commits.next; i++) { commit_t* commit = array_get(&(s->commits), i); - fprintf(stderr, "%d, %s (%d, %d)\n", i, commit->path ? commit->path : "(null)", commit->param.rename.cluster, commit->action); + fprintf(stderr, "%d, %s (%u, %d)\n", i, + commit->path ? commit->path : "(null)", + commit->param.rename.cluster, commit->action); } #endif diff --git a/chardev/char-socket.c b/chardev/char-socket.c index 95e4581..213a4c8 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -443,10 +443,24 @@ static char *qemu_chr_socket_address(SocketChardev *s, const char *prefix) s->is_listen ? ",server" : ""); break; case SOCKET_ADDRESS_TYPE_UNIX: - return g_strdup_printf("%sunix:%s%s", prefix, - s->addr->u.q_unix.path, + { + const char *tight = "", *abstract = ""; + UnixSocketAddress *sa = &s->addr->u.q_unix; + +#ifdef CONFIG_LINUX + if (sa->has_abstract && sa->abstract) { + abstract = ",abstract"; + if (sa->has_tight && sa->tight) { + tight = ",tight"; + } + } +#endif + + return g_strdup_printf("%sunix:%s%s%s%s", prefix, sa->path, + abstract, tight, s->is_listen ? ",server" : ""); break; + } case SOCKET_ADDRESS_TYPE_FD: return g_strdup_printf("%sfd:%s%s", prefix, s->addr->u.fd.str, s->is_listen ? ",server" : ""); @@ -1386,8 +1400,10 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend, const char *host = qemu_opt_get(opts, "host"); const char *port = qemu_opt_get(opts, "port"); const char *fd = qemu_opt_get(opts, "fd"); +#ifdef CONFIG_LINUX bool tight = qemu_opt_get_bool(opts, "tight", true); bool abstract = qemu_opt_get_bool(opts, "abstract", false); +#endif SocketAddressLegacy *addr; ChardevSocket *sock; @@ -1439,8 +1455,12 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend, addr->type = SOCKET_ADDRESS_LEGACY_KIND_UNIX; q_unix = addr->u.q_unix.data = g_new0(UnixSocketAddress, 1); q_unix->path = g_strdup(path); +#ifdef CONFIG_LINUX + q_unix->has_tight = true; q_unix->tight = tight; + q_unix->has_abstract = true; q_unix->abstract = abstract; +#endif } else if (host) { addr->type = SOCKET_ADDRESS_LEGACY_KIND_INET; addr->u.inet.data = g_new(InetSocketAddress, 1); diff --git a/chardev/char.c b/chardev/char.c index 7855312..aa42821 100644 --- a/chardev/char.c +++ b/chardev/char.c @@ -928,6 +928,7 @@ QemuOptsList qemu_chardev_opts = { },{ .name = "logappend", .type = QEMU_OPT_BOOL, +#ifdef CONFIG_LINUX },{ .name = "tight", .type = QEMU_OPT_BOOL, @@ -935,6 +936,7 @@ QemuOptsList qemu_chardev_opts = { },{ .name = "abstract", .type = QEMU_OPT_BOOL, +#endif }, { /* end of list */ } }, diff --git a/default-configs/devices/mips-softmmu-common.mak b/default-configs/devices/mips-softmmu-common.mak index da29c6c..ea78fe7 100644 --- a/default-configs/devices/mips-softmmu-common.mak +++ b/default-configs/devices/mips-softmmu-common.mak @@ -33,7 +33,6 @@ CONFIG_MC146818RTC=y CONFIG_EMPTY_SLOT=y CONFIG_MIPS_CPS=y CONFIG_MIPS_ITU=y -CONFIG_R4K=y CONFIG_MALTA=y CONFIG_PCNET_PCI=y CONFIG_MIPSSIM=y diff --git a/docs/system/deprecated.rst b/docs/system/deprecated.rst index 32a0e62..8c1dc76 100644 --- a/docs/system/deprecated.rst +++ b/docs/system/deprecated.rst @@ -328,12 +328,6 @@ The 'scsi-disk' device is deprecated. Users should use 'scsi-hd' or System emulator machines ------------------------ -mips ``r4k`` platform (since 5.0) -''''''''''''''''''''''''''''''''' - -This machine type is very old and unmaintained. Users should use the ``malta`` -machine type instead. - mips ``fulong2e`` machine (since 5.1) ''''''''''''''''''''''''''''''''''''' @@ -576,6 +570,12 @@ The version specific Spike machines have been removed in favour of the generic ``spike`` machine. If you need to specify an older version of the RISC-V spec you can use the ``-cpu rv64gcsu,priv_spec=v1.10.0`` command line argument. +mips ``r4k`` platform (removed in 5.2) +'''''''''''''''''''''''''''''''''''''' + +This machine type was very old and unmaintained. Users should use the ``malta`` +machine type instead. + Related binaries ---------------- diff --git a/hmp-commands.hx b/hmp-commands.hx index cd06838..ff2d7aa 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -254,6 +254,7 @@ ERST .help = "save screen from head 'head' of display device 'device' " "into PPM image 'filename'", .cmd = hmp_screendump, + .coroutine = true, }, SRST diff --git a/hw/core/loader-fit.c b/hw/core/loader-fit.c index c465921..b7c7b3b 100644 --- a/hw/core/loader-fit.c +++ b/hw/core/loader-fit.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/hw/display/ati_2d.c b/hw/display/ati_2d.c index 23a8ae0..4dc10ea 100644 --- a/hw/display/ati_2d.c +++ b/hw/display/ati_2d.c @@ -75,8 +75,9 @@ void ati_2d_blt(ATIVGAState *s) dst_stride *= bpp; } uint8_t *end = s->vga.vram_ptr + s->vga.vram_size; - if (dst_bits >= end || dst_bits + dst_x + (dst_y + s->regs.dst_height) * - dst_stride >= end) { + if (dst_x > 0x3fff || dst_y > 0x3fff || dst_bits >= end + || dst_bits + dst_x + + (dst_y + s->regs.dst_height) * dst_stride >= end) { qemu_log_mask(LOG_UNIMP, "blt outside vram not implemented\n"); return; } @@ -107,8 +108,9 @@ void ati_2d_blt(ATIVGAState *s) src_bits += s->regs.crtc_offset & 0x07ffffff; src_stride *= bpp; } - if (src_bits >= end || src_bits + src_x + - (src_y + s->regs.dst_height) * src_stride >= end) { + if (src_x > 0x3fff || src_y > 0x3fff || src_bits >= end + || src_bits + src_x + + (src_y + s->regs.dst_height) * src_stride >= end) { qemu_log_mask(LOG_UNIMP, "blt outside vram not implemented\n"); return; } diff --git a/hw/intc/loongson_liointc.c b/hw/intc/loongson_liointc.c index 30fb375..fbbfb57 100644 --- a/hw/intc/loongson_liointc.c +++ b/hw/intc/loongson_liointc.c @@ -130,7 +130,7 @@ liointc_read(void *opaque, hwaddr addr, unsigned int size) if (addr >= R_PERCORE_ISR(0) && addr < R_PERCORE_ISR(NUM_CORES)) { - int core = (addr - R_PERCORE_ISR(0)) / 4; + int core = (addr - R_PERCORE_ISR(0)) / 8; r = p->per_core_isr[core]; goto out; } @@ -173,7 +173,7 @@ liointc_write(void *opaque, hwaddr addr, if (addr >= R_PERCORE_ISR(0) && addr < R_PERCORE_ISR(NUM_CORES)) { - int core = (addr - R_PERCORE_ISR(0)) / 4; + int core = (addr - R_PERCORE_ISR(0)) / 8; p->per_core_isr[core] = value; goto out; } diff --git a/hw/intc/sifive_plic.c b/hw/intc/sifive_plic.c index f42fd69..97a1a27 100644 --- a/hw/intc/sifive_plic.c +++ b/hw/intc/sifive_plic.c @@ -30,6 +30,7 @@ #include "hw/intc/sifive_plic.h" #include "target/riscv/cpu.h" #include "sysemu/sysemu.h" +#include "migration/vmstate.h" #define RISCV_DEBUG_PLIC 0 @@ -448,11 +449,12 @@ static void sifive_plic_realize(DeviceState *dev, Error **errp) TYPE_SIFIVE_PLIC, plic->aperture_size); parse_hart_config(plic); plic->bitfield_words = (plic->num_sources + 31) >> 5; + plic->num_enables = plic->bitfield_words * plic->num_addrs; 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); + plic->enable = g_new0(uint32_t, plic->num_enables); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &plic->mmio); qdev_init_gpio_in(dev, sifive_plic_irq_request, plic->num_sources); @@ -472,12 +474,34 @@ static void sifive_plic_realize(DeviceState *dev, Error **errp) msi_nonbroken = true; } +static const VMStateDescription vmstate_sifive_plic = { + .name = "riscv_sifive_plic", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_VARRAY_UINT32(source_priority, SiFivePLICState, + num_sources, 0, + vmstate_info_uint32, uint32_t), + VMSTATE_VARRAY_UINT32(target_priority, SiFivePLICState, + num_addrs, 0, + vmstate_info_uint32, uint32_t), + VMSTATE_VARRAY_UINT32(pending, SiFivePLICState, bitfield_words, 0, + vmstate_info_uint32, uint32_t), + VMSTATE_VARRAY_UINT32(claimed, SiFivePLICState, bitfield_words, 0, + vmstate_info_uint32, uint32_t), + VMSTATE_VARRAY_UINT32(enable, SiFivePLICState, num_enables, 0, + vmstate_info_uint32, uint32_t), + VMSTATE_END_OF_LIST() + } +}; + static void sifive_plic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); device_class_set_props(dc, sifive_plic_properties); dc->realize = sifive_plic_realize; + dc->vmsd = &vmstate_sifive_plic; } static const TypeInfo sifive_plic_info = { diff --git a/hw/mips/Kconfig b/hw/mips/Kconfig index 67d39c5..8be7012 100644 --- a/hw/mips/Kconfig +++ b/hw/mips/Kconfig @@ -1,16 +1,3 @@ -config R4K - bool - select ISA_BUS - select SERIAL_ISA - select I8259 - select I8254 - select MC146818RTC - imply VGA_ISA - imply NE2000_ISA - select IDE_ISA - select PCKBD - select PFLASH_CFI01 - config MALTA bool select ISA_SUPERIO diff --git a/hw/mips/boston.c b/hw/mips/boston.c index 74c18ed..3356d7a 100644 --- a/hw/mips/boston.c +++ b/hw/mips/boston.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/hw/mips/cps.c b/hw/mips/cps.c index c624821..962b1b0 100644 --- a/hw/mips/cps.c +++ b/hw/mips/cps.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/hw/mips/meson.build b/hw/mips/meson.build index 46294b7..bcdf96b 100644 --- a/hw/mips/meson.build +++ b/hw/mips/meson.build @@ -6,6 +6,5 @@ mips_ss.add(when: 'CONFIG_MALTA', if_true: files('gt64xxx_pci.c', 'malta.c')) mips_ss.add(when: 'CONFIG_MIPSSIM', if_true: files('mipssim.c')) mips_ss.add(when: 'CONFIG_MIPS_BOSTON', if_true: [files('boston.c'), fdt]) mips_ss.add(when: 'CONFIG_MIPS_CPS', if_true: files('cps.c')) -mips_ss.add(when: 'CONFIG_R4K', if_true: files('r4k.c')) hw_arch += {'mips': mips_ss} diff --git a/hw/mips/r4k.c b/hw/mips/r4k.c deleted file mode 100644 index 3830854..0000000 --- a/hw/mips/r4k.c +++ /dev/null @@ -1,318 +0,0 @@ -/* - * QEMU/MIPS pseudo-board - * - * emulates a simple machine with ISA-like bus. - * ISA IO space mapped to the 0x14000000 (PHYS) and - * ISA memory at the 0x10000000 (PHYS, 16Mb in size). - * All peripherial devices are attached to this "bus" with - * the standard PC ISA addresses. - */ - -#include "qemu/osdep.h" -#include "qemu/units.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "cpu.h" -#include "hw/clock.h" -#include "hw/mips/mips.h" -#include "hw/mips/cpudevs.h" -#include "hw/intc/i8259.h" -#include "hw/char/serial.h" -#include "hw/isa/isa.h" -#include "net/net.h" -#include "hw/net/ne2000-isa.h" -#include "sysemu/sysemu.h" -#include "hw/boards.h" -#include "hw/block/flash.h" -#include "qemu/log.h" -#include "hw/mips/bios.h" -#include "hw/ide.h" -#include "hw/ide/internal.h" -#include "hw/loader.h" -#include "elf.h" -#include "hw/rtc/mc146818rtc.h" -#include "hw/input/i8042.h" -#include "hw/timer/i8254.h" -#include "exec/address-spaces.h" -#include "sysemu/qtest.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" -#include "qemu/error-report.h" - -#define MAX_IDE_BUS 2 - -static const int ide_iobase[2] = { 0x1f0, 0x170 }; -static const int ide_iobase2[2] = { 0x3f6, 0x376 }; -static const int ide_irq[2] = { 14, 15 }; - -static ISADevice *pit; /* PIT i8254 */ - -/* i8254 PIT is attached to the IRQ0 at PIC i8259 */ - -static struct _loaderparams { - int ram_size; - const char *kernel_filename; - const char *kernel_cmdline; - const char *initrd_filename; -} loaderparams; - -static void mips_qemu_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - if ((addr & 0xffff) == 0 && val == 42) { - qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); - } else if ((addr & 0xffff) == 4 && val == 42) { - qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); - } -} - -static uint64_t mips_qemu_read(void *opaque, hwaddr addr, - unsigned size) -{ - return 0; -} - -static const MemoryRegionOps mips_qemu_ops = { - .read = mips_qemu_read, - .write = mips_qemu_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -typedef struct ResetData { - MIPSCPU *cpu; - uint64_t vector; -} ResetData; - -static int64_t load_kernel(void) -{ - const size_t params_size = 264; - int64_t entry, kernel_high, initrd_size; - long kernel_size; - ram_addr_t initrd_offset; - uint32_t *params_buf; - int big_endian; - -#ifdef TARGET_WORDS_BIGENDIAN - big_endian = 1; -#else - big_endian = 0; -#endif - kernel_size = load_elf(loaderparams.kernel_filename, NULL, - cpu_mips_kseg0_to_phys, NULL, - (uint64_t *)&entry, NULL, - (uint64_t *)&kernel_high, NULL, big_endian, - EM_MIPS, 1, 0); - if (kernel_size < 0) { - error_report("could not load kernel '%s': %s", - loaderparams.kernel_filename, - load_elf_strerror(kernel_size)); - exit(1); - } - - /* load initrd */ - initrd_size = 0; - initrd_offset = 0; - if (loaderparams.initrd_filename) { - initrd_size = get_image_size(loaderparams.initrd_filename); - if (initrd_size > 0) { - initrd_offset = ROUND_UP(kernel_high, INITRD_PAGE_SIZE); - if (initrd_offset + initrd_size > ram_size) { - error_report("memory too small for initial ram disk '%s'", - loaderparams.initrd_filename); - exit(1); - } - initrd_size = load_image_targphys(loaderparams.initrd_filename, - initrd_offset, - ram_size - initrd_offset); - } - if (initrd_size == (target_ulong) -1) { - error_report("could not load initial ram disk '%s'", - loaderparams.initrd_filename); - exit(1); - } - } - - /* Store command line. */ - params_buf = g_malloc(params_size); - - params_buf[0] = tswap32(ram_size); - params_buf[1] = tswap32(0x12345678); - - if (initrd_size > 0) { - snprintf((char *)params_buf + 8, 256, - "rd_start=0x%" PRIx64 " rd_size=%" PRId64 " %s", - cpu_mips_phys_to_kseg0(NULL, initrd_offset), - initrd_size, loaderparams.kernel_cmdline); - } else { - snprintf((char *)params_buf + 8, 256, - "%s", loaderparams.kernel_cmdline); - } - - rom_add_blob_fixed("params", params_buf, params_size, - 16 * MiB - params_size); - - g_free(params_buf); - return entry; -} - -static void main_cpu_reset(void *opaque) -{ - ResetData *s = (ResetData *)opaque; - CPUMIPSState *env = &s->cpu->env; - - cpu_reset(CPU(s->cpu)); - env->active_tc.PC = s->vector; -} - -static const int sector_len = 32 * KiB; -static -void mips_r4k_init(MachineState *machine) -{ - const char *kernel_filename = machine->kernel_filename; - const char *kernel_cmdline = machine->kernel_cmdline; - const char *initrd_filename = machine->initrd_filename; - char *filename; - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *bios; - MemoryRegion *iomem = g_new(MemoryRegion, 1); - MemoryRegion *isa_io = g_new(MemoryRegion, 1); - MemoryRegion *isa_mem = g_new(MemoryRegion, 1); - int bios_size; - Clock *cpuclk; - MIPSCPU *cpu; - CPUMIPSState *env; - ResetData *reset_info; - int i; - qemu_irq *i8259; - ISABus *isa_bus; - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; - DriveInfo *dinfo; - int be; - - cpuclk = clock_new(OBJECT(machine), "cpu-refclk"); - clock_set_hz(cpuclk, 200000000); /* 200 MHz */ - - /* init CPUs */ - cpu = mips_cpu_create_with_clock(machine->cpu_type, cpuclk); - env = &cpu->env; - - reset_info = g_malloc0(sizeof(ResetData)); - reset_info->cpu = cpu; - reset_info->vector = env->active_tc.PC; - qemu_register_reset(main_cpu_reset, reset_info); - - /* allocate RAM */ - if (machine->ram_size > 256 * MiB) { - error_report("Too much memory for this machine: %" PRId64 "MB," - " maximum 256MB", ram_size / MiB); - exit(1); - } - memory_region_add_subregion(address_space_mem, 0, machine->ram); - - memory_region_init_io(iomem, NULL, &mips_qemu_ops, - NULL, "mips-qemu", 0x10000); - - memory_region_add_subregion(address_space_mem, 0x1fbf0000, iomem); - - /* - * Try to load a BIOS image. If this fails, we continue regardless, - * but initialize the hardware ourselves. When a kernel gets - * preloaded we also initialize the hardware, since the BIOS wasn't - * run. - */ - - if (bios_name == NULL) { - bios_name = BIOS_FILENAME; - } - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - if (filename) { - bios_size = get_image_size(filename); - } else { - bios_size = -1; - } -#ifdef TARGET_WORDS_BIGENDIAN - be = 1; -#else - be = 0; -#endif - dinfo = drive_get(IF_PFLASH, 0, 0); - if ((bios_size > 0) && (bios_size <= BIOS_SIZE)) { - bios = g_new(MemoryRegion, 1); - memory_region_init_rom(bios, NULL, "mips_r4k.bios", BIOS_SIZE, - &error_fatal); - memory_region_add_subregion(get_system_memory(), 0x1fc00000, bios); - - load_image_targphys(filename, 0x1fc00000, BIOS_SIZE); - } else if (dinfo != NULL) { - uint32_t mips_rom = 0x00400000; - if (!pflash_cfi01_register(0x1fc00000, "mips_r4k.bios", mips_rom, - blk_by_legacy_dinfo(dinfo), - sector_len, 4, 0, 0, 0, 0, be)) { - fprintf(stderr, "qemu: Error registering flash memory.\n"); - } - } else if (!qtest_enabled()) { - /* not fatal */ - warn_report("could not load MIPS bios '%s'", bios_name); - } - g_free(filename); - - if (kernel_filename) { - loaderparams.ram_size = machine->ram_size; - loaderparams.kernel_filename = kernel_filename; - loaderparams.kernel_cmdline = kernel_cmdline; - loaderparams.initrd_filename = initrd_filename; - reset_info->vector = load_kernel(); - } - - /* Init CPU internal devices */ - cpu_mips_irq_init_cpu(cpu); - cpu_mips_clock_init(cpu); - - /* ISA bus: IO space at 0x14000000, mem space at 0x10000000 */ - memory_region_init_alias(isa_io, NULL, "isa-io", - get_system_io(), 0, 0x00010000); - memory_region_init(isa_mem, NULL, "isa-mem", 0x01000000); - memory_region_add_subregion(get_system_memory(), 0x14000000, isa_io); - memory_region_add_subregion(get_system_memory(), 0x10000000, isa_mem); - isa_bus = isa_bus_new(NULL, isa_mem, get_system_io(), &error_abort); - - /* The PIC is attached to the MIPS CPU INT0 pin */ - i8259 = i8259_init(isa_bus, env->irq[2]); - isa_bus_irqs(isa_bus, i8259); - - mc146818_rtc_init(isa_bus, 2000, NULL); - - pit = i8254_pit_init(isa_bus, 0x40, 0, NULL); - - serial_hds_isa_init(isa_bus, 0, MAX_ISA_SERIAL_PORTS); - - isa_vga_init(isa_bus); - - if (nd_table[0].used) { - isa_ne2000_init(isa_bus, 0x300, 9, &nd_table[0]); - } - - ide_drive_get(hd, ARRAY_SIZE(hd)); - for (i = 0; i < MAX_IDE_BUS; i++) - isa_ide_init(isa_bus, ide_iobase[i], ide_iobase2[i], ide_irq[i], - hd[MAX_IDE_DEVS * i], - hd[MAX_IDE_DEVS * i + 1]); - - isa_create_simple(isa_bus, TYPE_I8042); -} - -static void mips_machine_init(MachineClass *mc) -{ - mc->deprecation_reason = "use malta machine type instead"; - mc->desc = "mips r4k platform"; - mc->init = mips_r4k_init; - mc->block_default_type = IF_IDE; -#ifdef TARGET_MIPS64 - mc->default_cpu_type = MIPS_CPU_TYPE_NAME("R4000"); -#else - mc->default_cpu_type = MIPS_CPU_TYPE_NAME("24Kf"); -#endif - mc->default_ram_id = "mips_r4k.ram"; -} - -DEFINE_MACHINE("mips", mips_machine_init) diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index 877ecff..dc44dc1 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -139,6 +139,15 @@ config MAC_VIA config AVR_POWER bool +config MCHP_PFSOC_DMC + bool + +config MCHP_PFSOC_IOSCB + bool + +config MCHP_PFSOC_SYSREG + bool + config SIFIVE_TEST bool diff --git a/hw/misc/mchp_pfsoc_dmc.c b/hw/misc/mchp_pfsoc_dmc.c new file mode 100644 index 0000000..15cf3d7 --- /dev/null +++ b/hw/misc/mchp_pfsoc_dmc.c @@ -0,0 +1,216 @@ +/* + * Microchip PolarFire SoC DDR Memory Controller module emulation + * + * Copyright (c) 2020 Wind River Systems, Inc. + * + * Author: + * Bin Meng <bin.meng@windriver.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that 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/bitops.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "hw/misc/mchp_pfsoc_dmc.h" + +/* DDR SGMII PHY module */ + +#define SGMII_PHY_IOC_REG1 0x208 +#define SGMII_PHY_TRAINING_STATUS 0x814 +#define SGMII_PHY_DQ_DQS_ERR_DONE 0x834 +#define SGMII_PHY_DQDQS_STATUS1 0x84c +#define SGMII_PHY_PVT_STAT 0xc20 + +static uint64_t mchp_pfsoc_ddr_sgmii_phy_read(void *opaque, hwaddr offset, + unsigned size) +{ + uint32_t val = 0; + static int training_status_bit; + + switch (offset) { + case SGMII_PHY_IOC_REG1: + /* See ddr_pvt_calibration() in HSS */ + val = BIT(4) | BIT(2); + break; + case SGMII_PHY_TRAINING_STATUS: + /* + * The codes logic emulates the training status change from + * DDR_TRAINING_IP_SM_BCLKSCLK to DDR_TRAINING_IP_SM_DQ_DQS. + * + * See ddr_setup() in mss_ddr.c in the HSS source codes. + */ + val = 1 << training_status_bit; + training_status_bit = (training_status_bit + 1) % 5; + break; + case SGMII_PHY_DQ_DQS_ERR_DONE: + /* + * DDR_TRAINING_IP_SM_VERIFY state in ddr_setup(), + * check that DQ/DQS training passed without error. + */ + val = 8; + break; + case SGMII_PHY_DQDQS_STATUS1: + /* + * DDR_TRAINING_IP_SM_VERIFY state in ddr_setup(), + * check that DQ/DQS calculated window is above 5 taps. + */ + val = 0xff; + break; + case SGMII_PHY_PVT_STAT: + /* See sgmii_channel_setup() in HSS */ + val = BIT(14) | BIT(6); + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device read " + "(size %d, offset 0x%" HWADDR_PRIx ")\n", + __func__, size, offset); + break; + } + + return val; +} + +static void mchp_pfsoc_ddr_sgmii_phy_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device write " + "(size %d, value 0x%" PRIx64 + ", offset 0x%" HWADDR_PRIx ")\n", + __func__, size, value, offset); +} + +static const MemoryRegionOps mchp_pfsoc_ddr_sgmii_phy_ops = { + .read = mchp_pfsoc_ddr_sgmii_phy_read, + .write = mchp_pfsoc_ddr_sgmii_phy_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void mchp_pfsoc_ddr_sgmii_phy_realize(DeviceState *dev, Error **errp) +{ + MchpPfSoCDdrSgmiiPhyState *s = MCHP_PFSOC_DDR_SGMII_PHY(dev); + + memory_region_init_io(&s->sgmii_phy, OBJECT(dev), + &mchp_pfsoc_ddr_sgmii_phy_ops, s, + "mchp.pfsoc.ddr_sgmii_phy", + MCHP_PFSOC_DDR_SGMII_PHY_REG_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->sgmii_phy); +} + +static void mchp_pfsoc_ddr_sgmii_phy_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "Microchip PolarFire SoC DDR SGMII PHY module"; + dc->realize = mchp_pfsoc_ddr_sgmii_phy_realize; +} + +static const TypeInfo mchp_pfsoc_ddr_sgmii_phy_info = { + .name = TYPE_MCHP_PFSOC_DDR_SGMII_PHY, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MchpPfSoCDdrSgmiiPhyState), + .class_init = mchp_pfsoc_ddr_sgmii_phy_class_init, +}; + +static void mchp_pfsoc_ddr_sgmii_phy_register_types(void) +{ + type_register_static(&mchp_pfsoc_ddr_sgmii_phy_info); +} + +type_init(mchp_pfsoc_ddr_sgmii_phy_register_types) + +/* DDR CFG module */ + +#define CFG_MT_DONE_ACK 0x4428 +#define CFG_STAT_DFI_INIT_COMPLETE 0x10034 +#define CFG_STAT_DFI_TRAINING_COMPLETE 0x10038 + +static uint64_t mchp_pfsoc_ddr_cfg_read(void *opaque, hwaddr offset, + unsigned size) +{ + uint32_t val = 0; + + switch (offset) { + case CFG_MT_DONE_ACK: + /* memory test in MTC_test() */ + val = BIT(0); + break; + case CFG_STAT_DFI_INIT_COMPLETE: + /* DDR_TRAINING_IP_SM_START_CHECK state in ddr_setup() */ + val = BIT(0); + break; + case CFG_STAT_DFI_TRAINING_COMPLETE: + /* DDR_TRAINING_IP_SM_VERIFY state in ddr_setup() */ + val = BIT(0); + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device read " + "(size %d, offset 0x%" HWADDR_PRIx ")\n", + __func__, size, offset); + break; + } + + return val; +} + +static void mchp_pfsoc_ddr_cfg_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device write " + "(size %d, value 0x%" PRIx64 + ", offset 0x%" HWADDR_PRIx ")\n", + __func__, size, value, offset); +} + +static const MemoryRegionOps mchp_pfsoc_ddr_cfg_ops = { + .read = mchp_pfsoc_ddr_cfg_read, + .write = mchp_pfsoc_ddr_cfg_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void mchp_pfsoc_ddr_cfg_realize(DeviceState *dev, Error **errp) +{ + MchpPfSoCDdrCfgState *s = MCHP_PFSOC_DDR_CFG(dev); + + memory_region_init_io(&s->cfg, OBJECT(dev), + &mchp_pfsoc_ddr_cfg_ops, s, + "mchp.pfsoc.ddr_cfg", + MCHP_PFSOC_DDR_CFG_REG_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->cfg); +} + +static void mchp_pfsoc_ddr_cfg_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "Microchip PolarFire SoC DDR CFG module"; + dc->realize = mchp_pfsoc_ddr_cfg_realize; +} + +static const TypeInfo mchp_pfsoc_ddr_cfg_info = { + .name = TYPE_MCHP_PFSOC_DDR_CFG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MchpPfSoCDdrCfgState), + .class_init = mchp_pfsoc_ddr_cfg_class_init, +}; + +static void mchp_pfsoc_ddr_cfg_register_types(void) +{ + type_register_static(&mchp_pfsoc_ddr_cfg_info); +} + +type_init(mchp_pfsoc_ddr_cfg_register_types) diff --git a/hw/misc/mchp_pfsoc_ioscb.c b/hw/misc/mchp_pfsoc_ioscb.c new file mode 100644 index 0000000..8b0d1ca --- /dev/null +++ b/hw/misc/mchp_pfsoc_ioscb.c @@ -0,0 +1,242 @@ +/* + * Microchip PolarFire SoC IOSCB module emulation + * + * Copyright (c) 2020 Wind River Systems, Inc. + * + * Author: + * Bin Meng <bin.meng@windriver.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that 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/bitops.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "hw/misc/mchp_pfsoc_ioscb.h" + +/* + * The whole IOSCB module registers map into the system address at 0x3000_0000, + * named as "System Port 0 (AXI-D0)". + */ +#define IOSCB_WHOLE_REG_SIZE 0x10000000 +#define IOSCB_SUBMOD_REG_SIZE 0x1000 + +/* + * There are many sub-modules in the IOSCB module. + * See Microchip PolarFire SoC documentation (Register_Map.zip), + * Register Map/PF_SoC_RegMap_V1_1/MPFS250T/mpfs250t_ioscb_memmap_dri.htm + * + * The following are sub-modules offsets that are of concern. + */ +#define IOSCB_LANE01_BASE 0x06500000 +#define IOSCB_LANE23_BASE 0x06510000 +#define IOSCB_CTRL_BASE 0x07020000 +#define IOSCB_CFG_BASE 0x07080000 +#define IOSCB_PLL_MSS_BASE 0x0E001000 +#define IOSCB_CFM_MSS_BASE 0x0E002000 +#define IOSCB_PLL_DDR_BASE 0x0E010000 +#define IOSCB_BC_DDR_BASE 0x0E020000 +#define IOSCB_IO_CALIB_DDR_BASE 0x0E040000 +#define IOSCB_PLL_SGMII_BASE 0x0E080000 +#define IOSCB_DLL_SGMII_BASE 0x0E100000 +#define IOSCB_CFM_SGMII_BASE 0x0E200000 +#define IOSCB_BC_SGMII_BASE 0x0E400000 +#define IOSCB_IO_CALIB_SGMII_BASE 0x0E800000 + +static uint64_t mchp_pfsoc_dummy_read(void *opaque, hwaddr offset, + unsigned size) +{ + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device read " + "(size %d, offset 0x%" HWADDR_PRIx ")\n", + __func__, size, offset); + + return 0; +} + +static void mchp_pfsoc_dummy_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device write " + "(size %d, value 0x%" PRIx64 + ", offset 0x%" HWADDR_PRIx ")\n", + __func__, size, value, offset); +} + +static const MemoryRegionOps mchp_pfsoc_dummy_ops = { + .read = mchp_pfsoc_dummy_read, + .write = mchp_pfsoc_dummy_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +/* All PLL modules in IOSCB have the same register layout */ + +#define PLL_CTRL 0x04 + +static uint64_t mchp_pfsoc_pll_read(void *opaque, hwaddr offset, + unsigned size) +{ + uint32_t val = 0; + + switch (offset) { + case PLL_CTRL: + /* PLL is locked */ + val = BIT(25); + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device read " + "(size %d, offset 0x%" HWADDR_PRIx ")\n", + __func__, size, offset); + break; + } + + return val; +} + +static const MemoryRegionOps mchp_pfsoc_pll_ops = { + .read = mchp_pfsoc_pll_read, + .write = mchp_pfsoc_dummy_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +/* IO_CALIB_DDR submodule */ + +#define IO_CALIB_DDR_IOC_REG1 0x08 + +static uint64_t mchp_pfsoc_io_calib_ddr_read(void *opaque, hwaddr offset, + unsigned size) +{ + uint32_t val = 0; + + switch (offset) { + case IO_CALIB_DDR_IOC_REG1: + /* calibration completed */ + val = BIT(2); + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device read " + "(size %d, offset 0x%" HWADDR_PRIx ")\n", + __func__, size, offset); + break; + } + + return val; +} + +static const MemoryRegionOps mchp_pfsoc_io_calib_ddr_ops = { + .read = mchp_pfsoc_io_calib_ddr_read, + .write = mchp_pfsoc_dummy_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void mchp_pfsoc_ioscb_realize(DeviceState *dev, Error **errp) +{ + MchpPfSoCIoscbState *s = MCHP_PFSOC_IOSCB(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init(&s->container, OBJECT(s), + "mchp.pfsoc.ioscb", IOSCB_WHOLE_REG_SIZE); + sysbus_init_mmio(sbd, &s->container); + + /* add subregions for all sub-modules in IOSCB */ + + memory_region_init_io(&s->lane01, OBJECT(s), &mchp_pfsoc_dummy_ops, s, + "mchp.pfsoc.ioscb.lane01", IOSCB_SUBMOD_REG_SIZE); + memory_region_add_subregion(&s->container, IOSCB_LANE01_BASE, &s->lane01); + + memory_region_init_io(&s->lane23, OBJECT(s), &mchp_pfsoc_dummy_ops, s, + "mchp.pfsoc.ioscb.lane23", IOSCB_SUBMOD_REG_SIZE); + memory_region_add_subregion(&s->container, IOSCB_LANE23_BASE, &s->lane23); + + memory_region_init_io(&s->ctrl, OBJECT(s), &mchp_pfsoc_dummy_ops, s, + "mchp.pfsoc.ioscb.ctrl", IOSCB_SUBMOD_REG_SIZE); + memory_region_add_subregion(&s->container, IOSCB_CTRL_BASE, &s->ctrl); + + memory_region_init_io(&s->cfg, OBJECT(s), &mchp_pfsoc_dummy_ops, s, + "mchp.pfsoc.ioscb.cfg", IOSCB_SUBMOD_REG_SIZE); + memory_region_add_subregion(&s->container, IOSCB_CFG_BASE, &s->cfg); + + memory_region_init_io(&s->pll_mss, OBJECT(s), &mchp_pfsoc_pll_ops, s, + "mchp.pfsoc.ioscb.pll_mss", IOSCB_SUBMOD_REG_SIZE); + memory_region_add_subregion(&s->container, IOSCB_PLL_MSS_BASE, &s->pll_mss); + + memory_region_init_io(&s->cfm_mss, OBJECT(s), &mchp_pfsoc_dummy_ops, s, + "mchp.pfsoc.ioscb.cfm_mss", IOSCB_SUBMOD_REG_SIZE); + memory_region_add_subregion(&s->container, IOSCB_CFM_MSS_BASE, &s->cfm_mss); + + memory_region_init_io(&s->pll_ddr, OBJECT(s), &mchp_pfsoc_pll_ops, s, + "mchp.pfsoc.ioscb.pll_ddr", IOSCB_SUBMOD_REG_SIZE); + memory_region_add_subregion(&s->container, IOSCB_PLL_DDR_BASE, &s->pll_ddr); + + memory_region_init_io(&s->bc_ddr, OBJECT(s), &mchp_pfsoc_dummy_ops, s, + "mchp.pfsoc.ioscb.bc_ddr", IOSCB_SUBMOD_REG_SIZE); + memory_region_add_subregion(&s->container, IOSCB_BC_DDR_BASE, &s->bc_ddr); + + memory_region_init_io(&s->io_calib_ddr, OBJECT(s), + &mchp_pfsoc_io_calib_ddr_ops, s, + "mchp.pfsoc.ioscb.io_calib_ddr", + IOSCB_SUBMOD_REG_SIZE); + memory_region_add_subregion(&s->container, IOSCB_IO_CALIB_DDR_BASE, + &s->io_calib_ddr); + + memory_region_init_io(&s->pll_sgmii, OBJECT(s), &mchp_pfsoc_pll_ops, s, + "mchp.pfsoc.ioscb.pll_sgmii", IOSCB_SUBMOD_REG_SIZE); + memory_region_add_subregion(&s->container, IOSCB_PLL_SGMII_BASE, + &s->pll_sgmii); + + memory_region_init_io(&s->dll_sgmii, OBJECT(s), &mchp_pfsoc_dummy_ops, s, + "mchp.pfsoc.ioscb.dll_sgmii", IOSCB_SUBMOD_REG_SIZE); + memory_region_add_subregion(&s->container, IOSCB_DLL_SGMII_BASE, + &s->dll_sgmii); + + memory_region_init_io(&s->cfm_sgmii, OBJECT(s), &mchp_pfsoc_dummy_ops, s, + "mchp.pfsoc.ioscb.cfm_sgmii", IOSCB_SUBMOD_REG_SIZE); + memory_region_add_subregion(&s->container, IOSCB_CFM_SGMII_BASE, + &s->cfm_sgmii); + + memory_region_init_io(&s->bc_sgmii, OBJECT(s), &mchp_pfsoc_dummy_ops, s, + "mchp.pfsoc.ioscb.bc_sgmii", IOSCB_SUBMOD_REG_SIZE); + memory_region_add_subregion(&s->container, IOSCB_BC_SGMII_BASE, + &s->bc_sgmii); + + memory_region_init_io(&s->io_calib_sgmii, OBJECT(s), &mchp_pfsoc_dummy_ops, + s, "mchp.pfsoc.ioscb.io_calib_sgmii", + IOSCB_SUBMOD_REG_SIZE); + memory_region_add_subregion(&s->container, IOSCB_IO_CALIB_SGMII_BASE, + &s->io_calib_sgmii); +} + +static void mchp_pfsoc_ioscb_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "Microchip PolarFire SoC IOSCB modules"; + dc->realize = mchp_pfsoc_ioscb_realize; +} + +static const TypeInfo mchp_pfsoc_ioscb_info = { + .name = TYPE_MCHP_PFSOC_IOSCB, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MchpPfSoCIoscbState), + .class_init = mchp_pfsoc_ioscb_class_init, +}; + +static void mchp_pfsoc_ioscb_register_types(void) +{ + type_register_static(&mchp_pfsoc_ioscb_info); +} + +type_init(mchp_pfsoc_ioscb_register_types) diff --git a/hw/misc/mchp_pfsoc_sysreg.c b/hw/misc/mchp_pfsoc_sysreg.c new file mode 100644 index 0000000..248a313 --- /dev/null +++ b/hw/misc/mchp_pfsoc_sysreg.c @@ -0,0 +1,99 @@ +/* + * Microchip PolarFire SoC SYSREG module emulation + * + * Copyright (c) 2020 Wind River Systems, Inc. + * + * Author: + * Bin Meng <bin.meng@windriver.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that 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/bitops.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "hw/misc/mchp_pfsoc_sysreg.h" + +#define ENVM_CR 0xb8 + +static uint64_t mchp_pfsoc_sysreg_read(void *opaque, hwaddr offset, + unsigned size) +{ + uint32_t val = 0; + + switch (offset) { + case ENVM_CR: + /* Indicate the eNVM is running at the configured divider rate */ + val = BIT(6); + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device read " + "(size %d, offset 0x%" HWADDR_PRIx ")\n", + __func__, size, offset); + break; + } + + return val; +} + +static void mchp_pfsoc_sysreg_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device write " + "(size %d, value 0x%" PRIx64 + ", offset 0x%" HWADDR_PRIx ")\n", + __func__, size, value, offset); +} + +static const MemoryRegionOps mchp_pfsoc_sysreg_ops = { + .read = mchp_pfsoc_sysreg_read, + .write = mchp_pfsoc_sysreg_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void mchp_pfsoc_sysreg_realize(DeviceState *dev, Error **errp) +{ + MchpPfSoCSysregState *s = MCHP_PFSOC_SYSREG(dev); + + memory_region_init_io(&s->sysreg, OBJECT(dev), + &mchp_pfsoc_sysreg_ops, s, + "mchp.pfsoc.sysreg", + MCHP_PFSOC_SYSREG_REG_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->sysreg); +} + +static void mchp_pfsoc_sysreg_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "Microchip PolarFire SoC SYSREG module"; + dc->realize = mchp_pfsoc_sysreg_realize; +} + +static const TypeInfo mchp_pfsoc_sysreg_info = { + .name = TYPE_MCHP_PFSOC_SYSREG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MchpPfSoCSysregState), + .class_init = mchp_pfsoc_sysreg_class_init, +}; + +static void mchp_pfsoc_sysreg_register_types(void) +{ + type_register_static(&mchp_pfsoc_sysreg_info); +} + +type_init(mchp_pfsoc_sysreg_register_types) diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 4a06cba..1cd48e8 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -23,6 +23,9 @@ softmmu_ss.add(when: 'CONFIG_ARM11SCU', if_true: files('arm11scu.c')) softmmu_ss.add(when: 'CONFIG_MOS6522', if_true: files('mos6522.c')) # RISC-V devices +softmmu_ss.add(when: 'CONFIG_MCHP_PFSOC_DMC', if_true: files('mchp_pfsoc_dmc.c')) +softmmu_ss.add(when: 'CONFIG_MCHP_PFSOC_IOSCB', if_true: files('mchp_pfsoc_ioscb.c')) +softmmu_ss.add(when: 'CONFIG_MCHP_PFSOC_SYSREG', if_true: files('mchp_pfsoc_sysreg.c')) softmmu_ss.add(when: 'CONFIG_SIFIVE_TEST', if_true: files('sifive_test.c')) softmmu_ss.add(when: 'CONFIG_SIFIVE_E_PRCI', if_true: files('sifive_e_prci.c')) softmmu_ss.add(when: 'CONFIG_SIFIVE_U_OTP', if_true: files('sifive_u_otp.c')) diff --git a/hw/misc/mips_cpc.c b/hw/misc/mips_cpc.c index 7c11fb3..4a94c87 100644 --- a/hw/misc/mips_cpc.c +++ b/hw/misc/mips_cpc.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/hw/misc/mips_itu.c b/hw/misc/mips_itu.c index 3540985..1333995 100644 --- a/hw/misc/mips_itu.c +++ b/hw/misc/mips_itu.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/hw/pci-host/xilinx-pcie.c b/hw/pci-host/xilinx-pcie.c index 3b32142..38d5901 100644 --- a/hw/pci-host/xilinx-pcie.c +++ b/hw/pci-host/xilinx-pcie.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index 2df978f..facb0cb 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -4,7 +4,10 @@ config IBEX config MICROCHIP_PFSOC bool select CADENCE_SDHCI + select MCHP_PFSOC_DMC + select MCHP_PFSOC_IOSCB select MCHP_PFSOC_MMUART + select MCHP_PFSOC_SYSREG select MSI_NONBROKEN select SIFIVE_CLINT select SIFIVE_PDMA diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c index 4627179..37ac46a 100644 --- a/hw/riscv/microchip_pfsoc.c +++ b/hw/riscv/microchip_pfsoc.c @@ -15,6 +15,8 @@ * 4) Cadence eMMC/SDHC controller and an SD card connected to it * 5) SiFive Platform DMA (Direct Memory Access Controller) * 6) GEM (Gigabit Ethernet MAC Controller) + * 7) DMC (DDR Memory Controller) + * 8) IOSCB modules * * This board currently generates devicetree dynamically that indicates at least * two harts and up to five harts. @@ -66,11 +68,30 @@ /* GEM version */ #define GEM_REVISION 0x0107010c +/* + * The complete description of the whole PolarFire SoC memory map is scattered + * in different documents. There are several places to look at for memory maps: + * + * 1 Chapter 11 "MSS Memory Map", in the doc "UG0880: PolarFire SoC FPGA + * Microprocessor Subsystem (MSS) User Guide", which can be downloaded from + * https://www.microsemi.com/document-portal/doc_download/ + * 1244570-ug0880-polarfire-soc-fpga-microprocessor-subsystem-mss-user-guide, + * describes the whole picture of the PolarFire SoC memory map. + * + * 2 A zip file for PolarFire soC memory map, which can be downloaded from + * https://www.microsemi.com/document-portal/doc_download/ + * 1244581-polarfire-soc-register-map, contains the following 2 major parts: + * - Register Map/PF_SoC_RegMap_V1_1/pfsoc_regmap.htm + * describes the complete integrated peripherals memory map + * - Register Map/PF_SoC_RegMap_V1_1/MPFS250T/mpfs250t_ioscb_memmap_dri.htm + * describes the complete IOSCB modules memory maps + */ static const struct MemmapEntry { hwaddr base; hwaddr size; } microchip_pfsoc_memmap[] = { - [MICROCHIP_PFSOC_DEBUG] = { 0x0, 0x1000 }, + [MICROCHIP_PFSOC_RSVD0] = { 0x0, 0x100 }, + [MICROCHIP_PFSOC_DEBUG] = { 0x100, 0xf00 }, [MICROCHIP_PFSOC_E51_DTIM] = { 0x1000000, 0x2000 }, [MICROCHIP_PFSOC_BUSERR_UNIT0] = { 0x1700000, 0x1000 }, [MICROCHIP_PFSOC_BUSERR_UNIT1] = { 0x1701000, 0x1000 }, @@ -85,11 +106,14 @@ static const struct MemmapEntry { [MICROCHIP_PFSOC_MMUART0] = { 0x20000000, 0x1000 }, [MICROCHIP_PFSOC_SYSREG] = { 0x20002000, 0x2000 }, [MICROCHIP_PFSOC_MPUCFG] = { 0x20005000, 0x1000 }, + [MICROCHIP_PFSOC_DDR_SGMII_PHY] = { 0x20007000, 0x1000 }, [MICROCHIP_PFSOC_EMMC_SD] = { 0x20008000, 0x1000 }, + [MICROCHIP_PFSOC_DDR_CFG] = { 0x20080000, 0x40000 }, [MICROCHIP_PFSOC_MMUART1] = { 0x20100000, 0x1000 }, [MICROCHIP_PFSOC_MMUART2] = { 0x20102000, 0x1000 }, [MICROCHIP_PFSOC_MMUART3] = { 0x20104000, 0x1000 }, [MICROCHIP_PFSOC_MMUART4] = { 0x20106000, 0x1000 }, + [MICROCHIP_PFSOC_I2C1] = { 0x2010b000, 0x1000 }, [MICROCHIP_PFSOC_GEM0] = { 0x20110000, 0x2000 }, [MICROCHIP_PFSOC_GEM1] = { 0x20112000, 0x2000 }, [MICROCHIP_PFSOC_GPIO0] = { 0x20120000, 0x1000 }, @@ -97,8 +121,11 @@ static const struct MemmapEntry { [MICROCHIP_PFSOC_GPIO2] = { 0x20122000, 0x1000 }, [MICROCHIP_PFSOC_ENVM_CFG] = { 0x20200000, 0x1000 }, [MICROCHIP_PFSOC_ENVM_DATA] = { 0x20220000, 0x20000 }, - [MICROCHIP_PFSOC_IOSCB_CFG] = { 0x37080000, 0x1000 }, - [MICROCHIP_PFSOC_DRAM] = { 0x80000000, 0x0 }, + [MICROCHIP_PFSOC_IOSCB] = { 0x30000000, 0x10000000 }, + [MICROCHIP_PFSOC_DRAM_LO] = { 0x80000000, 0x40000000 }, + [MICROCHIP_PFSOC_DRAM_LO_ALIAS] = { 0xc0000000, 0x40000000 }, + [MICROCHIP_PFSOC_DRAM_HI] = { 0x1000000000, 0x0 }, + [MICROCHIP_PFSOC_DRAM_HI_ALIAS] = { 0x1400000000, 0x0 }, }; static void microchip_pfsoc_soc_instance_init(Object *obj) @@ -131,11 +158,21 @@ static void microchip_pfsoc_soc_instance_init(Object *obj) object_initialize_child(obj, "dma-controller", &s->dma, TYPE_SIFIVE_PDMA); + object_initialize_child(obj, "sysreg", &s->sysreg, + TYPE_MCHP_PFSOC_SYSREG); + + object_initialize_child(obj, "ddr-sgmii-phy", &s->ddr_sgmii_phy, + TYPE_MCHP_PFSOC_DDR_SGMII_PHY); + object_initialize_child(obj, "ddr-cfg", &s->ddr_cfg, + TYPE_MCHP_PFSOC_DDR_CFG); + object_initialize_child(obj, "gem0", &s->gem0, TYPE_CADENCE_GEM); object_initialize_child(obj, "gem1", &s->gem1, TYPE_CADENCE_GEM); object_initialize_child(obj, "sd-controller", &s->sdhci, TYPE_CADENCE_SDHCI); + + object_initialize_child(obj, "ioscb", &s->ioscb, TYPE_MCHP_PFSOC_IOSCB); } static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) @@ -144,6 +181,7 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) MicrochipPFSoCState *s = MICROCHIP_PFSOC(dev); const struct MemmapEntry *memmap = microchip_pfsoc_memmap; MemoryRegion *system_memory = get_system_memory(); + MemoryRegion *rsvd0_mem = g_new(MemoryRegion, 1); MemoryRegion *e51_dtim_mem = g_new(MemoryRegion, 1); MemoryRegion *l2lim_mem = g_new(MemoryRegion, 1); MemoryRegion *envm_data = g_new(MemoryRegion, 1); @@ -163,6 +201,13 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) qdev_realize(DEVICE(&s->e_cluster), NULL, &error_abort); qdev_realize(DEVICE(&s->u_cluster), NULL, &error_abort); + /* Reserved Memory at address 0 */ + memory_region_init_ram(rsvd0_mem, NULL, "microchip.pfsoc.rsvd0_mem", + memmap[MICROCHIP_PFSOC_RSVD0].size, &error_fatal); + memory_region_add_subregion(system_memory, + memmap[MICROCHIP_PFSOC_RSVD0].base, + rsvd0_mem); + /* E51 DTIM */ memory_region_init_ram(e51_dtim_mem, NULL, "microchip.pfsoc.e51_dtim_mem", memmap[MICROCHIP_PFSOC_E51_DTIM].size, &error_fatal); @@ -251,15 +296,25 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) } /* SYSREG */ - create_unimplemented_device("microchip.pfsoc.sysreg", - memmap[MICROCHIP_PFSOC_SYSREG].base, - memmap[MICROCHIP_PFSOC_SYSREG].size); + sysbus_realize(SYS_BUS_DEVICE(&s->sysreg), errp); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysreg), 0, + memmap[MICROCHIP_PFSOC_SYSREG].base); /* MPUCFG */ create_unimplemented_device("microchip.pfsoc.mpucfg", memmap[MICROCHIP_PFSOC_MPUCFG].base, memmap[MICROCHIP_PFSOC_MPUCFG].size); + /* DDR SGMII PHY */ + sysbus_realize(SYS_BUS_DEVICE(&s->ddr_sgmii_phy), errp); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ddr_sgmii_phy), 0, + memmap[MICROCHIP_PFSOC_DDR_SGMII_PHY].base); + + /* DDR CFG */ + sysbus_realize(SYS_BUS_DEVICE(&s->ddr_cfg), errp); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ddr_cfg), 0, + memmap[MICROCHIP_PFSOC_DDR_CFG].base); + /* SDHCI */ sysbus_realize(SYS_BUS_DEVICE(&s->sdhci), errp); sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdhci), 0, @@ -289,6 +344,11 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(DEVICE(s->plic), MICROCHIP_PFSOC_MMUART4_IRQ), serial_hd(4)); + /* I2C1 */ + create_unimplemented_device("microchip.pfsoc.i2c1", + memmap[MICROCHIP_PFSOC_I2C1].base, + memmap[MICROCHIP_PFSOC_I2C1].size); + /* GEMs */ nd = &nd_table[0]; @@ -337,10 +397,10 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) memmap[MICROCHIP_PFSOC_ENVM_DATA].base, envm_data); - /* IOSCBCFG */ - create_unimplemented_device("microchip.pfsoc.ioscb.cfg", - memmap[MICROCHIP_PFSOC_IOSCB_CFG].base, - memmap[MICROCHIP_PFSOC_IOSCB_CFG].size); + /* IOSCB */ + sysbus_realize(SYS_BUS_DEVICE(&s->ioscb), errp); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ioscb), 0, + memmap[MICROCHIP_PFSOC_IOSCB].base); } static void microchip_pfsoc_soc_class_init(ObjectClass *oc, void *data) @@ -373,7 +433,11 @@ static void microchip_icicle_kit_machine_init(MachineState *machine) const struct MemmapEntry *memmap = microchip_pfsoc_memmap; MicrochipIcicleKitState *s = MICROCHIP_ICICLE_KIT_MACHINE(machine); MemoryRegion *system_memory = get_system_memory(); - MemoryRegion *main_mem = g_new(MemoryRegion, 1); + MemoryRegion *mem_low = g_new(MemoryRegion, 1); + MemoryRegion *mem_low_alias = g_new(MemoryRegion, 1); + MemoryRegion *mem_high = g_new(MemoryRegion, 1); + MemoryRegion *mem_high_alias = g_new(MemoryRegion, 1); + uint64_t mem_high_size; DriveInfo *dinfo = drive_get_next(IF_SD); /* Sanity check on RAM size */ @@ -390,10 +454,33 @@ static void microchip_icicle_kit_machine_init(MachineState *machine) qdev_realize(DEVICE(&s->soc), NULL, &error_abort); /* Register RAM */ - memory_region_init_ram(main_mem, NULL, "microchip.icicle.kit.ram", - machine->ram_size, &error_fatal); + memory_region_init_ram(mem_low, NULL, "microchip.icicle.kit.ram_low", + memmap[MICROCHIP_PFSOC_DRAM_LO].size, + &error_fatal); + memory_region_init_alias(mem_low_alias, NULL, + "microchip.icicle.kit.ram_low.alias", + mem_low, 0, + memmap[MICROCHIP_PFSOC_DRAM_LO_ALIAS].size); + memory_region_add_subregion(system_memory, + memmap[MICROCHIP_PFSOC_DRAM_LO].base, + mem_low); + memory_region_add_subregion(system_memory, + memmap[MICROCHIP_PFSOC_DRAM_LO_ALIAS].base, + mem_low_alias); + + mem_high_size = machine->ram_size - 1 * GiB; + + memory_region_init_ram(mem_high, NULL, "microchip.icicle.kit.ram_high", + mem_high_size, &error_fatal); + memory_region_init_alias(mem_high_alias, NULL, + "microchip.icicle.kit.ram_high.alias", + mem_high, 0, mem_high_size); + memory_region_add_subregion(system_memory, + memmap[MICROCHIP_PFSOC_DRAM_HI].base, + mem_high); memory_region_add_subregion(system_memory, - memmap[MICROCHIP_PFSOC_DRAM].base, main_mem); + memmap[MICROCHIP_PFSOC_DRAM_HI_ALIAS].base, + mem_high_alias); /* Load the firmware */ riscv_find_and_load_firmware(machine, BIOS_FILENAME, RESET_VECTOR, NULL); @@ -419,7 +506,15 @@ static void microchip_icicle_kit_machine_class_init(ObjectClass *oc, void *data) MICROCHIP_PFSOC_COMPUTE_CPU_COUNT; mc->min_cpus = MICROCHIP_PFSOC_MANAGEMENT_CPU_COUNT + 1; mc->default_cpus = mc->min_cpus; - mc->default_ram_size = 1 * GiB; + + /* + * Map 513 MiB high memory, the mimimum required high memory size, because + * HSS will do memory test against the high memory address range regardless + * of physical memory installed. + * + * See memory_tests() in mss_ddr.c in the HSS source code. + */ + mc->default_ram_size = 1537 * MiB; } static const TypeInfo microchip_icicle_kit_machine_typeinfo = { diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index b2472c6..2f19a9c 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -100,14 +100,25 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, int cpu; uint32_t *cells; char *nodename; + const char *dtb_filename; char ethclk_names[] = "pclk\0hclk"; uint32_t plic_phandle, prci_phandle, gpio_phandle, phandle = 1; uint32_t hfclk_phandle, rtcclk_phandle, phy_phandle; - fdt = s->fdt = create_device_tree(&s->fdt_size); - if (!fdt) { - error_report("create_device_tree() failed"); - exit(1); + dtb_filename = qemu_opt_get(qemu_get_machine_opts(), "dtb"); + if (dtb_filename) { + fdt = s->fdt = load_device_tree(dtb_filename, &s->fdt_size); + if (!fdt) { + error_report("load_device_tree() failed"); + exit(1); + } + goto update_bootargs; + } else { + 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", "SiFive HiFive Unleashed A00"); @@ -390,13 +401,14 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, qemu_fdt_add_subnode(fdt, "/chosen"); qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", nodename); - if (cmdline) { - qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline); - } - qemu_fdt_setprop_string(fdt, "/aliases", "serial0", nodename); g_free(nodename); + +update_bootargs: + if (cmdline) { + qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline); + } } static void sifive_u_machine_reset(void *opaque, int n, int level) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 6bfd10d..25cea7a 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -181,6 +181,7 @@ static void create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap, { void *fdt; int i, cpu, socket; + const char *dtb_filename; MachineState *mc = MACHINE(s); uint64_t addr, size; uint32_t *clint_cells, *plic_cells; @@ -194,10 +195,20 @@ static void create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap, hwaddr flashsize = virt_memmap[VIRT_FLASH].size / 2; hwaddr flashbase = virt_memmap[VIRT_FLASH].base; - fdt = s->fdt = create_device_tree(&s->fdt_size); - if (!fdt) { - error_report("create_device_tree() failed"); - exit(1); + dtb_filename = qemu_opt_get(qemu_get_machine_opts(), "dtb"); + if (dtb_filename) { + fdt = s->fdt = load_device_tree(dtb_filename, &s->fdt_size); + if (!fdt) { + error_report("load_device_tree() failed"); + exit(1); + } + goto update_bootargs; + } else { + 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"); @@ -418,9 +429,6 @@ static void create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap, qemu_fdt_add_subnode(fdt, "/chosen"); qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", name); - if (cmdline) { - qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline); - } g_free(name); name = g_strdup_printf("/soc/rtc@%lx", (long)memmap[VIRT_RTC].base); @@ -441,6 +449,11 @@ static void create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap, 2, flashbase + flashsize, 2, flashsize); qemu_fdt_setprop_cell(s->fdt, name, "bank-width", 4); g_free(name); + +update_bootargs: + if (cmdline) { + qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline); + } } static inline DeviceState *gpex_pcie_init(MemoryRegion *sys_mem, diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c index b1622b7..19e1933 100644 --- a/hw/usb/dev-serial.c +++ b/hw/usb/dev-serial.c @@ -20,85 +20,77 @@ #include "chardev/char-serial.h" #include "chardev/char-fe.h" #include "qom/object.h" +#include "trace.h" -//#define DEBUG_Serial - -#ifdef DEBUG_Serial -#define DPRINTF(fmt, ...) \ -do { printf("usb-serial: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#endif #define RECV_BUF (512 - (2 * 8)) /* Commands */ -#define FTDI_RESET 0 -#define FTDI_SET_MDM_CTRL 1 -#define FTDI_SET_FLOW_CTRL 2 -#define FTDI_SET_BAUD 3 -#define FTDI_SET_DATA 4 -#define FTDI_GET_MDM_ST 5 -#define FTDI_SET_EVENT_CHR 6 -#define FTDI_SET_ERROR_CHR 7 -#define FTDI_SET_LATENCY 9 -#define FTDI_GET_LATENCY 10 - -#define DeviceOutVendor ((USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8) -#define DeviceInVendor ((USB_DIR_IN |USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8) +#define FTDI_RESET 0 +#define FTDI_SET_MDM_CTRL 1 +#define FTDI_SET_FLOW_CTRL 2 +#define FTDI_SET_BAUD 3 +#define FTDI_SET_DATA 4 +#define FTDI_GET_MDM_ST 5 +#define FTDI_SET_EVENT_CHR 6 +#define FTDI_SET_ERROR_CHR 7 +#define FTDI_SET_LATENCY 9 +#define FTDI_GET_LATENCY 10 /* RESET */ -#define FTDI_RESET_SIO 0 -#define FTDI_RESET_RX 1 -#define FTDI_RESET_TX 2 +#define FTDI_RESET_SIO 0 +#define FTDI_RESET_RX 1 +#define FTDI_RESET_TX 2 /* SET_MDM_CTRL */ -#define FTDI_DTR 1 -#define FTDI_SET_DTR (FTDI_DTR << 8) -#define FTDI_RTS 2 -#define FTDI_SET_RTS (FTDI_RTS << 8) +#define FTDI_DTR 1 +#define FTDI_SET_DTR (FTDI_DTR << 8) +#define FTDI_RTS 2 +#define FTDI_SET_RTS (FTDI_RTS << 8) /* SET_FLOW_CTRL */ -#define FTDI_RTS_CTS_HS 1 -#define FTDI_DTR_DSR_HS 2 -#define FTDI_XON_XOFF_HS 4 +#define FTDI_NO_HS 0 +#define FTDI_RTS_CTS_HS 1 +#define FTDI_DTR_DSR_HS 2 +#define FTDI_XON_XOFF_HS 4 /* SET_DATA */ -#define FTDI_PARITY (0x7 << 8) -#define FTDI_ODD (0x1 << 8) -#define FTDI_EVEN (0x2 << 8) -#define FTDI_MARK (0x3 << 8) -#define FTDI_SPACE (0x4 << 8) +#define FTDI_PARITY (0x7 << 8) +#define FTDI_ODD (0x1 << 8) +#define FTDI_EVEN (0x2 << 8) +#define FTDI_MARK (0x3 << 8) +#define FTDI_SPACE (0x4 << 8) -#define FTDI_STOP (0x3 << 11) -#define FTDI_STOP1 (0x0 << 11) -#define FTDI_STOP15 (0x1 << 11) -#define FTDI_STOP2 (0x2 << 11) +#define FTDI_STOP (0x3 << 11) +#define FTDI_STOP1 (0x0 << 11) +#define FTDI_STOP15 (0x1 << 11) +#define FTDI_STOP2 (0x2 << 11) /* GET_MDM_ST */ /* TODO: should be sent every 40ms */ -#define FTDI_CTS (1<<4) // CTS line status -#define FTDI_DSR (1<<5) // DSR line status -#define FTDI_RI (1<<6) // RI line status -#define FTDI_RLSD (1<<7) // Receive Line Signal Detect +#define FTDI_CTS (1 << 4) /* CTS line status */ +#define FTDI_DSR (1 << 5) /* DSR line status */ +#define FTDI_RI (1 << 6) /* RI line status */ +#define FTDI_RLSD (1 << 7) /* Receive Line Signal Detect */ /* Status */ -#define FTDI_DR (1<<0) // Data Ready -#define FTDI_OE (1<<1) // Overrun Err -#define FTDI_PE (1<<2) // Parity Err -#define FTDI_FE (1<<3) // Framing Err -#define FTDI_BI (1<<4) // Break Interrupt -#define FTDI_THRE (1<<5) // Transmitter Holding Register -#define FTDI_TEMT (1<<6) // Transmitter Empty -#define FTDI_FIFO (1<<7) // Error in FIFO +#define FTDI_DR (1 << 0) /* Data Ready */ +#define FTDI_OE (1 << 1) /* Overrun Err */ +#define FTDI_PE (1 << 2) /* Parity Err */ +#define FTDI_FE (1 << 3) /* Framing Err */ +#define FTDI_BI (1 << 4) /* Break Interrupt */ +#define FTDI_THRE (1 << 5) /* Transmitter Holding Register */ +#define FTDI_TEMT (1 << 6) /* Transmitter Empty */ +#define FTDI_FIFO (1 << 7) /* Error in FIFO */ struct USBSerialState { USBDevice dev; + USBEndpoint *intr; uint8_t recv_buf[RECV_BUF]; uint16_t recv_ptr; @@ -106,6 +98,10 @@ struct USBSerialState { uint8_t event_chr; uint8_t error_chr; uint8_t event_trigger; + bool always_plugged; + uint8_t flow_control; + uint8_t xon; + uint8_t xoff; QEMUSerialSetParams params; int latency; /* ms */ CharBackend cs; @@ -189,21 +185,44 @@ static const USBDesc desc_braille = { .str = desc_strings, }; +static void usb_serial_set_flow_control(USBSerialState *s, + uint8_t flow_control) +{ + USBDevice *dev = USB_DEVICE(s); + USBBus *bus = usb_bus_from_device(dev); + + /* TODO: ioctl */ + s->flow_control = flow_control; + trace_usb_serial_set_flow_control(bus->busnr, dev->addr, flow_control); +} + +static void usb_serial_set_xonxoff(USBSerialState *s, int xonxoff) +{ + USBDevice *dev = USB_DEVICE(s); + USBBus *bus = usb_bus_from_device(dev); + + s->xon = xonxoff & 0xff; + s->xoff = (xonxoff >> 8) & 0xff; + + trace_usb_serial_set_xonxoff(bus->busnr, dev->addr, s->xon, s->xoff); +} + static void usb_serial_reset(USBSerialState *s) { - /* TODO: Set flow control to none */ s->event_chr = 0x0d; s->event_trigger = 0; s->recv_ptr = 0; s->recv_used = 0; /* TODO: purge in char driver */ + usb_serial_set_flow_control(s, FTDI_NO_HS); } static void usb_serial_handle_reset(USBDevice *dev) { - USBSerialState *s = (USBSerialState *)dev; + USBSerialState *s = USB_SERIAL(dev); + USBBus *bus = usb_bus_from_device(dev); - DPRINTF("Reset\n"); + trace_usb_serial_reset(bus->busnr, dev->addr); usb_serial_reset(s); /* TODO: Reset char device, send BREAK? */ @@ -216,29 +235,36 @@ static uint8_t usb_get_modem_lines(USBSerialState *s) if (qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_GET_TIOCM, &flags) == -ENOTSUP) { - return FTDI_CTS|FTDI_DSR|FTDI_RLSD; + return FTDI_CTS | FTDI_DSR | FTDI_RLSD; } ret = 0; - if (flags & CHR_TIOCM_CTS) + if (flags & CHR_TIOCM_CTS) { ret |= FTDI_CTS; - if (flags & CHR_TIOCM_DSR) + } + if (flags & CHR_TIOCM_DSR) { ret |= FTDI_DSR; - if (flags & CHR_TIOCM_RI) + } + if (flags & CHR_TIOCM_RI) { ret |= FTDI_RI; - if (flags & CHR_TIOCM_CAR) + } + if (flags & CHR_TIOCM_CAR) { ret |= FTDI_RLSD; + } return ret; } static void usb_serial_handle_control(USBDevice *dev, USBPacket *p, - int request, int value, int index, int length, uint8_t *data) + int request, int value, int index, + int length, uint8_t *data) { - USBSerialState *s = (USBSerialState *)dev; + USBSerialState *s = USB_SERIAL(dev); + USBBus *bus = usb_bus_from_device(dev); int ret; - DPRINTF("got control %x, value %x\n",request, value); + trace_usb_serial_handle_control(bus->busnr, dev->addr, request, value); + ret = usb_desc_handle_control(dev, p, request, value, index, length, data); if (ret >= 0) { return; @@ -248,8 +274,8 @@ static void usb_serial_handle_control(USBDevice *dev, USBPacket *p, case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: break; - /* Class specific requests. */ - case DeviceOutVendor | FTDI_RESET: + /* Class specific requests. */ + case VendorDeviceOutRequest | FTDI_RESET: switch (value) { case FTDI_RESET_SIO: usb_serial_reset(s); @@ -264,96 +290,131 @@ static void usb_serial_handle_control(USBDevice *dev, USBPacket *p, break; } break; - case DeviceOutVendor | FTDI_SET_MDM_CTRL: + case VendorDeviceOutRequest | FTDI_SET_MDM_CTRL: { static int flags; qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_GET_TIOCM, &flags); if (value & FTDI_SET_RTS) { - if (value & FTDI_RTS) + if (value & FTDI_RTS) { flags |= CHR_TIOCM_RTS; - else + } else { flags &= ~CHR_TIOCM_RTS; + } } if (value & FTDI_SET_DTR) { - if (value & FTDI_DTR) + if (value & FTDI_DTR) { flags |= CHR_TIOCM_DTR; - else + } else { flags &= ~CHR_TIOCM_DTR; + } } qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_SET_TIOCM, &flags); break; } - case DeviceOutVendor | FTDI_SET_FLOW_CTRL: - /* TODO: ioctl */ + case VendorDeviceOutRequest | FTDI_SET_FLOW_CTRL: { + uint8_t flow_control = index >> 8; + + usb_serial_set_flow_control(s, flow_control); + if (flow_control & FTDI_XON_XOFF_HS) { + usb_serial_set_xonxoff(s, value); + } break; - case DeviceOutVendor | FTDI_SET_BAUD: { + } + case VendorDeviceOutRequest | FTDI_SET_BAUD: { static const int subdivisors8[8] = { 0, 4, 2, 1, 3, 5, 6, 7 }; int subdivisor8 = subdivisors8[((value & 0xc000) >> 14) | ((index & 1) << 2)]; int divisor = value & 0x3fff; /* chip special cases */ - if (divisor == 1 && subdivisor8 == 0) + if (divisor == 1 && subdivisor8 == 0) { subdivisor8 = 4; - if (divisor == 0 && subdivisor8 == 0) + } + if (divisor == 0 && subdivisor8 == 0) { divisor = 1; + } s->params.speed = (48000000 / 2) / (8 * divisor + subdivisor8); + trace_usb_serial_set_baud(bus->busnr, dev->addr, s->params.speed); qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params); break; } - case DeviceOutVendor | FTDI_SET_DATA: + case VendorDeviceOutRequest | FTDI_SET_DATA: + switch (value & 0xff) { + case 7: + s->params.data_bits = 7; + break; + case 8: + s->params.data_bits = 8; + break; + default: + /* + * According to a comment in Linux's ftdi_sio.c original FTDI + * chips fall back to 8 data bits for unsupported data_bits + */ + trace_usb_serial_unsupported_data_bits(bus->busnr, dev->addr, + value & 0xff); + s->params.data_bits = 8; + } + switch (value & FTDI_PARITY) { - case 0: - s->params.parity = 'N'; - break; - case FTDI_ODD: - s->params.parity = 'O'; - break; - case FTDI_EVEN: - s->params.parity = 'E'; - break; - default: - DPRINTF("unsupported parity %d\n", value & FTDI_PARITY); - goto fail; + case 0: + s->params.parity = 'N'; + break; + case FTDI_ODD: + s->params.parity = 'O'; + break; + case FTDI_EVEN: + s->params.parity = 'E'; + break; + default: + trace_usb_serial_unsupported_parity(bus->busnr, dev->addr, + value & FTDI_PARITY); + goto fail; } + switch (value & FTDI_STOP) { - case FTDI_STOP1: - s->params.stop_bits = 1; - break; - case FTDI_STOP2: - s->params.stop_bits = 2; - break; - default: - DPRINTF("unsupported stop bits %d\n", value & FTDI_STOP); - goto fail; + case FTDI_STOP1: + s->params.stop_bits = 1; + break; + case FTDI_STOP2: + s->params.stop_bits = 2; + break; + default: + trace_usb_serial_unsupported_stopbits(bus->busnr, dev->addr, + value & FTDI_STOP); + goto fail; } + + trace_usb_serial_set_data(bus->busnr, dev->addr, s->params.parity, + s->params.data_bits, s->params.stop_bits); qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params); /* TODO: TX ON/OFF */ break; - case DeviceInVendor | FTDI_GET_MDM_ST: + case VendorDeviceRequest | FTDI_GET_MDM_ST: data[0] = usb_get_modem_lines(s) | 1; data[1] = FTDI_THRE | FTDI_TEMT; p->actual_length = 2; break; - case DeviceOutVendor | FTDI_SET_EVENT_CHR: + case VendorDeviceOutRequest | FTDI_SET_EVENT_CHR: /* TODO: handle it */ s->event_chr = value; break; - case DeviceOutVendor | FTDI_SET_ERROR_CHR: + case VendorDeviceOutRequest | FTDI_SET_ERROR_CHR: /* TODO: handle it */ s->error_chr = value; break; - case DeviceOutVendor | FTDI_SET_LATENCY: + case VendorDeviceOutRequest | FTDI_SET_LATENCY: s->latency = value; break; - case DeviceInVendor | FTDI_GET_LATENCY: + case VendorDeviceRequest | FTDI_GET_LATENCY: data[0] = s->latency; p->actual_length = 1; break; default: fail: - DPRINTF("got unsupported/bogus control %x, value %x\n", request, value); + trace_usb_serial_unsupported_control(bus->busnr, dev->addr, request, + value); p->status = USB_RET_STALL; break; } @@ -416,32 +477,37 @@ static void usb_serial_token_in(USBSerialState *s, USBPacket *p) static void usb_serial_handle_data(USBDevice *dev, USBPacket *p) { - USBSerialState *s = (USBSerialState *)dev; + USBSerialState *s = USB_SERIAL(dev); + USBBus *bus = usb_bus_from_device(dev); uint8_t devep = p->ep->nr; struct iovec *iov; int i; switch (p->pid) { case USB_TOKEN_OUT: - if (devep != 2) + if (devep != 2) { goto fail; + } for (i = 0; i < p->iov.niov; i++) { iov = p->iov.iov + i; - /* XXX this blocks entire thread. Rewrite to use - * qemu_chr_fe_write and background I/O callbacks */ + /* + * XXX this blocks entire thread. Rewrite to use + * qemu_chr_fe_write and background I/O callbacks + */ qemu_chr_fe_write_all(&s->cs, iov->iov_base, iov->iov_len); } p->actual_length = p->iov.size; break; case USB_TOKEN_IN: - if (devep != 1) + if (devep != 1) { goto fail; + } usb_serial_token_in(s, p); break; default: - DPRINTF("Bad token\n"); + trace_usb_serial_bad_token(bus->busnr, dev->addr); fail: p->status = USB_RET_STALL; break; @@ -464,21 +530,24 @@ static void usb_serial_read(void *opaque, const uint8_t *buf, int size) int first_size, start; /* room in the buffer? */ - if (size > (RECV_BUF - s->recv_used)) + if (size > (RECV_BUF - s->recv_used)) { size = RECV_BUF - s->recv_used; + } start = s->recv_ptr + s->recv_used; if (start < RECV_BUF) { /* copy data to end of buffer */ first_size = RECV_BUF - start; - if (first_size > size) + if (first_size > size) { first_size = size; + } memcpy(s->recv_buf + start, buf, first_size); /* wrap around to front if needed */ - if (size > first_size) + if (size > first_size) { memcpy(s->recv_buf, buf + first_size, size - first_size); + } } else { start -= RECV_BUF; memcpy(s->recv_buf + start, buf, size); @@ -493,23 +562,23 @@ static void usb_serial_event(void *opaque, QEMUChrEvent event) USBSerialState *s = opaque; switch (event) { - case CHR_EVENT_BREAK: - s->event_trigger |= FTDI_BI; - break; - case CHR_EVENT_OPENED: - if (!s->dev.attached) { - usb_device_attach(&s->dev, &error_abort); - } - break; - case CHR_EVENT_CLOSED: - if (s->dev.attached) { - usb_device_detach(&s->dev); - } - break; - case CHR_EVENT_MUX_IN: - case CHR_EVENT_MUX_OUT: - /* Ignore */ - break; + case CHR_EVENT_BREAK: + s->event_trigger |= FTDI_BI; + break; + case CHR_EVENT_OPENED: + if (!s->always_plugged && !s->dev.attached) { + usb_device_attach(&s->dev, &error_abort); + } + break; + case CHR_EVENT_CLOSED: + if (!s->always_plugged && s->dev.attached) { + usb_device_detach(&s->dev); + } + break; + case CHR_EVENT_MUX_IN: + case CHR_EVENT_MUX_OUT: + /* Ignore */ + break; } } @@ -537,7 +606,8 @@ static void usb_serial_realize(USBDevice *dev, Error **errp) usb_serial_event, NULL, s, NULL, true); usb_serial_handle_reset(dev); - if (qemu_chr_fe_backend_open(&s->cs) && !dev->attached) { + if ((s->always_plugged || qemu_chr_fe_backend_open(&s->cs)) && + !dev->attached) { usb_device_attach(dev, &error_abort); } s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1); @@ -549,8 +619,9 @@ static USBDevice *usb_braille_init(const char *unused) Chardev *cdrv; cdrv = qemu_chr_new("braille", "braille", NULL); - if (!cdrv) + if (!cdrv) { return NULL; + } dev = usb_new("usb-braille"); qdev_prop_set_chr(&dev->qdev, "chardev", cdrv); @@ -564,6 +635,7 @@ static const VMStateDescription vmstate_usb_serial = { static Property serial_properties[] = { DEFINE_PROP_CHR("chardev", USBSerialState, cs), + DEFINE_PROP_BOOL("always-plugged", USBSerialState, always_plugged, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/usb/trace-events b/hw/usb/trace-events index 72e4298..a3292d4 100644 --- a/hw/usb/trace-events +++ b/hw/usb/trace-events @@ -320,3 +320,16 @@ usb_host_parse_interface(int bus, int addr, int num, int alt, int active) "dev % usb_host_parse_endpoint(int bus, int addr, int ep, const char *dir, const char *type, int active) "dev %d:%d, ep %d, %s, %s, active %d" usb_host_parse_error(int bus, int addr, const char *errmsg) "dev %d:%d, msg %s" usb_host_remote_wakeup_removed(int bus, int addr) "dev %d:%d" + +# dev-serial.c +usb_serial_reset(int bus, int addr) "dev %d:%u reset" +usb_serial_handle_control(int bus, int addr, int request, int value) "dev %d:%u got control 0x%x, value 0x%x" +usb_serial_unsupported_parity(int bus, int addr, int value) "dev %d:%u unsupported parity %d" +usb_serial_unsupported_stopbits(int bus, int addr, int value) "dev %d:%u unsupported stop bits %d" +usb_serial_unsupported_control(int bus, int addr, int request, int value) "dev %d:%u got unsupported/bogus control 0x%x, value 0x%x" +usb_serial_unsupported_data_bits(int bus, int addr, int value) "dev %d:%u unsupported data bits %d, falling back to 8" +usb_serial_bad_token(int bus, int addr) "dev %d:%u bad token" +usb_serial_set_baud(int bus, int addr, int baud) "dev %d:%u baud rate %d" +usb_serial_set_data(int bus, int addr, int parity, int data, int stop) "dev %d:%u parity %c, data bits %d, stop bits %d" +usb_serial_set_flow_control(int bus, int addr, int index) "dev %d:%u flow control %d" +usb_serial_set_xonxoff(int bus, int addr, uint8_t xon, uint8_t xoff) "dev %d:%u xon 0x%x xoff 0x%x" diff --git a/include/hw/intc/sifive_plic.h b/include/hw/intc/sifive_plic.h index b75b1f1..1e451a2 100644 --- a/include/hw/intc/sifive_plic.h +++ b/include/hw/intc/sifive_plic.h @@ -52,6 +52,7 @@ struct SiFivePLICState { uint32_t num_addrs; uint32_t num_harts; uint32_t bitfield_words; + uint32_t num_enables; PLICAddr *addr_config; uint32_t *source_priority; uint32_t *target_priority; diff --git a/include/hw/mips/cps.h b/include/hw/mips/cps.h index 859a8d4..04d6362 100644 --- a/include/hw/mips/cps.h +++ b/include/hw/mips/cps.h @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/include/hw/misc/mchp_pfsoc_dmc.h b/include/hw/misc/mchp_pfsoc_dmc.h new file mode 100644 index 0000000..2baa141 --- /dev/null +++ b/include/hw/misc/mchp_pfsoc_dmc.h @@ -0,0 +1,56 @@ +/* + * Microchip PolarFire SoC DDR Memory Controller module emulation + * + * Copyright (c) 2020 Wind River Systems, Inc. + * + * Author: + * Bin Meng <bin.meng@windriver.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that 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/>. + */ + +#ifndef MCHP_PFSOC_DMC_H +#define MCHP_PFSOC_DMC_H + +/* DDR SGMII PHY module */ + +#define MCHP_PFSOC_DDR_SGMII_PHY_REG_SIZE 0x1000 + +typedef struct MchpPfSoCDdrSgmiiPhyState { + SysBusDevice parent; + MemoryRegion sgmii_phy; +} MchpPfSoCDdrSgmiiPhyState; + +#define TYPE_MCHP_PFSOC_DDR_SGMII_PHY "mchp.pfsoc.ddr_sgmii_phy" + +#define MCHP_PFSOC_DDR_SGMII_PHY(obj) \ + OBJECT_CHECK(MchpPfSoCDdrSgmiiPhyState, (obj), \ + TYPE_MCHP_PFSOC_DDR_SGMII_PHY) + +/* DDR CFG module */ + +#define MCHP_PFSOC_DDR_CFG_REG_SIZE 0x40000 + +typedef struct MchpPfSoCDdrCfgState { + SysBusDevice parent; + MemoryRegion cfg; +} MchpPfSoCDdrCfgState; + +#define TYPE_MCHP_PFSOC_DDR_CFG "mchp.pfsoc.ddr_cfg" + +#define MCHP_PFSOC_DDR_CFG(obj) \ + OBJECT_CHECK(MchpPfSoCDdrCfgState, (obj), \ + TYPE_MCHP_PFSOC_DDR_CFG) + +#endif /* MCHP_PFSOC_DMC_H */ diff --git a/include/hw/misc/mchp_pfsoc_ioscb.h b/include/hw/misc/mchp_pfsoc_ioscb.h new file mode 100644 index 0000000..9235523 --- /dev/null +++ b/include/hw/misc/mchp_pfsoc_ioscb.h @@ -0,0 +1,50 @@ +/* + * Microchip PolarFire SoC IOSCB module emulation + * + * Copyright (c) 2020 Wind River Systems, Inc. + * + * Author: + * Bin Meng <bin.meng@windriver.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that 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/>. + */ + +#ifndef MCHP_PFSOC_IOSCB_H +#define MCHP_PFSOC_IOSCB_H + +typedef struct MchpPfSoCIoscbState { + SysBusDevice parent; + MemoryRegion container; + MemoryRegion lane01; + MemoryRegion lane23; + MemoryRegion ctrl; + MemoryRegion cfg; + MemoryRegion pll_mss; + MemoryRegion cfm_mss; + MemoryRegion pll_ddr; + MemoryRegion bc_ddr; + MemoryRegion io_calib_ddr; + MemoryRegion pll_sgmii; + MemoryRegion dll_sgmii; + MemoryRegion cfm_sgmii; + MemoryRegion bc_sgmii; + MemoryRegion io_calib_sgmii; +} MchpPfSoCIoscbState; + +#define TYPE_MCHP_PFSOC_IOSCB "mchp.pfsoc.ioscb" + +#define MCHP_PFSOC_IOSCB(obj) \ + OBJECT_CHECK(MchpPfSoCIoscbState, (obj), TYPE_MCHP_PFSOC_IOSCB) + +#endif /* MCHP_PFSOC_IOSCB_H */ diff --git a/include/hw/misc/mchp_pfsoc_sysreg.h b/include/hw/misc/mchp_pfsoc_sysreg.h new file mode 100644 index 0000000..546ba68 --- /dev/null +++ b/include/hw/misc/mchp_pfsoc_sysreg.h @@ -0,0 +1,39 @@ +/* + * Microchip PolarFire SoC SYSREG module emulation + * + * Copyright (c) 2020 Wind River Systems, Inc. + * + * Author: + * Bin Meng <bin.meng@windriver.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that 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/>. + */ + +#ifndef MCHP_PFSOC_SYSREG_H +#define MCHP_PFSOC_SYSREG_H + +#define MCHP_PFSOC_SYSREG_REG_SIZE 0x2000 + +typedef struct MchpPfSoCSysregState { + SysBusDevice parent; + MemoryRegion sysreg; +} MchpPfSoCSysregState; + +#define TYPE_MCHP_PFSOC_SYSREG "mchp.pfsoc.sysreg" + +#define MCHP_PFSOC_SYSREG(obj) \ + OBJECT_CHECK(MchpPfSoCSysregState, (obj), \ + TYPE_MCHP_PFSOC_SYSREG) + +#endif /* MCHP_PFSOC_SYSREG_H */ diff --git a/include/hw/misc/mips_cpc.h b/include/hw/misc/mips_cpc.h index e5dccea..fcafbd5 100644 --- a/include/hw/misc/mips_cpc.h +++ b/include/hw/misc/mips_cpc.h @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/include/hw/misc/mips_itu.h b/include/hw/misc/mips_itu.h index 96347db..50d9611 100644 --- a/include/hw/misc/mips_itu.h +++ b/include/hw/misc/mips_itu.h @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/include/hw/pci-host/xilinx-pcie.h b/include/hw/pci-host/xilinx-pcie.h index f079e50..89be88d 100644 --- a/include/hw/pci-host/xilinx-pcie.h +++ b/include/hw/pci-host/xilinx-pcie.h @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/include/hw/riscv/microchip_pfsoc.h b/include/hw/riscv/microchip_pfsoc.h index 8bfc7e1..51d4463 100644 --- a/include/hw/riscv/microchip_pfsoc.h +++ b/include/hw/riscv/microchip_pfsoc.h @@ -24,6 +24,9 @@ #include "hw/char/mchp_pfsoc_mmuart.h" #include "hw/dma/sifive_pdma.h" +#include "hw/misc/mchp_pfsoc_dmc.h" +#include "hw/misc/mchp_pfsoc_ioscb.h" +#include "hw/misc/mchp_pfsoc_sysreg.h" #include "hw/net/cadence_gem.h" #include "hw/sd/cadence_sdhci.h" @@ -37,11 +40,15 @@ typedef struct MicrochipPFSoCState { RISCVHartArrayState e_cpus; RISCVHartArrayState u_cpus; DeviceState *plic; + MchpPfSoCDdrSgmiiPhyState ddr_sgmii_phy; + MchpPfSoCDdrCfgState ddr_cfg; + MchpPfSoCIoscbState ioscb; MchpPfSoCMMUartState *serial0; MchpPfSoCMMUartState *serial1; MchpPfSoCMMUartState *serial2; MchpPfSoCMMUartState *serial3; MchpPfSoCMMUartState *serial4; + MchpPfSoCSysregState sysreg; SiFivePDMAState dma; CadenceGEMState gem0; CadenceGEMState gem1; @@ -67,6 +74,7 @@ typedef struct MicrochipIcicleKitState { TYPE_MICROCHIP_ICICLE_KIT_MACHINE) enum { + MICROCHIP_PFSOC_RSVD0, MICROCHIP_PFSOC_DEBUG, MICROCHIP_PFSOC_E51_DTIM, MICROCHIP_PFSOC_BUSERR_UNIT0, @@ -82,11 +90,14 @@ enum { MICROCHIP_PFSOC_MMUART0, MICROCHIP_PFSOC_SYSREG, MICROCHIP_PFSOC_MPUCFG, + MICROCHIP_PFSOC_DDR_SGMII_PHY, MICROCHIP_PFSOC_EMMC_SD, + MICROCHIP_PFSOC_DDR_CFG, MICROCHIP_PFSOC_MMUART1, MICROCHIP_PFSOC_MMUART2, MICROCHIP_PFSOC_MMUART3, MICROCHIP_PFSOC_MMUART4, + MICROCHIP_PFSOC_I2C1, MICROCHIP_PFSOC_GEM0, MICROCHIP_PFSOC_GEM1, MICROCHIP_PFSOC_GPIO0, @@ -94,8 +105,11 @@ enum { MICROCHIP_PFSOC_GPIO2, MICROCHIP_PFSOC_ENVM_CFG, MICROCHIP_PFSOC_ENVM_DATA, - MICROCHIP_PFSOC_IOSCB_CFG, - MICROCHIP_PFSOC_DRAM, + MICROCHIP_PFSOC_IOSCB, + MICROCHIP_PFSOC_DRAM_LO, + MICROCHIP_PFSOC_DRAM_LO_ALIAS, + MICROCHIP_PFSOC_DRAM_HI, + MICROCHIP_PFSOC_DRAM_HI_ALIAS }; enum { diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index 56e9bad..a6a6684 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -1762,7 +1762,8 @@ err_out: goto out; } -void hmp_screendump(Monitor *mon, const QDict *qdict) +void coroutine_fn +hmp_screendump(Monitor *mon, const QDict *qdict) { const char *filename = qdict_get_str(qdict, "filename"); const char *id = qdict_get_try_str(qdict, "device"); diff --git a/qapi/sockets.json b/qapi/sockets.json index c0c640a..2e83452 100644 --- a/qapi/sockets.json +++ b/qapi/sockets.json @@ -74,18 +74,20 @@ # Captures a socket address in the local ("Unix socket") namespace. # # @path: filesystem path to use -# @tight: pass a socket address length confined to the minimum length of the -# abstract string, rather than the full sockaddr_un record length -# (only matters for abstract sockets, default true). (Since 5.1) -# @abstract: whether this is an abstract address, default false. (Since 5.1) +# @abstract: if true, this is a Linux abstract socket address. @path +# will be prefixed by a null byte, and optionally padded +# with null bytes. Defaults to false. (Since 5.1) +# @tight: if false, pad an abstract socket address with enough null +# bytes to make it fill struct sockaddr_un member sun_path. +# Defaults to true. (Since 5.1) # # Since: 1.3 ## { 'struct': 'UnixSocketAddress', 'data': { 'path': 'str', - '*tight': 'bool', - '*abstract': 'bool' } } + '*abstract': { 'type': 'bool', 'if': 'defined(CONFIG_LINUX)' }, + '*tight': { 'type': 'bool', 'if': 'defined(CONFIG_LINUX)' } } } ## # @VsockSocketAddress: diff --git a/qapi/ui.json b/qapi/ui.json index 9d67210..6c7b33c 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -98,7 +98,8 @@ # ## { 'command': 'screendump', - 'data': {'filename': 'str', '*device': 'str', '*head': 'int'} } + 'data': {'filename': 'str', '*device': 'str', '*head': 'int'}, + 'coroutine': true } ## # == Spice @@ -2751,7 +2751,6 @@ out: qemu_progress_end(); qemu_opts_del(opts); qemu_opts_free(create_opts); - qemu_opts_del(sn_opts); qobject_unref(open_opts); blk_unref(s.target); if (s.src) { @@ -2763,6 +2762,7 @@ out: g_free(s.src_sectors); g_free(s.src_alignment); fail_getopt: + qemu_opts_del(sn_opts); g_free(options); return !!ret; diff --git a/roms/Makefile b/roms/Makefile index 1489d47..7045e37 100644 --- a/roms/Makefile +++ b/roms/Makefile @@ -102,7 +102,7 @@ build-seabios-config-%: config.% OUT=$(CURDIR)/seabios/builds/$*/ all -.PHONY: sgabios skiboot +.PHONY: sgabios skiboot qboot sgabios: $(MAKE) -C sgabios cp sgabios/sgabios.bin ../pc-bios diff --git a/target/mips/cp0_helper.c b/target/mips/cp0_helper.c index 12143ac..709cc9a 100644 --- a/target/mips/cp0_helper.c +++ b/target/mips/cp0_helper.c @@ -8,7 +8,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/target/mips/dsp_helper.c b/target/mips/dsp_helper.c index 8c58eeb..09b6e5f 100644 --- a/target/mips/dsp_helper.c +++ b/target/mips/dsp_helper.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/target/mips/fpu_helper.c b/target/mips/fpu_helper.c index 6cc956c..020b768 100644 --- a/target/mips/fpu_helper.c +++ b/target/mips/fpu_helper.c @@ -8,7 +8,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/target/mips/gdbstub.c b/target/mips/gdbstub.c index 98f56e6..e39f8d7 100644 --- a/target/mips/gdbstub.c +++ b/target/mips/gdbstub.c @@ -7,7 +7,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/target/mips/helper.c b/target/mips/helper.c index afd78b1..063b65c 100644 --- a/target/mips/helper.c +++ b/target/mips/helper.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/target/mips/lmmi_helper.c b/target/mips/lmmi_helper.c index 6c645cf..abeb773 100644 --- a/target/mips/lmmi_helper.c +++ b/target/mips/lmmi_helper.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/target/mips/mips-semi.c b/target/mips/mips-semi.c index 10a710c..898251a 100644 --- a/target/mips/mips-semi.c +++ b/target/mips/mips-semi.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/target/mips/msa_helper.c b/target/mips/msa_helper.c index 6865add..249f0fd 100644 --- a/target/mips/msa_helper.c +++ b/target/mips/msa_helper.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/target/mips/op_helper.c b/target/mips/op_helper.c index 0050d06..5184a18 100644 --- a/target/mips/op_helper.c +++ b/target/mips/op_helper.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/target/mips/translate.c b/target/mips/translate.c index f449758..c64a1bc 100644 --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -10,7 +10,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -31442,8 +31442,8 @@ static void mips_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) #else ctx->mem_idx = hflags_mmu_index(ctx->hflags); #endif - ctx->default_tcg_memop_mask = (ctx->insn_flags & ISA_MIPS32R6) ? - MO_UNALN : MO_ALIGN; + ctx->default_tcg_memop_mask = (ctx->insn_flags & (ISA_MIPS32R6 | ISA_MIPS64R6 | + INSN_LOONGSON3A)) ? MO_UNALN : MO_ALIGN; LOG_DISAS("\ntb %p idx %d hflags %04x\n", ctx->base.tb, ctx->mem_idx, ctx->hflags); diff --git a/target/mips/translate_init.c.inc b/target/mips/translate_init.c.inc index fb5a9b3..ea85d5c 100644 --- a/target/mips/translate_init.c.inc +++ b/target/mips/translate_init.c.inc @@ -7,7 +7,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 0bbfd7f..6a0264f 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -22,6 +22,7 @@ #include "qemu/ctype.h" #include "qemu/log.h" #include "cpu.h" +#include "internals.h" #include "exec/exec-all.h" #include "qapi/error.h" #include "qemu/error-report.h" @@ -216,13 +217,15 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "pc ", env->pc); #ifndef CONFIG_USER_ONLY qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mhartid ", env->mhartid); - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mstatus ", env->mstatus); + qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mstatus ", (target_ulong)env->mstatus); #ifdef TARGET_RISCV32 - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mstatush ", env->mstatush); + qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mstatush ", + (target_ulong)(env->mstatus >> 32)); #endif if (riscv_has_ext(env, RVH)) { qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "hstatus ", env->hstatus); - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "vsstatus ", env->vsstatus); + qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "vsstatus ", + (target_ulong)env->vsstatus); } qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mip ", env->mip); qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mie ", env->mie); @@ -496,13 +499,6 @@ static void riscv_cpu_init(Object *obj) cpu_set_cpustate_pointers(cpu); } -#ifndef CONFIG_USER_ONLY -static const VMStateDescription vmstate_riscv_cpu = { - .name = "cpu", - .unmigratable = 1, -}; -#endif - static Property riscv_cpu_properties[] = { DEFINE_PROP_BOOL("i", RISCVCPU, cfg.ext_i, true), DEFINE_PROP_BOOL("e", RISCVCPU, cfg.ext_e, false), diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index de4705b..87b68af 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -144,14 +144,14 @@ struct CPURISCVState { target_ulong resetvec; target_ulong mhartid; - target_ulong mstatus; + /* + * For RV32 this is 32-bit mstatus and 32-bit mstatush. + * For RV64 this is a 64-bit mstatus. + */ + uint64_t mstatus; target_ulong mip; -#ifdef TARGET_RISCV32 - target_ulong mstatush; -#endif - uint32_t miclaim; target_ulong mie; @@ -183,16 +183,17 @@ struct CPURISCVState { uint64_t htimedelta; /* Virtual CSRs */ - target_ulong vsstatus; + /* + * For RV32 this is 32-bit vsstatus and 32-bit vsstatush. + * For RV64 this is a 64-bit vsstatus. + */ + uint64_t vsstatus; target_ulong vstvec; target_ulong vsscratch; target_ulong vsepc; target_ulong vscause; target_ulong vstval; target_ulong vsatp; -#ifdef TARGET_RISCV32 - target_ulong vsstatush; -#endif target_ulong mtval2; target_ulong mtinst; @@ -204,10 +205,7 @@ struct CPURISCVState { target_ulong scause_hs; target_ulong stval_hs; target_ulong satp_hs; - target_ulong mstatus_hs; -#ifdef TARGET_RISCV32 - target_ulong mstatush_hs; -#endif + uint64_t mstatus_hs; target_ulong scounteren; target_ulong mcounteren; diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index bd36062..daedad8 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -4,10 +4,10 @@ #define TARGET_RISCV_CPU_BITS_H #define get_field(reg, mask) (((reg) & \ - (target_ulong)(mask)) / ((mask) & ~((mask) << 1))) -#define set_field(reg, mask, val) (((reg) & ~(target_ulong)(mask)) | \ - (((target_ulong)(val) * ((mask) & ~((mask) << 1))) & \ - (target_ulong)(mask))) + (uint64_t)(mask)) / ((mask) & ~((mask) << 1))) +#define set_field(reg, mask, val) (((reg) & ~(uint64_t)(mask)) | \ + (((uint64_t)(val) * ((mask) & ~((mask) << 1))) & \ + (uint64_t)(mask))) /* Floating point round mode */ #define FSR_RD_SHIFT 5 @@ -381,19 +381,8 @@ #define MSTATUS_TVM 0x00100000 /* since: priv-1.10 */ #define MSTATUS_TW 0x20000000 /* since: priv-1.10 */ #define MSTATUS_TSR 0x40000000 /* since: priv-1.10 */ -#if defined(TARGET_RISCV64) #define MSTATUS_GVA 0x4000000000ULL #define MSTATUS_MPV 0x8000000000ULL -#elif defined(TARGET_RISCV32) -#define MSTATUS_GVA 0x00000040 -#define MSTATUS_MPV 0x00000080 -#endif - -#ifdef TARGET_RISCV32 -# define MSTATUS_MPV_ISSET(env) get_field(env->mstatush, MSTATUS_MPV) -#else -# define MSTATUS_MPV_ISSET(env) get_field(env->mstatus, MSTATUS_MPV) -#endif #define MSTATUS64_UXL 0x0000000300000000ULL #define MSTATUS64_SXL 0x0000000C00000000ULL diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 4652082..3eb3a03 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -110,27 +110,19 @@ bool riscv_cpu_fp_enabled(CPURISCVState *env) void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env) { - target_ulong mstatus_mask = MSTATUS_MXR | MSTATUS_SUM | MSTATUS_FS | - MSTATUS_SPP | MSTATUS_SPIE | MSTATUS_SIE; + uint64_t mstatus_mask = MSTATUS_MXR | MSTATUS_SUM | MSTATUS_FS | + MSTATUS_SPP | MSTATUS_SPIE | MSTATUS_SIE | + MSTATUS64_UXL; bool current_virt = riscv_cpu_virt_enabled(env); g_assert(riscv_has_ext(env, RVH)); -#if defined(TARGET_RISCV64) - mstatus_mask |= MSTATUS64_UXL; -#endif - if (current_virt) { /* Current V=1 and we are about to change to V=0 */ env->vsstatus = env->mstatus & mstatus_mask; env->mstatus &= ~mstatus_mask; env->mstatus |= env->mstatus_hs; -#if defined(TARGET_RISCV32) - env->vsstatush = env->mstatush; - env->mstatush |= env->mstatush_hs; -#endif - env->vstvec = env->stvec; env->stvec = env->stvec_hs; @@ -154,11 +146,6 @@ void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env) env->mstatus &= ~mstatus_mask; env->mstatus |= env->vsstatus; -#if defined(TARGET_RISCV32) - env->mstatush_hs = env->mstatush; - env->mstatush |= env->vsstatush; -#endif - env->stvec_hs = env->stvec; env->stvec = env->vstvec; @@ -727,7 +714,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, if (riscv_has_ext(env, RVH) && env->priv == PRV_M && access_type != MMU_INST_FETCH && get_field(env->mstatus, MSTATUS_MPRV) && - MSTATUS_MPV_ISSET(env)) { + get_field(env->mstatus, MSTATUS_MPV)) { riscv_cpu_set_two_stage_lookup(env, true); } @@ -799,7 +786,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, if (riscv_has_ext(env, RVH) && env->priv == PRV_M && access_type != MMU_INST_FETCH && get_field(env->mstatus, MSTATUS_MPRV) && - MSTATUS_MPV_ISSET(env)) { + get_field(env->mstatus, MSTATUS_MPV)) { riscv_cpu_set_two_stage_lookup(env, false); } @@ -862,7 +849,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; bool force_hs_execp = riscv_cpu_force_hs_excep_enabled(env); - target_ulong s; + uint64_t s; /* cs->exception is 32-bits wide unlike mcause which is XLEN-bits wide * so we mask off the MSB and separate into trap type and cause. @@ -995,19 +982,11 @@ void riscv_cpu_do_interrupt(CPUState *cs) if (riscv_cpu_virt_enabled(env)) { riscv_cpu_swap_hypervisor_regs(env); } -#ifdef TARGET_RISCV32 - env->mstatush = set_field(env->mstatush, MSTATUS_MPV, - riscv_cpu_virt_enabled(env)); - if (riscv_cpu_virt_enabled(env) && tval) { - env->mstatush = set_field(env->mstatush, MSTATUS_GVA, 1); - } -#else env->mstatus = set_field(env->mstatus, MSTATUS_MPV, - riscv_cpu_virt_enabled(env)); + riscv_cpu_virt_enabled(env)); if (riscv_cpu_virt_enabled(env) && tval) { env->mstatus = set_field(env->mstatus, MSTATUS_GVA, 1); } -#endif mtval2 = env->guest_phys_fault_addr; diff --git a/target/riscv/csr.c b/target/riscv/csr.c index aaef6c6..93263f8 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -446,8 +446,8 @@ static int validate_vm(CPURISCVState *env, target_ulong vm) static int write_mstatus(CPURISCVState *env, int csrno, target_ulong val) { - target_ulong mstatus = env->mstatus; - target_ulong mask = 0; + uint64_t mstatus = env->mstatus; + uint64_t mask = 0; int dirty; /* flush tlb on mstatus fields that affect VM */ @@ -480,19 +480,20 @@ static int write_mstatus(CPURISCVState *env, int csrno, target_ulong val) #ifdef TARGET_RISCV32 static int read_mstatush(CPURISCVState *env, int csrno, target_ulong *val) { - *val = env->mstatush; + *val = env->mstatus >> 32; return 0; } static int write_mstatush(CPURISCVState *env, int csrno, target_ulong val) { - if ((val ^ env->mstatush) & (MSTATUS_MPV)) { + uint64_t valh = (uint64_t)val << 32; + uint64_t mask = MSTATUS_MPV | MSTATUS_GVA; + + if ((valh ^ env->mstatus) & (MSTATUS_MPV)) { tlb_flush(env_cpu(env)); } - val &= MSTATUS_MPV | MSTATUS_GVA; - - env->mstatush = val; + env->mstatus = (env->mstatus & ~mask) | (valh & mask); return 0; } @@ -881,7 +882,7 @@ static int write_satp(CPURISCVState *env, int csrno, target_ulong val) if (env->priv == PRV_S && get_field(env->mstatus, MSTATUS_TVM)) { return -RISCV_EXCP_ILLEGAL_INST; } else { - if((val ^ env->satp) & SATP_ASID) { + if ((val ^ env->satp) & SATP_ASID) { tlb_flush(env_cpu(env)); } env->satp = val; @@ -1105,7 +1106,8 @@ static int read_vsstatus(CPURISCVState *env, int csrno, target_ulong *val) static int write_vsstatus(CPURISCVState *env, int csrno, target_ulong val) { - env->vsstatus = val; + uint64_t mask = (target_ulong)-1; + env->vsstatus = (env->vsstatus & ~mask) | (uint64_t)val; return 0; } diff --git a/target/riscv/internals.h b/target/riscv/internals.h index f1a546d..b15ad39 100644 --- a/target/riscv/internals.h +++ b/target/riscv/internals.h @@ -38,6 +38,10 @@ target_ulong fclass_d(uint64_t frs1); #define SEW32 2 #define SEW64 3 +#ifndef CONFIG_USER_ONLY +extern const VMStateDescription vmstate_riscv_cpu; +#endif + static inline uint64_t nanbox_s(float32 f) { return f | MAKE_64BIT_MASK(32, 32); diff --git a/target/riscv/machine.c b/target/riscv/machine.c new file mode 100644 index 0000000..44d4015 --- /dev/null +++ b/target/riscv/machine.c @@ -0,0 +1,196 @@ +/* + * RISC-V VMState Description + * + * Copyright (c) 2020 Huawei Technologies Co., Ltd + * + * 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 "cpu.h" +#include "qemu/error-report.h" +#include "sysemu/kvm.h" +#include "migration/cpu.h" + +static bool pmp_needed(void *opaque) +{ + RISCVCPU *cpu = opaque; + CPURISCVState *env = &cpu->env; + + return riscv_feature(env, RISCV_FEATURE_PMP); +} + +static int pmp_post_load(void *opaque, int version_id) +{ + RISCVCPU *cpu = opaque; + CPURISCVState *env = &cpu->env; + int i; + + for (i = 0; i < MAX_RISCV_PMPS; i++) { + pmp_update_rule_addr(env, i); + } + pmp_update_rule_nums(env); + + return 0; +} + +static const VMStateDescription vmstate_pmp_entry = { + .name = "cpu/pmp/entry", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINTTL(addr_reg, pmp_entry_t), + VMSTATE_UINT8(cfg_reg, pmp_entry_t), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_pmp = { + .name = "cpu/pmp", + .version_id = 1, + .minimum_version_id = 1, + .needed = pmp_needed, + .post_load = pmp_post_load, + .fields = (VMStateField[]) { + VMSTATE_STRUCT_ARRAY(env.pmp_state.pmp, RISCVCPU, MAX_RISCV_PMPS, + 0, vmstate_pmp_entry, pmp_entry_t), + VMSTATE_END_OF_LIST() + } +}; + +static bool hyper_needed(void *opaque) +{ + RISCVCPU *cpu = opaque; + CPURISCVState *env = &cpu->env; + + return riscv_has_ext(env, RVH); +} + +static bool vector_needed(void *opaque) +{ + RISCVCPU *cpu = opaque; + CPURISCVState *env = &cpu->env; + + return riscv_has_ext(env, RVV); +} + +static const VMStateDescription vmstate_vector = { + .name = "cpu/vector", + .version_id = 1, + .minimum_version_id = 1, + .needed = vector_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT64_ARRAY(env.vreg, RISCVCPU, 32 * RV_VLEN_MAX / 64), + VMSTATE_UINTTL(env.vxrm, RISCVCPU), + VMSTATE_UINTTL(env.vxsat, RISCVCPU), + VMSTATE_UINTTL(env.vl, RISCVCPU), + VMSTATE_UINTTL(env.vstart, RISCVCPU), + VMSTATE_UINTTL(env.vtype, RISCVCPU), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_hyper = { + .name = "cpu/hyper", + .version_id = 1, + .minimum_version_id = 1, + .needed = hyper_needed, + .fields = (VMStateField[]) { + VMSTATE_UINTTL(env.hstatus, RISCVCPU), + VMSTATE_UINTTL(env.hedeleg, RISCVCPU), + VMSTATE_UINTTL(env.hideleg, RISCVCPU), + VMSTATE_UINTTL(env.hcounteren, RISCVCPU), + VMSTATE_UINTTL(env.htval, RISCVCPU), + VMSTATE_UINTTL(env.htinst, RISCVCPU), + VMSTATE_UINTTL(env.hgatp, RISCVCPU), + VMSTATE_UINT64(env.htimedelta, RISCVCPU), + + VMSTATE_UINT64(env.vsstatus, RISCVCPU), + VMSTATE_UINTTL(env.vstvec, RISCVCPU), + VMSTATE_UINTTL(env.vsscratch, RISCVCPU), + VMSTATE_UINTTL(env.vsepc, RISCVCPU), + VMSTATE_UINTTL(env.vscause, RISCVCPU), + VMSTATE_UINTTL(env.vstval, RISCVCPU), + VMSTATE_UINTTL(env.vsatp, RISCVCPU), + + VMSTATE_UINTTL(env.mtval2, RISCVCPU), + VMSTATE_UINTTL(env.mtinst, RISCVCPU), + + VMSTATE_UINTTL(env.stvec_hs, RISCVCPU), + VMSTATE_UINTTL(env.sscratch_hs, RISCVCPU), + VMSTATE_UINTTL(env.sepc_hs, RISCVCPU), + VMSTATE_UINTTL(env.scause_hs, RISCVCPU), + VMSTATE_UINTTL(env.stval_hs, RISCVCPU), + VMSTATE_UINTTL(env.satp_hs, RISCVCPU), + VMSTATE_UINT64(env.mstatus_hs, RISCVCPU), + + VMSTATE_END_OF_LIST() + } +}; + +const VMStateDescription vmstate_riscv_cpu = { + .name = "cpu", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINTTL_ARRAY(env.gpr, RISCVCPU, 32), + VMSTATE_UINT64_ARRAY(env.fpr, RISCVCPU, 32), + VMSTATE_UINTTL(env.pc, RISCVCPU), + VMSTATE_UINTTL(env.load_res, RISCVCPU), + VMSTATE_UINTTL(env.load_val, RISCVCPU), + VMSTATE_UINTTL(env.frm, RISCVCPU), + VMSTATE_UINTTL(env.badaddr, RISCVCPU), + VMSTATE_UINTTL(env.guest_phys_fault_addr, RISCVCPU), + VMSTATE_UINTTL(env.priv_ver, RISCVCPU), + VMSTATE_UINTTL(env.vext_ver, RISCVCPU), + VMSTATE_UINTTL(env.misa, RISCVCPU), + VMSTATE_UINTTL(env.misa_mask, RISCVCPU), + VMSTATE_UINT32(env.features, RISCVCPU), + VMSTATE_UINTTL(env.priv, RISCVCPU), + VMSTATE_UINTTL(env.virt, RISCVCPU), + VMSTATE_UINTTL(env.resetvec, RISCVCPU), + VMSTATE_UINTTL(env.mhartid, RISCVCPU), + VMSTATE_UINT64(env.mstatus, RISCVCPU), + VMSTATE_UINTTL(env.mip, RISCVCPU), + VMSTATE_UINT32(env.miclaim, RISCVCPU), + VMSTATE_UINTTL(env.mie, RISCVCPU), + VMSTATE_UINTTL(env.mideleg, RISCVCPU), + VMSTATE_UINTTL(env.sptbr, RISCVCPU), + VMSTATE_UINTTL(env.satp, RISCVCPU), + VMSTATE_UINTTL(env.sbadaddr, RISCVCPU), + VMSTATE_UINTTL(env.mbadaddr, RISCVCPU), + VMSTATE_UINTTL(env.medeleg, RISCVCPU), + VMSTATE_UINTTL(env.stvec, RISCVCPU), + VMSTATE_UINTTL(env.sepc, RISCVCPU), + VMSTATE_UINTTL(env.scause, RISCVCPU), + VMSTATE_UINTTL(env.mtvec, RISCVCPU), + VMSTATE_UINTTL(env.mepc, RISCVCPU), + VMSTATE_UINTTL(env.mcause, RISCVCPU), + VMSTATE_UINTTL(env.mtval, RISCVCPU), + VMSTATE_UINTTL(env.scounteren, RISCVCPU), + VMSTATE_UINTTL(env.mcounteren, RISCVCPU), + VMSTATE_UINTTL(env.sscratch, RISCVCPU), + VMSTATE_UINTTL(env.mscratch, RISCVCPU), + VMSTATE_UINT64(env.mfromhost, RISCVCPU), + VMSTATE_UINT64(env.mtohost, RISCVCPU), + VMSTATE_UINT64(env.timecmp, RISCVCPU), + + VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * []) { + &vmstate_pmp, + &vmstate_hyper, + &vmstate_vector, + NULL + } +}; diff --git a/target/riscv/meson.build b/target/riscv/meson.build index abd647f..14a5c62 100644 --- a/target/riscv/meson.build +++ b/target/riscv/meson.build @@ -27,7 +27,8 @@ riscv_ss.add(files( riscv_softmmu_ss = ss.source_set() riscv_softmmu_ss.add(files( 'pmp.c', - 'monitor.c' + 'monitor.c', + 'machine.c' )) target_arch += {'riscv': riscv_ss} diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 4ce7357..e20d56d 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -78,7 +78,8 @@ target_ulong helper_csrrc(CPURISCVState *env, target_ulong src, target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb) { - target_ulong prev_priv, prev_virt, mstatus; + uint64_t mstatus; + target_ulong prev_priv, prev_virt; if (!(env->priv >= PRV_S)) { riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); @@ -147,18 +148,14 @@ target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb) riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); } - target_ulong mstatus = env->mstatus; + uint64_t mstatus = env->mstatus; target_ulong prev_priv = get_field(mstatus, MSTATUS_MPP); - target_ulong prev_virt = MSTATUS_MPV_ISSET(env); + target_ulong prev_virt = get_field(env->mstatus, MSTATUS_MPV); mstatus = set_field(mstatus, MSTATUS_MIE, get_field(mstatus, MSTATUS_MPIE)); mstatus = set_field(mstatus, MSTATUS_MPIE, 1); mstatus = set_field(mstatus, MSTATUS_MPP, PRV_U); -#ifdef TARGET_RISCV32 - env->mstatush = set_field(env->mstatush, MSTATUS_MPV, 0); -#else mstatus = set_field(mstatus, MSTATUS_MPV, 0); -#endif env->mstatus = mstatus; riscv_cpu_set_mode(env, prev_priv); diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index c394e86..2eda8e1 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -136,18 +136,8 @@ static void pmp_decode_napot(target_ulong a, target_ulong *sa, target_ulong *ea) } } - -/* Convert cfg/addr reg values here into simple 'sa' --> start address and 'ea' - * end address values. - * This function is called relatively infrequently whereas the check that - * an address is within a pmp rule is called often, so optimise that one - */ -static void pmp_update_rule(CPURISCVState *env, uint32_t pmp_index) +void pmp_update_rule_addr(CPURISCVState *env, uint32_t pmp_index) { - int i; - - env->pmp_state.num_rules = 0; - uint8_t this_cfg = env->pmp_state.pmp[pmp_index].cfg_reg; target_ulong this_addr = env->pmp_state.pmp[pmp_index].addr_reg; target_ulong prev_addr = 0u; @@ -186,7 +176,13 @@ static void pmp_update_rule(CPURISCVState *env, uint32_t pmp_index) env->pmp_state.addr[pmp_index].sa = sa; env->pmp_state.addr[pmp_index].ea = ea; +} +void pmp_update_rule_nums(CPURISCVState *env) +{ + int i; + + env->pmp_state.num_rules = 0; for (i = 0; i < MAX_RISCV_PMPS; i++) { const uint8_t a_field = pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg); @@ -196,6 +192,17 @@ static void pmp_update_rule(CPURISCVState *env, uint32_t pmp_index) } } +/* Convert cfg/addr reg values here into simple 'sa' --> start address and 'ea' + * end address values. + * This function is called relatively infrequently whereas the check that + * an address is within a pmp rule is called often, so optimise that one + */ +static void pmp_update_rule(CPURISCVState *env, uint32_t pmp_index) +{ + pmp_update_rule_addr(env, pmp_index); + pmp_update_rule_nums(env); +} + static int pmp_is_in_range(CPURISCVState *env, int pmp_index, target_ulong addr) { int result = 0; diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h index 6a8f072..6c6b4c9 100644 --- a/target/riscv/pmp.h +++ b/target/riscv/pmp.h @@ -62,5 +62,7 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, target_ulong size, pmp_priv_t priv, target_ulong mode); bool pmp_is_range_in_tlb(CPURISCVState *env, hwaddr tlb_sa, target_ulong *tlb_size); +void pmp_update_rule_addr(CPURISCVState *env, uint32_t pmp_index); +void pmp_update_rule_nums(CPURISCVState *env); #endif diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 63d2ace..814804a 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -543,10 +543,10 @@ class VM(qtest.QEMUQtestMachine): def __init__(self, path_suffix=''): name = "qemu%s-%d" % (path_suffix, os.getpid()) - super(VM, self).__init__(qemu_prog, qemu_opts, name=name, - test_dir=test_dir, - socket_scm_helper=socket_scm_helper, - sock_dir=sock_dir) + super().__init__(qemu_prog, qemu_opts, name=name, + test_dir=test_dir, + socket_scm_helper=socket_scm_helper, + sock_dir=sock_dir) self._num_drives = 0 def add_object(self, opts): @@ -747,6 +747,10 @@ class VM(qtest.QEMUQtestMachine): def wait_migration(self, expect_runstate: Optional[str]) -> bool: while True: event = self.event_wait('MIGRATION') + # We use the default timeout, and with a timeout, event_wait() + # never returns None + assert event + log(event, filters=[filter_qmp_event]) if event['data']['status'] in ('completed', 'failed'): break diff --git a/tests/qemu-iotests/pylintrc b/tests/qemu-iotests/pylintrc index 5481afe..cd3702e 100644 --- a/tests/qemu-iotests/pylintrc +++ b/tests/qemu-iotests/pylintrc @@ -17,6 +17,8 @@ disable=invalid-name, too-many-lines, too-many-locals, too-many-public-methods, + # pylint warns about Optional[] etc. as unsubscriptable in 3.9 + unsubscriptable-object, # These are temporary, and should be removed: missing-docstring, diff --git a/tests/qtest/cdrom-test.c b/tests/qtest/cdrom-test.c index eef242d..5af944a 100644 --- a/tests/qtest/cdrom-test.c +++ b/tests/qtest/cdrom-test.c @@ -217,7 +217,7 @@ int main(int argc, char **argv) add_cdrom_param_tests(sparc64machines); } else if (!strncmp(arch, "mips64", 6)) { const char *mips64machines[] = { - "magnum", "malta", "mips", "pica61", NULL + "magnum", "malta", "pica61", NULL }; add_cdrom_param_tests(mips64machines); } else if (g_str_equal(arch, "arm") || g_str_equal(arch, "aarch64")) { diff --git a/tests/qtest/endianness-test.c b/tests/qtest/endianness-test.c index 4e79e22..09ecb53 100644 --- a/tests/qtest/endianness-test.c +++ b/tests/qtest/endianness-test.c @@ -27,11 +27,9 @@ struct TestCase { static const TestCase test_cases[] = { { "i386", "pc", -1 }, - { "mips", "mips", 0x14000000, .bswap = true }, { "mips", "malta", 0x10000000, .bswap = true }, { "mips64", "magnum", 0x90000000, .bswap = true }, { "mips64", "pica61", 0x90000000, .bswap = true }, - { "mips64", "mips", 0x14000000, .bswap = true }, { "mips64", "malta", 0x10000000, .bswap = true }, { "mips64el", "fuloong2e", 0x1fd00000 }, { "ppc", "g3beige", 0xfe000000, .bswap = true, .superio = "i82378" }, diff --git a/tests/test-util-sockets.c b/tests/test-util-sockets.c index f6336e0..6748605 100644 --- a/tests/test-util-sockets.c +++ b/tests/test-util-sockets.c @@ -229,94 +229,105 @@ static void test_socket_fd_pass_num_nocli(void) } #endif -#ifdef __linux__ -static gchar *abstract_sock_name; +#ifdef CONFIG_LINUX -static gpointer unix_server_thread_func(gpointer user_data) +#define ABSTRACT_SOCKET_VARIANTS 3 + +typedef struct { + SocketAddress *server, *client[ABSTRACT_SOCKET_VARIANTS]; + bool expect_connect[ABSTRACT_SOCKET_VARIANTS]; +} abstract_socket_matrix_row; + +static gpointer unix_client_thread_func(gpointer user_data) { - SocketAddress addr; + abstract_socket_matrix_row *row = user_data; Error *err = NULL; - int fd = -1; - int connfd = -1; + int i, fd; + + for (i = 0; i < ABSTRACT_SOCKET_VARIANTS; i++) { + if (row->expect_connect[i]) { + fd = socket_connect(row->client[i], &error_abort); + g_assert_cmpint(fd, >=, 0); + } else { + fd = socket_connect(row->client[i], &err); + g_assert_cmpint(fd, ==, -1); + error_free_or_abort(&err); + } + close(fd); + } + return NULL; +} + +static void test_socket_unix_abstract_row(abstract_socket_matrix_row *test) +{ + int fd, connfd, i; + GThread *cli; struct sockaddr_un un; socklen_t len = sizeof(un); - addr.type = SOCKET_ADDRESS_TYPE_UNIX; - addr.u.q_unix.path = abstract_sock_name; - addr.u.q_unix.tight = user_data != NULL; - addr.u.q_unix.abstract = true; + /* Last one must connect, or else accept() below hangs */ + assert(test->expect_connect[ABSTRACT_SOCKET_VARIANTS - 1]); - fd = socket_listen(&addr, 1, &err); + fd = socket_listen(test->server, 1, &error_abort); g_assert_cmpint(fd, >=, 0); g_assert(fd_is_socket(fd)); - connfd = accept(fd, (struct sockaddr *)&un, &len); - g_assert_cmpint(connfd, !=, -1); + cli = g_thread_new("abstract_unix_client", + unix_client_thread_func, + test); + + for (i = 0; i < ABSTRACT_SOCKET_VARIANTS; i++) { + if (test->expect_connect[i]) { + connfd = accept(fd, (struct sockaddr *)&un, &len); + g_assert_cmpint(connfd, !=, -1); + close(connfd); + } + } close(fd); - - return NULL; + g_thread_join(cli); } -static gpointer unix_client_thread_func(gpointer user_data) +static void test_socket_unix_abstract(void) { - SocketAddress addr; - Error *err = NULL; - int fd = -1; + SocketAddress addr, addr_tight, addr_padded; + abstract_socket_matrix_row matrix[ABSTRACT_SOCKET_VARIANTS] = { + { &addr, + { &addr_tight, &addr_padded, &addr }, + { true, false, true } }, + { &addr_tight, + { &addr_padded, &addr, &addr_tight }, + { false, true, true } }, + { &addr_padded, + { &addr, &addr_tight, &addr_padded }, + { false, false, true } } + }; + int i; addr.type = SOCKET_ADDRESS_TYPE_UNIX; - addr.u.q_unix.path = abstract_sock_name; - addr.u.q_unix.tight = user_data != NULL; + addr.u.q_unix.path = g_strdup_printf("unix-%d-%u", + getpid(), g_random_int()); + addr.u.q_unix.has_abstract = true; addr.u.q_unix.abstract = true; + addr.u.q_unix.has_tight = false; + addr.u.q_unix.tight = false; - fd = socket_connect(&addr, &err); + addr_tight = addr; + addr_tight.u.q_unix.has_tight = true; + addr_tight.u.q_unix.tight = true; - g_assert_cmpint(fd, >=, 0); + addr_padded = addr; + addr_padded.u.q_unix.has_tight = true; + addr_padded.u.q_unix.tight = false; - close(fd); + for (i = 0; i < ABSTRACT_SOCKET_VARIANTS; i++) { + test_socket_unix_abstract_row(&matrix[i]); + } - return NULL; + g_free(addr.u.q_unix.path); } -static void test_socket_unix_abstract_good(void) -{ - GRand *r = g_rand_new(); - - abstract_sock_name = g_strdup_printf("unix-%d-%d", getpid(), - g_rand_int_range(r, 100, 1000)); - - /* non tight socklen serv and cli */ - GThread *serv = g_thread_new("abstract_unix_server", - unix_server_thread_func, - NULL); - - sleep(1); - - GThread *cli = g_thread_new("abstract_unix_client", - unix_client_thread_func, - NULL); - - g_thread_join(cli); - g_thread_join(serv); - - /* tight socklen serv and cli */ - serv = g_thread_new("abstract_unix_server", - unix_server_thread_func, - (gpointer)1); - - sleep(1); - - cli = g_thread_new("abstract_unix_client", - unix_client_thread_func, - (gpointer)1); - - g_thread_join(cli); - g_thread_join(serv); - - g_free(abstract_sock_name); - g_rand_free(r); -} -#endif +#endif /* CONFIG_LINUX */ int main(int argc, char **argv) { @@ -358,9 +369,9 @@ int main(int argc, char **argv) #endif } -#ifdef __linux__ - g_test_add_func("/util/socket/unix-abstract/good", - test_socket_unix_abstract_good); +#ifdef CONFIG_LINUX + g_test_add_func("/util/socket/unix-abstract", + test_socket_unix_abstract); #endif end: diff --git a/ui/console.c b/ui/console.c index 820e408..e8e5970 100644 --- a/ui/console.c +++ b/ui/console.c @@ -168,6 +168,7 @@ struct QemuConsole { QEMUFIFO out_fifo; uint8_t out_fifo_buf[16]; QEMUTimer *kbd_timer; + CoQueue dump_queue; QTAILQ_ENTRY(QemuConsole) next; }; @@ -195,7 +196,6 @@ static void dpy_refresh(DisplayState *s); static DisplayState *get_alloc_displaystate(void); static void text_console_update_cursor_timer(void); static void text_console_update_cursor(void *opaque); -static bool ppm_save(int fd, DisplaySurface *ds, Error **errp); static void gui_update(void *opaque) { @@ -264,6 +264,7 @@ static void gui_setup_refresh(DisplayState *ds) void graphic_hw_update_done(QemuConsole *con) { + qemu_co_queue_restart_all(&con->dump_queue); } void graphic_hw_update(QemuConsole *con) @@ -311,16 +312,16 @@ void graphic_hw_invalidate(QemuConsole *con) } } -static bool ppm_save(int fd, DisplaySurface *ds, Error **errp) +static bool ppm_save(int fd, pixman_image_t *image, Error **errp) { - int width = pixman_image_get_width(ds->image); - int height = pixman_image_get_height(ds->image); + int width = pixman_image_get_width(image); + int height = pixman_image_get_height(image); g_autoptr(Object) ioc = OBJECT(qio_channel_file_new_fd(fd)); g_autofree char *header = NULL; g_autoptr(pixman_image_t) linebuf = NULL; int y; - trace_ppm_save(fd, ds); + trace_ppm_save(fd, image); header = g_strdup_printf("P6\n%d %d\n%d\n", width, height, 255); if (qio_channel_write_all(QIO_CHANNEL(ioc), @@ -330,7 +331,7 @@ static bool ppm_save(int fd, DisplaySurface *ds, Error **errp) linebuf = qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, width); for (y = 0; y < height; y++) { - qemu_pixman_linebuf_fill(linebuf, ds->image, width, 0, y); + qemu_pixman_linebuf_fill(linebuf, image, width, 0, y); if (qio_channel_write_all(QIO_CHANNEL(ioc), (char *)pixman_image_get_data(linebuf), pixman_image_get_stride(linebuf), errp) < 0) { @@ -341,9 +342,17 @@ static bool ppm_save(int fd, DisplaySurface *ds, Error **errp) return true; } -void qmp_screendump(const char *filename, bool has_device, const char *device, - bool has_head, int64_t head, Error **errp) +static void graphic_hw_update_bh(void *con) { + graphic_hw_update(con); +} + +/* Safety: coroutine-only, concurrent-coroutine safe, main thread only */ +void coroutine_fn +qmp_screendump(const char *filename, bool has_device, const char *device, + bool has_head, int64_t head, Error **errp) +{ + g_autoptr(pixman_image_t) image = NULL; QemuConsole *con; DisplaySurface *surface; int fd; @@ -366,12 +375,24 @@ void qmp_screendump(const char *filename, bool has_device, const char *device, } } - graphic_hw_update(con); + if (qemu_co_queue_empty(&con->dump_queue)) { + /* Defer the update, it will restart the pending coroutines */ + aio_bh_schedule_oneshot(qemu_get_aio_context(), + graphic_hw_update_bh, con); + } + qemu_co_queue_wait(&con->dump_queue, NULL); + + /* + * All pending coroutines are woken up, while the BQL is held. No + * further graphic update are possible until it is released. Take + * an image ref before that. + */ surface = qemu_console_surface(con); if (!surface) { error_setg(errp, "no surface"); return; } + image = pixman_image_ref(surface->image); fd = qemu_open_old(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666); if (fd == -1) { @@ -380,7 +401,12 @@ void qmp_screendump(const char *filename, bool has_device, const char *device, return; } - if (!ppm_save(fd, surface, errp)) { + /* + * The image content could potentially be updated as the coroutine + * yields and releases the BQL. It could produce corrupted dump, but + * it should be otherwise safe. + */ + if (!ppm_save(fd, image, errp)) { qemu_unlink(filename); } } @@ -1296,6 +1322,7 @@ static QemuConsole *new_console(DisplayState *ds, console_type_t console_type, obj = object_new(TYPE_QEMU_CONSOLE); s = QEMU_CONSOLE(obj); + qemu_co_queue_init(&s->dump_queue); s->head = head; object_property_add_link(obj, "device", TYPE_DEVICE, (Object **)&s->device, diff --git a/ui/trace-events b/ui/trace-events index b7d7270..0ffcdb4 100644 --- a/ui/trace-events +++ b/ui/trace-events @@ -15,7 +15,7 @@ displaysurface_create_pixman(void *display_surface) "surface=%p" displaysurface_free(void *display_surface) "surface=%p" displaychangelistener_register(void *dcl, const char *name) "%p [ %s ]" displaychangelistener_unregister(void *dcl, const char *name) "%p [ %s ]" -ppm_save(int fd, void *display_surface) "fd=%d surface=%p" +ppm_save(int fd, void *image) "fd=%d image=%p" # gtk-egl.c # gtk-gl-area.c diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c index 0517b2e..f67111a 100644 --- a/ui/vnc-auth-sasl.c +++ b/ui/vnc-auth-sasl.c @@ -111,7 +111,8 @@ size_t vnc_client_write_sasl(VncState *vs) g_source_remove(vs->ioc_tag); } vs->ioc_tag = qio_channel_add_watch( - vs->ioc, G_IO_IN, vnc_client_io, vs, NULL); + vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR, + vnc_client_io, vs, NULL); } return ret; diff --git a/ui/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c index f072e16..d9c212f 100644 --- a/ui/vnc-auth-vencrypt.c +++ b/ui/vnc-auth-vencrypt.c @@ -79,7 +79,8 @@ static void vnc_tls_handshake_done(QIOTask *task, g_source_remove(vs->ioc_tag); } vs->ioc_tag = qio_channel_add_watch( - vs->ioc, G_IO_IN | G_IO_OUT, vnc_client_io, vs, NULL); + vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_OUT, + vnc_client_io, vs, NULL); start_auth_vencrypt_subauth(vs); } } diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c index 929391f..dbbfbef 100644 --- a/ui/vnc-jobs.c +++ b/ui/vnc-jobs.c @@ -151,7 +151,8 @@ void vnc_jobs_consume_buffer(VncState *vs) } if (vs->disconnecting == FALSE) { vs->ioc_tag = qio_channel_add_watch( - vs->ioc, G_IO_IN | G_IO_OUT, vnc_client_io, vs, NULL); + vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_OUT, + vnc_client_io, vs, NULL); } } buffer_move(&vs->output, &vs->jobs_buffer); diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c index 95c9703..6d79f3e 100644 --- a/ui/vnc-ws.c +++ b/ui/vnc-ws.c @@ -41,13 +41,14 @@ static void vncws_tls_handshake_done(QIOTask *task, g_source_remove(vs->ioc_tag); } vs->ioc_tag = qio_channel_add_watch( - QIO_CHANNEL(vs->ioc), G_IO_IN, vncws_handshake_io, vs, NULL); + QIO_CHANNEL(vs->ioc), G_IO_IN | G_IO_HUP | G_IO_ERR, + vncws_handshake_io, vs, NULL); } } gboolean vncws_tls_handshake_io(QIOChannel *ioc G_GNUC_UNUSED, - GIOCondition condition G_GNUC_UNUSED, + GIOCondition condition, void *opaque) { VncState *vs = opaque; @@ -59,6 +60,11 @@ gboolean vncws_tls_handshake_io(QIOChannel *ioc G_GNUC_UNUSED, vs->ioc_tag = 0; } + if (condition & (G_IO_HUP | G_IO_ERR)) { + vnc_client_error(vs); + return TRUE; + } + tls = qio_channel_tls_new_server( vs->ioc, vs->vd->tlscreds, @@ -105,13 +111,14 @@ static void vncws_handshake_done(QIOTask *task, g_source_remove(vs->ioc_tag); } vs->ioc_tag = qio_channel_add_watch( - vs->ioc, G_IO_IN, vnc_client_io, vs, NULL); + vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR, + vnc_client_io, vs, NULL); } } gboolean vncws_handshake_io(QIOChannel *ioc G_GNUC_UNUSED, - GIOCondition condition G_GNUC_UNUSED, + GIOCondition condition, void *opaque) { VncState *vs = opaque; @@ -122,6 +129,11 @@ gboolean vncws_handshake_io(QIOChannel *ioc G_GNUC_UNUSED, vs->ioc_tag = 0; } + if (condition & (G_IO_HUP | G_IO_ERR)) { + vnc_client_error(vs); + return TRUE; + } + wioc = qio_channel_websock_new_server(vs->ioc); qio_channel_set_name(QIO_CHANNEL(wioc), "vnc-ws-server-websock"); @@ -1398,7 +1398,8 @@ static size_t vnc_client_write_plain(VncState *vs) g_source_remove(vs->ioc_tag); } vs->ioc_tag = qio_channel_add_watch( - vs->ioc, G_IO_IN, vnc_client_io, vs, NULL); + vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR, + vnc_client_io, vs, NULL); } return ret; @@ -1435,7 +1436,8 @@ static void vnc_client_write(VncState *vs) g_source_remove(vs->ioc_tag); } vs->ioc_tag = qio_channel_add_watch( - vs->ioc, G_IO_IN, vnc_client_io, vs, NULL); + vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR, + vnc_client_io, vs, NULL); } vnc_unlock_output(vs); } @@ -1551,6 +1553,12 @@ gboolean vnc_client_io(QIOChannel *ioc G_GNUC_UNUSED, VncState *vs = opaque; assert(vs->magic == VNC_MAGIC); + + if (condition & (G_IO_HUP | G_IO_ERR)) { + vnc_disconnect_start(vs); + return TRUE; + } + if (condition & G_IO_IN) { if (vnc_client_read(vs) < 0) { /* vs is free()ed here */ @@ -1612,7 +1620,8 @@ void vnc_write(VncState *vs, const void *data, size_t len) g_source_remove(vs->ioc_tag); } vs->ioc_tag = qio_channel_add_watch( - vs->ioc, G_IO_IN | G_IO_OUT, vnc_client_io, vs, NULL); + vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_OUT, + vnc_client_io, vs, NULL); } buffer_append(&vs->output, data, len); @@ -3077,14 +3086,17 @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc, vs->websocket = 1; if (vd->tlscreds) { vs->ioc_tag = qio_channel_add_watch( - vs->ioc, G_IO_IN, vncws_tls_handshake_io, vs, NULL); + vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR, + vncws_tls_handshake_io, vs, NULL); } else { vs->ioc_tag = qio_channel_add_watch( - vs->ioc, G_IO_IN, vncws_handshake_io, vs, NULL); + vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR, + vncws_handshake_io, vs, NULL); } } else { vs->ioc_tag = qio_channel_add_watch( - vs->ioc, G_IO_IN, vnc_client_io, vs, NULL); + vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR, + vnc_client_io, vs, NULL); } vnc_client_cache_addr(vs); diff --git a/util/aio-win32.c b/util/aio-win32.c index e7b1d64..168717b 100644 --- a/util/aio-win32.c +++ b/util/aio-win32.c @@ -18,6 +18,7 @@ #include "qemu/osdep.h" #include "qemu-common.h" #include "block/block.h" +#include "qemu/main-loop.h" #include "qemu/queue.h" #include "qemu/sockets.h" #include "qapi/error.h" @@ -333,8 +334,13 @@ bool aio_poll(AioContext *ctx, bool blocking) * There cannot be two concurrent aio_poll calls for the same AioContext (or * an aio_poll concurrent with a GSource prepare/check/dispatch callback). * We rely on this below to avoid slow locked accesses to ctx->notify_me. + * + * aio_poll() may only be called in the AioContext's thread. iohandler_ctx + * is special in that it runs in the main thread, but that thread's context + * is qemu_aio_context. */ - assert(in_aio_context_home_thread(ctx)); + assert(in_aio_context_home_thread(ctx == iohandler_get_aio_context() ? + qemu_get_aio_context() : ctx)); progress = false; /* aio_notify can avoid the expensive event_notifier_set if diff --git a/util/qemu-coroutine-lock.c b/util/qemu-coroutine-lock.c index 36927b5..5816bf8 100644 --- a/util/qemu-coroutine-lock.c +++ b/util/qemu-coroutine-lock.c @@ -85,15 +85,13 @@ static bool qemu_co_queue_do_restart(CoQueue *queue, bool single) return true; } -bool coroutine_fn qemu_co_queue_next(CoQueue *queue) +bool qemu_co_queue_next(CoQueue *queue) { - assert(qemu_in_coroutine()); return qemu_co_queue_do_restart(queue, true); } -void coroutine_fn qemu_co_queue_restart_all(CoQueue *queue) +void qemu_co_queue_restart_all(CoQueue *queue) { - assert(qemu_in_coroutine()); qemu_co_queue_do_restart(queue, false); } diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index 38f8217..8af0278 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -860,10 +860,29 @@ static int vsock_parse(VsockSocketAddress *addr, const char *str, #ifndef _WIN32 +static bool saddr_is_abstract(UnixSocketAddress *saddr) +{ +#ifdef CONFIG_LINUX + return saddr->abstract; +#else + return false; +#endif +} + +static bool saddr_is_tight(UnixSocketAddress *saddr) +{ +#ifdef CONFIG_LINUX + return !saddr->has_tight || saddr->tight; +#else + return false; +#endif +} + static int unix_listen_saddr(UnixSocketAddress *saddr, int num, Error **errp) { + bool abstract = saddr_is_abstract(saddr); struct sockaddr_un un; int sock, fd; char *pathbuf = NULL; @@ -877,7 +896,7 @@ static int unix_listen_saddr(UnixSocketAddress *saddr, return -1; } - if (saddr->path && saddr->path[0]) { + if (saddr->path[0] || abstract) { path = saddr->path; } else { const char *tmpdir = getenv("TMPDIR"); @@ -887,10 +906,10 @@ static int unix_listen_saddr(UnixSocketAddress *saddr, pathlen = strlen(path); if (pathlen > sizeof(un.sun_path) || - (saddr->abstract && pathlen > (sizeof(un.sun_path) - 1))) { + (abstract && pathlen > (sizeof(un.sun_path) - 1))) { error_setg(errp, "UNIX socket path '%s' is too long", path); error_append_hint(errp, "Path must be less than %zu bytes\n", - saddr->abstract ? sizeof(un.sun_path) - 1 : + abstract ? sizeof(un.sun_path) - 1 : sizeof(un.sun_path)); goto err; } @@ -912,7 +931,7 @@ static int unix_listen_saddr(UnixSocketAddress *saddr, close(fd); } - if (!saddr->abstract && unlink(path) < 0 && errno != ENOENT) { + if (!abstract && unlink(path) < 0 && errno != ENOENT) { error_setg_errno(errp, errno, "Failed to unlink socket %s", path); goto err; @@ -922,10 +941,10 @@ static int unix_listen_saddr(UnixSocketAddress *saddr, un.sun_family = AF_UNIX; addrlen = sizeof(un); - if (saddr->abstract) { + if (abstract) { un.sun_path[0] = '\0'; memcpy(&un.sun_path[1], path, pathlen); - if (saddr->tight) { + if (saddr_is_tight(saddr)) { addrlen = offsetof(struct sockaddr_un, sun_path) + 1 + pathlen; } } else { @@ -952,6 +971,7 @@ err: static int unix_connect_saddr(UnixSocketAddress *saddr, Error **errp) { + bool abstract = saddr_is_abstract(saddr); struct sockaddr_un un; int sock, rc; size_t pathlen; @@ -970,10 +990,10 @@ static int unix_connect_saddr(UnixSocketAddress *saddr, Error **errp) pathlen = strlen(saddr->path); if (pathlen > sizeof(un.sun_path) || - (saddr->abstract && pathlen > (sizeof(un.sun_path) - 1))) { + (abstract && pathlen > (sizeof(un.sun_path) - 1))) { error_setg(errp, "UNIX socket path '%s' is too long", saddr->path); error_append_hint(errp, "Path must be less than %zu bytes\n", - saddr->abstract ? sizeof(un.sun_path) - 1 : + abstract ? sizeof(un.sun_path) - 1 : sizeof(un.sun_path)); goto err; } @@ -982,10 +1002,10 @@ static int unix_connect_saddr(UnixSocketAddress *saddr, Error **errp) un.sun_family = AF_UNIX; addrlen = sizeof(un); - if (saddr->abstract) { + if (abstract) { un.sun_path[0] = '\0'; memcpy(&un.sun_path[1], saddr->path, pathlen); - if (saddr->tight) { + if (saddr_is_tight(saddr)) { addrlen = offsetof(struct sockaddr_un, sun_path) + 1 + pathlen; } } else { @@ -1270,10 +1290,20 @@ socket_sockaddr_to_address_unix(struct sockaddr_storage *sa, addr = g_new0(SocketAddress, 1); addr->type = SOCKET_ADDRESS_TYPE_UNIX; - if (su->sun_path[0]) { - addr->u.q_unix.path = g_strndup(su->sun_path, sizeof(su->sun_path)); +#ifdef CONFIG_LINUX + if (!su->sun_path[0]) { + /* Linux abstract socket */ + addr->u.q_unix.path = g_strndup(su->sun_path + 1, + sizeof(su->sun_path) - 1); + addr->u.q_unix.has_abstract = true; + addr->u.q_unix.abstract = true; + addr->u.q_unix.has_tight = true; + addr->u.q_unix.tight = salen < sizeof(*su); + return addr; } +#endif + addr->u.q_unix.path = g_strndup(su->sun_path, sizeof(su->sun_path)); return addr; } #endif /* WIN32 */ |