diff options
Diffstat (limited to 'hw')
49 files changed, 1891 insertions, 413 deletions
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 3671b42738..78b56149b6 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -1,7 +1,7 @@ obj-y += boot.o collie.o exynos4_boards.o gumstix.o highbank.o obj-y += integratorcp.o kzm.o mainstone.o musicpal.o nseries.o obj-y += omap_sx1.o palm.o realview.o spitz.o stellaris.o -obj-y += tosa.o versatilepb.o vexpress.o xilinx_zynq.o z2.o +obj-y += tosa.o versatilepb.o vexpress.o virt.o xilinx_zynq.o z2.o obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o obj-y += omap1.o omap2.o strongarm.o diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 583ec7992e..55d552f3a8 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -228,23 +228,31 @@ static void set_kernel_args_old(const struct arm_boot_info *info) static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo) { void *fdt = NULL; - char *filename; int size, rc; uint32_t acells, scells; - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, binfo->dtb_filename); - if (!filename) { - fprintf(stderr, "Couldn't open dtb file %s\n", binfo->dtb_filename); - goto fail; - } + if (binfo->dtb_filename) { + char *filename; + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, binfo->dtb_filename); + if (!filename) { + fprintf(stderr, "Couldn't open dtb file %s\n", binfo->dtb_filename); + goto fail; + } - fdt = load_device_tree(filename, &size); - if (!fdt) { - fprintf(stderr, "Couldn't open dtb file %s\n", filename); + fdt = load_device_tree(filename, &size); + if (!fdt) { + fprintf(stderr, "Couldn't open dtb file %s\n", filename); + g_free(filename); + goto fail; + } g_free(filename); - goto fail; + } else if (binfo->get_dtb) { + fdt = binfo->get_dtb(binfo, &size); + if (!fdt) { + fprintf(stderr, "Board was unable to create a dtb blob\n"); + goto fail; + } } - g_free(filename); acells = qemu_devtree_getprop_cell(fdt, "/", "#address-cells"); scells = qemu_devtree_getprop_cell(fdt, "/", "#size-cells"); @@ -438,7 +446,7 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) /* for device tree boot, we pass the DTB directly in r2. Otherwise * we point to the kernel args. */ - if (info->dtb_filename) { + if (info->dtb_filename || info->get_dtb) { /* Place the DTB after the initrd in memory. Note that some * kernels will trash anything in the 4K page the initrd * ends in, so make sure the DTB isn't caught up in that. diff --git a/hw/arm/integratorcp.c b/hw/arm/integratorcp.c index c44b2a499c..a759689b44 100644 --- a/hw/arm/integratorcp.c +++ b/hw/arm/integratorcp.c @@ -36,6 +36,7 @@ typedef struct IntegratorCMState { uint32_t cm_init; uint32_t cm_flags; uint32_t cm_nvflags; + uint32_t cm_refcnt_offset; uint32_t int_level; uint32_t irq_enabled; uint32_t fiq_enabled; @@ -82,9 +83,13 @@ static uint64_t integratorcm_read(void *opaque, hwaddr offset, return s->cm_sdram; case 9: /* CM_INIT */ return s->cm_init; - case 10: /* CM_REFCT */ - /* ??? High frequency timer. */ - hw_error("integratorcm_read: CM_REFCT"); + case 10: /* CM_REFCNT */ + /* This register, CM_REFCNT, provides a 32-bit count value. + * The count increments at the fixed reference clock frequency of 24MHz + * and can be used as a real-time counter. + */ + return (uint32_t)muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 24, + 1000) - s->cm_refcnt_offset; case 12: /* CM_FLAGS */ return s->cm_flags; case 14: /* CM_NVFLAGS */ @@ -257,6 +262,8 @@ static int integratorcm_init(SysBusDevice *dev) } memcpy(integrator_spd + 73, "QEMU-MEMORY", 11); s->cm_init = 0x00000112; + s->cm_refcnt_offset = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 24, + 1000); memory_region_init_ram(&s->flash, OBJECT(s), "integrator.flash", 0x100000); vmstate_register_ram_global(&s->flash); diff --git a/hw/arm/virt.c b/hw/arm/virt.c new file mode 100644 index 0000000000..9531b5a574 --- /dev/null +++ b/hw/arm/virt.c @@ -0,0 +1,452 @@ +/* + * ARM mach-virt emulation + * + * Copyright (c) 2013 Linaro Limited + * + * 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/>. + * + * Emulate a virtual board which works by passing Linux all the information + * it needs about what devices are present via the device tree. + * There are some restrictions about what we can do here: + * + we can only present devices whose Linux drivers will work based + * purely on the device tree with no platform data at all + * + we want to present a very stripped-down minimalist platform, + * both because this reduces the security attack surface from the guest + * and also because it reduces our exposure to being broken when + * the kernel updates its device tree bindings and requires further + * information in a device binding that we aren't providing. + * This is essentially the same approach kvmtool uses. + */ + +#include "hw/sysbus.h" +#include "hw/arm/arm.h" +#include "hw/arm/primecell.h" +#include "hw/devices.h" +#include "net/net.h" +#include "sysemu/device_tree.h" +#include "sysemu/sysemu.h" +#include "sysemu/kvm.h" +#include "hw/boards.h" +#include "exec/address-spaces.h" +#include "qemu/bitops.h" +#include "qemu/error-report.h" + +#define NUM_VIRTIO_TRANSPORTS 32 + +/* Number of external interrupt lines to configure the GIC with */ +#define NUM_IRQS 128 + +#define GIC_FDT_IRQ_TYPE_SPI 0 +#define GIC_FDT_IRQ_TYPE_PPI 1 + +#define GIC_FDT_IRQ_FLAGS_EDGE_LO_HI 1 +#define GIC_FDT_IRQ_FLAGS_EDGE_HI_LO 2 +#define GIC_FDT_IRQ_FLAGS_LEVEL_HI 4 +#define GIC_FDT_IRQ_FLAGS_LEVEL_LO 8 + +#define GIC_FDT_IRQ_PPI_CPU_START 8 +#define GIC_FDT_IRQ_PPI_CPU_WIDTH 8 + +enum { + VIRT_FLASH, + VIRT_MEM, + VIRT_CPUPERIPHS, + VIRT_GIC_DIST, + VIRT_GIC_CPU, + VIRT_UART, + VIRT_MMIO, +}; + +typedef struct MemMapEntry { + hwaddr base; + hwaddr size; +} MemMapEntry; + +typedef struct VirtBoardInfo { + struct arm_boot_info bootinfo; + const char *cpu_model; + const char *qdevname; + const char *gic_compatible; + const MemMapEntry *memmap; + const int *irqmap; + int smp_cpus; + void *fdt; + int fdt_size; + uint32_t clock_phandle; +} VirtBoardInfo; + +/* Addresses and sizes of our components. + * 0..128MB is space for a flash device so we can run bootrom code such as UEFI. + * 128MB..256MB is used for miscellaneous device I/O. + * 256MB..1GB is reserved for possible future PCI support (ie where the + * PCI memory window will go if we add a PCI host controller). + * 1GB and up is RAM (which may happily spill over into the + * high memory region beyond 4GB). + * This represents a compromise between how much RAM can be given to + * a 32 bit VM and leaving space for expansion and in particular for PCI. + */ +static const MemMapEntry a15memmap[] = { + /* Space up to 0x8000000 is reserved for a boot ROM */ + [VIRT_FLASH] = { 0, 0x8000000 }, + [VIRT_CPUPERIPHS] = { 0x8000000, 0x8000 }, + /* GIC distributor and CPU interfaces sit inside the CPU peripheral space */ + [VIRT_GIC_DIST] = { 0x8001000, 0x1000 }, + [VIRT_GIC_CPU] = { 0x8002000, 0x1000 }, + [VIRT_UART] = { 0x9000000, 0x1000 }, + [VIRT_MMIO] = { 0xa000000, 0x200 }, + /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */ + /* 0x10000000 .. 0x40000000 reserved for PCI */ + [VIRT_MEM] = { 0x40000000, 30ULL * 1024 * 1024 * 1024 }, +}; + +static const int a15irqmap[] = { + [VIRT_UART] = 1, + [VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */ +}; + +static VirtBoardInfo machines[] = { + { + .cpu_model = "cortex-a15", + .qdevname = "a15mpcore_priv", + .gic_compatible = "arm,cortex-a15-gic", + .memmap = a15memmap, + .irqmap = a15irqmap, + }, + { + .cpu_model = "host", + /* We use the A15 private peripheral model to get a V2 GIC */ + .qdevname = "a15mpcore_priv", + .gic_compatible = "arm,cortex-a15-gic", + .memmap = a15memmap, + .irqmap = a15irqmap, + }, +}; + +static VirtBoardInfo *find_machine_info(const char *cpu) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(machines); i++) { + if (strcmp(cpu, machines[i].cpu_model) == 0) { + return &machines[i]; + } + } + return NULL; +} + +static void create_fdt(VirtBoardInfo *vbi) +{ + void *fdt = create_device_tree(&vbi->fdt_size); + + if (!fdt) { + error_report("create_device_tree() failed"); + exit(1); + } + + vbi->fdt = fdt; + + /* Header */ + qemu_devtree_setprop_string(fdt, "/", "compatible", "linux,dummy-virt"); + qemu_devtree_setprop_cell(fdt, "/", "#address-cells", 0x2); + qemu_devtree_setprop_cell(fdt, "/", "#size-cells", 0x2); + + /* + * /chosen and /memory nodes must exist for load_dtb + * to fill in necessary properties later + */ + qemu_devtree_add_subnode(fdt, "/chosen"); + qemu_devtree_add_subnode(fdt, "/memory"); + qemu_devtree_setprop_string(fdt, "/memory", "device_type", "memory"); + + /* Clock node, for the benefit of the UART. The kernel device tree + * binding documentation claims the PL011 node clock properties are + * optional but in practice if you omit them the kernel refuses to + * probe for the device. + */ + vbi->clock_phandle = qemu_devtree_alloc_phandle(fdt); + qemu_devtree_add_subnode(fdt, "/apb-pclk"); + qemu_devtree_setprop_string(fdt, "/apb-pclk", "compatible", "fixed-clock"); + qemu_devtree_setprop_cell(fdt, "/apb-pclk", "#clock-cells", 0x0); + qemu_devtree_setprop_cell(fdt, "/apb-pclk", "clock-frequency", 24000000); + qemu_devtree_setprop_string(fdt, "/apb-pclk", "clock-output-names", + "clk24mhz"); + qemu_devtree_setprop_cell(fdt, "/apb-pclk", "phandle", vbi->clock_phandle); + + /* No PSCI for TCG yet */ + if (kvm_enabled()) { + qemu_devtree_add_subnode(fdt, "/psci"); + qemu_devtree_setprop_string(fdt, "/psci", "compatible", "arm,psci"); + qemu_devtree_setprop_string(fdt, "/psci", "method", "hvc"); + qemu_devtree_setprop_cell(fdt, "/psci", "cpu_suspend", + PSCI_FN_CPU_SUSPEND); + qemu_devtree_setprop_cell(fdt, "/psci", "cpu_off", PSCI_FN_CPU_OFF); + qemu_devtree_setprop_cell(fdt, "/psci", "cpu_on", PSCI_FN_CPU_ON); + qemu_devtree_setprop_cell(fdt, "/psci", "migrate", PSCI_FN_MIGRATE); + } +} + +static void fdt_add_timer_nodes(const VirtBoardInfo *vbi) +{ + /* Note that on A15 h/w these interrupts are level-triggered, + * but for the GIC implementation provided by both QEMU and KVM + * they are edge-triggered. + */ + uint32_t irqflags = GIC_FDT_IRQ_FLAGS_EDGE_LO_HI; + + irqflags = deposit32(irqflags, GIC_FDT_IRQ_PPI_CPU_START, + GIC_FDT_IRQ_PPI_CPU_WIDTH, (1 << vbi->smp_cpus) - 1); + + qemu_devtree_add_subnode(vbi->fdt, "/timer"); + qemu_devtree_setprop_string(vbi->fdt, "/timer", + "compatible", "arm,armv7-timer"); + qemu_devtree_setprop_cells(vbi->fdt, "/timer", "interrupts", + GIC_FDT_IRQ_TYPE_PPI, 13, irqflags, + GIC_FDT_IRQ_TYPE_PPI, 14, irqflags, + GIC_FDT_IRQ_TYPE_PPI, 11, irqflags, + GIC_FDT_IRQ_TYPE_PPI, 10, irqflags); +} + +static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi) +{ + int cpu; + + qemu_devtree_add_subnode(vbi->fdt, "/cpus"); + qemu_devtree_setprop_cell(vbi->fdt, "/cpus", "#address-cells", 0x1); + qemu_devtree_setprop_cell(vbi->fdt, "/cpus", "#size-cells", 0x0); + + for (cpu = vbi->smp_cpus - 1; cpu >= 0; cpu--) { + char *nodename = g_strdup_printf("/cpus/cpu@%d", cpu); + ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(cpu)); + + qemu_devtree_add_subnode(vbi->fdt, nodename); + qemu_devtree_setprop_string(vbi->fdt, nodename, "device_type", "cpu"); + qemu_devtree_setprop_string(vbi->fdt, nodename, "compatible", + armcpu->dtb_compatible); + + if (vbi->smp_cpus > 1) { + qemu_devtree_setprop_string(vbi->fdt, nodename, + "enable-method", "psci"); + } + + qemu_devtree_setprop_cell(vbi->fdt, nodename, "reg", cpu); + g_free(nodename); + } +} + +static void fdt_add_gic_node(const VirtBoardInfo *vbi) +{ + uint32_t gic_phandle; + + gic_phandle = qemu_devtree_alloc_phandle(vbi->fdt); + qemu_devtree_setprop_cell(vbi->fdt, "/", "interrupt-parent", gic_phandle); + + qemu_devtree_add_subnode(vbi->fdt, "/intc"); + qemu_devtree_setprop_string(vbi->fdt, "/intc", "compatible", + vbi->gic_compatible); + qemu_devtree_setprop_cell(vbi->fdt, "/intc", "#interrupt-cells", 3); + qemu_devtree_setprop(vbi->fdt, "/intc", "interrupt-controller", NULL, 0); + qemu_devtree_setprop_sized_cells(vbi->fdt, "/intc", "reg", + 2, vbi->memmap[VIRT_GIC_DIST].base, + 2, vbi->memmap[VIRT_GIC_DIST].size, + 2, vbi->memmap[VIRT_GIC_CPU].base, + 2, vbi->memmap[VIRT_GIC_CPU].size); + qemu_devtree_setprop_cell(vbi->fdt, "/intc", "phandle", gic_phandle); +} + +static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic) +{ + char *nodename; + hwaddr base = vbi->memmap[VIRT_UART].base; + hwaddr size = vbi->memmap[VIRT_UART].size; + int irq = vbi->irqmap[VIRT_UART]; + const char compat[] = "arm,pl011\0arm,primecell"; + const char clocknames[] = "uartclk\0apb_pclk"; + + sysbus_create_simple("pl011", base, pic[irq]); + + nodename = g_strdup_printf("/pl011@%" PRIx64, base); + qemu_devtree_add_subnode(vbi->fdt, nodename); + /* Note that we can't use setprop_string because of the embedded NUL */ + qemu_devtree_setprop(vbi->fdt, nodename, "compatible", + compat, sizeof(compat)); + qemu_devtree_setprop_sized_cells(vbi->fdt, nodename, "reg", + 2, base, 2, size); + qemu_devtree_setprop_cells(vbi->fdt, nodename, "interrupts", + GIC_FDT_IRQ_TYPE_SPI, irq, + GIC_FDT_IRQ_FLAGS_EDGE_LO_HI); + qemu_devtree_setprop_cells(vbi->fdt, nodename, "clocks", + vbi->clock_phandle, vbi->clock_phandle); + qemu_devtree_setprop(vbi->fdt, nodename, "clock-names", + clocknames, sizeof(clocknames)); + g_free(nodename); +} + +static void create_virtio_devices(const VirtBoardInfo *vbi, qemu_irq *pic) +{ + int i; + hwaddr size = vbi->memmap[VIRT_MMIO].size; + + /* Note that we have to create the transports in forwards order + * so that command line devices are inserted lowest address first, + * and then add dtb nodes in reverse order so that they appear in + * the finished device tree lowest address first. + */ + for (i = 0; i < NUM_VIRTIO_TRANSPORTS; i++) { + int irq = vbi->irqmap[VIRT_MMIO] + i; + hwaddr base = vbi->memmap[VIRT_MMIO].base + i * size; + + sysbus_create_simple("virtio-mmio", base, pic[irq]); + } + + for (i = NUM_VIRTIO_TRANSPORTS - 1; i >= 0; i--) { + char *nodename; + int irq = vbi->irqmap[VIRT_MMIO] + i; + hwaddr base = vbi->memmap[VIRT_MMIO].base + i * size; + + nodename = g_strdup_printf("/virtio_mmio@%" PRIx64, base); + qemu_devtree_add_subnode(vbi->fdt, nodename); + qemu_devtree_setprop_string(vbi->fdt, nodename, + "compatible", "virtio,mmio"); + qemu_devtree_setprop_sized_cells(vbi->fdt, nodename, "reg", + 2, base, 2, size); + qemu_devtree_setprop_cells(vbi->fdt, nodename, "interrupts", + GIC_FDT_IRQ_TYPE_SPI, irq, + GIC_FDT_IRQ_FLAGS_EDGE_LO_HI); + g_free(nodename); + } +} + +static void *machvirt_dtb(const struct arm_boot_info *binfo, int *fdt_size) +{ + const VirtBoardInfo *board = (const VirtBoardInfo *)binfo; + + *fdt_size = board->fdt_size; + return board->fdt; +} + +static void machvirt_init(QEMUMachineInitArgs *args) +{ + qemu_irq pic[NUM_IRQS]; + MemoryRegion *sysmem = get_system_memory(); + int n; + MemoryRegion *ram = g_new(MemoryRegion, 1); + DeviceState *dev; + SysBusDevice *busdev; + const char *cpu_model = args->cpu_model; + VirtBoardInfo *vbi; + + if (!cpu_model) { + cpu_model = "cortex-a15"; + } + + vbi = find_machine_info(cpu_model); + + if (!vbi) { + error_report("mach-virt: CPU %s not supported", cpu_model); + exit(1); + } + + vbi->smp_cpus = smp_cpus; + + /* + * Only supported method of starting secondary CPUs is PSCI and + * PSCI is not yet supported with TCG, so limit smp_cpus to 1 + * if we're not using KVM. + */ + if (!kvm_enabled() && smp_cpus > 1) { + error_report("mach-virt: must enable KVM to use multiple CPUs"); + exit(1); + } + + if (args->ram_size > vbi->memmap[VIRT_MEM].size) { + error_report("mach-virt: cannot model more than 30GB RAM"); + exit(1); + } + + create_fdt(vbi); + fdt_add_timer_nodes(vbi); + + for (n = 0; n < smp_cpus; n++) { + ObjectClass *oc = cpu_class_by_name(TYPE_ARM_CPU, cpu_model); + Object *cpuobj; + + if (!oc) { + fprintf(stderr, "Unable to find CPU definition\n"); + exit(1); + } + cpuobj = object_new(object_class_get_name(oc)); + + /* Secondary CPUs start in PSCI powered-down state */ + if (n > 0) { + object_property_set_bool(cpuobj, true, "start-powered-off", NULL); + } + object_property_set_bool(cpuobj, true, "realized", NULL); + } + fdt_add_cpu_nodes(vbi); + + memory_region_init_ram(ram, NULL, "mach-virt.ram", args->ram_size); + vmstate_register_ram_global(ram); + memory_region_add_subregion(sysmem, vbi->memmap[VIRT_MEM].base, ram); + + dev = qdev_create(NULL, vbi->qdevname); + qdev_prop_set_uint32(dev, "num-cpu", smp_cpus); + /* Note that the num-irq property counts both internal and external + * interrupts; there are always 32 of the former (mandated by GIC spec). + */ + qdev_prop_set_uint32(dev, "num-irq", NUM_IRQS + 32); + qdev_init_nofail(dev); + busdev = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(busdev, 0, vbi->memmap[VIRT_CPUPERIPHS].base); + fdt_add_gic_node(vbi); + for (n = 0; n < smp_cpus; n++) { + DeviceState *cpudev = DEVICE(qemu_get_cpu(n)); + + sysbus_connect_irq(busdev, n, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); + } + + for (n = 0; n < NUM_IRQS; n++) { + pic[n] = qdev_get_gpio_in(dev, n); + } + + create_uart(vbi, pic); + + /* Create mmio transports, so the user can create virtio backends + * (which will be automatically plugged in to the transports). If + * no backend is created the transport will just sit harmlessly idle. + */ + create_virtio_devices(vbi, pic); + + vbi->bootinfo.ram_size = args->ram_size; + vbi->bootinfo.kernel_filename = args->kernel_filename; + vbi->bootinfo.kernel_cmdline = args->kernel_cmdline; + vbi->bootinfo.initrd_filename = args->initrd_filename; + vbi->bootinfo.nb_cpus = smp_cpus; + vbi->bootinfo.board_id = -1; + vbi->bootinfo.loader_start = vbi->memmap[VIRT_MEM].base; + vbi->bootinfo.get_dtb = machvirt_dtb; + arm_load_kernel(ARM_CPU(first_cpu), &vbi->bootinfo); +} + +static QEMUMachine machvirt_a15_machine = { + .name = "virt", + .desc = "ARM Virtual Machine", + .init = machvirt_init, + .max_cpus = 4, +}; + +static void machvirt_machine_init(void) +{ + qemu_register_machine(&machvirt_a15_machine); +} + +machine_init(machvirt_machine_init); diff --git a/hw/audio/adlib.c b/hw/audio/adlib.c index bd8e9d9815..e88d2dd845 100644 --- a/hw/audio/adlib.c +++ b/hw/audio/adlib.c @@ -347,8 +347,8 @@ static void adlib_realizefn (DeviceState *dev, Error **errp) s->samples = AUD_get_buffer_size_out (s->voice) >> SHIFT; s->mixbuf = g_malloc0 (s->samples << SHIFT); - adlib_portio_list[1].offset = s->port; - adlib_portio_list[2].offset = s->port + 8; + adlib_portio_list[0].offset = s->port; + adlib_portio_list[1].offset = s->port + 8; portio_list_init (port_list, OBJECT(s), adlib_portio_list, s, "adlib"); portio_list_add (port_list, isa_address_space_io(&s->parent_obj), 0); } diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c index 4327264394..6ab8c245d3 100644 --- a/hw/audio/intel-hda.c +++ b/hw/audio/intel-hda.c @@ -444,6 +444,7 @@ static bool intel_hda_xfer(HDACodecDevice *dev, uint32_t stnr, bool output, } } if (d->dp_lbase & 0x01) { + s = st - d->st; addr = intel_hda_addr(d->dp_lbase & ~0x01, d->dp_ubase); stl_le_pci_dma(&d->pci, addr + 8*s, st->lpib); } diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c index f8ccbdd13a..f18db53bca 100644 --- a/hw/char/cadence_uart.c +++ b/hw/char/cadence_uart.c @@ -120,8 +120,8 @@ typedef struct { uint64_t char_tx_time; CharDriverState *chr; qemu_irq irq; - struct QEMUTimer *fifo_trigger_handle; - struct QEMUTimer *tx_time_handle; + QEMUTimer *fifo_trigger_handle; + QEMUTimer *tx_time_handle; } UartState; static void uart_update_status(UartState *s) diff --git a/hw/cpu/a9mpcore.c b/hw/cpu/a9mpcore.c index 918a7d1291..c09358c6e7 100644 --- a/hw/cpu/a9mpcore.c +++ b/hw/cpu/a9mpcore.c @@ -24,11 +24,14 @@ static void a9mp_priv_initfn(Object *obj) memory_region_init(&s->container, obj, "a9mp-priv-container", 0x2000); sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->container); + object_initialize(&s->scu, sizeof(s->scu), TYPE_A9_SCU); + qdev_set_parent_bus(DEVICE(&s->scu), sysbus_get_default()); + object_initialize(&s->gic, sizeof(s->gic), TYPE_ARM_GIC); qdev_set_parent_bus(DEVICE(&s->gic), sysbus_get_default()); - object_initialize(&s->scu, sizeof(s->scu), TYPE_A9_SCU); - qdev_set_parent_bus(DEVICE(&s->scu), sysbus_get_default()); + object_initialize(&s->gtimer, sizeof(s->gtimer), TYPE_A9_GTIMER); + qdev_set_parent_bus(DEVICE(&s->gtimer), sysbus_get_default()); object_initialize(&s->mptimer, sizeof(s->mptimer), TYPE_ARM_MPTIMER); qdev_set_parent_bus(DEVICE(&s->mptimer), sysbus_get_default()); @@ -41,11 +44,21 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp) { SysBusDevice *sbd = SYS_BUS_DEVICE(dev); A9MPPrivState *s = A9MPCORE_PRIV(dev); - DeviceState *gicdev, *scudev, *mptimerdev, *wdtdev; - SysBusDevice *timerbusdev, *wdtbusdev, *gicbusdev, *scubusdev; + DeviceState *scudev, *gicdev, *gtimerdev, *mptimerdev, *wdtdev; + SysBusDevice *scubusdev, *gicbusdev, *gtimerbusdev, *mptimerbusdev, + *wdtbusdev; Error *err = NULL; int i; + scudev = DEVICE(&s->scu); + qdev_prop_set_uint32(scudev, "num-cpu", s->num_cpu); + object_property_set_bool(OBJECT(&s->scu), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + scubusdev = SYS_BUS_DEVICE(&s->scu); + gicdev = DEVICE(&s->gic); qdev_prop_set_uint32(gicdev, "num-cpu", s->num_cpu); qdev_prop_set_uint32(gicdev, "num-irq", s->num_irq); @@ -62,14 +75,14 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp) /* Pass through inbound GPIO lines to the GIC */ qdev_init_gpio_in(dev, a9mp_priv_set_irq, s->num_irq - 32); - scudev = DEVICE(&s->scu); - qdev_prop_set_uint32(scudev, "num-cpu", s->num_cpu); - object_property_set_bool(OBJECT(&s->scu), true, "realized", &err); + gtimerdev = DEVICE(&s->gtimer); + qdev_prop_set_uint32(gtimerdev, "num-cpu", s->num_cpu); + object_property_set_bool(OBJECT(&s->gtimer), true, "realized", &err); if (err != NULL) { error_propagate(errp, err); return; } - scubusdev = SYS_BUS_DEVICE(&s->scu); + gtimerbusdev = SYS_BUS_DEVICE(&s->gtimer); mptimerdev = DEVICE(&s->mptimer); qdev_prop_set_uint32(mptimerdev, "num-cpu", s->num_cpu); @@ -78,7 +91,7 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp) error_propagate(errp, err); return; } - timerbusdev = SYS_BUS_DEVICE(&s->mptimer); + mptimerbusdev = SYS_BUS_DEVICE(&s->mptimer); wdtdev = DEVICE(&s->wdt); qdev_prop_set_uint32(wdtdev, "num-cpu", s->num_cpu); @@ -97,30 +110,33 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp) * 0x0600-0x06ff -- private timers and watchdogs * 0x0700-0x0fff -- nothing * 0x1000-0x1fff -- GIC Distributor - * - * We should implement the global timer but don't currently do so. */ memory_region_add_subregion(&s->container, 0, sysbus_mmio_get_region(scubusdev, 0)); /* GIC CPU interface */ memory_region_add_subregion(&s->container, 0x100, sysbus_mmio_get_region(gicbusdev, 1)); + memory_region_add_subregion(&s->container, 0x200, + sysbus_mmio_get_region(gtimerbusdev, 0)); /* Note that the A9 exposes only the "timer/watchdog for this core" * memory region, not the "timer/watchdog for core X" ones 11MPcore has. */ memory_region_add_subregion(&s->container, 0x600, - sysbus_mmio_get_region(timerbusdev, 0)); + sysbus_mmio_get_region(mptimerbusdev, 0)); memory_region_add_subregion(&s->container, 0x620, sysbus_mmio_get_region(wdtbusdev, 0)); memory_region_add_subregion(&s->container, 0x1000, sysbus_mmio_get_region(gicbusdev, 0)); /* Wire up the interrupt from each watchdog and timer. - * For each core the timer is PPI 29 and the watchdog PPI 30. + * For each core the global timer is PPI 27, the private + * timer is PPI 29 and the watchdog PPI 30. */ for (i = 0; i < s->num_cpu; i++) { int ppibase = (s->num_irq - 32) + i * 32; - sysbus_connect_irq(timerbusdev, i, + sysbus_connect_irq(gtimerbusdev, i, + qdev_get_gpio_in(gicdev, ppibase + 27)); + sysbus_connect_irq(mptimerbusdev, i, qdev_get_gpio_in(gicdev, ppibase + 29)); sysbus_connect_irq(wdtbusdev, i, qdev_get_gpio_in(gicdev, ppibase + 30)); diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c index d34b0c4170..84f1367716 100644 --- a/hw/display/qxl-render.c +++ b/hw/display/qxl-render.c @@ -20,6 +20,7 @@ */ #include "qxl.h" +#include "trace.h" static void qxl_blit(PCIQXLDevice *qxl, QXLRect *rect) { diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index a6a8cdc2e1..aba292ccde 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -23,6 +23,7 @@ */ #include "hw/hw.h" #include "hw/loader.h" +#include "trace.h" #include "ui/console.h" #include "hw/pci/pci.h" diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 1f22fb60a4..48312f5a83 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -285,7 +285,8 @@ static inline void build_append_array(GArray *array, GArray *val) g_array_append_vals(array, val->data, val->len); } -static void build_append_nameseg(GArray *array, const char *format, ...) +static void GCC_FMT_ATTR(2, 3) +build_append_nameseg(GArray *array, const char *format, ...) { /* It would be nicer to use g_string_vprintf but it's only there in 2.22 */ char s[] = "XXXX"; @@ -630,7 +631,7 @@ build_append_notify(GArray *device, const char *name, GArray *method = build_alloc_array(); uint8_t op = 0x14; /* MethodOp */ - build_append_nameseg(method, name); + build_append_nameseg(method, "%s", name); build_append_byte(method, 0x02); /* MethodFlags: ArgCount */ for (i = skip; i < count; i++) { GArray *target = build_alloc_array(); @@ -923,10 +924,16 @@ build_mcfg_q35(GArray *table_data, GArray *linker, AcpiMcfgInfo *info) static void build_dsdt(GArray *table_data, GArray *linker, AcpiMiscInfo *misc) { - void *dsdt; + AcpiTableHeader *dsdt; + assert(misc->dsdt_code && misc->dsdt_size); + dsdt = acpi_data_push(table_data, misc->dsdt_size); memcpy(dsdt, misc->dsdt_code, misc->dsdt_size); + + memset(dsdt, 0, sizeof *dsdt); + build_header(linker, table_data, dsdt, ACPI_DSDT_SIGNATURE, + misc->dsdt_size, 1); } /* Build final rsdt table */ diff --git a/hw/i386/acpi-dsdt.dsl b/hw/i386/acpi-dsdt.dsl index 90efce0d18..a377424f39 100644 --- a/hw/i386/acpi-dsdt.dsl +++ b/hw/i386/acpi-dsdt.dsl @@ -235,7 +235,7 @@ DefinitionBlock ( } Return (0x0B) } - Method(IQCR, 1, NotSerialized) { + Method(IQCR, 1, Serialized) { // _CRS method - get current settings Name(PRR0, ResourceTemplate() { Interrupt(, Level, ActiveHigh, Shared) { 0 } diff --git a/hw/i386/acpi-dsdt.hex.generated b/hw/i386/acpi-dsdt.hex.generated index 2c011070c4..f8bd4ea1b5 100644 --- a/hw/i386/acpi-dsdt.hex.generated +++ b/hw/i386/acpi-dsdt.hex.generated @@ -8,7 +8,7 @@ static unsigned char AcpiDsdtAmlCode[] = { 0x0, 0x0, 0x1, -0xe0, +0xd8, 0x42, 0x58, 0x50, @@ -3379,7 +3379,7 @@ static unsigned char AcpiDsdtAmlCode[] = { 0x51, 0x43, 0x52, -0x1, +0x9, 0x8, 0x50, 0x52, diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 12c436e7f1..3cd8f383f3 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1093,21 +1093,13 @@ PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size, return guest_info; } -void pc_init_pci64_hole(PcPciInfo *pci_info, uint64_t pci_hole64_start, - uint64_t pci_hole64_size) +/* setup pci memory address space mapping into system address space */ +void pc_pci_as_mapping_init(Object *owner, MemoryRegion *system_memory, + MemoryRegion *pci_address_space) { - if ((sizeof(hwaddr) == 4) || (!pci_hole64_size)) { - return; - } - /* - * BIOS does not set MTRR entries for the 64 bit window, so no need to - * align address to power of two. Align address at 1G, this makes sure - * it can be exactly covered with a PAT entry even when using huge - * pages. - */ - pci_info->w64.begin = ROUND_UP(pci_hole64_start, 0x1ULL << 30); - pci_info->w64.end = pci_info->w64.begin + pci_hole64_size; - assert(pci_info->w64.begin <= pci_info->w64.end); + /* Set to lower priority than RAM */ + memory_region_add_subregion_overlap(system_memory, 0x0, + pci_address_space, -1); } void pc_acpi_init(const char *default_dsdt) @@ -1261,7 +1253,8 @@ static const MemoryRegionOps ioportF0_io_ops = { void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, ISADevice **rtc_state, ISADevice **floppy, - bool no_vmport) + bool no_vmport, + uint32 hpet_irqs) { int i; DriveInfo *fd[MAX_FD]; @@ -1288,9 +1281,21 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, * when the HPET wants to take over. Thus we have to disable the latter. */ if (!no_hpet && (!kvm_irqchip_in_kernel() || kvm_has_pit_state2())) { - hpet = sysbus_try_create_simple("hpet", HPET_BASE, NULL); - + /* In order to set property, here not using sysbus_try_create_simple */ + hpet = qdev_try_create(NULL, TYPE_HPET); if (hpet) { + /* For pc-piix-*, hpet's intcap is always IRQ2. For pc-q35-1.7 + * and earlier, use IRQ2 for compat. Otherwise, use IRQ16~23, + * IRQ8 and IRQ2. + */ + uint8_t compat = object_property_get_int(OBJECT(hpet), + HPET_INTCAP, NULL); + if (!compat) { + qdev_prop_set_uint32(hpet, HPET_INTCAP, hpet_irqs); + } + qdev_init_nofail(hpet); + sysbus_mmio_map(SYS_BUS_DEVICE(hpet), 0, HPET_BASE); + for (i = 0; i < GSI_NUM_PINS; i++) { sysbus_connect_irq(SYS_BUS_DEVICE(hpet), i, gsi[i]); } diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 2111f0192c..4e0dae7981 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -28,6 +28,7 @@ #include "hw/loader.h" #include "hw/i386/pc.h" #include "hw/i386/apic.h" +#include "hw/i386/smbios.h" #include "hw/pci/pci.h" #include "hw/pci/pci_ids.h" #include "hw/usb.h" @@ -59,6 +60,7 @@ static const int ide_irq[MAX_IDE_BUS] = { 14, 15 }; static bool has_pci_info; static bool has_acpi_build = true; +static bool smbios_type1_defaults = true; /* PC hardware initialisation */ static void pc_init1(QEMUMachineInitArgs *args, @@ -114,7 +116,7 @@ static void pc_init1(QEMUMachineInitArgs *args, if (pci_enabled) { pci_memory = g_new(MemoryRegion, 1); - memory_region_init(pci_memory, NULL, "pci", INT64_MAX); + memory_region_init(pci_memory, NULL, "pci", UINT64_MAX); rom_memory = pci_memory; } else { pci_memory = NULL; @@ -128,6 +130,12 @@ static void pc_init1(QEMUMachineInitArgs *args, guest_info->has_pci_info = has_pci_info; guest_info->isapc_ram_fw = !pci_enabled; + if (smbios_type1_defaults) { + /* These values are guest ABI, do not change */ + smbios_set_type1_defaults("QEMU", "Standard PC (i440FX + PIIX, 1996)", + args->machine->name); + } + /* allocate ram and load rom/bios */ if (!xen_enabled()) { fw_cfg = pc_memory_init(system_memory, @@ -149,8 +157,6 @@ static void pc_init1(QEMUMachineInitArgs *args, if (pci_enabled) { pci_bus = i440fx_init(&i440fx_state, &piix3_devfn, &isa_bus, gsi, system_memory, system_io, args->ram_size, - below_4g_mem_size, - 0x100000000ULL - below_4g_mem_size, above_4g_mem_size, pci_memory, ram_memory); } else { @@ -183,7 +189,8 @@ static void pc_init1(QEMUMachineInitArgs *args, pc_vga_init(isa_bus, pci_enabled ? pci_bus : NULL); /* init basic PC hardware */ - pc_basic_device_init(isa_bus, gsi, &rtc_state, &floppy, xen_enabled()); + pc_basic_device_init(isa_bus, gsi, &rtc_state, &floppy, xen_enabled(), + 0x4); pc_nic_init(isa_bus, pci_bus); @@ -235,8 +242,14 @@ static void pc_init_pci(QEMUMachineInitArgs *args) pc_init1(args, 1, 1); } +static void pc_compat_1_7(QEMUMachineInitArgs *args) +{ + smbios_type1_defaults = false; +} + static void pc_compat_1_6(QEMUMachineInitArgs *args) { + pc_compat_1_7(args); has_pci_info = false; rom_file_in_ram = false; has_acpi_build = false; @@ -267,6 +280,12 @@ static void pc_compat_1_2(QEMUMachineInitArgs *args) disable_kvm_pv_eoi(); } +static void pc_init_pci_1_7(QEMUMachineInitArgs *args) +{ + pc_compat_1_7(args); + pc_init_pci(args); +} + static void pc_init_pci_1_6(QEMUMachineInitArgs *args) { pc_compat_1_6(args); @@ -303,6 +322,7 @@ static void pc_init_pci_no_kvmclock(QEMUMachineInitArgs *args) { has_pci_info = false; has_acpi_build = false; + smbios_type1_defaults = false; disable_kvm_pv_eoi(); enable_compat_apic_id_mode(); pc_init1(args, 1, 0); @@ -312,6 +332,7 @@ static void pc_init_isa(QEMUMachineInitArgs *args) { has_pci_info = false; has_acpi_build = false; + smbios_type1_defaults = false; if (!args->cpu_model) { args->cpu_model = "486"; } @@ -339,13 +360,24 @@ static void pc_xen_hvm_init(QEMUMachineInitArgs *args) .desc = "Standard PC (i440FX + PIIX, 1996)", \ .hot_add_cpu = pc_hot_add_cpu +#define PC_I440FX_2_0_MACHINE_OPTIONS \ + PC_I440FX_MACHINE_OPTIONS, \ + .default_machine_opts = "firmware=bios-256k.bin" + +static QEMUMachine pc_i440fx_machine_v2_0 = { + PC_I440FX_2_0_MACHINE_OPTIONS, + .name = "pc-i440fx-2.0", + .alias = "pc", + .init = pc_init_pci, + .is_default = 1, +}; + #define PC_I440FX_1_7_MACHINE_OPTIONS PC_I440FX_MACHINE_OPTIONS + static QEMUMachine pc_i440fx_machine_v1_7 = { PC_I440FX_1_7_MACHINE_OPTIONS, .name = "pc-i440fx-1.7", - .alias = "pc", - .init = pc_init_pci, - .is_default = 1, + .init = pc_init_pci_1_7, }; #define PC_I440FX_1_6_MACHINE_OPTIONS PC_I440FX_MACHINE_OPTIONS @@ -747,6 +779,7 @@ static QEMUMachine xenfv_machine = { static void pc_machine_init(void) { + qemu_register_machine(&pc_i440fx_machine_v2_0); qemu_register_machine(&pc_i440fx_machine_v1_7); qemu_register_machine(&pc_i440fx_machine_v1_6); qemu_register_machine(&pc_i440fx_machine_v1_5); diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 600fc02ebe..07f38ff704 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -39,6 +39,7 @@ #include "hw/pci-host/q35.h" #include "exec/address-spaces.h" #include "hw/i386/ich9.h" +#include "hw/i386/smbios.h" #include "hw/ide/pci.h" #include "hw/ide/ahci.h" #include "hw/usb.h" @@ -49,6 +50,7 @@ static bool has_pci_info; static bool has_acpi_build = true; +static bool smbios_type1_defaults = true; /* PC hardware initialisation */ static void pc_q35_init(QEMUMachineInitArgs *args) @@ -101,7 +103,7 @@ static void pc_q35_init(QEMUMachineInitArgs *args) /* pci enabled */ if (pci_enabled) { pci_memory = g_new(MemoryRegion, 1); - memory_region_init(pci_memory, NULL, "pci", INT64_MAX); + memory_region_init(pci_memory, NULL, "pci", UINT64_MAX); rom_memory = pci_memory; } else { pci_memory = NULL; @@ -113,6 +115,12 @@ static void pc_q35_init(QEMUMachineInitArgs *args) guest_info->isapc_ram_fw = false; guest_info->has_acpi_build = has_acpi_build; + if (smbios_type1_defaults) { + /* These values are guest ABI, do not change */ + smbios_set_type1_defaults("QEMU", "Standard PC (Q35 + ICH9, 2009)", + args->machine->name); + } + /* allocate ram and load rom/bios */ if (!xen_enabled()) { pc_memory_init(get_system_memory(), @@ -182,7 +190,7 @@ static void pc_q35_init(QEMUMachineInitArgs *args) pc_register_ferr_irq(gsi[13]); /* init basic PC hardware */ - pc_basic_device_init(isa_bus, gsi, &rtc_state, &floppy, false); + pc_basic_device_init(isa_bus, gsi, &rtc_state, &floppy, false, 0xff0104); /* connect pm stuff to lpc */ ich9_lpc_pm_init(lpc); @@ -217,8 +225,14 @@ static void pc_q35_init(QEMUMachineInitArgs *args) } } +static void pc_compat_1_7(QEMUMachineInitArgs *args) +{ + smbios_type1_defaults = false; +} + static void pc_compat_1_6(QEMUMachineInitArgs *args) { + pc_compat_1_7(args); has_pci_info = false; rom_file_in_ram = false; has_acpi_build = false; @@ -236,6 +250,12 @@ static void pc_compat_1_4(QEMUMachineInitArgs *args) x86_cpu_compat_set_features("Westmere", FEAT_1_ECX, 0, CPUID_EXT_PCLMULQDQ); } +static void pc_q35_init_1_7(QEMUMachineInitArgs *args) +{ + pc_compat_1_7(args); + pc_q35_init(args); +} + static void pc_q35_init_1_6(QEMUMachineInitArgs *args) { pc_compat_1_6(args); @@ -259,13 +279,27 @@ static void pc_q35_init_1_4(QEMUMachineInitArgs *args) .desc = "Standard PC (Q35 + ICH9, 2009)", \ .hot_add_cpu = pc_hot_add_cpu +#define PC_Q35_2_0_MACHINE_OPTIONS \ + PC_Q35_MACHINE_OPTIONS, \ + .default_machine_opts = "firmware=bios-256k.bin" + +static QEMUMachine pc_q35_machine_v2_0 = { + PC_Q35_2_0_MACHINE_OPTIONS, + .name = "pc-q35-2.0", + .alias = "q35", + .init = pc_q35_init, +}; + #define PC_Q35_1_7_MACHINE_OPTIONS PC_Q35_MACHINE_OPTIONS static QEMUMachine pc_q35_machine_v1_7 = { PC_Q35_1_7_MACHINE_OPTIONS, .name = "pc-q35-1.7", - .alias = "q35", - .init = pc_q35_init, + .init = pc_q35_init_1_7, + .compat_props = (GlobalProperty[]) { + PC_Q35_COMPAT_1_7, + { /* end of list */ } + }, }; #define PC_Q35_1_6_MACHINE_OPTIONS PC_Q35_MACHINE_OPTIONS @@ -275,7 +309,7 @@ static QEMUMachine pc_q35_machine_v1_6 = { .name = "pc-q35-1.6", .init = pc_q35_init_1_6, .compat_props = (GlobalProperty[]) { - PC_COMPAT_1_6, + PC_Q35_COMPAT_1_6, { /* end of list */ } }, }; @@ -285,7 +319,7 @@ static QEMUMachine pc_q35_machine_v1_5 = { .name = "pc-q35-1.5", .init = pc_q35_init_1_5, .compat_props = (GlobalProperty[]) { - PC_COMPAT_1_5, + PC_Q35_COMPAT_1_5, { /* end of list */ } }, }; @@ -306,6 +340,7 @@ static QEMUMachine pc_q35_machine_v1_4 = { static void pc_q35_machine_init(void) { + qemu_register_machine(&pc_q35_machine_v2_0); qemu_register_machine(&pc_q35_machine_v1_7); qemu_register_machine(&pc_q35_machine_v1_6); qemu_register_machine(&pc_q35_machine_v1_5); diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl index 21c89b098b..575c5d7376 100644 --- a/hw/i386/q35-acpi-dsdt.dsl +++ b/hw/i386/q35-acpi-dsdt.dsl @@ -333,7 +333,7 @@ DefinitionBlock ( } Return (0x0B) } - Method(IQCR, 1, NotSerialized) { + Method(IQCR, 1, Serialized) { // _CRS method - get current settings Name(PRR0, ResourceTemplate() { Interrupt(, Level, ActiveHigh, Shared) { 0 } diff --git a/hw/i386/q35-acpi-dsdt.hex.generated b/hw/i386/q35-acpi-dsdt.hex.generated index 32c16ff86f..111ad3e9c2 100644 --- a/hw/i386/q35-acpi-dsdt.hex.generated +++ b/hw/i386/q35-acpi-dsdt.hex.generated @@ -8,7 +8,7 @@ static unsigned char Q35AcpiDsdtAmlCode[] = { 0x0, 0x0, 0x1, -0x6, +0xfe, 0x42, 0x58, 0x50, @@ -5338,7 +5338,7 @@ static unsigned char Q35AcpiDsdtAmlCode[] = { 0x51, 0x43, 0x52, -0x1, +0x9, 0x8, 0x50, 0x52, diff --git a/hw/i386/smbios.c b/hw/i386/smbios.c index d3f1ee65c6..e8f41ad435 100644 --- a/hw/i386/smbios.c +++ b/hw/i386/smbios.c @@ -256,6 +256,20 @@ static void smbios_build_type_1_fields(void) } } +void smbios_set_type1_defaults(const char *manufacturer, + const char *product, const char *version) +{ + if (!type1.manufacturer) { + type1.manufacturer = manufacturer; + } + if (!type1.product) { + type1.product = product; + } + if (!type1.version) { + type1.version = version; + } +} + uint8_t *smbios_get_table(size_t *length) { if (!smbios_immutable) { diff --git a/hw/misc/vfio.c b/hw/misc/vfio.c index f7f8a19ee8..9aecaa82bc 100644 --- a/hw/misc/vfio.c +++ b/hw/misc/vfio.c @@ -52,6 +52,8 @@ /* Extra debugging, trap acceleration paths for more logging */ #define VFIO_ALLOW_MMAP 1 #define VFIO_ALLOW_KVM_INTX 1 +#define VFIO_ALLOW_KVM_MSI 1 +#define VFIO_ALLOW_KVM_MSIX 1 struct VFIODevice; @@ -208,6 +210,17 @@ static QLIST_HEAD(, VFIOContainer) static QLIST_HEAD(, VFIOGroup) group_list = QLIST_HEAD_INITIALIZER(group_list); +#ifdef CONFIG_KVM +/* + * We have a single VFIO pseudo device per KVM VM. Once created it lives + * for the life of the VM. Closing the file descriptor only drops our + * reference to it and the device's reference to kvm. Therefore once + * initialized, this file descriptor is only released on QEMU exit and + * we'll re-use it should another vfio device be attached before then. + */ +static int vfio_kvm_device_fd = -1; +#endif + static void vfio_disable_interrupts(VFIODevice *vdev); static uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len); static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr, @@ -579,9 +592,21 @@ static void vfio_msi_interrupt(void *opaque) return; } - DPRINTF("%s(%04x:%02x:%02x.%x) vector %d\n", __func__, +#ifdef VFIO_DEBUG + MSIMessage msg; + + if (vdev->interrupt == VFIO_INT_MSIX) { + msg = msi_get_message(&vdev->pdev, nr); + } else if (vdev->interrupt == VFIO_INT_MSI) { + msg = msix_get_message(&vdev->pdev, nr); + } else { + abort(); + } + + DPRINTF("%s(%04x:%02x:%02x.%x) vector %d 0x%"PRIx64"/0x%x\n", __func__, vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function, nr); + vdev->host.function, nr, msg.address, msg.data); +#endif if (vdev->interrupt == VFIO_INT_MSIX) { msix_notify(&vdev->pdev, nr); @@ -649,7 +674,8 @@ static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr, * Attempt to enable route through KVM irqchip, * default to userspace handling if unavailable. */ - vector->virq = msg ? kvm_irqchip_add_msi_route(kvm_state, *msg) : -1; + vector->virq = msg && VFIO_ALLOW_KVM_MSIX ? + kvm_irqchip_add_msi_route(kvm_state, *msg) : -1; if (vector->virq < 0 || kvm_irqchip_add_irqfd_notifier(kvm_state, &vector->interrupt, NULL, vector->virq) < 0) { @@ -816,7 +842,8 @@ retry: * Attempt to enable route through KVM irqchip, * default to userspace handling if unavailable. */ - vector->virq = kvm_irqchip_add_msi_route(kvm_state, vector->msg); + vector->virq = VFIO_ALLOW_KVM_MSI ? + kvm_irqchip_add_msi_route(kvm_state, vector->msg) : -1; if (vector->virq < 0 || kvm_irqchip_add_irqfd_notifier(kvm_state, &vector->interrupt, NULL, vector->virq) < 0) { @@ -878,8 +905,20 @@ static void vfio_disable_msi_common(VFIODevice *vdev) static void vfio_disable_msix(VFIODevice *vdev) { + int i; + msix_unset_vector_notifiers(&vdev->pdev); + /* + * MSI-X will only release vectors if MSI-X is still enabled on the + * device, check through the rest and release it ourselves if necessary. + */ + for (i = 0; i < vdev->nr_vectors; i++) { + if (vdev->msi_vectors[i].use) { + vfio_msix_vector_release(&vdev->pdev, i); + } + } + if (vdev->nr_vectors) { vfio_disable_irqindex(vdev, VFIO_PCI_MSIX_IRQ_INDEX); } @@ -1800,6 +1839,34 @@ static void vfio_probe_nvidia_bar5_window_quirk(VFIODevice *vdev, int nr) vdev->host.function); } +static void vfio_nvidia_88000_quirk_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + VFIOQuirk *quirk = opaque; + VFIODevice *vdev = quirk->vdev; + PCIDevice *pdev = &vdev->pdev; + hwaddr base = quirk->data.address_match & TARGET_PAGE_MASK; + + vfio_generic_quirk_write(opaque, addr, data, size); + + /* + * Nvidia seems to acknowledge MSI interrupts by writing 0xff to the + * MSI capability ID register. Both the ID and next register are + * read-only, so we allow writes covering either of those to real hw. + * NB - only fixed for the 0x88000 MMIO window. + */ + if ((pdev->cap_present & QEMU_PCI_CAP_MSI) && + vfio_range_contained(addr, size, pdev->msi_cap, PCI_MSI_FLAGS)) { + vfio_bar_write(&vdev->bars[quirk->data.bar], addr + base, data, size); + } +} + +static const MemoryRegionOps vfio_nvidia_88000_quirk = { + .read = vfio_generic_quirk_read, + .write = vfio_nvidia_88000_quirk_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + /* * Finally, BAR0 itself. We want to redirect any accesses to either * 0x1800 or 0x88000 through the PCI config space access functions. @@ -1826,7 +1893,7 @@ static void vfio_probe_nvidia_bar0_88000_quirk(VFIODevice *vdev, int nr) quirk->data.address_mask = PCIE_CONFIG_SPACE_SIZE - 1; quirk->data.bar = nr; - memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_generic_quirk, + memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_nvidia_88000_quirk, quirk, "vfio-nvidia-bar0-88000-quirk", TARGET_PAGE_ALIGN(quirk->data.address_mask + 1)); memory_region_add_subregion_overlap(&vdev->bars[nr].mem, @@ -3041,6 +3108,59 @@ static void vfio_pci_reset_handler(void *opaque) } } +static void vfio_kvm_device_add_group(VFIOGroup *group) +{ +#ifdef CONFIG_KVM + struct kvm_device_attr attr = { + .group = KVM_DEV_VFIO_GROUP, + .attr = KVM_DEV_VFIO_GROUP_ADD, + .addr = (uint64_t)(unsigned long)&group->fd, + }; + + if (!kvm_enabled()) { + return; + } + + if (vfio_kvm_device_fd < 0) { + struct kvm_create_device cd = { + .type = KVM_DEV_TYPE_VFIO, + }; + + if (kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd)) { + DPRINTF("KVM_CREATE_DEVICE: %m\n"); + return; + } + + vfio_kvm_device_fd = cd.fd; + } + + if (ioctl(vfio_kvm_device_fd, KVM_SET_DEVICE_ATTR, &attr)) { + error_report("Failed to add group %d to KVM VFIO device: %m", + group->groupid); + } +#endif +} + +static void vfio_kvm_device_del_group(VFIOGroup *group) +{ +#ifdef CONFIG_KVM + struct kvm_device_attr attr = { + .group = KVM_DEV_VFIO_GROUP, + .attr = KVM_DEV_VFIO_GROUP_DEL, + .addr = (uint64_t)(unsigned long)&group->fd, + }; + + if (vfio_kvm_device_fd < 0) { + return; + } + + if (ioctl(vfio_kvm_device_fd, KVM_SET_DEVICE_ATTR, &attr)) { + error_report("Failed to remove group %d to KVM VFIO device: %m", + group->groupid); + } +#endif +} + static int vfio_connect_container(VFIOGroup *group) { VFIOContainer *container; @@ -3189,6 +3309,8 @@ static VFIOGroup *vfio_get_group(int groupid) QLIST_INSERT_HEAD(&group_list, group, next); + vfio_kvm_device_add_group(group); + return group; } @@ -3198,6 +3320,7 @@ static void vfio_put_group(VFIOGroup *group) return; } + vfio_kvm_device_del_group(group); vfio_disconnect_container(group); QLIST_REMOVE(group, next); DPRINTF("vfio_put_group: close group->fd\n"); diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index 4a355bbbef..92dc2f21fa 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -222,8 +222,13 @@ #define PHY_REG_INT_ST_ENERGY 0x0010 /***********************************************************************/ -#define GEM_RX_REJECT 1 -#define GEM_RX_ACCEPT 0 +#define GEM_RX_REJECT (-1) +#define GEM_RX_PROMISCUOUS_ACCEPT (-2) +#define GEM_RX_BROADCAST_ACCEPT (-3) +#define GEM_RX_MULTICAST_HASH_ACCEPT (-4) +#define GEM_RX_UNICAST_HASH_ACCEPT (-5) + +#define GEM_RX_SAR_ACCEPT 0 /***********************************************************************/ @@ -236,6 +241,13 @@ #define DESC_0_RX_WRAP 0x00000002 #define DESC_0_RX_OWNERSHIP 0x00000001 +#define R_DESC_1_RX_SAR_SHIFT 25 +#define R_DESC_1_RX_SAR_LENGTH 2 +#define R_DESC_1_RX_SAR_MATCH (1 << 27) +#define R_DESC_1_RX_UNICAST_HASH (1 << 29) +#define R_DESC_1_RX_MULTICAST_HASH (1 << 30) +#define R_DESC_1_RX_BROADCAST (1 << 31) + #define DESC_1_RX_SOF 0x00004000 #define DESC_1_RX_EOF 0x00008000 @@ -315,6 +327,28 @@ static inline void rx_desc_set_length(unsigned *desc, unsigned len) desc[1] |= len; } +static inline void rx_desc_set_broadcast(unsigned *desc) +{ + desc[1] |= R_DESC_1_RX_BROADCAST; +} + +static inline void rx_desc_set_unicast_hash(unsigned *desc) +{ + desc[1] |= R_DESC_1_RX_UNICAST_HASH; +} + +static inline void rx_desc_set_multicast_hash(unsigned *desc) +{ + desc[1] |= R_DESC_1_RX_MULTICAST_HASH; +} + +static inline void rx_desc_set_sar(unsigned *desc, int sar_idx) +{ + desc[1] = deposit32(desc[1], R_DESC_1_RX_SAR_SHIFT, R_DESC_1_RX_SAR_LENGTH, + sar_idx); + desc[1] |= R_DESC_1_RX_SAR_MATCH; +} + #define TYPE_CADENCE_GEM "cadence_gem" #define GEM(obj) OBJECT_CHECK(GemState, (obj), TYPE_CADENCE_GEM) @@ -346,6 +380,11 @@ typedef struct GemState { uint32_t rx_desc_addr; uint32_t tx_desc_addr; + uint8_t can_rx_state; /* Debug only */ + + unsigned rx_desc[2]; + + bool sar_active[4]; } GemState; /* The broadcast MAC address: 0xFFFFFFFFFFFF */ @@ -415,13 +454,28 @@ static int gem_can_receive(NetClientState *nc) s = qemu_get_nic_opaque(nc); - DB_PRINT("\n"); - /* Do nothing if receive is not enabled. */ if (!(s->regs[GEM_NWCTRL] & GEM_NWCTRL_RXENA)) { + if (s->can_rx_state != 1) { + s->can_rx_state = 1; + DB_PRINT("can't receive - no enable\n"); + } return 0; } + if (rx_desc_get_ownership(s->rx_desc) == 1) { + if (s->can_rx_state != 2) { + s->can_rx_state = 2; + DB_PRINT("can't receive - busy buffer descriptor 0x%x\n", + s->rx_desc_addr); + } + return 0; + } + + if (s->can_rx_state != 0) { + s->can_rx_state = 0; + DB_PRINT("can receive 0x%x\n", s->rx_desc_addr); + } return 1; } @@ -527,7 +581,10 @@ static unsigned calc_mac_hash(const uint8_t *mac) * Accept or reject this destination address? * Returns: * GEM_RX_REJECT: reject - * GEM_RX_ACCEPT: accept + * >= 0: Specific address accept (which matched SAR is returned) + * others for various other modes of accept: + * GEM_RM_PROMISCUOUS_ACCEPT, GEM_RX_BROADCAST_ACCEPT, + * GEM_RX_MULTICAST_HASH_ACCEPT or GEM_RX_UNICAST_HASH_ACCEPT */ static int gem_mac_address_filter(GemState *s, const uint8_t *packet) { @@ -536,7 +593,7 @@ static int gem_mac_address_filter(GemState *s, const uint8_t *packet) /* Promiscuous mode? */ if (s->regs[GEM_NWCFG] & GEM_NWCFG_PROMISC) { - return GEM_RX_ACCEPT; + return GEM_RX_PROMISCUOUS_ACCEPT; } if (!memcmp(packet, broadcast_addr, 6)) { @@ -544,7 +601,7 @@ static int gem_mac_address_filter(GemState *s, const uint8_t *packet) if (s->regs[GEM_NWCFG] & GEM_NWCFG_BCAST_REJ) { return GEM_RX_REJECT; } - return GEM_RX_ACCEPT; + return GEM_RX_BROADCAST_ACCEPT; } /* Accept packets -w- hash match? */ @@ -555,53 +612,67 @@ static int gem_mac_address_filter(GemState *s, const uint8_t *packet) hash_index = calc_mac_hash(packet); if (hash_index < 32) { if (s->regs[GEM_HASHLO] & (1<<hash_index)) { - return GEM_RX_ACCEPT; + return packet[0] == 0x01 ? GEM_RX_MULTICAST_HASH_ACCEPT : + GEM_RX_UNICAST_HASH_ACCEPT; } } else { hash_index -= 32; if (s->regs[GEM_HASHHI] & (1<<hash_index)) { - return GEM_RX_ACCEPT; + return packet[0] == 0x01 ? GEM_RX_MULTICAST_HASH_ACCEPT : + GEM_RX_UNICAST_HASH_ACCEPT; } } } /* Check all 4 specific addresses */ gem_spaddr = (uint8_t *)&(s->regs[GEM_SPADDR1LO]); - for (i = 0; i < 4; i++) { - if (!memcmp(packet, gem_spaddr, 6)) { - return GEM_RX_ACCEPT; + for (i = 3; i >= 0; i--) { + if (s->sar_active[i] && !memcmp(packet, gem_spaddr + 8 * i, 6)) { + return GEM_RX_SAR_ACCEPT + i; } - - gem_spaddr += 8; } /* No address match; reject the packet */ return GEM_RX_REJECT; } +static void gem_get_rx_desc(GemState *s) +{ + DB_PRINT("read descriptor 0x%x\n", (unsigned)s->rx_desc_addr); + /* read current descriptor */ + cpu_physical_memory_read(s->rx_desc_addr, + (uint8_t *)s->rx_desc, sizeof(s->rx_desc)); + + /* Descriptor owned by software ? */ + if (rx_desc_get_ownership(s->rx_desc) == 1) { + DB_PRINT("descriptor 0x%x owned by sw.\n", + (unsigned)s->rx_desc_addr); + s->regs[GEM_RXSTATUS] |= GEM_RXSTATUS_NOBUF; + s->regs[GEM_ISR] |= GEM_INT_RXUSED & ~(s->regs[GEM_IMR]); + /* Handle interrupt consequences */ + gem_update_int_status(s); + } +} + /* * gem_receive: * Fit a packet handed to us by QEMU into the receive descriptor ring. */ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) { - unsigned desc[2]; - hwaddr packet_desc_addr, last_desc_addr; GemState *s; unsigned rxbufsize, bytes_to_copy; unsigned rxbuf_offset; uint8_t rxbuf[2048]; uint8_t *rxbuf_ptr; + bool first_desc = true; + int maf; s = qemu_get_nic_opaque(nc); - /* Do nothing if receive is not enabled. */ - if (!gem_can_receive(nc)) { - return -1; - } - /* Is this destination MAC address "for us" ? */ - if (gem_mac_address_filter(s, buf) == GEM_RX_REJECT) { + maf = gem_mac_address_filter(s, buf); + if (maf == GEM_RX_REJECT) { return -1; } @@ -633,6 +704,14 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) GEM_DMACFG_RBUFSZ_S) * GEM_DMACFG_RBUFSZ_MUL; bytes_to_copy = size; + /* Pad to minimum length. Assume FCS field is stripped, logic + * below will increment it to the real minimum of 64 when + * not FCS stripping + */ + if (size < 60) { + size = 60; + } + /* Strip of FCS field ? (usually yes) */ if (s->regs[GEM_NWCFG] & GEM_NWCFG_STRIP_FCS) { rxbuf_ptr = (void *)buf; @@ -659,95 +738,71 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) size += 4; } - /* Pad to minimum length */ - if (size < 64) { - size = 64; - } - DB_PRINT("config bufsize: %d packet size: %ld\n", rxbufsize, size); - packet_desc_addr = s->rx_desc_addr; - while (1) { - DB_PRINT("read descriptor 0x%x\n", (unsigned)packet_desc_addr); - /* read current descriptor */ - cpu_physical_memory_read(packet_desc_addr, - (uint8_t *)&desc[0], sizeof(desc)); - - /* Descriptor owned by software ? */ - if (rx_desc_get_ownership(desc) == 1) { - DB_PRINT("descriptor 0x%x owned by sw.\n", - (unsigned)packet_desc_addr); - s->regs[GEM_RXSTATUS] |= GEM_RXSTATUS_NOBUF; - s->regs[GEM_ISR] |= GEM_INT_RXUSED & ~(s->regs[GEM_IMR]); - /* Handle interrupt consequences */ - gem_update_int_status(s); + while (bytes_to_copy) { + /* Do nothing if receive is not enabled. */ + if (!gem_can_receive(nc)) { + assert(!first_desc); return -1; } DB_PRINT("copy %d bytes to 0x%x\n", MIN(bytes_to_copy, rxbufsize), - rx_desc_get_buffer(desc)); - - /* - * Let's have QEMU lend a helping hand. - */ - if (rx_desc_get_buffer(desc) == 0) { - DB_PRINT("Invalid RX buffer (NULL) for descriptor 0x%x\n", - (unsigned)packet_desc_addr); - break; - } + rx_desc_get_buffer(s->rx_desc)); /* Copy packet data to emulated DMA buffer */ - cpu_physical_memory_write(rx_desc_get_buffer(desc) + rxbuf_offset, + cpu_physical_memory_write(rx_desc_get_buffer(s->rx_desc) + rxbuf_offset, rxbuf_ptr, MIN(bytes_to_copy, rxbufsize)); - bytes_to_copy -= MIN(bytes_to_copy, rxbufsize); rxbuf_ptr += MIN(bytes_to_copy, rxbufsize); + bytes_to_copy -= MIN(bytes_to_copy, rxbufsize); + + /* Update the descriptor. */ + if (first_desc) { + rx_desc_set_sof(s->rx_desc); + first_desc = false; + } if (bytes_to_copy == 0) { + rx_desc_set_eof(s->rx_desc); + rx_desc_set_length(s->rx_desc, size); + } + rx_desc_set_ownership(s->rx_desc); + + switch (maf) { + case GEM_RX_PROMISCUOUS_ACCEPT: + break; + case GEM_RX_BROADCAST_ACCEPT: + rx_desc_set_broadcast(s->rx_desc); + break; + case GEM_RX_UNICAST_HASH_ACCEPT: + rx_desc_set_unicast_hash(s->rx_desc); + break; + case GEM_RX_MULTICAST_HASH_ACCEPT: + rx_desc_set_multicast_hash(s->rx_desc); break; + case GEM_RX_REJECT: + abort(); + default: /* SAR */ + rx_desc_set_sar(s->rx_desc, maf); } + /* Descriptor write-back. */ + cpu_physical_memory_write(s->rx_desc_addr, + (uint8_t *)s->rx_desc, sizeof(s->rx_desc)); + /* Next descriptor */ - if (rx_desc_get_wrap(desc)) { - packet_desc_addr = s->regs[GEM_RXQBASE]; + if (rx_desc_get_wrap(s->rx_desc)) { + DB_PRINT("wrapping RX descriptor list\n"); + s->rx_desc_addr = s->regs[GEM_RXQBASE]; } else { - packet_desc_addr += 8; + DB_PRINT("incrementing RX descriptor list\n"); + s->rx_desc_addr += 8; } + gem_get_rx_desc(s); } - DB_PRINT("set length: %ld, EOF on descriptor 0x%x\n", size, - (unsigned)packet_desc_addr); - - /* Update last descriptor with EOF and total length */ - rx_desc_set_eof(desc); - rx_desc_set_length(desc, size); - cpu_physical_memory_write(packet_desc_addr, - (uint8_t *)&desc[0], sizeof(desc)); - - /* Advance RX packet descriptor Q */ - last_desc_addr = packet_desc_addr; - packet_desc_addr = s->rx_desc_addr; - s->rx_desc_addr = last_desc_addr; - if (rx_desc_get_wrap(desc)) { - s->rx_desc_addr = s->regs[GEM_RXQBASE]; - DB_PRINT("wrapping RX descriptor list\n"); - } else { - DB_PRINT("incrementing RX descriptor list\n"); - s->rx_desc_addr += 8; - } - - DB_PRINT("set SOF, OWN on descriptor 0x%08x\n", (unsigned)packet_desc_addr); - /* Count it */ gem_receive_updatestats(s, buf, size); - /* Update first descriptor (which could also be the last) */ - /* read descriptor */ - cpu_physical_memory_read(packet_desc_addr, - (uint8_t *)&desc[0], sizeof(desc)); - rx_desc_set_sof(desc); - rx_desc_set_ownership(desc); - cpu_physical_memory_write(packet_desc_addr, - (uint8_t *)&desc[0], sizeof(desc)); - s->regs[GEM_RXSTATUS] |= GEM_RXSTATUS_FRMRCVD; s->regs[GEM_ISR] |= GEM_INT_RXCMPL & ~(s->regs[GEM_IMR]); @@ -893,7 +948,7 @@ static void gem_transmit(GemState *s) gem_transmit_updatestats(s, tx_packet, total_bytes); /* Send the packet somewhere */ - if (s->phy_loop) { + if (s->phy_loop || (s->regs[GEM_NWCTRL] & GEM_NWCTRL_LOCALLOOP)) { gem_receive(qemu_get_queue(s->nic), tx_packet, total_bytes); } else { qemu_send_packet(qemu_get_queue(s->nic), tx_packet, @@ -949,6 +1004,7 @@ static void gem_phy_reset(GemState *s) static void gem_reset(DeviceState *d) { + int i; GemState *s = GEM(d); DB_PRINT("\n"); @@ -968,6 +1024,10 @@ static void gem_reset(DeviceState *d) s->regs[GEM_DESCONF5] = 0x002f2145; s->regs[GEM_DESCONF6] = 0x00000200; + for (i = 0; i < 4; i++) { + s->sar_active[i] = false; + } + gem_phy_reset(s); gem_update_int_status(s); @@ -1069,19 +1129,21 @@ static void gem_write(void *opaque, hwaddr offset, uint64_t val, /* Squash bits which are read only in write value */ val &= ~(s->regs_ro[offset]); - /* Preserve (only) bits which are read only in register */ - readonly = s->regs[offset]; - readonly &= s->regs_ro[offset]; - - /* Squash bits which are write 1 to clear */ - val &= ~(s->regs_w1c[offset] & val); + /* Preserve (only) bits which are read only and wtc in register */ + readonly = s->regs[offset] & (s->regs_ro[offset] | s->regs_w1c[offset]); /* Copy register write to backing store */ - s->regs[offset] = val | readonly; + s->regs[offset] = (val & ~s->regs_w1c[offset]) | readonly; + + /* do w1c */ + s->regs[offset] &= ~(s->regs_w1c[offset] & val); /* Handle register write side effects */ switch (offset) { case GEM_NWCTRL: + if (val & GEM_NWCTRL_RXENA) { + gem_get_rx_desc(s); + } if (val & GEM_NWCTRL_TXSTART) { gem_transmit(s); } @@ -1089,7 +1151,7 @@ static void gem_write(void *opaque, hwaddr offset, uint64_t val, /* Reset to start of Q when transmit disabled. */ s->tx_desc_addr = s->regs[GEM_TXQBASE]; } - if (val & GEM_NWCTRL_RXENA) { + if (gem_can_receive(qemu_get_queue(s->nic))) { qemu_flush_queued_packets(qemu_get_queue(s->nic)); } break; @@ -1114,6 +1176,18 @@ static void gem_write(void *opaque, hwaddr offset, uint64_t val, s->regs[GEM_IMR] |= val; gem_update_int_status(s); break; + case GEM_SPADDR1LO: + case GEM_SPADDR2LO: + case GEM_SPADDR3LO: + case GEM_SPADDR4LO: + s->sar_active[(offset - GEM_SPADDR1LO) / 2] = false; + break; + case GEM_SPADDR1HI: + case GEM_SPADDR2HI: + case GEM_SPADDR3HI: + case GEM_SPADDR4HI: + s->sar_active[(offset - GEM_SPADDR1HI) / 2] = true; + break; case GEM_PHYMNTNC: if (val & GEM_PHYMNTNC_OP_W) { uint32_t phy_addr, reg_num; @@ -1181,15 +1255,17 @@ static int gem_init(SysBusDevice *sbd) static const VMStateDescription vmstate_cadence_gem = { .name = "cadence_gem", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, + .version_id = 2, + .minimum_version_id = 2, + .minimum_version_id_old = 2, .fields = (VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, GemState, GEM_MAXREG), VMSTATE_UINT16_ARRAY(phy_regs, GemState, 32), VMSTATE_UINT8(phy_loop, GemState), VMSTATE_UINT32(rx_desc_addr, GemState), VMSTATE_UINT32(tx_desc_addr, GemState), + VMSTATE_BOOL_ARRAY(sar_active, GemState, 4), + VMSTATE_END_OF_LIST(), } }; diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 2b193bf6df..36266083b2 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -610,11 +610,11 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd, return VIRTIO_NET_ERR; } - n->mac_table.in_use = 0; - n->mac_table.first_multi = 0; - n->mac_table.uni_overflow = 0; - n->mac_table.multi_overflow = 0; - memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN); + int in_use = 0; + int first_multi = 0; + uint8_t uni_overflow = 0; + uint8_t multi_overflow = 0; + uint8_t *macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN); s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries, sizeof(mac_data.entries)); @@ -629,19 +629,19 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd, } if (mac_data.entries <= MAC_TABLE_ENTRIES) { - s = iov_to_buf(iov, iov_cnt, 0, n->mac_table.macs, + s = iov_to_buf(iov, iov_cnt, 0, macs, mac_data.entries * ETH_ALEN); if (s != mac_data.entries * ETH_ALEN) { goto error; } - n->mac_table.in_use += mac_data.entries; + in_use += mac_data.entries; } else { - n->mac_table.uni_overflow = 1; + uni_overflow = 1; } iov_discard_front(&iov, &iov_cnt, mac_data.entries * ETH_ALEN); - n->mac_table.first_multi = n->mac_table.in_use; + first_multi = in_use; s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries, sizeof(mac_data.entries)); @@ -656,24 +656,29 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd, goto error; } - if (n->mac_table.in_use + mac_data.entries <= MAC_TABLE_ENTRIES) { - s = iov_to_buf(iov, iov_cnt, 0, - &n->mac_table.macs[n->mac_table.in_use * ETH_ALEN], + if (in_use + mac_data.entries <= MAC_TABLE_ENTRIES) { + s = iov_to_buf(iov, iov_cnt, 0, &macs[in_use * ETH_ALEN], mac_data.entries * ETH_ALEN); if (s != mac_data.entries * ETH_ALEN) { goto error; } - n->mac_table.in_use += mac_data.entries; + in_use += mac_data.entries; } else { - n->mac_table.multi_overflow = 1; + multi_overflow = 1; } + n->mac_table.in_use = in_use; + n->mac_table.first_multi = first_multi; + n->mac_table.uni_overflow = uni_overflow; + n->mac_table.multi_overflow = multi_overflow; + memcpy(n->mac_table.macs, macs, MAC_TABLE_ENTRIES * ETH_ALEN); + g_free(macs); rxfilter_notify(nc); return VIRTIO_NET_OK; error: - rxfilter_notify(nc); + g_free(macs); return VIRTIO_NET_ERR; } @@ -1428,7 +1433,7 @@ static NetClientInfo net_virtio_info = { .size = sizeof(NICState), .can_receive = virtio_net_can_receive, .receive = virtio_net_receive, - .cleanup = virtio_net_cleanup, + .cleanup = virtio_net_cleanup, .link_status_changed = virtio_net_set_link_status, .query_rx_filter = virtio_net_query_rxfilter, }; diff --git a/hw/nvram/eeprom93xx.c b/hw/nvram/eeprom93xx.c index 08f4df586c..a98f924b81 100644 --- a/hw/nvram/eeprom93xx.c +++ b/hw/nvram/eeprom93xx.c @@ -126,7 +126,7 @@ static const VMStateDescription vmstate_eeprom = { .version_id = EEPROM_VERSION, .minimum_version_id = OLD_EEPROM_VERSION, .minimum_version_id_old = OLD_EEPROM_VERSION, - .fields = (VMStateField []) { + .fields = (VMStateField[]) { VMSTATE_UINT8(tick, eeprom_t), VMSTATE_UINT8(address, eeprom_t), VMSTATE_UINT8(command, eeprom_t), @@ -157,13 +157,13 @@ void eeprom93xx_write(eeprom_t *eeprom, int eecs, int eesk, int eedi) logout("CS=%u SK=%u DI=%u DO=%u, tick = %u\n", eecs, eesk, eedi, eedo, tick); - if (! eeprom->eecs && eecs) { + if (!eeprom->eecs && eecs) { /* Start chip select cycle. */ logout("Cycle start, waiting for 1st start bit (0)\n"); tick = 0; command = 0x0; address = 0x0; - } else if (eeprom->eecs && ! eecs) { + } else if (eeprom->eecs && !eecs) { /* End chip select cycle. This triggers write / erase. */ if (eeprom->writable) { uint8_t subcommand = address >> (eeprom->addrbits - 2); @@ -189,7 +189,7 @@ void eeprom93xx_write(eeprom_t *eeprom, int eecs, int eesk, int eedi) } /* Output DO is tristate, read results in 1. */ eedo = 1; - } else if (eecs && ! eeprom->eesk && eesk) { + } else if (eecs && !eeprom->eesk && eesk) { /* Raising edge of clock shifts data in. */ if (tick == 0) { /* Wait for 1st start bit. */ @@ -230,20 +230,20 @@ void eeprom93xx_write(eeprom_t *eeprom, int eecs, int eesk, int eedi) if (command == 0) { /* Command code in upper 2 bits of address. */ switch (address >> (eeprom->addrbits - 2)) { - case 0: - logout("write disable command\n"); - eeprom->writable = 0; - break; - case 1: - logout("write all command\n"); - break; - case 2: - logout("erase all command\n"); - break; - case 3: - logout("write enable command\n"); - eeprom->writable = 1; - break; + case 0: + logout("write disable command\n"); + eeprom->writable = 0; + break; + case 1: + logout("write all command\n"); + break; + case 2: + logout("erase all command\n"); + break; + case 3: + logout("write enable command\n"); + eeprom->writable = 1; + break; } } else { /* Read, write or erase word. */ @@ -276,7 +276,7 @@ uint16_t eeprom93xx_read(eeprom_t *eeprom) { /* Return status of pin DO (0 or 1). */ logout("CS=%u DO=%u\n", eeprom->eecs, eeprom->eedo); - return (eeprom->eedo); + return eeprom->eedo; } #if 0 @@ -296,18 +296,18 @@ eeprom_t *eeprom93xx_new(DeviceState *dev, uint16_t nwords) uint8_t addrbits; switch (nwords) { - case 16: - case 64: - addrbits = 6; - break; - case 128: - case 256: - addrbits = 8; - break; - default: - assert(!"Unsupported EEPROM size, fallback to 64 words!"); - nwords = 64; - addrbits = 6; + case 16: + case 64: + addrbits = 6; + break; + case 128: + case 256: + addrbits = 8; + break; + default: + assert(!"Unsupported EEPROM size, fallback to 64 words!"); + nwords = 64; + addrbits = 6; } eeprom = (eeprom_t *)g_malloc0(sizeof(*eeprom) + nwords * 2); diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c index edc974ece3..63be7f6cee 100644 --- a/hw/pci-host/piix.c +++ b/hw/pci-host/piix.c @@ -103,8 +103,6 @@ struct PCII440FXState { MemoryRegion *system_memory; MemoryRegion *pci_address_space; MemoryRegion *ram_memory; - MemoryRegion pci_hole; - MemoryRegion pci_hole_64bit; PAMMemoryRegion pam_regions[13]; MemoryRegion smram_region; uint8_t smm_enabled; @@ -313,8 +311,6 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state, MemoryRegion *address_space_mem, MemoryRegion *address_space_io, ram_addr_t ram_size, - hwaddr pci_hole_start, - hwaddr pci_hole_size, ram_addr_t above_4g_mem_size, MemoryRegion *pci_address_space, MemoryRegion *ram_memory) @@ -327,7 +323,6 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state, PCII440FXState *f; unsigned i; I440FXState *i440fx; - uint64_t pci_hole64_size; dev = qdev_create(NULL, TYPE_I440FX_PCI_HOST_BRIDGE); s = PCI_HOST_BRIDGE(dev); @@ -355,23 +350,10 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state, i440fx->pci_info.w32.begin = 0xe0000000; } - memory_region_init_alias(&f->pci_hole, OBJECT(d), "pci-hole", f->pci_address_space, - pci_hole_start, pci_hole_size); - memory_region_add_subregion(f->system_memory, pci_hole_start, &f->pci_hole); - - pci_hole64_size = pci_host_get_hole64_size(i440fx->pci_hole64_size); - - pc_init_pci64_hole(&i440fx->pci_info, 0x100000000ULL + above_4g_mem_size, - pci_hole64_size); - memory_region_init_alias(&f->pci_hole_64bit, OBJECT(d), "pci-hole64", - f->pci_address_space, - i440fx->pci_info.w64.begin, - pci_hole64_size); - if (pci_hole64_size) { - memory_region_add_subregion(f->system_memory, - i440fx->pci_info.w64.begin, - &f->pci_hole_64bit); - } + /* setup pci memory mapping */ + pc_pci_as_mapping_init(OBJECT(f), f->system_memory, + f->pci_address_space); + memory_region_init_alias(&f->smram_region, OBJECT(d), "smram-region", f->pci_address_space, 0xa0000, 0x20000); memory_region_add_subregion_overlap(f->system_memory, 0xa0000, diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index c043998e32..81c82404d6 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -356,28 +356,11 @@ static int mch_init(PCIDevice *d) { int i; MCHPCIState *mch = MCH_PCI_DEVICE(d); - uint64_t pci_hole64_size; - - /* setup pci memory regions */ - memory_region_init_alias(&mch->pci_hole, OBJECT(mch), "pci-hole", - mch->pci_address_space, - mch->below_4g_mem_size, - 0x100000000ULL - mch->below_4g_mem_size); - memory_region_add_subregion(mch->system_memory, mch->below_4g_mem_size, - &mch->pci_hole); - - pci_hole64_size = pci_host_get_hole64_size(mch->pci_hole64_size); - pc_init_pci64_hole(&mch->pci_info, 0x100000000ULL + mch->above_4g_mem_size, - pci_hole64_size); - memory_region_init_alias(&mch->pci_hole_64bit, OBJECT(mch), "pci-hole64", - mch->pci_address_space, - mch->pci_info.w64.begin, - pci_hole64_size); - if (pci_hole64_size) { - memory_region_add_subregion(mch->system_memory, - mch->pci_info.w64.begin, - &mch->pci_hole_64bit); - } + + /* setup pci memory mapping */ + pc_pci_as_mapping_init(OBJECT(mch), mch->system_memory, + mch->pci_address_space); + /* smram */ cpu_smm_register(&mch_set_smm, mch); memory_region_init_alias(&mch->smram_region, OBJECT(mch), "smram-region", diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 49eca955aa..82c11ecde4 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -1330,7 +1330,7 @@ static const pci_class_desc pci_class_descriptions[] = { 0x0601, "ISA bridge", "isa"}, { 0x0602, "EISA bridge", "eisa"}, { 0x0603, "MC bridge", "mca"}, - { 0x0604, "PCI bridge", "pci"}, + { 0x0604, "PCI bridge", "pci-bridge"}, { 0x0605, "PCMCIA bridge", "pcmcia"}, { 0x0606, "NUBUS bridge", "nubus"}, { 0x0607, "CARDBUS bridge", "cardbus"}, diff --git a/hw/pci/pci_bridge.c b/hw/pci/pci_bridge.c index 290ababb8b..f72872ebcf 100644 --- a/hw/pci/pci_bridge.c +++ b/hw/pci/pci_bridge.c @@ -372,7 +372,7 @@ int pci_bridge_initfn(PCIDevice *dev, const char *typename) sec_bus->parent_dev = dev; sec_bus->map_irq = br->map_irq ? br->map_irq : pci_swizzle_map_irq_fn; sec_bus->address_space_mem = &br->address_space_mem; - memory_region_init(&br->address_space_mem, OBJECT(br), "pci_bridge_pci", INT64_MAX); + memory_region_init(&br->address_space_mem, OBJECT(br), "pci_bridge_pci", UINT64_MAX); sec_bus->address_space_io = &br->address_space_io; memory_region_init(&br->address_space_io, OBJECT(br), "pci_bridge_io", 65536); br->windows = pci_bridge_region_init(br); diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index 1e578dd59d..c1faf9ce27 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -34,7 +34,6 @@ #define MAX_CPUS 1 #define BIOS_SIZE (1024 * 1024) -#define BIOS_FILENAME "ppc_rom.bin" #define NVRAM_SIZE 0x2000 #define PROM_FILENAME "openbios-ppc" #define PROM_ADDR 0xfff00000 diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index bf2d3d4b35..114be64480 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -684,7 +684,7 @@ static inline void cpu_ppc_hdecr_excp(PowerPCCPU *cpu) } static void __cpu_ppc_store_decr(PowerPCCPU *cpu, uint64_t *nextp, - struct QEMUTimer *timer, + QEMUTimer *timer, void (*raise_excp)(PowerPCCPU *), uint32_t decr, uint32_t value, int is_excp) @@ -856,9 +856,9 @@ typedef struct ppc40x_timer_t ppc40x_timer_t; struct ppc40x_timer_t { uint64_t pit_reload; /* PIT auto-reload value */ uint64_t fit_next; /* Tick for next FIT interrupt */ - struct QEMUTimer *fit_timer; + QEMUTimer *fit_timer; uint64_t wdt_next; /* Tick for next WDT interrupt */ - struct QEMUTimer *wdt_timer; + QEMUTimer *wdt_timer; /* 405 have the PIT, 440 have a DECR. */ unsigned int decr_excp; diff --git a/hw/ppc/ppc405_uc.c b/hw/ppc/ppc405_uc.c index 6d6a7f1203..8109f92200 100644 --- a/hw/ppc/ppc405_uc.c +++ b/hw/ppc/ppc405_uc.c @@ -1234,7 +1234,7 @@ struct ppc4xx_gpt_t { MemoryRegion iomem; int64_t tb_offset; uint32_t tb_freq; - struct QEMUTimer *timer; + QEMUTimer *timer; qemu_irq irqs[5]; uint32_t oe; uint32_t ol; diff --git a/hw/ppc/ppc_booke.c b/hw/ppc/ppc_booke.c index b421620708..d8399602d6 100644 --- a/hw/ppc/ppc_booke.c +++ b/hw/ppc/ppc_booke.c @@ -64,10 +64,10 @@ typedef struct booke_timer_t booke_timer_t; struct booke_timer_t { uint64_t fit_next; - struct QEMUTimer *fit_timer; + QEMUTimer *fit_timer; uint64_t wdt_next; - struct QEMUTimer *wdt_timer; + QEMUTimer *wdt_timer; uint32_t flags; }; diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index edb4cb0413..2beedd45e9 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -555,7 +555,7 @@ static int spapr_phb_init(SysBusDevice *s) /* Initialize memory regions */ sprintf(namebuf, "%s.mmio", sphb->dtbusname); - memory_region_init(&sphb->memspace, OBJECT(sphb), namebuf, INT64_MAX); + memory_region_init(&sphb->memspace, OBJECT(sphb), namebuf, UINT64_MAX); sprintf(namebuf, "%s.mmio-alias", sphb->dtbusname); memory_region_init_alias(&sphb->memwindow, OBJECT(sphb), diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index ea916d1466..3496c0bbd8 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -886,7 +886,6 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) case RELEASE: case ERASE: case ALLOW_MEDIUM_REMOVAL: - case VERIFY_10: case SEEK_10: case SYNCHRONIZE_CACHE: case SYNCHRONIZE_CACHE_16: @@ -903,6 +902,16 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) case ALLOW_OVERWRITE: cmd->xfer = 0; break; + case VERIFY_10: + case VERIFY_12: + case VERIFY_16: + if ((buf[1] & 2) == 0) { + cmd->xfer = 0; + } else if ((buf[1] & 4) == 1) { + cmd->xfer = 1; + } + cmd->xfer *= dev->blocksize; + break; case MODE_SENSE: break; case WRITE_SAME_10: @@ -1100,6 +1109,9 @@ static void scsi_cmd_xfer_mode(SCSICommand *cmd) case WRITE_VERIFY_12: case WRITE_16: case WRITE_VERIFY_16: + case VERIFY_10: + case VERIFY_12: + case VERIFY_16: case COPY: case COPY_VERIFY: case COMPARE: @@ -1293,6 +1305,11 @@ const struct SCSISense sense_code_ILLEGAL_REQ_REMOVAL_PREVENTED = { .key = ILLEGAL_REQUEST, .asc = 0x53, .ascq = 0x02 }; +/* Illegal request, Invalid Transfer Tag */ +const struct SCSISense sense_code_INVALID_TAG = { + .key = ILLEGAL_REQUEST, .asc = 0x4b, .ascq = 0x01 +}; + /* Command aborted, I/O process terminated */ const struct SCSISense sense_code_IO_ERROR = { .key = ABORTED_COMMAND, .asc = 0x00, .ascq = 0x06 @@ -1308,6 +1325,11 @@ const struct SCSISense sense_code_LUN_FAILURE = { .key = ABORTED_COMMAND, .asc = 0x3e, .ascq = 0x01 }; +/* Command aborted, Overlapped Commands Attempted */ +const struct SCSISense sense_code_OVERLAPPED_COMMANDS = { + .key = ABORTED_COMMAND, .asc = 0x4e, .ascq = 0x00 +}; + /* Unit attention, Capacity data has changed */ const struct SCSISense sense_code_CAPACITY_CHANGED = { .key = UNIT_ATTENTION, .asc = 0x2a, .ascq = 0x09 diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 74e6a14c29..7653411097 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -41,6 +41,7 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0) #include <scsi/sg.h> #endif +#define SCSI_WRITE_SAME_MAX 524288 #define SCSI_DMA_BUF_SIZE 131072 #define SCSI_MAX_INQUIRY_LEN 256 #define SCSI_MAX_MODE_LEN 256 @@ -634,6 +635,8 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) buflen = 0x40; memset(outbuf + 4, 0, buflen - 4); + outbuf[4] = 0x1; /* wsnz */ + /* optimal transfer length granularity */ outbuf[6] = (min_io_size >> 8) & 0xff; outbuf[7] = min_io_size & 0xff; @@ -1543,10 +1546,16 @@ done: static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf) { + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); uint8_t *p = inbuf; int len = r->req.cmd.xfer; UnmapCBData *data; + /* Reject ANCHOR=1. */ + if (r->req.cmd.buf[1] & 0x1) { + goto invalid_field; + } + if (len < 8) { goto invalid_param_len; } @@ -1560,6 +1569,11 @@ static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf) goto invalid_param_len; } + if (bdrv_is_read_only(s->qdev.conf.bs)) { + scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED)); + return; + } + data = g_new0(UnmapCBData, 1); data->r = r; data->inbuf = &p[8]; @@ -1572,6 +1586,115 @@ static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf) invalid_param_len: scsi_check_condition(r, SENSE_CODE(INVALID_PARAM_LEN)); + return; + +invalid_field: + scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); +} + +typedef struct WriteSameCBData { + SCSIDiskReq *r; + int64_t sector; + int nb_sectors; + QEMUIOVector qiov; + struct iovec iov; +} WriteSameCBData; + +static void scsi_write_same_complete(void *opaque, int ret) +{ + WriteSameCBData *data = opaque; + SCSIDiskReq *r = data->r; + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + + assert(r->req.aiocb != NULL); + r->req.aiocb = NULL; + bdrv_acct_done(s->qdev.conf.bs, &r->acct); + if (r->req.io_canceled) { + goto done; + } + + if (ret < 0) { + if (scsi_handle_rw_error(r, -ret)) { + goto done; + } + } + + data->nb_sectors -= data->iov.iov_len / 512; + data->sector += data->iov.iov_len / 512; + data->iov.iov_len = MIN(data->nb_sectors * 512, data->iov.iov_len); + if (data->iov.iov_len) { + bdrv_acct_start(s->qdev.conf.bs, &r->acct, data->iov.iov_len, BDRV_ACCT_WRITE); + r->req.aiocb = bdrv_aio_writev(s->qdev.conf.bs, data->sector, + &data->qiov, data->iov.iov_len / 512, + scsi_write_same_complete, data); + return; + } + + scsi_req_complete(&r->req, GOOD); + +done: + if (!r->req.io_canceled) { + scsi_req_unref(&r->req); + } + qemu_vfree(data->iov.iov_base); + g_free(data); +} + +static void scsi_disk_emulate_write_same(SCSIDiskReq *r, uint8_t *inbuf) +{ + SCSIRequest *req = &r->req; + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); + uint32_t nb_sectors = scsi_data_cdb_length(r->req.cmd.buf); + WriteSameCBData *data; + uint8_t *buf; + int i; + + /* Fail if PBDATA=1 or LBDATA=1 or ANCHOR=1. */ + if (nb_sectors == 0 || (req->cmd.buf[1] & 0x16)) { + scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); + return; + } + + if (bdrv_is_read_only(s->qdev.conf.bs)) { + scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED)); + return; + } + if (!check_lba_range(s, r->req.cmd.lba, nb_sectors)) { + scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE)); + return; + } + + if (buffer_is_zero(inbuf, s->qdev.blocksize)) { + int flags = (req->cmd.buf[1] & 0x8) ? BDRV_REQ_MAY_UNMAP : 0; + + /* The request is used as the AIO opaque value, so add a ref. */ + scsi_req_ref(&r->req); + bdrv_acct_start(s->qdev.conf.bs, &r->acct, nb_sectors * s->qdev.blocksize, + BDRV_ACCT_WRITE); + r->req.aiocb = bdrv_aio_write_zeroes(s->qdev.conf.bs, + r->req.cmd.lba * (s->qdev.blocksize / 512), + nb_sectors * (s->qdev.blocksize / 512), + flags, scsi_aio_complete, r); + return; + } + + data = g_new0(WriteSameCBData, 1); + data->r = r; + data->sector = r->req.cmd.lba * (s->qdev.blocksize / 512); + data->nb_sectors = nb_sectors * (s->qdev.blocksize / 512); + data->iov.iov_len = MIN(data->nb_sectors * 512, SCSI_WRITE_SAME_MAX); + data->iov.iov_base = buf = qemu_blockalign(s->qdev.conf.bs, data->iov.iov_len); + qemu_iovec_init_external(&data->qiov, &data->iov, 1); + + for (i = 0; i < data->iov.iov_len; i += s->qdev.blocksize) { + memcpy(&buf[i], inbuf, s->qdev.blocksize); + } + + scsi_req_ref(&r->req); + bdrv_acct_start(s->qdev.conf.bs, &r->acct, data->iov.iov_len, BDRV_ACCT_WRITE); + r->req.aiocb = bdrv_aio_writev(s->qdev.conf.bs, data->sector, + &data->qiov, data->iov.iov_len / 512, + scsi_write_same_complete, data); } static void scsi_disk_emulate_write_data(SCSIRequest *req) @@ -1597,6 +1720,19 @@ static void scsi_disk_emulate_write_data(SCSIRequest *req) scsi_disk_emulate_unmap(r, r->iov.iov_base); break; + case VERIFY_10: + case VERIFY_12: + case VERIFY_16: + if (r->req.status == -1) { + scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); + } + break; + + case WRITE_SAME_10: + case WRITE_SAME_16: + scsi_disk_emulate_write_same(r, r->iov.iov_base); + break; + default: abort(); } @@ -1837,31 +1973,20 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf) case UNMAP: DPRINTF("Unmap (len %lu)\n", (long)r->req.cmd.xfer); break; - case WRITE_SAME_10: - case WRITE_SAME_16: - nb_sectors = scsi_data_cdb_length(r->req.cmd.buf); - if (bdrv_is_read_only(s->qdev.conf.bs)) { - scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED)); - return 0; - } - if (!check_lba_range(s, r->req.cmd.lba, nb_sectors)) { - goto illegal_lba; - } - - /* - * We only support WRITE SAME with the unmap bit set for now. - */ - if (!(req->cmd.buf[1] & 0x8)) { + case VERIFY_10: + case VERIFY_12: + case VERIFY_16: + DPRINTF("Verify (bytchk %lu)\n", (r->req.buf[1] >> 1) & 3); + if (req->cmd.buf[1] & 6) { goto illegal_request; } - - /* The request is used as the AIO opaque value, so add a ref. */ - scsi_req_ref(&r->req); - r->req.aiocb = bdrv_aio_discard(s->qdev.conf.bs, - r->req.cmd.lba * (s->qdev.blocksize / 512), - nb_sectors * (s->qdev.blocksize / 512), - scsi_aio_complete, r); - return 0; + break; + case WRITE_SAME_10: + case WRITE_SAME_16: + DPRINTF("WRITE SAME %d (len %lu)\n", + req->cmd.buf[0] == WRITE_SAME_10 ? 10 : 16, + (long)r->req.cmd.xfer); + break; default: DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]); scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE)); @@ -1936,10 +2061,6 @@ static int32_t scsi_disk_dma_command(SCSIRequest *req, uint8_t *buf) scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED)); return 0; } - /* fallthrough */ - case VERIFY_10: - case VERIFY_12: - case VERIFY_16: DPRINTF("Write %s(sector %" PRId64 ", count %u)\n", (command & 0xe) == 0xe ? "And Verify " : "", r->req.cmd.lba, len); @@ -2207,14 +2328,14 @@ static const SCSIReqOps *const scsi_disk_reqops_dispatch[256] = { [UNMAP] = &scsi_disk_emulate_reqops, [WRITE_SAME_10] = &scsi_disk_emulate_reqops, [WRITE_SAME_16] = &scsi_disk_emulate_reqops, + [VERIFY_10] = &scsi_disk_emulate_reqops, + [VERIFY_12] = &scsi_disk_emulate_reqops, + [VERIFY_16] = &scsi_disk_emulate_reqops, [READ_6] = &scsi_disk_dma_reqops, [READ_10] = &scsi_disk_dma_reqops, [READ_12] = &scsi_disk_dma_reqops, [READ_16] = &scsi_disk_dma_reqops, - [VERIFY_10] = &scsi_disk_dma_reqops, - [VERIFY_12] = &scsi_disk_dma_reqops, - [VERIFY_16] = &scsi_disk_dma_reqops, [WRITE_6] = &scsi_disk_dma_reqops, [WRITE_10] = &scsi_disk_dma_reqops, [WRITE_12] = &scsi_disk_dma_reqops, diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs index eca590570e..3ae091c95e 100644 --- a/hw/timer/Makefile.objs +++ b/hw/timer/Makefile.objs @@ -1,5 +1,6 @@ common-obj-$(CONFIG_ARM_TIMER) += arm_timer.o common-obj-$(CONFIG_ARM_MPTIMER) += arm_mptimer.o +common-obj-$(CONFIG_A9_GTIMER) += a9gtimer.o common-obj-$(CONFIG_CADENCE) += cadence_ttc.o common-obj-$(CONFIG_DS1338) += ds1338.o common-obj-$(CONFIG_HPET) += hpet.o diff --git a/hw/timer/a9gtimer.c b/hw/timer/a9gtimer.c new file mode 100644 index 0000000000..a0656d58a1 --- /dev/null +++ b/hw/timer/a9gtimer.c @@ -0,0 +1,369 @@ +/* + * Global peripheral timer block for ARM A9MP + * + * (C) 2013 Xilinx Inc. + * + * Written by François LEGAL + * Written by Peter Crosthwaite <peter.crosthwaite@xilinx.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 of the License, or (at your option) any later version. + * + * 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 "hw/timer/a9gtimer.h" +#include "qemu/timer.h" +#include "qemu/bitops.h" +#include "qemu/log.h" + +#ifndef A9_GTIMER_ERR_DEBUG +#define A9_GTIMER_ERR_DEBUG 0 +#endif + +#define DB_PRINT_L(level, ...) do { \ + if (A9_GTIMER_ERR_DEBUG > (level)) { \ + fprintf(stderr, ": %s: ", __func__); \ + fprintf(stderr, ## __VA_ARGS__); \ + } \ +} while (0); + +#define DB_PRINT(...) DB_PRINT_L(0, ## __VA_ARGS__) + +static inline int a9_gtimer_get_current_cpu(A9GTimerState *s) +{ + if (current_cpu->cpu_index >= s->num_cpu) { + hw_error("a9gtimer: num-cpu %d but this cpu is %d!\n", + s->num_cpu, current_cpu->cpu_index); + } + return current_cpu->cpu_index; +} + +static inline uint64_t a9_gtimer_get_conv(A9GTimerState *s) +{ + uint64_t prescale = extract32(s->control, R_CONTROL_PRESCALER_SHIFT, + R_CONTROL_PRESCALER_LEN); + + return (prescale + 1) * 10; +} + +static A9GTimerUpdate a9_gtimer_get_update(A9GTimerState *s) +{ + A9GTimerUpdate ret; + + ret.now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + ret.new = s->ref_counter + + (ret.now - s->cpu_ref_time) / a9_gtimer_get_conv(s); + return ret; +} + +static void a9_gtimer_update(A9GTimerState *s, bool sync) +{ + + A9GTimerUpdate update = a9_gtimer_get_update(s); + int i; + int64_t next_cdiff = 0; + + for (i = 0; i < s->num_cpu; ++i) { + A9GTimerPerCPU *gtb = &s->per_cpu[i]; + int64_t cdiff = 0; + + if ((s->control & R_CONTROL_TIMER_ENABLE) && + (gtb->control & R_CONTROL_COMP_ENABLE)) { + /* R2p0+, where the compare function is >= */ + while (gtb->compare < update.new) { + DB_PRINT("Compare event happened for CPU %d\n", i); + gtb->status = 1; + if (gtb->control & R_CONTROL_AUTO_INCREMENT) { + DB_PRINT("Auto incrementing timer compare by %" PRId32 "\n", + gtb->inc); + gtb->compare += gtb->inc; + } else { + break; + } + } + cdiff = (int64_t)gtb->compare - (int64_t)update.new + 1; + if (cdiff > 0 && (cdiff < next_cdiff || !next_cdiff)) { + next_cdiff = cdiff; + } + } + + qemu_set_irq(gtb->irq, + gtb->status && (gtb->control & R_CONTROL_IRQ_ENABLE)); + } + + timer_del(s->timer); + if (next_cdiff) { + DB_PRINT("scheduling qemu_timer to fire again in %" + PRIx64 " cycles\n", next_cdiff); + timer_mod(s->timer, update.now + next_cdiff * a9_gtimer_get_conv(s)); + } + + if (s->control & R_CONTROL_TIMER_ENABLE) { + s->counter = update.new; + } + + if (sync) { + s->cpu_ref_time = update.now; + s->ref_counter = s->counter; + } +} + +static void a9_gtimer_update_no_sync(void *opaque) +{ + A9GTimerState *s = A9_GTIMER(opaque); + + return a9_gtimer_update(s, false); +} + +static uint64_t a9_gtimer_read(void *opaque, hwaddr addr, unsigned size) +{ + A9GTimerPerCPU *gtb = (A9GTimerPerCPU *)opaque; + A9GTimerState *s = gtb->parent; + A9GTimerUpdate update; + uint64_t ret = 0; + int shift = 0; + + switch (addr) { + case R_COUNTER_HI: + shift = 32; + /* fallthrough */ + case R_COUNTER_LO: + update = a9_gtimer_get_update(s); + ret = extract64(update.new, shift, 32); + break; + case R_CONTROL: + ret = s->control | gtb->control; + break; + case R_INTERRUPT_STATUS: + ret = gtb->status; + break; + case R_COMPARATOR_HI: + shift = 32; + /* fallthrough */ + case R_COMPARATOR_LO: + ret = extract64(gtb->compare, shift, 32); + break; + case R_AUTO_INCREMENT: + ret = gtb->inc; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "bad a9gtimer register: %x\n", + (unsigned)addr); + return 0; + } + + DB_PRINT("addr:%#x data:%#08" PRIx64 "\n", (unsigned)addr, ret); + return ret; +} + +static void a9_gtimer_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + A9GTimerPerCPU *gtb = (A9GTimerPerCPU *)opaque; + A9GTimerState *s = gtb->parent; + int shift = 0; + + DB_PRINT("addr:%#x data:%#08" PRIx64 "\n", (unsigned)addr, value); + + switch (addr) { + case R_COUNTER_HI: + shift = 32; + /* fallthrough */ + case R_COUNTER_LO: + /* + * Keep it simple - ARM docco explicitly says to disable timer before + * modding it, so dont bother trying to do all the difficult on the fly + * timer modifications - (if they even work in real hardware??). + */ + if (s->control & R_CONTROL_TIMER_ENABLE) { + qemu_log_mask(LOG_GUEST_ERROR, "Cannot mod running ARM gtimer\n"); + return; + } + s->counter = deposit64(s->counter, shift, 32, value); + return; + case R_CONTROL: + a9_gtimer_update(s, (value ^ s->control) & R_CONTROL_NEEDS_SYNC); + gtb->control = value & R_CONTROL_BANKED; + s->control = value & ~R_CONTROL_BANKED; + break; + case R_INTERRUPT_STATUS: + a9_gtimer_update(s, false); + gtb->status &= ~value; + break; + case R_COMPARATOR_HI: + shift = 32; + /* fallthrough */ + case R_COMPARATOR_LO: + a9_gtimer_update(s, false); + gtb->compare = deposit64(gtb->compare, shift, 32, value); + break; + case R_AUTO_INCREMENT: + gtb->inc = value; + return; + default: + return; + } + + a9_gtimer_update(s, false); +} + +/* Wrapper functions to implement the "read global timer for + * the current CPU" memory regions. + */ +static uint64_t a9_gtimer_this_read(void *opaque, hwaddr addr, + unsigned size) +{ + A9GTimerState *s = A9_GTIMER(opaque); + int id = a9_gtimer_get_current_cpu(s); + + /* no \n so concatenates with message from read fn */ + DB_PRINT("CPU:%d:", id); + + return a9_gtimer_read(&s->per_cpu[id], addr, size); +} + +static void a9_gtimer_this_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + A9GTimerState *s = A9_GTIMER(opaque); + int id = a9_gtimer_get_current_cpu(s); + + /* no \n so concatenates with message from write fn */ + DB_PRINT("CPU:%d:", id); + + a9_gtimer_write(&s->per_cpu[id], addr, value, size); +} + +static const MemoryRegionOps a9_gtimer_this_ops = { + .read = a9_gtimer_this_read, + .write = a9_gtimer_this_write, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static const MemoryRegionOps a9_gtimer_ops = { + .read = a9_gtimer_read, + .write = a9_gtimer_write, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void a9_gtimer_reset(DeviceState *dev) +{ + A9GTimerState *s = A9_GTIMER(dev); + int i; + + s->counter = 0; + s->control = 0; + + for (i = 0; i < s->num_cpu; i++) { + A9GTimerPerCPU *gtb = &s->per_cpu[i]; + + gtb->control = 0; + gtb->status = 0; + gtb->compare = 0; + gtb->inc = 0; + } + a9_gtimer_update(s, false); +} + +static void a9_gtimer_realize(DeviceState *dev, Error **errp) +{ + A9GTimerState *s = A9_GTIMER(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + int i; + + if (s->num_cpu < 1 || s->num_cpu > A9_GTIMER_MAX_CPUS) { + error_setg(errp, "%s: num-cpu must be between 1 and %d\n", + __func__, A9_GTIMER_MAX_CPUS); + return; + } + + memory_region_init_io(&s->iomem, OBJECT(dev), &a9_gtimer_this_ops, s, + "a9gtimer shared", 0x20); + sysbus_init_mmio(sbd, &s->iomem); + s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, a9_gtimer_update_no_sync, s); + + for (i = 0; i < s->num_cpu; i++) { + A9GTimerPerCPU *gtb = &s->per_cpu[i]; + + gtb->parent = s; + sysbus_init_irq(sbd, >b->irq); + memory_region_init_io(>b->iomem, OBJECT(dev), &a9_gtimer_ops, gtb, + "a9gtimer per cpu", 0x20); + sysbus_init_mmio(sbd, >b->iomem); + } +} + +static const VMStateDescription vmstate_a9_gtimer_per_cpu = { + .name = "arm.cortex-a9-global-timer.percpu", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(control, A9GTimerPerCPU), + VMSTATE_UINT64(compare, A9GTimerPerCPU), + VMSTATE_UINT32(status, A9GTimerPerCPU), + VMSTATE_UINT32(inc, A9GTimerPerCPU), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_a9_gtimer = { + .name = "arm.cortex-a9-global-timer", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_TIMER(timer, A9GTimerState), + VMSTATE_UINT64(counter, A9GTimerState), + VMSTATE_UINT64(ref_counter, A9GTimerState), + VMSTATE_UINT64(cpu_ref_time, A9GTimerState), + VMSTATE_STRUCT_VARRAY_UINT32(per_cpu, A9GTimerState, num_cpu, + 1, vmstate_a9_gtimer_per_cpu, + A9GTimerPerCPU), + VMSTATE_END_OF_LIST() + } +}; + +static Property a9_gtimer_properties[] = { + DEFINE_PROP_UINT32("num-cpu", A9GTimerState, num_cpu, 0), + DEFINE_PROP_END_OF_LIST() +}; + +static void a9_gtimer_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = a9_gtimer_realize; + dc->vmsd = &vmstate_a9_gtimer; + dc->reset = a9_gtimer_reset; + dc->props = a9_gtimer_properties; +} + +static const TypeInfo a9_gtimer_info = { + .name = TYPE_A9_GTIMER, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(A9GTimerState), + .class_init = a9_gtimer_class_init, +}; + +static void a9_gtimer_register_types(void) +{ + type_register_static(&a9_gtimer_info); +} + +type_init(a9_gtimer_register_types) diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index 2eb75ea945..bb3bf98745 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -42,7 +42,6 @@ #define HPET_MSI_SUPPORT 0 -#define TYPE_HPET "hpet" #define HPET(obj) OBJECT_CHECK(HPETState, (obj), TYPE_HPET) struct HPETState; @@ -73,6 +72,7 @@ typedef struct HPETState { uint8_t rtc_irq_level; qemu_irq pit_enabled; uint8_t num_timers; + uint32_t intcap; HPETTimer timer[HPET_MAX_TIMERS]; /* Memory-mapped, software visible registers */ @@ -198,13 +198,23 @@ static void update_irq(struct HPETTimer *timer, int set) if (!set || !timer_enabled(timer) || !hpet_enabled(timer->state)) { s->isr &= ~mask; if (!timer_fsb_route(timer)) { - qemu_irq_lower(s->irqs[route]); + /* fold the ICH PIRQ# pin's internal inversion logic into hpet */ + if (route >= ISA_NUM_IRQS) { + qemu_irq_raise(s->irqs[route]); + } else { + qemu_irq_lower(s->irqs[route]); + } } } else if (timer_fsb_route(timer)) { stl_le_phys(timer->fsb >> 32, timer->fsb & 0xffffffff); } else if (timer->config & HPET_TN_TYPE_LEVEL) { s->isr |= mask; - qemu_irq_raise(s->irqs[route]); + /* fold the ICH PIRQ# pin's internal inversion logic into hpet */ + if (route >= ISA_NUM_IRQS) { + qemu_irq_lower(s->irqs[route]); + } else { + qemu_irq_raise(s->irqs[route]); + } } else { s->isr &= ~mask; qemu_irq_pulse(s->irqs[route]); @@ -653,8 +663,8 @@ static void hpet_reset(DeviceState *d) if (s->flags & (1 << HPET_MSI_SUPPORT)) { timer->config |= HPET_TN_FSB_CAP; } - /* advertise availability of ioapic inti2 */ - timer->config |= 0x00000004ULL << 32; + /* advertise availability of ioapic int */ + timer->config |= (uint64_t)s->intcap << 32; timer->period = 0ULL; timer->wrap_flag = 0; } @@ -703,6 +713,9 @@ static void hpet_realize(DeviceState *dev, Error **errp) int i; HPETTimer *timer; + if (!s->intcap) { + error_printf("Hpet's intcap not initialized.\n"); + } if (hpet_cfg.count == UINT8_MAX) { /* first instance */ hpet_cfg.count = 0; @@ -743,6 +756,7 @@ static void hpet_realize(DeviceState *dev, Error **errp) static Property hpet_device_properties[] = { DEFINE_PROP_UINT8("timers", HPETState, num_timers, HPET_MIN_TIMERS), DEFINE_PROP_BIT("msi", HPETState, flags, HPET_MSI_SUPPORT, false), + DEFINE_PROP_UINT32(HPET_INTCAP, HPETState, intcap, 0), DEFINE_PROP_END_OF_LIST(), }; @@ -757,11 +771,6 @@ static void hpet_device_class_init(ObjectClass *klass, void *data) dc->props = hpet_device_properties; } -bool hpet_find(void) -{ - return object_resolve_path_type("", TYPE_HPET, NULL); -} - static const TypeInfo hpet_device_info = { .name = TYPE_HPET, .parent = TYPE_SYS_BUS_DEVICE, diff --git a/hw/timer/m48t59.c b/hw/timer/m48t59.c index d3d78ec5a8..be0592b53d 100644 --- a/hw/timer/m48t59.c +++ b/hw/timer/m48t59.c @@ -61,8 +61,8 @@ struct M48t59State { time_t stop_time; /* Alarm & watchdog */ struct tm alarm; - struct QEMUTimer *alrm_timer; - struct QEMUTimer *wd_timer; + QEMUTimer *alrm_timer; + QEMUTimer *wd_timer; /* NVRAM storage */ uint8_t *buffer; /* Model parameters */ diff --git a/hw/usb/bus.c b/hw/usb/bus.c index ca329bef29..09848c6320 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -203,6 +203,24 @@ void usb_device_ep_stopped(USBDevice *dev, USBEndpoint *ep) } } +int usb_device_alloc_streams(USBDevice *dev, USBEndpoint **eps, int nr_eps, + int streams) +{ + USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); + if (klass->alloc_streams) { + return klass->alloc_streams(dev, eps, nr_eps, streams); + } + return 0; +} + +void usb_device_free_streams(USBDevice *dev, USBEndpoint **eps, int nr_eps) +{ + USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); + if (klass->free_streams) { + klass->free_streams(dev, eps, nr_eps); + } +} + static int usb_qdev_init(DeviceState *qdev) { USBDevice *dev = USB_DEVICE(qdev); diff --git a/hw/usb/core.c b/hw/usb/core.c index cf59a1abcf..67ba7d6018 100644 --- a/hw/usb/core.c +++ b/hw/usb/core.c @@ -623,6 +623,7 @@ void usb_ep_reset(USBDevice *dev) dev->ep_ctl.type = USB_ENDPOINT_XFER_CONTROL; dev->ep_ctl.ifnum = 0; dev->ep_ctl.max_packet_size = 64; + dev->ep_ctl.max_streams = 0; dev->ep_ctl.dev = dev; dev->ep_ctl.pipeline = false; for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) { @@ -636,6 +637,8 @@ void usb_ep_reset(USBDevice *dev) dev->ep_out[ep].ifnum = USB_INTERFACE_INVALID; dev->ep_in[ep].max_packet_size = 0; dev->ep_out[ep].max_packet_size = 0; + dev->ep_in[ep].max_streams = 0; + dev->ep_out[ep].max_streams = 0; dev->ep_in[ep].dev = dev; dev->ep_out[ep].dev = dev; dev->ep_in[ep].pipeline = false; @@ -764,6 +767,25 @@ int usb_ep_get_max_packet_size(USBDevice *dev, int pid, int ep) return uep->max_packet_size; } +void usb_ep_set_max_streams(USBDevice *dev, int pid, int ep, uint8_t raw) +{ + struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); + int MaxStreams; + + MaxStreams = raw & 0x1f; + if (MaxStreams) { + uep->max_streams = 1 << MaxStreams; + } else { + uep->max_streams = 0; + } +} + +int usb_ep_get_max_streams(USBDevice *dev, int pid, int ep) +{ + struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); + return uep->max_streams; +} + void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled) { struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); diff --git a/hw/usb/desc.c b/hw/usb/desc.c index bf6c522682..f18a043500 100644 --- a/hw/usb/desc.c +++ b/hw/usb/desc.c @@ -6,16 +6,6 @@ /* ------------------------------------------------------------------ */ -static uint8_t usb_lo(uint16_t val) -{ - return val & 0xff; -} - -static uint8_t usb_hi(uint16_t val) -{ - return (val >> 8) & 0xff; -} - int usb_desc_device(const USBDescID *id, const USBDescDevice *dev, uint8_t *dest, size_t len) { @@ -385,6 +375,8 @@ static void usb_desc_ep_init(USBDevice *dev) usb_ep_set_ifnum(dev, pid, ep, iface->bInterfaceNumber); usb_ep_set_max_packet_size(dev, pid, ep, iface->eps[e].wMaxPacketSize); + usb_ep_set_max_streams(dev, pid, ep, + iface->eps[e].bmAttributes_super); } } } diff --git a/hw/usb/desc.h b/hw/usb/desc.h index ddd3e7485c..81327b0e74 100644 --- a/hw/usb/desc.h +++ b/hw/usb/desc.h @@ -194,6 +194,17 @@ struct USBDesc { #define USB_DESC_FLAG_SUPER (1 << 1) +/* little helpers */ +static inline uint8_t usb_lo(uint16_t val) +{ + return val & 0xff; +} + +static inline uint8_t usb_hi(uint16_t val) +{ + return (val >> 8) & 0xff; +} + /* generate usb packages from structs */ int usb_desc_device(const USBDescID *id, const USBDescDevice *dev, uint8_t *dest, size_t len); diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c index 59567200ae..5e667f0199 100644 --- a/hw/usb/dev-hid.c +++ b/hw/usb/dev-hid.c @@ -236,7 +236,7 @@ static const USBDescDevice desc_device_tablet2 = { .bNumInterfaces = 1, .bConfigurationValue = 1, .iConfiguration = STR_CONFIG_TABLET, - .bmAttributes = 0x80, + .bmAttributes = 0xa0, .bMaxPower = 50, .nif = 1, .ifs = &desc_iface_tablet2, diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c index 70ed2d1dbd..997b715952 100644 --- a/hw/usb/dev-uas.c +++ b/hw/usb/dev-uas.c @@ -55,7 +55,7 @@ typedef struct { uint8_t id; uint8_t reserved; uint16_t tag; -} QEMU_PACKED uas_ui_header; +} QEMU_PACKED uas_iu_header; typedef struct { uint8_t prio_taskattr; /* 6:3 priority, 2:0 task attribute */ @@ -65,7 +65,7 @@ typedef struct { uint64_t lun; uint8_t cdb[16]; uint8_t add_cdb[]; -} QEMU_PACKED uas_ui_command; +} QEMU_PACKED uas_iu_command; typedef struct { uint16_t status_qualifier; @@ -73,29 +73,29 @@ typedef struct { uint8_t reserved[7]; uint16_t sense_length; uint8_t sense_data[18]; -} QEMU_PACKED uas_ui_sense; +} QEMU_PACKED uas_iu_sense; typedef struct { - uint16_t add_response_info; + uint8_t add_response_info[3]; uint8_t response_code; -} QEMU_PACKED uas_ui_response; +} QEMU_PACKED uas_iu_response; typedef struct { uint8_t function; uint8_t reserved; uint16_t task_tag; uint64_t lun; -} QEMU_PACKED uas_ui_task_mgmt; +} QEMU_PACKED uas_iu_task_mgmt; typedef struct { - uas_ui_header hdr; + uas_iu_header hdr; union { - uas_ui_command command; - uas_ui_sense sense; - uas_ui_task_mgmt task; - uas_ui_response response; + uas_iu_command command; + uas_iu_sense sense; + uas_iu_task_mgmt task; + uas_iu_response response; }; -} QEMU_PACKED uas_ui; +} QEMU_PACKED uas_iu; /* --------------------------------------------------------------------- */ @@ -122,8 +122,8 @@ struct UASDevice { UASRequest *dataout2; /* usb 3.0 only */ - USBPacket *data3[UAS_MAX_STREAMS]; - USBPacket *status3[UAS_MAX_STREAMS]; + USBPacket *data3[UAS_MAX_STREAMS + 1]; + USBPacket *status3[UAS_MAX_STREAMS + 1]; }; struct UASRequest { @@ -145,7 +145,7 @@ struct UASRequest { struct UASStatus { uint32_t stream; - uas_ui status; + uas_iu status; uint32_t length; QTAILQ_ENTRY(UASStatus) next; }; @@ -338,7 +338,7 @@ static UASStatus *usb_uas_alloc_status(UASDevice *uas, uint8_t id, uint16_t tag) st->status.hdr.id = id; st->status.hdr.tag = cpu_to_be16(tag); - st->length = sizeof(uas_ui_header); + st->length = sizeof(uas_iu_header); if (uas_using_streams(uas)) { st->stream = tag; } @@ -392,15 +392,13 @@ static void usb_uas_queue_status(UASDevice *uas, UASStatus *st, int length) } } -static void usb_uas_queue_response(UASDevice *uas, uint16_t tag, - uint8_t code, uint16_t add_info) +static void usb_uas_queue_response(UASDevice *uas, uint16_t tag, uint8_t code) { UASStatus *st = usb_uas_alloc_status(uas, UAS_UI_RESPONSE, tag); trace_usb_uas_response(uas->dev.addr, tag, code); st->status.response.response_code = code; - st->status.response.add_response_info = cpu_to_be16(add_info); - usb_uas_queue_status(uas, st, sizeof(uas_ui_response)); + usb_uas_queue_status(uas, st, sizeof(uas_iu_response)); } static void usb_uas_queue_sense(UASRequest *req, uint8_t status) @@ -416,10 +414,28 @@ static void usb_uas_queue_sense(UASRequest *req, uint8_t status) sizeof(st->status.sense.sense_data)); st->status.sense.sense_length = cpu_to_be16(slen); } - len = sizeof(uas_ui_sense) - sizeof(st->status.sense.sense_data) + slen; + len = sizeof(uas_iu_sense) - sizeof(st->status.sense.sense_data) + slen; usb_uas_queue_status(req->uas, st, len); } +static void usb_uas_queue_fake_sense(UASDevice *uas, uint16_t tag, + struct SCSISense sense) +{ + UASStatus *st = usb_uas_alloc_status(uas, UAS_UI_SENSE, tag); + int len, slen = 0; + + st->status.sense.status = CHECK_CONDITION; + st->status.sense.status_qualifier = cpu_to_be16(0); + st->status.sense.sense_data[0] = 0x70; + st->status.sense.sense_data[2] = sense.key; + st->status.sense.sense_data[7] = 10; + st->status.sense.sense_data[12] = sense.asc; + st->status.sense.sense_data[13] = sense.ascq; + slen = 18; + len = sizeof(uas_iu_sense) - sizeof(st->status.sense.sense_data) + slen; + usb_uas_queue_status(uas, st, len); +} + static void usb_uas_queue_read_ready(UASRequest *req) { UASStatus *st = usb_uas_alloc_status(req->uas, UAS_UI_READ_READY, @@ -518,14 +534,14 @@ static void usb_uas_start_next_transfer(UASDevice *uas) } } -static UASRequest *usb_uas_alloc_request(UASDevice *uas, uas_ui *ui) +static UASRequest *usb_uas_alloc_request(UASDevice *uas, uas_iu *iu) { UASRequest *req; req = g_new0(UASRequest, 1); req->uas = uas; - req->tag = be16_to_cpu(ui->hdr.tag); - req->lun = be64_to_cpu(ui->command.lun); + req->tag = be16_to_cpu(iu->hdr.tag); + req->lun = be64_to_cpu(iu->command.lun); req->dev = usb_uas_get_dev(req->uas, req->lun); return req; } @@ -648,7 +664,7 @@ static void usb_uas_cancel_io(USBDevice *dev, USBPacket *p) return; } if (uas_using_streams(uas)) { - for (i = 0; i < UAS_MAX_STREAMS; i++) { + for (i = 0; i <= UAS_MAX_STREAMS; i++) { if (uas->status3[i] == p) { uas->status3[i] = NULL; return; @@ -668,16 +684,20 @@ static void usb_uas_cancel_io(USBDevice *dev, USBPacket *p) assert(!"canceled usb packet not found"); } -static void usb_uas_command(UASDevice *uas, uas_ui *ui) +static void usb_uas_command(UASDevice *uas, uas_iu *iu) { UASRequest *req; uint32_t len; + uint16_t tag = be16_to_cpu(iu->hdr.tag); - req = usb_uas_find_request(uas, be16_to_cpu(ui->hdr.tag)); + if (uas_using_streams(uas) && tag > UAS_MAX_STREAMS) { + goto invalid_tag; + } + req = usb_uas_find_request(uas, tag); if (req) { goto overlapped_tag; } - req = usb_uas_alloc_request(uas, ui); + req = usb_uas_alloc_request(uas, iu); if (req->dev == NULL) { goto bad_target; } @@ -694,7 +714,7 @@ static void usb_uas_command(UASDevice *uas, uas_ui *ui) req->req = scsi_req_new(req->dev, req->tag, usb_uas_get_lun(req->lun), - ui->command.cdb, req); + iu->command.cdb, req); if (uas->requestlog) { scsi_req_print(req->req); } @@ -705,105 +725,97 @@ static void usb_uas_command(UASDevice *uas, uas_ui *ui) } return; +invalid_tag: + usb_uas_queue_fake_sense(uas, tag, sense_code_INVALID_TAG); + return; + overlapped_tag: - usb_uas_queue_response(uas, req->tag, UAS_RC_OVERLAPPED_TAG, 0); + usb_uas_queue_fake_sense(uas, tag, sense_code_OVERLAPPED_COMMANDS); return; bad_target: - /* - * FIXME: Seems to upset linux, is this wrong? - * NOTE: Happens only with no scsi devices at the bus, not sure - * this is a valid UAS setup in the first place. - */ - usb_uas_queue_response(uas, req->tag, UAS_RC_INVALID_INFO_UNIT, 0); + usb_uas_queue_fake_sense(uas, tag, sense_code_LUN_NOT_SUPPORTED); g_free(req); } -static void usb_uas_task(UASDevice *uas, uas_ui *ui) +static void usb_uas_task(UASDevice *uas, uas_iu *iu) { - uint16_t tag = be16_to_cpu(ui->hdr.tag); - uint64_t lun64 = be64_to_cpu(ui->task.lun); + uint16_t tag = be16_to_cpu(iu->hdr.tag); + uint64_t lun64 = be64_to_cpu(iu->task.lun); SCSIDevice *dev = usb_uas_get_dev(uas, lun64); int lun = usb_uas_get_lun(lun64); UASRequest *req; uint16_t task_tag; - req = usb_uas_find_request(uas, be16_to_cpu(ui->hdr.tag)); + if (uas_using_streams(uas) && tag > UAS_MAX_STREAMS) { + goto invalid_tag; + } + req = usb_uas_find_request(uas, be16_to_cpu(iu->hdr.tag)); if (req) { goto overlapped_tag; } + if (dev == NULL) { + goto incorrect_lun; + } - switch (ui->task.function) { + switch (iu->task.function) { case UAS_TMF_ABORT_TASK: - task_tag = be16_to_cpu(ui->task.task_tag); + task_tag = be16_to_cpu(iu->task.task_tag); trace_usb_uas_tmf_abort_task(uas->dev.addr, tag, task_tag); - if (dev == NULL) { - goto bad_target; - } - if (dev->lun != lun) { - goto incorrect_lun; - } req = usb_uas_find_request(uas, task_tag); if (req && req->dev == dev) { scsi_req_cancel(req->req); } - usb_uas_queue_response(uas, tag, UAS_RC_TMF_COMPLETE, 0); + usb_uas_queue_response(uas, tag, UAS_RC_TMF_COMPLETE); break; case UAS_TMF_LOGICAL_UNIT_RESET: trace_usb_uas_tmf_logical_unit_reset(uas->dev.addr, tag, lun); - if (dev == NULL) { - goto bad_target; - } - if (dev->lun != lun) { - goto incorrect_lun; - } qdev_reset_all(&dev->qdev); - usb_uas_queue_response(uas, tag, UAS_RC_TMF_COMPLETE, 0); + usb_uas_queue_response(uas, tag, UAS_RC_TMF_COMPLETE); break; default: - trace_usb_uas_tmf_unsupported(uas->dev.addr, tag, ui->task.function); - usb_uas_queue_response(uas, tag, UAS_RC_TMF_NOT_SUPPORTED, 0); + trace_usb_uas_tmf_unsupported(uas->dev.addr, tag, iu->task.function); + usb_uas_queue_response(uas, tag, UAS_RC_TMF_NOT_SUPPORTED); break; } return; -overlapped_tag: - usb_uas_queue_response(uas, req->tag, UAS_RC_OVERLAPPED_TAG, 0); +invalid_tag: + usb_uas_queue_response(uas, tag, UAS_RC_INVALID_INFO_UNIT); return; -bad_target: - /* FIXME: correct? [see long comment in usb_uas_command()] */ - usb_uas_queue_response(uas, tag, UAS_RC_INVALID_INFO_UNIT, 0); +overlapped_tag: + usb_uas_queue_response(uas, req->tag, UAS_RC_OVERLAPPED_TAG); return; incorrect_lun: - usb_uas_queue_response(uas, tag, UAS_RC_INCORRECT_LUN, 0); + usb_uas_queue_response(uas, tag, UAS_RC_INCORRECT_LUN); } static void usb_uas_handle_data(USBDevice *dev, USBPacket *p) { UASDevice *uas = DO_UPCAST(UASDevice, dev, dev); - uas_ui ui; + uas_iu iu; UASStatus *st; UASRequest *req; int length; switch (p->ep->nr) { case UAS_PIPE_ID_COMMAND: - length = MIN(sizeof(ui), p->iov.size); - usb_packet_copy(p, &ui, length); - switch (ui.hdr.id) { + length = MIN(sizeof(iu), p->iov.size); + usb_packet_copy(p, &iu, length); + switch (iu.hdr.id) { case UAS_UI_COMMAND: - usb_uas_command(uas, &ui); + usb_uas_command(uas, &iu); break; case UAS_UI_TASK_MGMT: - usb_uas_task(uas, &ui); + usb_uas_task(uas, &iu); break; default: - fprintf(stderr, "%s: unknown command ui: id 0x%x\n", - __func__, ui.hdr.id); + fprintf(stderr, "%s: unknown command iu: id 0x%x\n", + __func__, iu.hdr.id); p->status = USB_RET_STALL; break; } diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 22bdbf4a7d..355bbd6bed 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -28,6 +28,7 @@ */ #include "hw/usb/hcd-ehci.h" +#include "trace.h" /* Capability Registers Base Address - section 2.2 */ #define CAPLENGTH 0x0000 /* 1-byte, 0x0001 reserved */ @@ -826,9 +827,9 @@ static void ehci_child_detach(USBPort *port, USBDevice *child) static void ehci_wakeup(USBPort *port) { EHCIState *s = port->opaque; - uint32_t portsc = s->portsc[port->index]; + uint32_t *portsc = &s->portsc[port->index]; - if (portsc & PORTSC_POWNER) { + if (*portsc & PORTSC_POWNER) { USBPort *companion = s->companion_ports[port->index]; if (companion->ops->wakeup) { companion->ops->wakeup(companion); @@ -836,6 +837,12 @@ static void ehci_wakeup(USBPort *port) return; } + if (*portsc & PORTSC_SUSPEND) { + trace_usb_ehci_port_wakeup(port->index); + *portsc |= PORTSC_FPRES; + ehci_raise_irq(s, USBSTS_PCD); + } + qemu_bh_schedule(s->async_bh); } @@ -1067,6 +1074,14 @@ static void ehci_port_write(void *ptr, hwaddr addr, } } + if ((val & PORTSC_SUSPEND) && !(*portsc & PORTSC_SUSPEND)) { + trace_usb_ehci_port_suspend(port); + } + if (!(val & PORTSC_FPRES) && (*portsc & PORTSC_FPRES)) { + trace_usb_ehci_port_resume(port); + val &= ~PORTSC_SUSPEND; + } + *portsc &= ~PORTSC_RO_MASK; *portsc |= val; trace_usb_ehci_portsc_change(addr + s->portscbase, addr >> 2, *portsc, old); diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h index 065c9fa741..1ad4b96cce 100644 --- a/hw/usb/hcd-ehci.h +++ b/hw/usb/hcd-ehci.h @@ -21,7 +21,6 @@ #include "qemu/timer.h" #include "hw/usb.h" #include "monitor/monitor.h" -#include "trace.h" #include "sysemu/dma.h" #include "sysemu/sysemu.h" #include "hw/pci/pci.h" diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 835f65ed81..bafe08590b 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -1150,6 +1150,111 @@ static void xhci_free_streams(XHCIEPContext *epctx) epctx->nr_pstreams = 0; } +static int xhci_epmask_to_eps_with_streams(XHCIState *xhci, + unsigned int slotid, + uint32_t epmask, + XHCIEPContext **epctxs, + USBEndpoint **eps) +{ + XHCISlot *slot; + XHCIEPContext *epctx; + USBEndpoint *ep; + int i, j; + + assert(slotid >= 1 && slotid <= xhci->numslots); + + slot = &xhci->slots[slotid - 1]; + + for (i = 2, j = 0; i <= 31; i++) { + if (!(epmask & (1 << i))) { + continue; + } + + epctx = slot->eps[i - 1]; + ep = xhci_epid_to_usbep(xhci, slotid, i); + if (!epctx || !epctx->nr_pstreams || !ep) { + continue; + } + + if (epctxs) { + epctxs[j] = epctx; + } + eps[j++] = ep; + } + return j; +} + +static void xhci_free_device_streams(XHCIState *xhci, unsigned int slotid, + uint32_t epmask) +{ + USBEndpoint *eps[30]; + int nr_eps; + + nr_eps = xhci_epmask_to_eps_with_streams(xhci, slotid, epmask, NULL, eps); + if (nr_eps) { + usb_device_free_streams(eps[0]->dev, eps, nr_eps); + } +} + +static TRBCCode xhci_alloc_device_streams(XHCIState *xhci, unsigned int slotid, + uint32_t epmask) +{ + XHCIEPContext *epctxs[30]; + USBEndpoint *eps[30]; + int i, r, nr_eps, req_nr_streams, dev_max_streams; + + nr_eps = xhci_epmask_to_eps_with_streams(xhci, slotid, epmask, epctxs, + eps); + if (nr_eps == 0) { + return CC_SUCCESS; + } + + req_nr_streams = epctxs[0]->nr_pstreams; + dev_max_streams = eps[0]->max_streams; + + for (i = 1; i < nr_eps; i++) { + /* + * HdG: I don't expect these to ever trigger, but if they do we need + * to come up with another solution, ie group identical endpoints + * together and make an usb_device_alloc_streams call per group. + */ + if (epctxs[i]->nr_pstreams != req_nr_streams) { + FIXME("guest streams config not identical for all eps"); + return CC_RESOURCE_ERROR; + } + if (eps[i]->max_streams != dev_max_streams) { + FIXME("device streams config not identical for all eps"); + return CC_RESOURCE_ERROR; + } + } + + /* + * max-streams in both the device descriptor and in the controller is a + * power of 2. But stream id 0 is reserved, so if a device can do up to 4 + * streams the guest will ask for 5 rounded up to the next power of 2 which + * becomes 8. For emulated devices usb_device_alloc_streams is a nop. + * + * For redirected devices however this is an issue, as there we must ask + * the real xhci controller to alloc streams, and the host driver for the + * real xhci controller will likely disallow allocating more streams then + * the device can handle. + * + * So we limit the requested nr_streams to the maximum number the device + * can handle. + */ + if (req_nr_streams > dev_max_streams) { + req_nr_streams = dev_max_streams; + } + + r = usb_device_alloc_streams(eps[0]->dev, eps, nr_eps, req_nr_streams); + if (r != 0) { + fprintf(stderr, "xhci: alloc streams failed\n"); + return CC_RESOURCE_ERROR; + } + + return CC_SUCCESS; +} + static XHCIStreamContext *xhci_find_stream(XHCIEPContext *epctx, unsigned int streamid, uint32_t *cc_error) @@ -1495,7 +1600,8 @@ static TRBCCode xhci_reset_ep(XHCIState *xhci, unsigned int slotid, } if (!xhci->slots[slotid-1].uport || - !xhci->slots[slotid-1].uport->dev) { + !xhci->slots[slotid-1].uport->dev || + !xhci->slots[slotid-1].uport->dev->attached) { return CC_USB_TRANSACTION_ERROR; } @@ -1982,6 +2088,14 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, return; } + /* If the device has been detached, but the guest has not noticed this + yet the 2 above checks will succeed, but we must NOT continue */ + if (!xhci->slots[slotid - 1].uport || + !xhci->slots[slotid - 1].uport->dev || + !xhci->slots[slotid - 1].uport->dev->attached) { + return; + } + if (epctx->retry) { XHCITransfer *xfer = epctx->retry; @@ -2206,7 +2320,7 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid, trace_usb_xhci_slot_address(slotid, uport->path); dev = uport->dev; - if (!dev) { + if (!dev || !dev->attached) { fprintf(stderr, "xhci: port %s not connected\n", uport->path); return CC_USB_TRANSACTION_ERROR; } @@ -2313,6 +2427,8 @@ static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid, return CC_CONTEXT_STATE_ERROR; } + xhci_free_device_streams(xhci, slotid, ictl_ctx[0] | ictl_ctx[1]); + for (i = 2; i <= 31; i++) { if (ictl_ctx[0] & (1<<i)) { xhci_disable_ep(xhci, slotid, i); @@ -2334,6 +2450,16 @@ static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid, } } + res = xhci_alloc_device_streams(xhci, slotid, ictl_ctx[1]); + if (res != CC_SUCCESS) { + for (i = 2; i <= 31; i++) { + if (ictl_ctx[1] & (1 << i)) { + xhci_disable_ep(xhci, slotid, i); + } + } + return res; + } + slot_ctx[3] &= ~(SLOT_STATE_MASK << SLOT_STATE_SHIFT); slot_ctx[3] |= SLOT_CONFIGURED << SLOT_STATE_SHIFT; slot_ctx[0] &= ~(SLOT_CONTEXT_ENTRIES_MASK << SLOT_CONTEXT_ENTRIES_SHIFT); @@ -3016,6 +3142,14 @@ static void xhci_oper_write(void *ptr, hwaddr reg, } else if (!(val & USBCMD_RS) && (xhci->usbcmd & USBCMD_RS)) { xhci_stop(xhci); } + if (val & USBCMD_CSS) { + /* save state */ + xhci->usbsts &= ~USBSTS_SRE; + } + if (val & USBCMD_CRS) { + /* restore state */ + xhci->usbsts |= USBSTS_SRE; + } xhci->usbcmd = val & 0xc0f; xhci_mfwrap_update(xhci); if (val & USBCMD_HCRST) { diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c index ca2d460785..d58cb616b1 100644 --- a/hw/xen/xen_pt.c +++ b/hw/xen/xen_pt.c @@ -570,7 +570,8 @@ static void xen_pt_region_update(XenPCIPassthroughState *s, if (args.rc) { XEN_PT_WARN(d, "Region: %d (addr: %#"FMT_PCIBUS ", len: %#"FMT_PCIBUS") is overlapped.\n", - bar, sec->offset_within_address_space, sec->size); + bar, sec->offset_within_address_space, + int128_get64(sec->size)); } if (d->io_regions[bar].type & PCI_BASE_ADDRESS_SPACE_IO) { diff --git a/hw/xen/xen_pvdevice.c b/hw/xen/xen_pvdevice.c index 1132c8934f..c2189473ba 100644 --- a/hw/xen/xen_pvdevice.c +++ b/hw/xen/xen_pvdevice.c @@ -74,6 +74,10 @@ static int xen_pv_init(PCIDevice *pci_dev) XenPVDevice *d = XEN_PV_DEVICE(pci_dev); uint8_t *pci_conf; + /* device-id property must always be supplied */ + if (d->device_id == 0xffff) + return -1; + pci_conf = pci_dev->config; pci_set_word(pci_conf + PCI_VENDOR_ID, d->vendor_id); @@ -99,7 +103,7 @@ static int xen_pv_init(PCIDevice *pci_dev) static Property xen_pv_props[] = { DEFINE_PROP_UINT16("vendor-id", XenPVDevice, vendor_id, PCI_VENDOR_ID_XEN), - DEFINE_PROP_UINT16("device-id", XenPVDevice, device_id, PCI_DEVICE_ID_XEN_PVDEVICE), + DEFINE_PROP_UINT16("device-id", XenPVDevice, device_id, 0xffff), DEFINE_PROP_UINT8("revision", XenPVDevice, revision, 0x01), DEFINE_PROP_UINT32("size", XenPVDevice, size, 0x400000), DEFINE_PROP_END_OF_LIST() |