diff options
Diffstat (limited to 'hw')
83 files changed, 3338 insertions, 1227 deletions
diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index df8c0db909..8fd25a5926 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -302,6 +302,11 @@ static const VMStateDescription vmstate_cpuhp_state = { } }; +static bool piix4_vmstate_need_smbus(void *opaque, int version_id) +{ + return pm_smbus_vmstate_needed(); +} + /* qemu-kvm 1.2 uses version 3 but advertised as 2 * To support incoming qemu-kvm 1.2 migration, change version_id * and minimum_version_id to 2 below (which breaks migration from @@ -321,6 +326,8 @@ static const VMStateDescription vmstate_acpi = { VMSTATE_UINT16(ar.pm1.evt.en, PIIX4PMState), VMSTATE_UINT16(ar.pm1.cnt.cnt, PIIX4PMState), VMSTATE_STRUCT(apm, PIIX4PMState, 0, vmstate_apm, APMState), + VMSTATE_STRUCT_TEST(smb, PIIX4PMState, piix4_vmstate_need_smbus, 3, + pmsmb_vmstate, PMSMBus), VMSTATE_TIMER_PTR(ar.tmr.timer, PIIX4PMState), VMSTATE_INT64(ar.tmr.overflow_time, PIIX4PMState), VMSTATE_STRUCT(ar.gpe, PIIX4PMState, 2, vmstate_gpe, ACPIGPE), diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index fa40e8d641..fa57c7c770 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -35,6 +35,7 @@ obj-$(CONFIG_ASPEED_SOC) += aspeed_soc.o aspeed.o obj-$(CONFIG_MPS2) += mps2.o obj-$(CONFIG_MPS2) += mps2-tz.o obj-$(CONFIG_MSF2) += msf2-soc.o msf2-som.o +obj-$(CONFIG_MUSCA) += musca.o obj-$(CONFIG_ARMSSE) += armsse.o obj-$(CONFIG_FSL_IMX7) += fsl-imx7.o mcimx7d-sabre.o obj-$(CONFIG_ARM_SMMUV3) += smmu-common.o smmuv3.o diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index 9a8c49547d..76cc690579 100644 --- a/hw/arm/armsse.c +++ b/hw/arm/armsse.c @@ -11,6 +11,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" +#include "qemu/bitops.h" #include "qapi/error.h" #include "trace.h" #include "hw/sysbus.h" @@ -29,6 +30,7 @@ struct ARMSSEInfo { int sram_banks; int num_cpus; uint32_t sys_version; + uint32_t cpuwait_rst; SysConfigFormat sys_config_format; bool has_mhus; bool has_ppus; @@ -43,6 +45,7 @@ static const ARMSSEInfo armsse_variants[] = { .sram_banks = 1, .num_cpus = 1, .sys_version = 0x41743, + .cpuwait_rst = 0, .sys_config_format = IoTKitFormat, .has_mhus = false, .has_ppus = false, @@ -55,6 +58,7 @@ static const ARMSSEInfo armsse_variants[] = { .sram_banks = 4, .num_cpus = 2, .sys_version = 0x22041743, + .cpuwait_rst = 2, .sys_config_format = SSE200Format, .has_mhus = true, .has_ppus = true, @@ -110,15 +114,16 @@ static bool irq_is_common[32] = { /* 30, 31: reserved */ }; -/* Create an alias region of @size bytes starting at @base +/* + * Create an alias region in @container of @size bytes starting at @base * which mirrors the memory starting at @orig. */ -static void make_alias(ARMSSE *s, MemoryRegion *mr, const char *name, - hwaddr base, hwaddr size, hwaddr orig) +static void make_alias(ARMSSE *s, MemoryRegion *mr, MemoryRegion *container, + const char *name, hwaddr base, hwaddr size, hwaddr orig) { - memory_region_init_alias(mr, NULL, name, &s->container, orig, size); + memory_region_init_alias(mr, NULL, name, container, orig, size); /* The alias is even lower priority than unimplemented_device regions */ - memory_region_add_subregion_overlap(&s->container, base, mr, -1500); + memory_region_add_subregion_overlap(container, base, mr, -1500); } static void irq_status_forwarder(void *opaque, int n, int level) @@ -281,9 +286,9 @@ static void armsse_init(Object *obj) sizeof(s->sysinfo), TYPE_IOTKIT_SYSINFO); if (info->has_mhus) { sysbus_init_child_obj(obj, "mhu0", &s->mhu[0], sizeof(s->mhu[0]), - TYPE_UNIMPLEMENTED_DEVICE); + TYPE_ARMSSE_MHU); sysbus_init_child_obj(obj, "mhu1", &s->mhu[1], sizeof(s->mhu[1]), - TYPE_UNIMPLEMENTED_DEVICE); + TYPE_ARMSSE_MHU); } if (info->has_ppus) { for (i = 0; i < info->num_cpus; i++) { @@ -494,31 +499,33 @@ static void armsse_realize(DeviceState *dev, Error **errp) qdev_prop_set_uint32(cpudev, "num-irq", s->exp_numirq + 32); /* - * In real hardware the initial Secure VTOR is set from the INITSVTOR0 - * register in the IoT Kit System Control Register block, and the - * initial value of that is in turn specifiable by the FPGA that - * instantiates the IoT Kit. In QEMU we don't implement this wrinkle, - * and simply set the CPU's init-svtor to the IoT Kit default value. - * In SSE-200 the situation is similar, except that the default value - * is a reset-time signal input. Typically a board using the SSE-200 - * will have a system control processor whose boot firmware initializes - * the INITSVTOR* registers before powering up the CPUs in any case, - * so the hardware's default value doesn't matter. QEMU doesn't emulate + * In real hardware the initial Secure VTOR is set from the INITSVTOR* + * registers in the IoT Kit System Control Register block. In QEMU + * we set the initial value here, and also the reset value of the + * sysctl register, from this object's QOM init-svtor property. + * If the guest changes the INITSVTOR* registers at runtime then the + * code in iotkit-sysctl.c will update the CPU init-svtor property + * (which will then take effect on the next CPU warm-reset). + * + * Note that typically a board using the SSE-200 will have a system + * control processor whose boot firmware initializes the INITSVTOR* + * registers before powering up the CPUs. QEMU doesn't emulate * the control processor, so instead we behave in the way that the - * firmware does. All boards currently known about have firmware that - * sets the INITSVTOR0 and INITSVTOR1 registers to 0x10000000, like the - * IoTKit default. We can make this more configurable if necessary. + * firmware does: the initial value should be set by the board code + * (using the init-svtor property on the ARMSSE object) to match + * whatever its firmware does. */ - qdev_prop_set_uint32(cpudev, "init-svtor", 0x10000000); + qdev_prop_set_uint32(cpudev, "init-svtor", s->init_svtor); /* - * Start all CPUs except CPU0 powered down. In real hardware it is - * a configurable property of the SSE-200 which CPUs start powered up - * (via the CPUWAIT0_RST and CPUWAIT1_RST parameters), but since all - * the boards we care about start CPU0 and leave CPU1 powered off, - * we hard-code that for now. We can add QOM properties for this + * CPUs start powered down if the corresponding bit in the CPUWAIT + * register is 1. In real hardware the CPUWAIT register reset value is + * a configurable property of the SSE-200 (via the CPUWAIT0_RST and + * CPUWAIT1_RST parameters), but since all the boards we care about + * start CPU0 and leave CPU1 powered off, we hard-code that in + * info->cpuwait_rst for now. We can add QOM properties for this * later if necessary. */ - if (i > 0) { + if (extract32(info->cpuwait_rst, i, 1)) { object_property_set_bool(cpuobj, true, "start-powered-off", &err); if (err) { error_propagate(errp, err); @@ -608,16 +615,21 @@ static void armsse_realize(DeviceState *dev, Error **errp) } /* Set up the big aliases first */ - make_alias(s, &s->alias1, "alias 1", 0x10000000, 0x10000000, 0x00000000); - make_alias(s, &s->alias2, "alias 2", 0x30000000, 0x10000000, 0x20000000); + make_alias(s, &s->alias1, &s->container, "alias 1", + 0x10000000, 0x10000000, 0x00000000); + make_alias(s, &s->alias2, &s->container, + "alias 2", 0x30000000, 0x10000000, 0x20000000); /* The 0x50000000..0x5fffffff region is not a pure alias: it has * a few extra devices that only appear there (generally the * control interfaces for the protection controllers). * We implement this by mapping those devices over the top of this - * alias MR at a higher priority. + * alias MR at a higher priority. Some of the devices in this range + * are per-CPU, so we must put this alias in the per-cpu containers. */ - make_alias(s, &s->alias3, "alias 3", 0x50000000, 0x10000000, 0x40000000); - + for (i = 0; i < info->num_cpus; i++) { + make_alias(s, &s->alias3[i], &s->cpu_container[i], + "alias 3", 0x50000000, 0x10000000, 0x40000000); + } /* Security controller */ object_property_set_bool(OBJECT(&s->secctl), true, "realized", &err); @@ -761,27 +773,49 @@ static void armsse_realize(DeviceState *dev, Error **errp) } if (info->has_mhus) { + /* + * An SSE-200 with only one CPU should have only one MHU created, + * with the region where the second MHU usually is being RAZ/WI. + * We don't implement that SSE-200 config; if we want to support + * it then this code needs to be enhanced to handle creating the + * RAZ/WI region instead of the second MHU. + */ + assert(info->num_cpus == ARRAY_SIZE(s->mhu)); + for (i = 0; i < ARRAY_SIZE(s->mhu); i++) { - char *name = g_strdup_printf("MHU%d", i); - char *port = g_strdup_printf("port[%d]", i + 3); + char *port; + int cpunum; + SysBusDevice *mhu_sbd = SYS_BUS_DEVICE(&s->mhu[i]); - qdev_prop_set_string(DEVICE(&s->mhu[i]), "name", name); - qdev_prop_set_uint64(DEVICE(&s->mhu[i]), "size", 0x1000); object_property_set_bool(OBJECT(&s->mhu[i]), true, "realized", &err); if (err) { error_propagate(errp, err); return; } - mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mhu[i]), 0); + port = g_strdup_printf("port[%d]", i + 3); + mr = sysbus_mmio_get_region(mhu_sbd, 0); object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), port, &err); + g_free(port); if (err) { error_propagate(errp, err); return; } - g_free(name); - g_free(port); + + /* + * Each MHU has an irq line for each CPU: + * MHU 0 irq line 0 -> CPU 0 IRQ 6 + * MHU 0 irq line 1 -> CPU 1 IRQ 6 + * MHU 1 irq line 0 -> CPU 0 IRQ 7 + * MHU 1 irq line 1 -> CPU 1 IRQ 7 + */ + for (cpunum = 0; cpunum < info->num_cpus; cpunum++) { + DeviceState *cpudev = DEVICE(&s->armv7m[cpunum]); + + sysbus_connect_irq(mhu_sbd, cpunum, + qdev_get_gpio_in(cpudev, 6 + i)); + } } } @@ -970,6 +1004,14 @@ static void armsse_realize(DeviceState *dev, Error **errp) /* System information registers */ sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysinfo), 0, 0x40020000); /* System control registers */ + object_property_set_int(OBJECT(&s->sysctl), info->sys_version, + "SYS_VERSION", &err); + object_property_set_int(OBJECT(&s->sysctl), info->cpuwait_rst, + "CPUWAIT_RST", &err); + object_property_set_int(OBJECT(&s->sysctl), s->init_svtor, + "INITSVTOR0_RST", &err); + object_property_set_int(OBJECT(&s->sysctl), s->init_svtor, + "INITSVTOR1_RST", &err); object_property_set_bool(OBJECT(&s->sysctl), true, "realized", &err); if (err) { error_propagate(errp, err); @@ -1185,6 +1227,7 @@ static Property armsse_properties[] = { DEFINE_PROP_UINT32("EXP_NUMIRQ", ARMSSE, exp_numirq, 64), DEFINE_PROP_UINT32("MAINCLK", ARMSSE, mainclk_frq, 0), DEFINE_PROP_UINT32("SRAM_ADDR_WIDTH", ARMSSE, sram_addr_width, 15), + DEFINE_PROP_UINT32("init-svtor", ARMSSE, init_svtor, 0x10000000), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 5158985482..996812498d 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -18,7 +18,7 @@ #include "hw/arm/aspeed.h" #include "hw/arm/aspeed_soc.h" #include "hw/boards.h" -#include "hw/i2c/smbus.h" +#include "hw/i2c/smbus_eeprom.h" #include "qemu/log.h" #include "sysemu/block-backend.h" #include "hw/loader.h" diff --git a/hw/arm/musca.c b/hw/arm/musca.c new file mode 100644 index 0000000000..23aff43f4b --- /dev/null +++ b/hw/arm/musca.c @@ -0,0 +1,669 @@ +/* + * Arm Musca-B1 test chip board emulation + * + * Copyright (c) 2019 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* + * The Musca boards are a reference implementation of a system using + * the SSE-200 subsystem for embedded: + * https://developer.arm.com/products/system-design/development-boards/iot-test-chips-and-boards/musca-a-test-chip-board + * https://developer.arm.com/products/system-design/development-boards/iot-test-chips-and-boards/musca-b-test-chip-board + * We model the A and B1 variants of this board, as described in the TRMs: + * http://infocenter.arm.com/help/topic/com.arm.doc.101107_0000_00_en/index.html + * http://infocenter.arm.com/help/topic/com.arm.doc.101312_0000_00_en/index.html + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "exec/address-spaces.h" +#include "sysemu/sysemu.h" +#include "hw/arm/arm.h" +#include "hw/arm/armsse.h" +#include "hw/boards.h" +#include "hw/char/pl011.h" +#include "hw/core/split-irq.h" +#include "hw/misc/tz-mpc.h" +#include "hw/misc/tz-ppc.h" +#include "hw/misc/unimp.h" +#include "hw/timer/pl031.h" + +#define MUSCA_NUMIRQ_MAX 96 +#define MUSCA_PPC_MAX 3 +#define MUSCA_MPC_MAX 5 + +typedef struct MPCInfo MPCInfo; + +typedef enum MuscaType { + MUSCA_A, + MUSCA_B1, +} MuscaType; + +typedef struct { + MachineClass parent; + MuscaType type; + uint32_t init_svtor; + int sram_addr_width; + int num_irqs; + const MPCInfo *mpc_info; + int num_mpcs; +} MuscaMachineClass; + +typedef struct { + MachineState parent; + + ARMSSE sse; + /* RAM and flash */ + MemoryRegion ram[MUSCA_MPC_MAX]; + SplitIRQ cpu_irq_splitter[MUSCA_NUMIRQ_MAX]; + SplitIRQ sec_resp_splitter; + TZPPC ppc[MUSCA_PPC_MAX]; + MemoryRegion container; + UnimplementedDeviceState eflash[2]; + UnimplementedDeviceState qspi; + TZMPC mpc[MUSCA_MPC_MAX]; + UnimplementedDeviceState mhu[2]; + UnimplementedDeviceState pwm[3]; + UnimplementedDeviceState i2s; + PL011State uart[2]; + UnimplementedDeviceState i2c[2]; + UnimplementedDeviceState spi; + UnimplementedDeviceState scc; + UnimplementedDeviceState timer; + PL031State rtc; + UnimplementedDeviceState pvt; + UnimplementedDeviceState sdio; + UnimplementedDeviceState gpio; + UnimplementedDeviceState cryptoisland; +} MuscaMachineState; + +#define TYPE_MUSCA_MACHINE "musca" +#define TYPE_MUSCA_A_MACHINE MACHINE_TYPE_NAME("musca-a") +#define TYPE_MUSCA_B1_MACHINE MACHINE_TYPE_NAME("musca-b1") + +#define MUSCA_MACHINE(obj) \ + OBJECT_CHECK(MuscaMachineState, obj, TYPE_MUSCA_MACHINE) +#define MUSCA_MACHINE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(MuscaMachineClass, obj, TYPE_MUSCA_MACHINE) +#define MUSCA_MACHINE_CLASS(klass) \ + OBJECT_CLASS_CHECK(MuscaMachineClass, klass, TYPE_MUSCA_MACHINE) + +/* + * Main SYSCLK frequency in Hz + * TODO this should really be different for the two cores, but we + * don't model that in our SSE-200 model yet. + */ +#define SYSCLK_FRQ 40000000 + +static qemu_irq get_sse_irq_in(MuscaMachineState *mms, int irqno) +{ + /* Return a qemu_irq which will signal IRQ n to all CPUs in the SSE. */ + assert(irqno < MUSCA_NUMIRQ_MAX); + + return qdev_get_gpio_in(DEVICE(&mms->cpu_irq_splitter[irqno]), 0); +} + +/* + * Most of the devices in the Musca board sit behind Peripheral Protection + * Controllers. These data structures define the layout of which devices + * sit behind which PPCs. + * The devfn for each port is a function which creates, configures + * and initializes the device, returning the MemoryRegion which + * needs to be plugged into the downstream end of the PPC port. + */ +typedef MemoryRegion *MakeDevFn(MuscaMachineState *mms, void *opaque, + const char *name, hwaddr size); + +typedef struct PPCPortInfo { + const char *name; + MakeDevFn *devfn; + void *opaque; + hwaddr addr; + hwaddr size; +} PPCPortInfo; + +typedef struct PPCInfo { + const char *name; + PPCPortInfo ports[TZ_NUM_PORTS]; +} PPCInfo; + +static MemoryRegion *make_unimp_dev(MuscaMachineState *mms, + void *opaque, const char *name, hwaddr size) +{ + /* + * Initialize, configure and realize a TYPE_UNIMPLEMENTED_DEVICE, + * and return a pointer to its MemoryRegion. + */ + UnimplementedDeviceState *uds = opaque; + + sysbus_init_child_obj(OBJECT(mms), name, uds, + sizeof(UnimplementedDeviceState), + TYPE_UNIMPLEMENTED_DEVICE); + qdev_prop_set_string(DEVICE(uds), "name", name); + qdev_prop_set_uint64(DEVICE(uds), "size", size); + object_property_set_bool(OBJECT(uds), true, "realized", &error_fatal); + return sysbus_mmio_get_region(SYS_BUS_DEVICE(uds), 0); +} + +typedef enum MPCInfoType { + MPC_RAM, + MPC_ROM, + MPC_CRYPTOISLAND, +} MPCInfoType; + +struct MPCInfo { + const char *name; + hwaddr addr; + hwaddr size; + MPCInfoType type; +}; + +/* Order of the MPCs here must match the order of the bits in SECMPCINTSTATUS */ +static const MPCInfo a_mpc_info[] = { { + .name = "qspi", + .type = MPC_ROM, + .addr = 0x00200000, + .size = 0x00800000, + }, { + .name = "sram", + .type = MPC_RAM, + .addr = 0x00000000, + .size = 0x00200000, + } +}; + +static const MPCInfo b1_mpc_info[] = { { + .name = "qspi", + .type = MPC_ROM, + .addr = 0x00000000, + .size = 0x02000000, + }, { + .name = "sram", + .type = MPC_RAM, + .addr = 0x0a400000, + .size = 0x00080000, + }, { + .name = "eflash0", + .type = MPC_ROM, + .addr = 0x0a000000, + .size = 0x00200000, + }, { + .name = "eflash1", + .type = MPC_ROM, + .addr = 0x0a200000, + .size = 0x00200000, + }, { + .name = "cryptoisland", + .type = MPC_CRYPTOISLAND, + .addr = 0x0a000000, + .size = 0x00200000, + } +}; + +static MemoryRegion *make_mpc(MuscaMachineState *mms, void *opaque, + const char *name, hwaddr size) +{ + /* + * Create an MPC and the RAM or flash behind it. + * MPC 0: eFlash 0 + * MPC 1: eFlash 1 + * MPC 2: SRAM + * MPC 3: QSPI flash + * MPC 4: CryptoIsland + * For now we implement the flash regions as ROM (ie not programmable) + * (with their control interface memory regions being unimplemented + * stubs behind the PPCs). + * The whole CryptoIsland region behind its MPC is an unimplemented stub. + */ + MuscaMachineClass *mmc = MUSCA_MACHINE_GET_CLASS(mms); + TZMPC *mpc = opaque; + int i = mpc - &mms->mpc[0]; + MemoryRegion *downstream; + MemoryRegion *upstream; + UnimplementedDeviceState *uds; + char *mpcname; + const MPCInfo *mpcinfo = mmc->mpc_info; + + mpcname = g_strdup_printf("%s-mpc", mpcinfo[i].name); + + switch (mpcinfo[i].type) { + case MPC_ROM: + downstream = &mms->ram[i]; + memory_region_init_rom(downstream, NULL, mpcinfo[i].name, + mpcinfo[i].size, &error_fatal); + break; + case MPC_RAM: + downstream = &mms->ram[i]; + memory_region_init_ram(downstream, NULL, mpcinfo[i].name, + mpcinfo[i].size, &error_fatal); + break; + case MPC_CRYPTOISLAND: + /* We don't implement the CryptoIsland yet */ + uds = &mms->cryptoisland; + sysbus_init_child_obj(OBJECT(mms), name, uds, + sizeof(UnimplementedDeviceState), + TYPE_UNIMPLEMENTED_DEVICE); + qdev_prop_set_string(DEVICE(uds), "name", mpcinfo[i].name); + qdev_prop_set_uint64(DEVICE(uds), "size", mpcinfo[i].size); + object_property_set_bool(OBJECT(uds), true, "realized", &error_fatal); + downstream = sysbus_mmio_get_region(SYS_BUS_DEVICE(uds), 0); + break; + default: + g_assert_not_reached(); + } + + sysbus_init_child_obj(OBJECT(mms), mpcname, mpc, sizeof(mms->mpc[0]), + TYPE_TZ_MPC); + object_property_set_link(OBJECT(mpc), OBJECT(downstream), + "downstream", &error_fatal); + object_property_set_bool(OBJECT(mpc), true, "realized", &error_fatal); + /* Map the upstream end of the MPC into system memory */ + upstream = sysbus_mmio_get_region(SYS_BUS_DEVICE(mpc), 1); + memory_region_add_subregion(get_system_memory(), mpcinfo[i].addr, upstream); + /* and connect its interrupt to the SSE-200 */ + qdev_connect_gpio_out_named(DEVICE(mpc), "irq", 0, + qdev_get_gpio_in_named(DEVICE(&mms->sse), + "mpcexp_status", i)); + + g_free(mpcname); + /* Return the register interface MR for our caller to map behind the PPC */ + return sysbus_mmio_get_region(SYS_BUS_DEVICE(mpc), 0); +} + +static MemoryRegion *make_rtc(MuscaMachineState *mms, void *opaque, + const char *name, hwaddr size) +{ + PL031State *rtc = opaque; + + sysbus_init_child_obj(OBJECT(mms), name, rtc, sizeof(mms->rtc), TYPE_PL031); + object_property_set_bool(OBJECT(rtc), true, "realized", &error_fatal); + sysbus_connect_irq(SYS_BUS_DEVICE(rtc), 0, get_sse_irq_in(mms, 39)); + return sysbus_mmio_get_region(SYS_BUS_DEVICE(rtc), 0); +} + +static MemoryRegion *make_uart(MuscaMachineState *mms, void *opaque, + const char *name, hwaddr size) +{ + PL011State *uart = opaque; + int i = uart - &mms->uart[0]; + int irqbase = 7 + i * 6; + SysBusDevice *s; + + sysbus_init_child_obj(OBJECT(mms), name, uart, sizeof(mms->uart[0]), + TYPE_PL011); + qdev_prop_set_chr(DEVICE(uart), "chardev", serial_hd(i)); + object_property_set_bool(OBJECT(uart), true, "realized", &error_fatal); + s = SYS_BUS_DEVICE(uart); + sysbus_connect_irq(s, 0, get_sse_irq_in(mms, irqbase + 5)); /* combined */ + sysbus_connect_irq(s, 1, get_sse_irq_in(mms, irqbase + 0)); /* RX */ + sysbus_connect_irq(s, 2, get_sse_irq_in(mms, irqbase + 1)); /* TX */ + sysbus_connect_irq(s, 3, get_sse_irq_in(mms, irqbase + 2)); /* RT */ + sysbus_connect_irq(s, 4, get_sse_irq_in(mms, irqbase + 3)); /* MS */ + sysbus_connect_irq(s, 5, get_sse_irq_in(mms, irqbase + 4)); /* E */ + return sysbus_mmio_get_region(SYS_BUS_DEVICE(uart), 0); +} + +static MemoryRegion *make_musca_a_devs(MuscaMachineState *mms, void *opaque, + const char *name, hwaddr size) +{ + /* + * Create the container MemoryRegion for all the devices that live + * behind the Musca-A PPC's single port. These devices don't have a PPC + * port each, but we use the PPCPortInfo struct as a convenient way + * to describe them. Note that addresses here are relative to the base + * address of the PPC port region: 0x40100000, and devices appear both + * at the 0x4... NS region and the 0x5... S region. + */ + int i; + MemoryRegion *container = &mms->container; + + const PPCPortInfo devices[] = { + { "uart0", make_uart, &mms->uart[0], 0x1000, 0x1000 }, + { "uart1", make_uart, &mms->uart[1], 0x2000, 0x1000 }, + { "spi", make_unimp_dev, &mms->spi, 0x3000, 0x1000 }, + { "i2c0", make_unimp_dev, &mms->i2c[0], 0x4000, 0x1000 }, + { "i2c1", make_unimp_dev, &mms->i2c[1], 0x5000, 0x1000 }, + { "i2s", make_unimp_dev, &mms->i2s, 0x6000, 0x1000 }, + { "pwm0", make_unimp_dev, &mms->pwm[0], 0x7000, 0x1000 }, + { "rtc", make_rtc, &mms->rtc, 0x8000, 0x1000 }, + { "qspi", make_unimp_dev, &mms->qspi, 0xa000, 0x1000 }, + { "timer", make_unimp_dev, &mms->timer, 0xb000, 0x1000 }, + { "scc", make_unimp_dev, &mms->scc, 0xc000, 0x1000 }, + { "pwm1", make_unimp_dev, &mms->pwm[1], 0xe000, 0x1000 }, + { "pwm2", make_unimp_dev, &mms->pwm[2], 0xf000, 0x1000 }, + { "gpio", make_unimp_dev, &mms->gpio, 0x10000, 0x1000 }, + { "mpc0", make_mpc, &mms->mpc[0], 0x12000, 0x1000 }, + { "mpc1", make_mpc, &mms->mpc[1], 0x13000, 0x1000 }, + }; + + memory_region_init(container, OBJECT(mms), "musca-device-container", size); + + for (i = 0; i < ARRAY_SIZE(devices); i++) { + const PPCPortInfo *pinfo = &devices[i]; + MemoryRegion *mr; + + mr = pinfo->devfn(mms, pinfo->opaque, pinfo->name, pinfo->size); + memory_region_add_subregion(container, pinfo->addr, mr); + } + + return &mms->container; +} + +static void musca_init(MachineState *machine) +{ + MuscaMachineState *mms = MUSCA_MACHINE(machine); + MuscaMachineClass *mmc = MUSCA_MACHINE_GET_CLASS(mms); + MachineClass *mc = MACHINE_GET_CLASS(machine); + MemoryRegion *system_memory = get_system_memory(); + DeviceState *ssedev; + DeviceState *dev_splitter; + const PPCInfo *ppcs; + int num_ppcs; + int i; + + assert(mmc->num_irqs <= MUSCA_NUMIRQ_MAX); + assert(mmc->num_mpcs <= MUSCA_MPC_MAX); + + if (strcmp(machine->cpu_type, mc->default_cpu_type) != 0) { + error_report("This board can only be used with CPU %s", + mc->default_cpu_type); + exit(1); + } + + sysbus_init_child_obj(OBJECT(machine), "sse-200", &mms->sse, + sizeof(mms->sse), TYPE_SSE200); + ssedev = DEVICE(&mms->sse); + object_property_set_link(OBJECT(&mms->sse), OBJECT(system_memory), + "memory", &error_fatal); + qdev_prop_set_uint32(ssedev, "EXP_NUMIRQ", mmc->num_irqs); + qdev_prop_set_uint32(ssedev, "init-svtor", mmc->init_svtor); + qdev_prop_set_uint32(ssedev, "SRAM_ADDR_WIDTH", mmc->sram_addr_width); + qdev_prop_set_uint32(ssedev, "MAINCLK", SYSCLK_FRQ); + object_property_set_bool(OBJECT(&mms->sse), true, "realized", + &error_fatal); + + /* + * We need to create splitters to feed the IRQ inputs + * for each CPU in the SSE-200 from each device in the board. + */ + for (i = 0; i < mmc->num_irqs; i++) { + char *name = g_strdup_printf("musca-irq-splitter%d", i); + SplitIRQ *splitter = &mms->cpu_irq_splitter[i]; + + object_initialize_child(OBJECT(machine), name, + splitter, sizeof(*splitter), + TYPE_SPLIT_IRQ, &error_fatal, NULL); + g_free(name); + + object_property_set_int(OBJECT(splitter), 2, "num-lines", + &error_fatal); + object_property_set_bool(OBJECT(splitter), true, "realized", + &error_fatal); + qdev_connect_gpio_out(DEVICE(splitter), 0, + qdev_get_gpio_in_named(ssedev, "EXP_IRQ", i)); + qdev_connect_gpio_out(DEVICE(splitter), 1, + qdev_get_gpio_in_named(ssedev, + "EXP_CPU1_IRQ", i)); + } + + /* + * The sec_resp_cfg output from the SSE-200 must be split into multiple + * lines, one for each of the PPCs we create here. + */ + object_initialize(&mms->sec_resp_splitter, sizeof(mms->sec_resp_splitter), + TYPE_SPLIT_IRQ); + object_property_add_child(OBJECT(machine), "sec-resp-splitter", + OBJECT(&mms->sec_resp_splitter), &error_fatal); + object_property_set_int(OBJECT(&mms->sec_resp_splitter), + ARRAY_SIZE(mms->ppc), "num-lines", &error_fatal); + object_property_set_bool(OBJECT(&mms->sec_resp_splitter), true, + "realized", &error_fatal); + dev_splitter = DEVICE(&mms->sec_resp_splitter); + qdev_connect_gpio_out_named(ssedev, "sec_resp_cfg", 0, + qdev_get_gpio_in(dev_splitter, 0)); + + /* + * Most of the devices in the board are behind Peripheral Protection + * Controllers. The required order for initializing things is: + * + initialize the PPC + * + initialize, configure and realize downstream devices + * + connect downstream device MemoryRegions to the PPC + * + realize the PPC + * + map the PPC's MemoryRegions to the places in the address map + * where the downstream devices should appear + * + wire up the PPC's control lines to the SSE object + * + * The PPC mapping differs for the -A and -B1 variants; the -A version + * is much simpler, using only a single port of a single PPC and putting + * all the devices behind that. + */ + const PPCInfo a_ppcs[] = { { + .name = "ahb_ppcexp0", + .ports = { + { "musca-devices", make_musca_a_devs, 0, 0x40100000, 0x100000 }, + }, + }, + }; + + /* + * Devices listed with an 0x4.. address appear in both the NS 0x4.. region + * and the 0x5.. S region. Devices listed with an 0x5.. address appear + * only in the S region. + */ + const PPCInfo b1_ppcs[] = { { + .name = "apb_ppcexp0", + .ports = { + { "eflash0", make_unimp_dev, &mms->eflash[0], + 0x52400000, 0x1000 }, + { "eflash1", make_unimp_dev, &mms->eflash[1], + 0x52500000, 0x1000 }, + { "qspi", make_unimp_dev, &mms->qspi, 0x42800000, 0x100000 }, + { "mpc0", make_mpc, &mms->mpc[0], 0x52000000, 0x1000 }, + { "mpc1", make_mpc, &mms->mpc[1], 0x52100000, 0x1000 }, + { "mpc2", make_mpc, &mms->mpc[2], 0x52200000, 0x1000 }, + { "mpc3", make_mpc, &mms->mpc[3], 0x52300000, 0x1000 }, + { "mhu0", make_unimp_dev, &mms->mhu[0], 0x42600000, 0x100000 }, + { "mhu1", make_unimp_dev, &mms->mhu[1], 0x42700000, 0x100000 }, + { }, /* port 9: unused */ + { }, /* port 10: unused */ + { }, /* port 11: unused */ + { }, /* port 12: unused */ + { }, /* port 13: unused */ + { "mpc4", make_mpc, &mms->mpc[4], 0x52e00000, 0x1000 }, + }, + }, { + .name = "apb_ppcexp1", + .ports = { + { "pwm0", make_unimp_dev, &mms->pwm[0], 0x40101000, 0x1000 }, + { "pwm1", make_unimp_dev, &mms->pwm[1], 0x40102000, 0x1000 }, + { "pwm2", make_unimp_dev, &mms->pwm[2], 0x40103000, 0x1000 }, + { "i2s", make_unimp_dev, &mms->i2s, 0x40104000, 0x1000 }, + { "uart0", make_uart, &mms->uart[0], 0x40105000, 0x1000 }, + { "uart1", make_uart, &mms->uart[1], 0x40106000, 0x1000 }, + { "i2c0", make_unimp_dev, &mms->i2c[0], 0x40108000, 0x1000 }, + { "i2c1", make_unimp_dev, &mms->i2c[1], 0x40109000, 0x1000 }, + { "spi", make_unimp_dev, &mms->spi, 0x4010a000, 0x1000 }, + { "scc", make_unimp_dev, &mms->scc, 0x5010b000, 0x1000 }, + { "timer", make_unimp_dev, &mms->timer, 0x4010c000, 0x1000 }, + { "rtc", make_rtc, &mms->rtc, 0x4010d000, 0x1000 }, + { "pvt", make_unimp_dev, &mms->pvt, 0x4010e000, 0x1000 }, + { "sdio", make_unimp_dev, &mms->sdio, 0x4010f000, 0x1000 }, + }, + }, { + .name = "ahb_ppcexp0", + .ports = { + { }, /* port 0: unused */ + { "gpio", make_unimp_dev, &mms->gpio, 0x41000000, 0x1000 }, + }, + }, + }; + + switch (mmc->type) { + case MUSCA_A: + ppcs = a_ppcs; + num_ppcs = ARRAY_SIZE(a_ppcs); + break; + case MUSCA_B1: + ppcs = b1_ppcs; + num_ppcs = ARRAY_SIZE(b1_ppcs); + break; + default: + g_assert_not_reached(); + } + assert(num_ppcs <= MUSCA_PPC_MAX); + + for (i = 0; i < num_ppcs; i++) { + const PPCInfo *ppcinfo = &ppcs[i]; + TZPPC *ppc = &mms->ppc[i]; + DeviceState *ppcdev; + int port; + char *gpioname; + + sysbus_init_child_obj(OBJECT(machine), ppcinfo->name, ppc, + sizeof(TZPPC), TYPE_TZ_PPC); + ppcdev = DEVICE(ppc); + + for (port = 0; port < TZ_NUM_PORTS; port++) { + const PPCPortInfo *pinfo = &ppcinfo->ports[port]; + MemoryRegion *mr; + char *portname; + + if (!pinfo->devfn) { + continue; + } + + mr = pinfo->devfn(mms, pinfo->opaque, pinfo->name, pinfo->size); + portname = g_strdup_printf("port[%d]", port); + object_property_set_link(OBJECT(ppc), OBJECT(mr), + portname, &error_fatal); + g_free(portname); + } + + object_property_set_bool(OBJECT(ppc), true, "realized", &error_fatal); + + for (port = 0; port < TZ_NUM_PORTS; port++) { + const PPCPortInfo *pinfo = &ppcinfo->ports[port]; + + if (!pinfo->devfn) { + continue; + } + sysbus_mmio_map(SYS_BUS_DEVICE(ppc), port, pinfo->addr); + + gpioname = g_strdup_printf("%s_nonsec", ppcinfo->name); + qdev_connect_gpio_out_named(ssedev, gpioname, port, + qdev_get_gpio_in_named(ppcdev, + "cfg_nonsec", + port)); + g_free(gpioname); + gpioname = g_strdup_printf("%s_ap", ppcinfo->name); + qdev_connect_gpio_out_named(ssedev, gpioname, port, + qdev_get_gpio_in_named(ppcdev, + "cfg_ap", port)); + g_free(gpioname); + } + + gpioname = g_strdup_printf("%s_irq_enable", ppcinfo->name); + qdev_connect_gpio_out_named(ssedev, gpioname, 0, + qdev_get_gpio_in_named(ppcdev, + "irq_enable", 0)); + g_free(gpioname); + gpioname = g_strdup_printf("%s_irq_clear", ppcinfo->name); + qdev_connect_gpio_out_named(ssedev, gpioname, 0, + qdev_get_gpio_in_named(ppcdev, + "irq_clear", 0)); + g_free(gpioname); + gpioname = g_strdup_printf("%s_irq_status", ppcinfo->name); + qdev_connect_gpio_out_named(ppcdev, "irq", 0, + qdev_get_gpio_in_named(ssedev, + gpioname, 0)); + g_free(gpioname); + + qdev_connect_gpio_out(dev_splitter, i, + qdev_get_gpio_in_named(ppcdev, + "cfg_sec_resp", 0)); + } + + armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, 0x2000000); +} + +static void musca_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->default_cpus = 2; + mc->min_cpus = mc->default_cpus; + mc->max_cpus = mc->default_cpus; + mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33"); + mc->init = musca_init; +} + +static void musca_a_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + MuscaMachineClass *mmc = MUSCA_MACHINE_CLASS(oc); + + mc->desc = "ARM Musca-A board (dual Cortex-M33)"; + mmc->type = MUSCA_A; + mmc->init_svtor = 0x10200000; + mmc->sram_addr_width = 15; + mmc->num_irqs = 64; + mmc->mpc_info = a_mpc_info; + mmc->num_mpcs = ARRAY_SIZE(a_mpc_info); +} + +static void musca_b1_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + MuscaMachineClass *mmc = MUSCA_MACHINE_CLASS(oc); + + mc->desc = "ARM Musca-B1 board (dual Cortex-M33)"; + mmc->type = MUSCA_B1; + /* + * This matches the DAPlink firmware which boots from QSPI. There + * is also a firmware blob which boots from the eFlash, which + * uses init_svtor = 0x1A000000. QEMU doesn't currently support that, + * though we could in theory expose a machine property on the command + * line to allow the user to request eFlash boot. + */ + mmc->init_svtor = 0x10000000; + mmc->sram_addr_width = 17; + mmc->num_irqs = 96; + mmc->mpc_info = b1_mpc_info; + mmc->num_mpcs = ARRAY_SIZE(b1_mpc_info); +} + +static const TypeInfo musca_info = { + .name = TYPE_MUSCA_MACHINE, + .parent = TYPE_MACHINE, + .abstract = true, + .instance_size = sizeof(MuscaMachineState), + .class_size = sizeof(MuscaMachineClass), + .class_init = musca_class_init, +}; + +static const TypeInfo musca_a_info = { + .name = TYPE_MUSCA_A_MACHINE, + .parent = TYPE_MUSCA_MACHINE, + .class_init = musca_a_class_init, +}; + +static const TypeInfo musca_b1_info = { + .name = TYPE_MUSCA_B1_MACHINE, + .parent = TYPE_MUSCA_MACHINE, + .class_init = musca_b1_class_init, +}; + +static void musca_machine_init(void) +{ + type_register_static(&musca_info); + type_register_static(&musca_a_info); + type_register_static(&musca_b1_info); +} + +type_init(musca_machine_init); diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index f598a1c053..3d7c88910e 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -1286,7 +1286,7 @@ static int pxa2xx_i2c_event(I2CSlave *i2c, enum i2c_event event) return 0; } -static int pxa2xx_i2c_rx(I2CSlave *i2c) +static uint8_t pxa2xx_i2c_rx(I2CSlave *i2c) { PXA2xxI2CSlaveState *slave = PXA2XX_I2C_SLAVE(i2c); PXA2xxI2CState *s = slave->host; diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index 442529cc65..7b45fe3ccf 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -811,7 +811,7 @@ static void stellaris_i2c_write(void *opaque, hwaddr offset, /* TODO: Handle errors. */ if (s->msa & 1) { /* Recv */ - s->mdr = i2c_recv(s->bus) & 0xff; + s->mdr = i2c_recv(s->bus); } else { /* Send */ i2c_send(s->bus, s->mdr); diff --git a/hw/arm/tosa.c b/hw/arm/tosa.c index 7a925fa5e6..eef9d427e7 100644 --- a/hw/arm/tosa.c +++ b/hw/arm/tosa.c @@ -197,10 +197,10 @@ static int tosa_dac_event(I2CSlave *i2c, enum i2c_event event) return 0; } -static int tosa_dac_recv(I2CSlave *s) +static uint8_t tosa_dac_recv(I2CSlave *s) { printf("%s: recv not supported!!!\n", __func__); - return -1; + return 0xff; } static void tosa_tg_init(PXA2xxState *cpu) diff --git a/hw/arm/z2.c b/hw/arm/z2.c index 697a822f1e..6f18d924df 100644 --- a/hw/arm/z2.c +++ b/hw/arm/z2.c @@ -243,7 +243,7 @@ static int aer915_event(I2CSlave *i2c, enum i2c_event event) return 0; } -static int aer915_recv(I2CSlave *slave) +static uint8_t aer915_recv(I2CSlave *slave) { AER915State *s = AER915(slave); int retval = 0x00; diff --git a/hw/audio/wm8750.c b/hw/audio/wm8750.c index f4aa838f62..169b006ade 100644 --- a/hw/audio/wm8750.c +++ b/hw/audio/wm8750.c @@ -561,7 +561,7 @@ static int wm8750_tx(I2CSlave *i2c, uint8_t data) return 0; } -static int wm8750_rx(I2CSlave *i2c) +static uint8_t wm8750_rx(I2CSlave *i2c) { return 0x00; } diff --git a/hw/block/dataplane/xen-block.c b/hw/block/dataplane/xen-block.c index c6a15da024..f1523c5b45 100644 --- a/hw/block/dataplane/xen-block.c +++ b/hw/block/dataplane/xen-block.c @@ -281,10 +281,6 @@ static void xen_block_complete_aio(void *opaque, int ret) break; case BLKIF_OP_WRITE: case BLKIF_OP_FLUSH_DISKCACHE: - if (!request->req.nr_segments) { - break; - } - break; default: break; } @@ -298,6 +294,7 @@ static void xen_block_complete_aio(void *opaque, int ret) if (!request->req.nr_segments) { break; } + /* fall through */ case BLKIF_OP_READ: if (request->status == BLKIF_RSP_OKAY) { block_acct_done(blk_get_stats(dataplane->blk), &request->acct); diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index cf7f47eaba..0cc3c590b9 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -28,9 +28,28 @@ #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio-access.h" -/* We don't support discard yet, hide associated config fields. */ +/* Config size before the discard support (hide associated config fields) */ #define VIRTIO_BLK_CFG_SIZE offsetof(struct virtio_blk_config, \ max_discard_sectors) +/* + * Starting from the discard feature, we can use this array to properly + * set the config size depending on the features enabled. + */ +static VirtIOFeature feature_sizes[] = { + {.flags = 1ULL << VIRTIO_BLK_F_DISCARD, + .end = virtio_endof(struct virtio_blk_config, discard_sector_alignment)}, + {.flags = 1ULL << VIRTIO_BLK_F_WRITE_ZEROES, + .end = virtio_endof(struct virtio_blk_config, write_zeroes_may_unmap)}, + {} +}; + +static void virtio_blk_set_config_size(VirtIOBlock *s, uint64_t host_features) +{ + s->config_size = MAX(VIRTIO_BLK_CFG_SIZE, + virtio_feature_get_config_size(feature_sizes, host_features)); + + assert(s->config_size <= sizeof(struct virtio_blk_config)); +} static void virtio_blk_init_request(VirtIOBlock *s, VirtQueue *vq, VirtIOBlockReq *req) @@ -65,7 +84,7 @@ static void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status) } static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error, - bool is_read) + bool is_read, bool acct_failed) { VirtIOBlock *s = req->dev; BlockErrorAction action = blk_get_error_action(s->blk, is_read, error); @@ -78,7 +97,9 @@ static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error, s->rq = req; } else if (action == BLOCK_ERROR_ACTION_REPORT) { virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR); - block_acct_failed(blk_get_stats(s->blk), &req->acct); + if (acct_failed) { + block_acct_failed(blk_get_stats(s->blk), &req->acct); + } virtio_blk_free_request(req); } @@ -116,7 +137,7 @@ static void virtio_blk_rw_complete(void *opaque, int ret) * the memory until the request is completed (which will * happen on the other side of the migration). */ - if (virtio_blk_handle_rw_error(req, -ret, is_read)) { + if (virtio_blk_handle_rw_error(req, -ret, is_read, true)) { continue; } } @@ -135,7 +156,7 @@ static void virtio_blk_flush_complete(void *opaque, int ret) aio_context_acquire(blk_get_aio_context(s->conf.conf.blk)); if (ret) { - if (virtio_blk_handle_rw_error(req, -ret, 0)) { + if (virtio_blk_handle_rw_error(req, -ret, 0, true)) { goto out; } } @@ -148,6 +169,30 @@ out: aio_context_release(blk_get_aio_context(s->conf.conf.blk)); } +static void virtio_blk_discard_write_zeroes_complete(void *opaque, int ret) +{ + VirtIOBlockReq *req = opaque; + VirtIOBlock *s = req->dev; + bool is_write_zeroes = (virtio_ldl_p(VIRTIO_DEVICE(s), &req->out.type) & + ~VIRTIO_BLK_T_BARRIER) == VIRTIO_BLK_T_WRITE_ZEROES; + + aio_context_acquire(blk_get_aio_context(s->conf.conf.blk)); + if (ret) { + if (virtio_blk_handle_rw_error(req, -ret, false, is_write_zeroes)) { + goto out; + } + } + + virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); + if (is_write_zeroes) { + block_acct_done(blk_get_stats(s->blk), &req->acct); + } + virtio_blk_free_request(req); + +out: + aio_context_release(blk_get_aio_context(s->conf.conf.blk)); +} + #ifdef __linux__ typedef struct { @@ -243,7 +288,7 @@ static int virtio_blk_handle_scsi_req(VirtIOBlockReq *req) */ scsi = (void *)elem->in_sg[elem->in_num - 2].iov_base; - if (!blk->conf.scsi) { + if (!virtio_has_feature(blk->host_features, VIRTIO_BLK_F_SCSI)) { status = VIRTIO_BLK_S_UNSUPP; goto fail; } @@ -481,6 +526,84 @@ static bool virtio_blk_sect_range_ok(VirtIOBlock *dev, return true; } +static uint8_t virtio_blk_handle_discard_write_zeroes(VirtIOBlockReq *req, + struct virtio_blk_discard_write_zeroes *dwz_hdr, bool is_write_zeroes) +{ + VirtIOBlock *s = req->dev; + VirtIODevice *vdev = VIRTIO_DEVICE(s); + uint64_t sector; + uint32_t num_sectors, flags, max_sectors; + uint8_t err_status; + int bytes; + + sector = virtio_ldq_p(vdev, &dwz_hdr->sector); + num_sectors = virtio_ldl_p(vdev, &dwz_hdr->num_sectors); + flags = virtio_ldl_p(vdev, &dwz_hdr->flags); + max_sectors = is_write_zeroes ? s->conf.max_write_zeroes_sectors : + s->conf.max_discard_sectors; + + /* + * max_sectors is at most BDRV_REQUEST_MAX_SECTORS, this check + * make us sure that "num_sectors << BDRV_SECTOR_BITS" can fit in + * the integer variable. + */ + if (unlikely(num_sectors > max_sectors)) { + err_status = VIRTIO_BLK_S_IOERR; + goto err; + } + + bytes = num_sectors << BDRV_SECTOR_BITS; + + if (unlikely(!virtio_blk_sect_range_ok(s, sector, bytes))) { + err_status = VIRTIO_BLK_S_IOERR; + goto err; + } + + /* + * The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for discard + * and write zeroes commands if any unknown flag is set. + */ + if (unlikely(flags & ~VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) { + err_status = VIRTIO_BLK_S_UNSUPP; + goto err; + } + + if (is_write_zeroes) { /* VIRTIO_BLK_T_WRITE_ZEROES */ + int blk_aio_flags = 0; + + if (flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP) { + blk_aio_flags |= BDRV_REQ_MAY_UNMAP; + } + + block_acct_start(blk_get_stats(s->blk), &req->acct, bytes, + BLOCK_ACCT_WRITE); + + blk_aio_pwrite_zeroes(s->blk, sector << BDRV_SECTOR_BITS, + bytes, blk_aio_flags, + virtio_blk_discard_write_zeroes_complete, req); + } else { /* VIRTIO_BLK_T_DISCARD */ + /* + * The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for + * discard commands if the unmap flag is set. + */ + if (unlikely(flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) { + err_status = VIRTIO_BLK_S_UNSUPP; + goto err; + } + + blk_aio_pdiscard(s->blk, sector << BDRV_SECTOR_BITS, bytes, + virtio_blk_discard_write_zeroes_complete, req); + } + + return VIRTIO_BLK_S_OK; + +err: + if (is_write_zeroes) { + block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_WRITE); + } + return err_status; +} + static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) { uint32_t type; @@ -582,6 +705,47 @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) virtio_blk_free_request(req); break; } + /* + * VIRTIO_BLK_T_DISCARD and VIRTIO_BLK_T_WRITE_ZEROES are defined with + * VIRTIO_BLK_T_OUT flag set. We masked this flag in the switch statement, + * so we must mask it for these requests, then we will check if it is set. + */ + case VIRTIO_BLK_T_DISCARD & ~VIRTIO_BLK_T_OUT: + case VIRTIO_BLK_T_WRITE_ZEROES & ~VIRTIO_BLK_T_OUT: + { + struct virtio_blk_discard_write_zeroes dwz_hdr; + size_t out_len = iov_size(out_iov, out_num); + bool is_write_zeroes = (type & ~VIRTIO_BLK_T_BARRIER) == + VIRTIO_BLK_T_WRITE_ZEROES; + uint8_t err_status; + + /* + * Unsupported if VIRTIO_BLK_T_OUT is not set or the request contains + * more than one segment. + */ + if (unlikely(!(type & VIRTIO_BLK_T_OUT) || + out_len > sizeof(dwz_hdr))) { + virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP); + virtio_blk_free_request(req); + return 0; + } + + if (unlikely(iov_to_buf(out_iov, out_num, 0, &dwz_hdr, + sizeof(dwz_hdr)) != sizeof(dwz_hdr))) { + virtio_error(vdev, "virtio-blk discard/write_zeroes header" + " too short"); + return -1; + } + + err_status = virtio_blk_handle_discard_write_zeroes(req, &dwz_hdr, + is_write_zeroes); + if (err_status != VIRTIO_BLK_S_OK) { + virtio_blk_req_complete(req, err_status); + virtio_blk_free_request(req); + } + + break; + } default: virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP); virtio_blk_free_request(req); @@ -675,6 +839,7 @@ static void virtio_blk_dma_restart_bh(void *opaque) if (mrb.num_reqs) { virtio_blk_submit_multireq(s->blk, &mrb); } + blk_dec_in_flight(s->conf.conf.blk); aio_context_release(blk_get_aio_context(s->conf.conf.blk)); } @@ -688,8 +853,11 @@ static void virtio_blk_dma_restart_cb(void *opaque, int running, } if (!s->bh) { + /* FIXME The data plane is not started yet, so these requests are + * processed in the main thread. */ s->bh = aio_bh_new(blk_get_aio_context(s->conf.conf.blk), virtio_blk_dma_restart_bh, s); + blk_inc_in_flight(s->conf.conf.blk); qemu_bh_schedule(s->bh); } } @@ -761,8 +929,25 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config) blkcfg.alignment_offset = 0; blkcfg.wce = blk_enable_write_cache(s->blk); virtio_stw_p(vdev, &blkcfg.num_queues, s->conf.num_queues); - memcpy(config, &blkcfg, VIRTIO_BLK_CFG_SIZE); - QEMU_BUILD_BUG_ON(VIRTIO_BLK_CFG_SIZE > sizeof(blkcfg)); + if (virtio_has_feature(s->host_features, VIRTIO_BLK_F_DISCARD)) { + virtio_stl_p(vdev, &blkcfg.max_discard_sectors, + s->conf.max_discard_sectors); + virtio_stl_p(vdev, &blkcfg.discard_sector_alignment, + blk_size >> BDRV_SECTOR_BITS); + /* + * We support only one segment per request since multiple segments + * are not widely used and there are no userspace APIs that allow + * applications to submit multiple segments in a single call. + */ + virtio_stl_p(vdev, &blkcfg.max_discard_seg, 1); + } + if (virtio_has_feature(s->host_features, VIRTIO_BLK_F_WRITE_ZEROES)) { + virtio_stl_p(vdev, &blkcfg.max_write_zeroes_sectors, + s->conf.max_write_zeroes_sectors); + blkcfg.write_zeroes_may_unmap = 1; + virtio_stl_p(vdev, &blkcfg.max_write_zeroes_seg, 1); + } + memcpy(config, &blkcfg, s->config_size); } static void virtio_blk_set_config(VirtIODevice *vdev, const uint8_t *config) @@ -770,8 +955,7 @@ static void virtio_blk_set_config(VirtIODevice *vdev, const uint8_t *config) VirtIOBlock *s = VIRTIO_BLK(vdev); struct virtio_blk_config blkcfg; - memcpy(&blkcfg, config, VIRTIO_BLK_CFG_SIZE); - QEMU_BUILD_BUG_ON(VIRTIO_BLK_CFG_SIZE > sizeof(blkcfg)); + memcpy(&blkcfg, config, s->config_size); aio_context_acquire(blk_get_aio_context(s->blk)); blk_set_enable_write_cache(s->blk, blkcfg.wce != 0); @@ -783,12 +967,15 @@ static uint64_t virtio_blk_get_features(VirtIODevice *vdev, uint64_t features, { VirtIOBlock *s = VIRTIO_BLK(vdev); + /* Firstly sync all virtio-blk possible supported features */ + features |= s->host_features; + virtio_add_feature(&features, VIRTIO_BLK_F_SEG_MAX); virtio_add_feature(&features, VIRTIO_BLK_F_GEOMETRY); virtio_add_feature(&features, VIRTIO_BLK_F_TOPOLOGY); virtio_add_feature(&features, VIRTIO_BLK_F_BLK_SIZE); if (virtio_has_feature(features, VIRTIO_F_VERSION_1)) { - if (s->conf.scsi) { + if (virtio_has_feature(s->host_features, VIRTIO_BLK_F_SCSI)) { error_setg(errp, "Please set scsi=off for virtio-blk devices in order to use virtio 1.0"); return 0; } @@ -797,9 +984,6 @@ static uint64_t virtio_blk_get_features(VirtIODevice *vdev, uint64_t features, virtio_add_feature(&features, VIRTIO_BLK_F_SCSI); } - if (s->conf.config_wce) { - virtio_add_feature(&features, VIRTIO_BLK_F_CONFIG_WCE); - } if (blk_enable_write_cache(s->blk)) { virtio_add_feature(&features, VIRTIO_BLK_F_WCE); } @@ -954,7 +1138,28 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp) return; } - virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK, VIRTIO_BLK_CFG_SIZE); + if (virtio_has_feature(s->host_features, VIRTIO_BLK_F_DISCARD) && + (!conf->max_discard_sectors || + conf->max_discard_sectors > BDRV_REQUEST_MAX_SECTORS)) { + error_setg(errp, "invalid max-discard-sectors property (%" PRIu32 ")" + ", must be between 1 and %d", + conf->max_discard_sectors, (int)BDRV_REQUEST_MAX_SECTORS); + return; + } + + if (virtio_has_feature(s->host_features, VIRTIO_BLK_F_WRITE_ZEROES) && + (!conf->max_write_zeroes_sectors || + conf->max_write_zeroes_sectors > BDRV_REQUEST_MAX_SECTORS)) { + error_setg(errp, "invalid max-write-zeroes-sectors property (%" PRIu32 + "), must be between 1 and %d", + conf->max_write_zeroes_sectors, + (int)BDRV_REQUEST_MAX_SECTORS); + return; + } + + virtio_blk_set_config_size(s, s->host_features); + + virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK, s->config_size); s->blk = conf->conf.blk; s->rq = NULL; @@ -1013,9 +1218,11 @@ static Property virtio_blk_properties[] = { DEFINE_BLOCK_ERROR_PROPERTIES(VirtIOBlock, conf.conf), DEFINE_BLOCK_CHS_PROPERTIES(VirtIOBlock, conf.conf), DEFINE_PROP_STRING("serial", VirtIOBlock, conf.serial), - DEFINE_PROP_BIT("config-wce", VirtIOBlock, conf.config_wce, 0, true), + DEFINE_PROP_BIT64("config-wce", VirtIOBlock, host_features, + VIRTIO_BLK_F_CONFIG_WCE, true), #ifdef __linux__ - DEFINE_PROP_BIT("scsi", VirtIOBlock, conf.scsi, 0, false), + DEFINE_PROP_BIT64("scsi", VirtIOBlock, host_features, + VIRTIO_BLK_F_SCSI, false), #endif DEFINE_PROP_BIT("request-merging", VirtIOBlock, conf.request_merging, 0, true), @@ -1023,6 +1230,14 @@ static Property virtio_blk_properties[] = { DEFINE_PROP_UINT16("queue-size", VirtIOBlock, conf.queue_size, 128), DEFINE_PROP_LINK("iothread", VirtIOBlock, conf.iothread, TYPE_IOTHREAD, IOThread *), + DEFINE_PROP_BIT64("discard", VirtIOBlock, host_features, + VIRTIO_BLK_F_DISCARD, true), + DEFINE_PROP_BIT64("write-zeroes", VirtIOBlock, host_features, + VIRTIO_BLK_F_WRITE_ZEROES, true), + DEFINE_PROP_UINT32("max-discard-sectors", VirtIOBlock, + conf.max_discard_sectors, BDRV_REQUEST_MAX_SECTORS), + DEFINE_PROP_UINT32("max-write-zeroes-sectors", VirtIOBlock, + conf.max_write_zeroes_sectors, BDRV_REQUEST_MAX_SECTORS), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/block/xen-block.c b/hw/block/xen-block.c index 5012af9cb6..70fc2455e8 100644 --- a/hw/block/xen-block.c +++ b/hw/block/xen-block.c @@ -351,21 +351,28 @@ static void xen_block_get_vdev(Object *obj, Visitor *v, const char *name, g_free(str); } -static unsigned int vbd_name_to_disk(const char *name, const char **endp) +static int vbd_name_to_disk(const char *name, const char **endp, + unsigned long *disk) { - unsigned int disk = 0; + unsigned int n = 0; while (*name != '\0') { if (!g_ascii_isalpha(*name) || !g_ascii_islower(*name)) { break; } - disk *= 26; - disk += *name++ - 'a' + 1; + n *= 26; + n += *name++ - 'a' + 1; } *endp = name; - return disk - 1; + if (!n) { + return -1; + } + + *disk = n - 1; + + return 0; } static void xen_block_set_vdev(Object *obj, Visitor *v, const char *name, @@ -413,13 +420,14 @@ static void xen_block_set_vdev(Object *obj, Visitor *v, const char *name, } if (*end == 'p') { - p = (char *) ++end; - if (*end == '\0') { + if (*(++end) == '\0') { goto invalid; } } } else { - vdev->disk = vbd_name_to_disk(p, &end); + if (vbd_name_to_disk(p, &end, &vdev->disk)) { + goto invalid; + } } if (*end != '\0') { @@ -735,12 +743,12 @@ static XenBlockDrive *xen_block_drive_create(const char *id, } g_strfreev(v); - } - - if (!filename) { - error_setg(errp, "no filename"); + } else { + error_setg(errp, "no params"); goto done; } + + assert(filename); assert(driver); drive = g_new0(XenBlockDrive, 1); @@ -750,6 +758,7 @@ static XenBlockDrive *xen_block_drive_create(const char *id, qdict_put_str(file_layer, "driver", "file"); qdict_put_str(file_layer, "filename", filename); + g_free(filename); if (mode && *mode != 'w') { qdict_put_bool(file_layer, "read-only", true); @@ -785,16 +794,17 @@ static XenBlockDrive *xen_block_drive_create(const char *id, driver_layer = qdict_new(); qdict_put_str(driver_layer, "driver", driver); + g_free(driver); + qdict_put_obj(driver_layer, "file", QOBJECT(file_layer)); g_assert(!drive->node_name); drive->node_name = xen_block_blockdev_add(drive->id, driver_layer, &local_err); -done: - g_free(driver); - g_free(filename); + qobject_unref(driver_layer); +done: if (local_err) { error_propagate(errp, local_err); xen_block_drive_destroy(drive, NULL); diff --git a/hw/char/pl011.c b/hw/char/pl011.c index 2aa277fc4f..e5dd448f85 100644 --- a/hw/char/pl011.c +++ b/hw/char/pl011.c @@ -7,40 +7,24 @@ * This code is licensed under the GPL. */ +/* + * QEMU interface: + * + sysbus MMIO region 0: device registers + * + sysbus IRQ 0: UARTINTR (combined interrupt line) + * + sysbus IRQ 1: UARTRXINTR (receive FIFO interrupt line) + * + sysbus IRQ 2: UARTTXINTR (transmit FIFO interrupt line) + * + sysbus IRQ 3: UARTRTINTR (receive timeout interrupt line) + * + sysbus IRQ 4: UARTMSINTR (momem status interrupt line) + * + sysbus IRQ 5: UARTEINTR (error interrupt line) + */ + #include "qemu/osdep.h" +#include "hw/char/pl011.h" #include "hw/sysbus.h" #include "chardev/char-fe.h" #include "qemu/log.h" #include "trace.h" -#define TYPE_PL011 "pl011" -#define PL011(obj) OBJECT_CHECK(PL011State, (obj), TYPE_PL011) - -typedef struct PL011State { - SysBusDevice parent_obj; - - MemoryRegion iomem; - uint32_t readbuff; - uint32_t flags; - uint32_t lcr; - uint32_t rsr; - uint32_t cr; - uint32_t dmacr; - uint32_t int_enabled; - uint32_t int_level; - uint32_t read_fifo[16]; - uint32_t ilpr; - uint32_t ibrd; - uint32_t fbrd; - uint32_t ifl; - int read_pos; - int read_count; - int read_trigger; - CharBackend chr; - qemu_irq irq; - const unsigned char *id; -} PL011State; - #define PL011_INT_TX 0x20 #define PL011_INT_RX 0x10 @@ -49,18 +33,46 @@ typedef struct PL011State { #define PL011_FLAG_TXFF 0x20 #define PL011_FLAG_RXFE 0x10 +/* Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC */ +#define INT_OE (1 << 10) +#define INT_BE (1 << 9) +#define INT_PE (1 << 8) +#define INT_FE (1 << 7) +#define INT_RT (1 << 6) +#define INT_TX (1 << 5) +#define INT_RX (1 << 4) +#define INT_DSR (1 << 3) +#define INT_DCD (1 << 2) +#define INT_CTS (1 << 1) +#define INT_RI (1 << 0) +#define INT_E (INT_OE | INT_BE | INT_PE | INT_FE) +#define INT_MS (INT_RI | INT_DSR | INT_DCD | INT_CTS) + static const unsigned char pl011_id_arm[8] = { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; static const unsigned char pl011_id_luminary[8] = { 0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 }; +/* Which bits in the interrupt status matter for each outbound IRQ line ? */ +static const uint32_t irqmask[] = { + INT_E | INT_MS | INT_RT | INT_TX | INT_RX, /* combined IRQ */ + INT_RX, + INT_TX, + INT_RT, + INT_MS, + INT_E, +}; + static void pl011_update(PL011State *s) { uint32_t flags; + int i; flags = s->int_level & s->int_enabled; trace_pl011_irq_state(flags != 0); - qemu_set_irq(s->irq, flags != 0); + for (i = 0; i < ARRAY_SIZE(s->irq); i++) { + qemu_set_irq(s->irq[i], (flags & irqmask[i]) != 0); + } } static uint64_t pl011_read(void *opaque, hwaddr offset, @@ -131,7 +143,7 @@ static uint64_t pl011_read(void *opaque, hwaddr offset, break; default: qemu_log_mask(LOG_GUEST_ERROR, - "pl011_read: Bad offset %x\n", (int)offset); + "pl011_read: Bad offset 0x%x\n", (int)offset); r = 0; break; } @@ -220,7 +232,7 @@ static void pl011_write(void *opaque, hwaddr offset, break; default: qemu_log_mask(LOG_GUEST_ERROR, - "pl011_write: Bad offset %x\n", (int)offset); + "pl011_write: Bad offset 0x%x\n", (int)offset); } } @@ -311,10 +323,13 @@ static void pl011_init(Object *obj) { SysBusDevice *sbd = SYS_BUS_DEVICE(obj); PL011State *s = PL011(obj); + int i; memory_region_init_io(&s->iomem, OBJECT(s), &pl011_ops, s, "pl011", 0x1000); sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq); + for (i = 0; i < ARRAY_SIZE(s->irq); i++) { + sysbus_init_irq(sbd, &s->irq[i]); + } s->read_trigger = 1; s->ifl = 0x12; @@ -357,7 +372,7 @@ static void pl011_luminary_init(Object *obj) } static const TypeInfo pl011_luminary_info = { - .name = "pl011_luminary", + .name = TYPE_PL011_LUMINARY, .parent = TYPE_PL011, .instance_init = pl011_luminary_init, }; diff --git a/hw/core/machine.c b/hw/core/machine.c index 077fbd182a..766ca5899d 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -33,6 +33,8 @@ GlobalProperty hw_compat_3_1[] = { { "usb-kbd", "serial", "42" }, { "usb-mouse", "serial", "42" }, { "usb-kbd", "serial", "42" }, + { "virtio-blk-device", "discard", "false" }, + { "virtio-blk-device", "write-zeroes", "false" }, }; const size_t hw_compat_3_1_len = G_N_ELEMENTS(hw_compat_3_1); diff --git a/hw/display/qxl.c b/hw/display/qxl.c index da8fd5a40a..c8ce5781e0 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -276,7 +276,8 @@ static void qxl_spice_monitors_config_async(PCIQXLDevice *qxl, int replay) QXL_COOKIE_TYPE_POST_LOAD_MONITORS_CONFIG, 0)); } else { -#if SPICE_SERVER_VERSION >= 0x000c06 /* release 0.12.6 */ +/* >= release 0.12.6, < release 0.14.2 */ +#if SPICE_SERVER_VERSION >= 0x000c06 && SPICE_SERVER_VERSION < 0x000e02 if (qxl->max_outputs) { spice_qxl_set_max_monitors(&qxl->ssd.qxl, qxl->max_outputs); } @@ -2188,6 +2189,17 @@ static void qxl_realize_common(PCIQXLDevice *qxl, Error **errp) SPICE_INTERFACE_QXL_MAJOR, SPICE_INTERFACE_QXL_MINOR); return; } + +#if SPICE_SERVER_VERSION >= 0x000e02 /* release 0.14.2 */ + char device_address[256] = ""; + if (qemu_spice_fill_device_address(qxl->vga.con, device_address, 256)) { + spice_qxl_set_device_info(&qxl->ssd.qxl, + device_address, + 0, + qxl->max_outputs); + } +#endif + qemu_add_vm_change_state_handler(qxl_vm_change_state_handler, qxl); qxl->update_irq = qemu_bh_new(qxl_update_irq_bh, qxl); diff --git a/hw/display/sii9022.c b/hw/display/sii9022.c index eaf11a6e7b..9994385c35 100644 --- a/hw/display/sii9022.c +++ b/hw/display/sii9022.c @@ -79,7 +79,7 @@ static int sii9022_event(I2CSlave *i2c, enum i2c_event event) return 0; } -static int sii9022_rx(I2CSlave *i2c) +static uint8_t sii9022_rx(I2CSlave *i2c) { sii9022_state *s = SII9022(i2c); uint8_t res = 0x00; diff --git a/hw/display/ssd0303.c b/hw/display/ssd0303.c index eb90ba26be..8edf34986c 100644 --- a/hw/display/ssd0303.c +++ b/hw/display/ssd0303.c @@ -62,10 +62,10 @@ typedef struct { uint8_t framebuffer[132*8]; } ssd0303_state; -static int ssd0303_recv(I2CSlave *i2c) +static uint8_t ssd0303_recv(I2CSlave *i2c) { BADF("Reads not implemented\n"); - return -1; + return 0xff; } static int ssd0303_send(I2CSlave *i2c, uint8_t data) diff --git a/hw/display/trace-events b/hw/display/trace-events index 387c6b8931..37d3264bb2 100644 --- a/hw/display/trace-events +++ b/hw/display/trace-events @@ -35,6 +35,7 @@ vmware_setmode(uint32_t w, uint32_t h, uint32_t bpp) "%dx%d @ %d bpp" # hw/display/virtio-gpu.c virtio_gpu_features(bool virgl) "virgl %d" virtio_gpu_cmd_get_display_info(void) "" +virtio_gpu_cmd_get_edid(uint32_t scanout) "scanout %d" virtio_gpu_cmd_set_scanout(uint32_t id, uint32_t res, uint32_t w, uint32_t h, uint32_t x, uint32_t y) "id %d, res 0x%x, w %d, h %d, x %d, y %d" virtio_gpu_cmd_res_create_2d(uint32_t res, uint32_t fmt, uint32_t w, uint32_t h) "res 0x%x, fmt 0x%x, w %d, h %d" virtio_gpu_cmd_res_create_3d(uint32_t res, uint32_t fmt, uint32_t w, uint32_t h, uint32_t d) "res 0x%x, fmt 0x%x, w %d, h %d, d %d" diff --git a/hw/display/virtio-gpu-3d.c b/hw/display/virtio-gpu-3d.c index bc6e99c943..2d302526ab 100644 --- a/hw/display/virtio-gpu-3d.c +++ b/hw/display/virtio-gpu-3d.c @@ -404,11 +404,6 @@ void virtio_gpu_virgl_process_cmd(VirtIOGPU *g, { VIRTIO_GPU_FILL_CMD(cmd->cmd_hdr); - cmd->waiting = g->renderer_blocked; - if (cmd->waiting) { - return; - } - virgl_renderer_force_ctx_0(); switch (cmd->cmd_hdr.type) { case VIRTIO_GPU_CMD_CTX_CREATE: @@ -468,6 +463,9 @@ void virtio_gpu_virgl_process_cmd(VirtIOGPU *g, case VIRTIO_GPU_CMD_GET_DISPLAY_INFO: virtio_gpu_get_display_info(g, cmd); break; + case VIRTIO_GPU_CMD_GET_EDID: + virtio_gpu_get_edid(g, cmd); + break; default: cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; break; @@ -604,22 +602,6 @@ void virtio_gpu_virgl_reset(VirtIOGPU *g) } } -void virtio_gpu_gl_block(void *opaque, bool block) -{ - VirtIOGPU *g = opaque; - - if (block) { - g->renderer_blocked++; - } else { - g->renderer_blocked--; - } - assert(g->renderer_blocked >= 0); - - if (g->renderer_blocked == 0) { - virtio_gpu_process_cmdq(g); - } -} - int virtio_gpu_virgl_init(VirtIOGPU *g) { int ret; diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index c6fab56f9b..a3627f58a9 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -21,6 +21,7 @@ #include "hw/virtio/virtio.h" #include "hw/virtio/virtio-gpu.h" #include "hw/virtio/virtio-bus.h" +#include "hw/display/edid.h" #include "migration/blocker.h" #include "qemu/log.h" #include "qapi/error.h" @@ -207,6 +208,9 @@ static uint64_t virtio_gpu_get_features(VirtIODevice *vdev, uint64_t features, if (virtio_gpu_virgl_enabled(g->conf)) { features |= (1 << VIRTIO_GPU_F_VIRGL); } + if (virtio_gpu_edid_enabled(g->conf)) { + features |= (1 << VIRTIO_GPU_F_EDID); + } return features; } @@ -301,6 +305,40 @@ void virtio_gpu_get_display_info(VirtIOGPU *g, sizeof(display_info)); } +static void +virtio_gpu_generate_edid(VirtIOGPU *g, int scanout, + struct virtio_gpu_resp_edid *edid) +{ + qemu_edid_info info = { + .prefx = g->req_state[scanout].width, + .prefy = g->req_state[scanout].height, + }; + + edid->size = cpu_to_le32(sizeof(edid->edid)); + qemu_edid_generate(edid->edid, sizeof(edid->edid), &info); +} + +void virtio_gpu_get_edid(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_resp_edid edid; + struct virtio_gpu_cmd_get_edid get_edid; + + VIRTIO_GPU_FILL_CMD(get_edid); + virtio_gpu_bswap_32(&get_edid, sizeof(get_edid)); + + if (get_edid.scanout >= g->conf.max_outputs) { + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return; + } + + trace_virtio_gpu_cmd_get_edid(get_edid.scanout); + memset(&edid, 0, sizeof(edid)); + edid.hdr.type = VIRTIO_GPU_RESP_OK_EDID; + virtio_gpu_generate_edid(g, get_edid.scanout, &edid); + virtio_gpu_ctrl_response(g, cmd, &edid.hdr, sizeof(edid)); +} + static pixman_format_code_t get_pixman_format(uint32_t virtio_gpu_format) { switch (virtio_gpu_format) { @@ -839,6 +877,9 @@ static void virtio_gpu_simple_process_cmd(VirtIOGPU *g, case VIRTIO_GPU_CMD_GET_DISPLAY_INFO: virtio_gpu_get_display_info(g, cmd); break; + case VIRTIO_GPU_CMD_GET_EDID: + virtio_gpu_get_edid(g, cmd); + break; case VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: virtio_gpu_resource_create_2d(g, cmd); break; @@ -889,12 +930,14 @@ void virtio_gpu_process_cmdq(VirtIOGPU *g) while (!QTAILQ_EMPTY(&g->cmdq)) { cmd = QTAILQ_FIRST(&g->cmdq); + if (g->renderer_blocked) { + break; + } + /* process command */ VIRGL(g, virtio_gpu_virgl_process_cmd, virtio_gpu_simple_process_cmd, g, cmd); - if (cmd->waiting) { - break; - } + QTAILQ_REMOVE(&g->cmdq, cmd, next); if (virtio_gpu_stats_enabled(g->conf)) { g->stats.requests++; @@ -936,7 +979,6 @@ static void virtio_gpu_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) cmd->vq = vq; cmd->error = 0; cmd->finished = false; - cmd->waiting = false; QTAILQ_INSERT_TAIL(&g->cmdq, cmd, next); cmd = virtqueue_pop(vq, sizeof(struct virtio_gpu_ctrl_command)); } @@ -1030,14 +1072,28 @@ static int virtio_gpu_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info) return 0; } +static void virtio_gpu_gl_block(void *opaque, bool block) +{ + VirtIOGPU *g = opaque; + + if (block) { + g->renderer_blocked++; + } else { + g->renderer_blocked--; + } + assert(g->renderer_blocked >= 0); + + if (g->renderer_blocked == 0) { + virtio_gpu_process_cmdq(g); + } +} + const GraphicHwOps virtio_gpu_ops = { .invalidate = virtio_gpu_invalidate_display, .gfx_update = virtio_gpu_update_display, .text_update = virtio_gpu_text_update, .ui_info = virtio_gpu_ui_info, -#ifdef CONFIG_VIRGL .gl_block = virtio_gpu_gl_block, -#endif }; static const VMStateDescription vmstate_virtio_gpu_scanout = { @@ -1238,10 +1294,9 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp) } } - g->config_size = sizeof(struct virtio_gpu_config); g->virtio_config.num_scanouts = cpu_to_le32(g->conf.max_outputs); virtio_init(VIRTIO_DEVICE(g), "virtio-gpu", VIRTIO_ID_GPU, - g->config_size); + sizeof(struct virtio_gpu_config)); g->req_state[0].width = g->conf.xres; g->req_state[0].height = g->conf.yres; @@ -1268,7 +1323,6 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp) QTAILQ_INIT(&g->fenceq); g->enabled_output_bitmask = 1; - g->qdev = qdev; for (i = 0; i < g->conf.max_outputs; i++) { g->scanout[i].con = @@ -1356,6 +1410,8 @@ static Property virtio_gpu_properties[] = { DEFINE_PROP_BIT("stats", VirtIOGPU, conf.flags, VIRTIO_GPU_FLAG_STATS_ENABLED, false), #endif + DEFINE_PROP_BIT("edid", VirtIOGPU, conf.flags, + VIRTIO_GPU_FLAG_EDID_ENABLED, false), DEFINE_PROP_UINT32("xres", VirtIOGPU, conf.xres, 1024), DEFINE_PROP_UINT32("yres", VirtIOGPU, conf.yres, 768), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/gpio/max7310.c b/hw/gpio/max7310.c index 1a2478b5a9..c6f686c3eb 100644 --- a/hw/gpio/max7310.c +++ b/hw/gpio/max7310.c @@ -39,7 +39,7 @@ static void max7310_reset(DeviceState *dev) s->command = 0x00; } -static int max7310_rx(I2CSlave *i2c) +static uint8_t max7310_rx(I2CSlave *i2c) { MAX7310State *s = MAX7310(i2c); diff --git a/hw/hppa/dino.c b/hw/hppa/dino.c index 360716de57..40f9e1a963 100644 --- a/hw/hppa/dino.c +++ b/hw/hppa/dino.c @@ -178,7 +178,7 @@ static MemTxResult dino_chip_read_with_attrs(void *opaque, hwaddr addr, case DINO_PCI_IO_DATA ... DINO_PCI_IO_DATA + 3: /* Read from PCI IO space. */ io = &address_space_io; - ioaddr = s->parent_obj.config_reg; + ioaddr = s->parent_obj.config_reg + (addr & 3); switch (size) { case 1: val = address_space_ldub(io, ioaddr, attrs, &ret); @@ -250,7 +250,7 @@ static MemTxResult dino_chip_write_with_attrs(void *opaque, hwaddr addr, case DINO_IO_DATA ... DINO_PCI_IO_DATA + 3: /* Write into PCI IO space. */ io = &address_space_io; - ioaddr = s->parent_obj.config_reg; + ioaddr = s->parent_obj.config_reg + (addr & 3); switch (size) { case 1: address_space_stb(io, ioaddr, val, attrs, &ret); @@ -360,6 +360,27 @@ static const MemoryRegionOps dino_config_data_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static uint64_t dino_config_addr_read(void *opaque, hwaddr addr, unsigned len) +{ + PCIHostState *s = opaque; + return s->config_reg; +} + +static void dino_config_addr_write(void *opaque, hwaddr addr, + uint64_t val, unsigned len) +{ + PCIHostState *s = opaque; + s->config_reg = val & ~3U; +} + +static const MemoryRegionOps dino_config_addr_ops = { + .read = dino_config_addr_read, + .write = dino_config_addr_write, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .endianness = DEVICE_BIG_ENDIAN, +}; + static AddressSpace *dino_pcihost_set_iommu(PCIBus *bus, void *opaque, int devfn) { @@ -440,7 +461,7 @@ PCIBus *dino_init(MemoryRegion *addr_space, /* Dino PCI config. */ memory_region_init_io(&s->parent_obj.conf_mem, OBJECT(&s->parent_obj), - &pci_host_conf_be_ops, dev, "pci-conf-idx", 4); + &dino_config_addr_ops, dev, "pci-conf-idx", 4); memory_region_init_io(&s->parent_obj.data_mem, OBJECT(&s->parent_obj), &dino_config_data_ops, dev, "pci-conf-data", 4); memory_region_add_subregion(&s->this_mem, DINO_PCI_CONFIG_ADDR, diff --git a/hw/i2c/Makefile.objs b/hw/i2c/Makefile.objs index cecee486f7..9205cbee16 100644 --- a/hw/i2c/Makefile.objs +++ b/hw/i2c/Makefile.objs @@ -1,4 +1,4 @@ -common-obj-$(CONFIG_I2C) += core.o smbus.o +common-obj-$(CONFIG_I2C) += core.o smbus_slave.o smbus_master.o common-obj-$(CONFIG_SMBUS_EEPROM) += smbus_eeprom.o common-obj-$(CONFIG_DDC) += i2c-ddc.o common-obj-$(CONFIG_VERSATILE_I2C) += versatile_i2c.o diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index a2dfa82760..a085510cfd 100644 --- a/hw/i2c/aspeed_i2c.c +++ b/hw/i2c/aspeed_i2c.c @@ -189,16 +189,11 @@ static uint8_t aspeed_i2c_get_state(AspeedI2CBus *bus) static void aspeed_i2c_handle_rx_cmd(AspeedI2CBus *bus) { - int ret; + uint8_t ret; aspeed_i2c_set_state(bus, I2CD_MRXD); ret = i2c_recv(bus->bus); - if (ret < 0) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: read failed\n", __func__); - ret = 0xff; - } else { - bus->intr_status |= I2CD_INTR_RX_DONE; - } + bus->intr_status |= I2CD_INTR_RX_DONE; bus->buf = (ret & I2CD_BYTE_BUF_RX_MASK) << I2CD_BYTE_BUF_RX_SHIFT; if (bus->cmd & I2CD_M_S_RX_CMD_LAST) { i2c_nack(bus->bus); diff --git a/hw/i2c/core.c b/hw/i2c/core.c index b54725985a..15237ad073 100644 --- a/hw/i2c/core.c +++ b/hw/i2c/core.c @@ -191,23 +191,17 @@ int i2c_send_recv(I2CBus *bus, uint8_t *data, bool send) } return ret ? -1 : 0; } else { - if ((QLIST_EMPTY(&bus->current_devs)) || (bus->broadcast)) { - return -1; - } - - sc = I2C_SLAVE_GET_CLASS(QLIST_FIRST(&bus->current_devs)->elt); - if (sc->recv) { - s = QLIST_FIRST(&bus->current_devs)->elt; - ret = sc->recv(s); - trace_i2c_recv(s->address, ret); - if (ret < 0) { - return ret; - } else { - *data = ret; - return 0; + ret = 0xff; + if (!QLIST_EMPTY(&bus->current_devs) && !bus->broadcast) { + sc = I2C_SLAVE_GET_CLASS(QLIST_FIRST(&bus->current_devs)->elt); + if (sc->recv) { + s = QLIST_FIRST(&bus->current_devs)->elt; + ret = sc->recv(s); + trace_i2c_recv(s->address, ret); } } - return -1; + *data = ret; + return 0; } } @@ -216,12 +210,12 @@ int i2c_send(I2CBus *bus, uint8_t data) return i2c_send_recv(bus, &data, true); } -int i2c_recv(I2CBus *bus) +uint8_t i2c_recv(I2CBus *bus) { - uint8_t data; - int ret = i2c_send_recv(bus, &data, false); + uint8_t data = 0xff; - return ret < 0 ? ret : data; + i2c_send_recv(bus, &data, false); + return data; } void i2c_nack(I2CBus *bus) diff --git a/hw/i2c/exynos4210_i2c.c b/hw/i2c/exynos4210_i2c.c index c96fa7d7be..d154b05739 100644 --- a/hw/i2c/exynos4210_i2c.c +++ b/hw/i2c/exynos4210_i2c.c @@ -106,16 +106,10 @@ static inline void exynos4210_i2c_raise_interrupt(Exynos4210I2CState *s) static void exynos4210_i2c_data_receive(void *opaque) { Exynos4210I2CState *s = (Exynos4210I2CState *)opaque; - int ret; s->i2cstat &= ~I2CSTAT_LAST_BIT; s->scl_free = false; - ret = i2c_recv(s->bus); - if (ret < 0 && (s->i2ccon & I2CCON_ACK_GEN)) { - s->i2cstat |= I2CSTAT_LAST_BIT; /* Data is not acknowledged */ - } else { - s->i2cds = ret; - } + s->i2cds = i2c_recv(s->bus); exynos4210_i2c_raise_interrupt(s); } diff --git a/hw/i2c/i2c-ddc.c b/hw/i2c/i2c-ddc.c index 0a0367ff38..7aa8727771 100644 --- a/hw/i2c/i2c-ddc.c +++ b/hw/i2c/i2c-ddc.c @@ -51,7 +51,7 @@ static int i2c_ddc_event(I2CSlave *i2c, enum i2c_event event) return 0; } -static int i2c_ddc_rx(I2CSlave *i2c) +static uint8_t i2c_ddc_rx(I2CSlave *i2c) { I2CDDCState *s = I2CDDC(i2c); diff --git a/hw/i2c/imx_i2c.c b/hw/i2c/imx_i2c.c index 6c81b98ebd..6da5224e2e 100644 --- a/hw/i2c/imx_i2c.c +++ b/hw/i2c/imx_i2c.c @@ -120,7 +120,7 @@ static uint64_t imx_i2c_read(void *opaque, hwaddr offset, value = s->i2dr_read; if (imx_i2c_is_master(s)) { - int ret = 0xff; + uint8_t ret = 0xff; if (s->address == ADDR_RESET) { /* something is wrong as the address is not set */ @@ -133,15 +133,7 @@ static uint64_t imx_i2c_read(void *opaque, hwaddr offset, } else { /* get the next byte */ ret = i2c_recv(s->bus); - - if (ret >= 0) { - imx_i2c_raise_interrupt(s); - } else { - qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: read failed " - "for device 0x%02x\n", TYPE_IMX_I2C, - __func__, s->address); - ret = 0xff; - } + imx_i2c_raise_interrupt(s); } s->i2dr_read = ret; diff --git a/hw/i2c/pm_smbus.c b/hw/i2c/pm_smbus.c index 03062740cc..e48544f909 100644 --- a/hw/i2c/pm_smbus.c +++ b/hw/i2c/pm_smbus.c @@ -19,8 +19,9 @@ */ #include "qemu/osdep.h" #include "hw/hw.h" +#include "hw/boards.h" #include "hw/i2c/pm_smbus.h" -#include "hw/i2c/smbus.h" +#include "hw/i2c/smbus_master.h" #define SMBHSTSTS 0x00 #define SMBHSTCNT 0x02 @@ -118,19 +119,30 @@ static void smb_transaction(PMSMBus *s) } break; case PROT_I2C_BLOCK_READ: - if (read) { - int xfersize = s->smb_data0; - if (xfersize > sizeof(s->smb_data)) { - xfersize = sizeof(s->smb_data); - } - ret = smbus_read_block(bus, addr, s->smb_data1, s->smb_data, - xfersize, false, true); - goto data8; - } else { - /* The manual says the behavior is undefined, just set DEV_ERR. */ + /* According to the Linux i2c-i801 driver: + * NB: page 240 of ICH5 datasheet shows that the R/#W + * bit should be cleared here, even when reading. + * However if SPD Write Disable is set (Lynx Point and later), + * the read will fail if we don't set the R/#W bit. + * So at least Linux may or may not set the read bit here. + * So just ignore the read bit for this command. + */ + if (i2c_start_transfer(bus, addr, 0)) { goto error; } - break; + ret = i2c_send(bus, s->smb_data1); + if (ret) { + goto error; + } + if (i2c_start_transfer(bus, addr, 1)) { + goto error; + } + s->in_i2c_block_read = true; + s->smb_blkdata = i2c_recv(s->smbus); + s->op_done = false; + s->smb_stat |= STS_HOST_BUSY | STS_BYTE_DONE; + goto out; + case PROT_BLOCK_DATA: if (read) { ret = smbus_read_block(bus, addr, cmd, s->smb_data, @@ -208,6 +220,7 @@ static void smb_transaction_start(PMSMBus *s) { if (s->smb_ctl & CTL_INTREN) { smb_transaction(s); + s->start_transaction_on_status_read = false; } else { /* Do not execute immediately the command; it will be * executed when guest will read SMB_STAT register. This @@ -217,6 +230,7 @@ static void smb_transaction_start(PMSMBus *s) * checking for status. If STS_HOST_BUSY doesn't get * set, it gets stuck. */ s->smb_stat |= STS_HOST_BUSY; + s->start_transaction_on_status_read = true; } } @@ -226,19 +240,38 @@ smb_irq_value(PMSMBus *s) return ((s->smb_stat & ~STS_HOST_BUSY) != 0) && (s->smb_ctl & CTL_INTREN); } +static bool +smb_byte_by_byte(PMSMBus *s) +{ + if (s->op_done) { + return false; + } + if (s->in_i2c_block_read) { + return true; + } + return !(s->smb_auxctl & AUX_BLK); +} + static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val, unsigned width) { PMSMBus *s = opaque; + uint8_t clear_byte_done; SMBUS_DPRINTF("SMB writeb port=0x%04" HWADDR_PRIx " val=0x%02" PRIx64 "\n", addr, val); switch(addr) { case SMBHSTSTS: + clear_byte_done = s->smb_stat & val & STS_BYTE_DONE; s->smb_stat &= ~(val & ~STS_HOST_BUSY); - if (!s->op_done && !(s->smb_auxctl & AUX_BLK)) { + if (clear_byte_done && smb_byte_by_byte(s)) { uint8_t read = s->smb_addr & 0x01; + if (s->in_i2c_block_read) { + /* See comment below PROT_I2C_BLOCK_READ above. */ + read = 1; + } + s->smb_index++; if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) { s->smb_index = 0; @@ -268,12 +301,23 @@ static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val, s->smb_stat |= STS_BYTE_DONE; } else if (s->smb_ctl & CTL_LAST_BYTE) { s->op_done = true; - s->smb_blkdata = s->smb_data[s->smb_index]; + if (s->in_i2c_block_read) { + s->in_i2c_block_read = false; + s->smb_blkdata = i2c_recv(s->smbus); + i2c_nack(s->smbus); + i2c_end_transfer(s->smbus); + } else { + s->smb_blkdata = s->smb_data[s->smb_index]; + } s->smb_index = 0; s->smb_stat |= STS_INTR; s->smb_stat &= ~STS_HOST_BUSY; } else { - s->smb_blkdata = s->smb_data[s->smb_index]; + if (s->in_i2c_block_read) { + s->smb_blkdata = i2c_recv(s->smbus); + } else { + s->smb_blkdata = s->smb_data[s->smb_index]; + } s->smb_stat |= STS_BYTE_DONE; } } @@ -284,6 +328,10 @@ static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val, if (!s->op_done) { s->smb_index = 0; s->op_done = true; + if (s->in_i2c_block_read) { + s->in_i2c_block_read = false; + i2c_end_transfer(s->smbus); + } } smb_transaction_start(s); } @@ -337,8 +385,9 @@ static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width) switch(addr) { case SMBHSTSTS: val = s->smb_stat; - if (s->smb_stat & STS_HOST_BUSY) { + if (s->start_transaction_on_status_read) { /* execute command now */ + s->start_transaction_on_status_read = false; s->smb_stat &= ~STS_HOST_BUSY; smb_transaction(s); } @@ -359,10 +408,10 @@ static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width) val = s->smb_data1; break; case SMBBLKDAT: - if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) { - s->smb_index = 0; - } - if (s->smb_auxctl & AUX_BLK) { + if (s->smb_auxctl & AUX_BLK && !s->in_i2c_block_read) { + if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) { + s->smb_index = 0; + } val = s->smb_data[s->smb_index++]; if (!s->op_done && s->smb_index == s->smb_data0) { s->op_done = true; @@ -405,6 +454,36 @@ static const MemoryRegionOps pm_smbus_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; +bool pm_smbus_vmstate_needed(void) +{ + MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); + + return !mc->smbus_no_migration_support; +} + +const VMStateDescription pmsmb_vmstate = { + .name = "pmsmb", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(smb_stat, PMSMBus), + VMSTATE_UINT8(smb_ctl, PMSMBus), + VMSTATE_UINT8(smb_cmd, PMSMBus), + VMSTATE_UINT8(smb_addr, PMSMBus), + VMSTATE_UINT8(smb_data0, PMSMBus), + VMSTATE_UINT8(smb_data1, PMSMBus), + VMSTATE_UINT32(smb_index, PMSMBus), + VMSTATE_UINT8_ARRAY(smb_data, PMSMBus, PM_SMBUS_MAX_MSG_SIZE), + VMSTATE_UINT8(smb_auxctl, PMSMBus), + VMSTATE_UINT8(smb_blkdata, PMSMBus), + VMSTATE_BOOL(i2c_enable, PMSMBus), + VMSTATE_BOOL(op_done, PMSMBus), + VMSTATE_BOOL(in_i2c_block_read, PMSMBus), + VMSTATE_BOOL(start_transaction_on_status_read, PMSMBus), + VMSTATE_END_OF_LIST() + } +}; + void pm_smbus_init(DeviceState *parent, PMSMBus *smb, bool force_aux_blk) { smb->op_done = true; diff --git a/hw/i2c/smbus.c b/hw/i2c/smbus.c deleted file mode 100644 index 30028bfcc2..0000000000 --- a/hw/i2c/smbus.c +++ /dev/null @@ -1,379 +0,0 @@ -/* - * QEMU SMBus device emulation. - * - * Copyright (c) 2007 CodeSourcery. - * Written by Paul Brook - * - * This code is licensed under the LGPL. - */ - -/* TODO: Implement PEC. */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/i2c/i2c.h" -#include "hw/i2c/smbus.h" - -//#define DEBUG_SMBUS 1 - -#ifdef DEBUG_SMBUS -#define DPRINTF(fmt, ...) \ -do { printf("smbus(%02x): " fmt , dev->i2c.address, ## __VA_ARGS__); } while (0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__); exit(1);} while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__);} while (0) -#endif - -enum { - SMBUS_IDLE, - SMBUS_WRITE_DATA, - SMBUS_RECV_BYTE, - SMBUS_READ_DATA, - SMBUS_DONE, - SMBUS_CONFUSED = -1 -}; - -static void smbus_do_quick_cmd(SMBusDevice *dev, int recv) -{ - SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev); - - DPRINTF("Quick Command %d\n", recv); - if (sc->quick_cmd) { - sc->quick_cmd(dev, recv); - } -} - -static void smbus_do_write(SMBusDevice *dev) -{ - SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev); - - if (dev->data_len == 0) { - smbus_do_quick_cmd(dev, 0); - } else if (dev->data_len == 1) { - DPRINTF("Send Byte\n"); - if (sc->send_byte) { - sc->send_byte(dev, dev->data_buf[0]); - } - } else { - dev->command = dev->data_buf[0]; - DPRINTF("Command %d len %d\n", dev->command, dev->data_len - 1); - if (sc->write_data) { - sc->write_data(dev, dev->command, dev->data_buf + 1, - dev->data_len - 1); - } - } -} - -static int smbus_i2c_event(I2CSlave *s, enum i2c_event event) -{ - SMBusDevice *dev = SMBUS_DEVICE(s); - - switch (event) { - case I2C_START_SEND: - switch (dev->mode) { - case SMBUS_IDLE: - DPRINTF("Incoming data\n"); - dev->mode = SMBUS_WRITE_DATA; - break; - default: - BADF("Unexpected send start condition in state %d\n", dev->mode); - dev->mode = SMBUS_CONFUSED; - break; - } - break; - - case I2C_START_RECV: - switch (dev->mode) { - case SMBUS_IDLE: - DPRINTF("Read mode\n"); - dev->mode = SMBUS_RECV_BYTE; - break; - case SMBUS_WRITE_DATA: - if (dev->data_len == 0) { - BADF("Read after write with no data\n"); - dev->mode = SMBUS_CONFUSED; - } else { - if (dev->data_len > 1) { - smbus_do_write(dev); - } else { - dev->command = dev->data_buf[0]; - DPRINTF("%02x: Command %d\n", dev->i2c.address, - dev->command); - } - DPRINTF("Read mode\n"); - dev->data_len = 0; - dev->mode = SMBUS_READ_DATA; - } - break; - default: - BADF("Unexpected recv start condition in state %d\n", dev->mode); - dev->mode = SMBUS_CONFUSED; - break; - } - break; - - case I2C_FINISH: - switch (dev->mode) { - case SMBUS_WRITE_DATA: - smbus_do_write(dev); - break; - case SMBUS_RECV_BYTE: - smbus_do_quick_cmd(dev, 1); - break; - case SMBUS_READ_DATA: - BADF("Unexpected stop during receive\n"); - break; - default: - /* Nothing to do. */ - break; - } - dev->mode = SMBUS_IDLE; - dev->data_len = 0; - break; - - case I2C_NACK: - switch (dev->mode) { - case SMBUS_DONE: - /* Nothing to do. */ - break; - case SMBUS_READ_DATA: - dev->mode = SMBUS_DONE; - break; - default: - BADF("Unexpected NACK in state %d\n", dev->mode); - dev->mode = SMBUS_CONFUSED; - break; - } - } - - return 0; -} - -static int smbus_i2c_recv(I2CSlave *s) -{ - SMBusDevice *dev = SMBUS_DEVICE(s); - SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev); - int ret; - - switch (dev->mode) { - case SMBUS_RECV_BYTE: - if (sc->receive_byte) { - ret = sc->receive_byte(dev); - } else { - ret = 0; - } - DPRINTF("Receive Byte %02x\n", ret); - dev->mode = SMBUS_DONE; - break; - case SMBUS_READ_DATA: - if (sc->read_data) { - ret = sc->read_data(dev, dev->command, dev->data_len); - dev->data_len++; - } else { - ret = 0; - } - DPRINTF("Read data %02x\n", ret); - break; - default: - BADF("Unexpected read in state %d\n", dev->mode); - dev->mode = SMBUS_CONFUSED; - ret = 0; - break; - } - return ret; -} - -static int smbus_i2c_send(I2CSlave *s, uint8_t data) -{ - SMBusDevice *dev = SMBUS_DEVICE(s); - - switch (dev->mode) { - case SMBUS_WRITE_DATA: - DPRINTF("Write data %02x\n", data); - if (dev->data_len >= sizeof(dev->data_buf)) { - BADF("Too many bytes sent\n"); - } else { - dev->data_buf[dev->data_len++] = data; - } - break; - default: - BADF("Unexpected write in state %d\n", dev->mode); - break; - } - return 0; -} - -/* Master device commands. */ -int smbus_quick_command(I2CBus *bus, uint8_t addr, int read) -{ - if (i2c_start_transfer(bus, addr, read)) { - return -1; - } - i2c_end_transfer(bus); - return 0; -} - -int smbus_receive_byte(I2CBus *bus, uint8_t addr) -{ - uint8_t data; - - if (i2c_start_transfer(bus, addr, 1)) { - return -1; - } - data = i2c_recv(bus); - i2c_nack(bus); - i2c_end_transfer(bus); - return data; -} - -int smbus_send_byte(I2CBus *bus, uint8_t addr, uint8_t data) -{ - if (i2c_start_transfer(bus, addr, 0)) { - return -1; - } - i2c_send(bus, data); - i2c_end_transfer(bus); - return 0; -} - -int smbus_read_byte(I2CBus *bus, uint8_t addr, uint8_t command) -{ - uint8_t data; - if (i2c_start_transfer(bus, addr, 0)) { - return -1; - } - i2c_send(bus, command); - if (i2c_start_transfer(bus, addr, 1)) { - i2c_end_transfer(bus); - return -1; - } - data = i2c_recv(bus); - i2c_nack(bus); - i2c_end_transfer(bus); - return data; -} - -int smbus_write_byte(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t data) -{ - if (i2c_start_transfer(bus, addr, 0)) { - return -1; - } - i2c_send(bus, command); - i2c_send(bus, data); - i2c_end_transfer(bus); - return 0; -} - -int smbus_read_word(I2CBus *bus, uint8_t addr, uint8_t command) -{ - uint16_t data; - if (i2c_start_transfer(bus, addr, 0)) { - return -1; - } - i2c_send(bus, command); - if (i2c_start_transfer(bus, addr, 1)) { - i2c_end_transfer(bus); - return -1; - } - data = i2c_recv(bus); - data |= i2c_recv(bus) << 8; - i2c_nack(bus); - i2c_end_transfer(bus); - return data; -} - -int smbus_write_word(I2CBus *bus, uint8_t addr, uint8_t command, uint16_t data) -{ - if (i2c_start_transfer(bus, addr, 0)) { - return -1; - } - i2c_send(bus, command); - i2c_send(bus, data & 0xff); - i2c_send(bus, data >> 8); - i2c_end_transfer(bus); - return 0; -} - -int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data, - int len, bool recv_len, bool send_cmd) -{ - int rlen; - int i; - - if (send_cmd) { - if (i2c_start_transfer(bus, addr, 0)) { - return -1; - } - i2c_send(bus, command); - } - if (i2c_start_transfer(bus, addr, 1)) { - if (send_cmd) { - i2c_end_transfer(bus); - } - return -1; - } - if (recv_len) { - rlen = i2c_recv(bus); - } else { - rlen = len; - } - if (rlen > len) { - rlen = 0; - } - for (i = 0; i < rlen; i++) { - data[i] = i2c_recv(bus); - } - i2c_nack(bus); - i2c_end_transfer(bus); - return rlen; -} - -int smbus_write_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data, - int len, bool send_len) -{ - int i; - - if (len > 32) - len = 32; - - if (i2c_start_transfer(bus, addr, 0)) { - return -1; - } - i2c_send(bus, command); - if (send_len) { - i2c_send(bus, len); - } - for (i = 0; i < len; i++) { - i2c_send(bus, data[i]); - } - i2c_end_transfer(bus); - return 0; -} - -static void smbus_device_class_init(ObjectClass *klass, void *data) -{ - I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); - - sc->event = smbus_i2c_event; - sc->recv = smbus_i2c_recv; - sc->send = smbus_i2c_send; -} - -static const TypeInfo smbus_device_type_info = { - .name = TYPE_SMBUS_DEVICE, - .parent = TYPE_I2C_SLAVE, - .instance_size = sizeof(SMBusDevice), - .abstract = true, - .class_size = sizeof(SMBusDeviceClass), - .class_init = smbus_device_class_init, -}; - -static void smbus_device_register_types(void) -{ - type_register_static(&smbus_device_type_info); -} - -type_init(smbus_device_register_types) diff --git a/hw/i2c/smbus_eeprom.c b/hw/i2c/smbus_eeprom.c index 01b9439014..37167e7244 100644 --- a/hw/i2c/smbus_eeprom.c +++ b/hw/i2c/smbus_eeprom.c @@ -26,39 +26,35 @@ #include "qemu/units.h" #include "qapi/error.h" #include "hw/hw.h" +#include "hw/boards.h" #include "hw/i2c/i2c.h" -#include "hw/i2c/smbus.h" +#include "hw/i2c/smbus_slave.h" +#include "hw/i2c/smbus_eeprom.h" //#define DEBUG +#define TYPE_SMBUS_EEPROM "smbus-eeprom" + +#define SMBUS_EEPROM(obj) \ + OBJECT_CHECK(SMBusEEPROMDevice, (obj), TYPE_SMBUS_EEPROM) + +#define SMBUS_EEPROM_SIZE 256 + typedef struct SMBusEEPROMDevice { SMBusDevice smbusdev; - void *data; + uint8_t data[SMBUS_EEPROM_SIZE]; + void *init_data; uint8_t offset; + bool accessed; } SMBusEEPROMDevice; -static void eeprom_quick_cmd(SMBusDevice *dev, uint8_t read) -{ -#ifdef DEBUG - printf("eeprom_quick_cmd: addr=0x%02x read=%d\n", dev->i2c.address, read); -#endif -} - -static void eeprom_send_byte(SMBusDevice *dev, uint8_t val) -{ - SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev; -#ifdef DEBUG - printf("eeprom_send_byte: addr=0x%02x val=0x%02x\n", - dev->i2c.address, val); -#endif - eeprom->offset = val; -} - static uint8_t eeprom_receive_byte(SMBusDevice *dev) { - SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev; + SMBusEEPROMDevice *eeprom = SMBUS_EEPROM(dev); uint8_t *data = eeprom->data; uint8_t val = data[eeprom->offset++]; + + eeprom->accessed = true; #ifdef DEBUG printf("eeprom_receive_byte: addr=0x%02x val=0x%02x\n", dev->i2c.address, val); @@ -66,48 +62,77 @@ static uint8_t eeprom_receive_byte(SMBusDevice *dev) return val; } -static void eeprom_write_data(SMBusDevice *dev, uint8_t cmd, uint8_t *buf, int len) +static int eeprom_write_data(SMBusDevice *dev, uint8_t *buf, uint8_t len) { - SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev; - int n; + SMBusEEPROMDevice *eeprom = SMBUS_EEPROM(dev); + uint8_t *data = eeprom->data; + + eeprom->accessed = true; #ifdef DEBUG printf("eeprom_write_byte: addr=0x%02x cmd=0x%02x val=0x%02x\n", - dev->i2c.address, cmd, buf[0]); + dev->i2c.address, buf[0], buf[1]); #endif - /* A page write operation is not a valid SMBus command. - It is a block write without a length byte. Fortunately we - get the full block anyway. */ - /* TODO: Should this set the current location? */ - if (cmd + len > 256) - n = 256 - cmd; - else - n = len; - memcpy(eeprom->data + cmd, buf, n); - len -= n; - if (len) - memcpy(eeprom->data, buf + n, len); + /* len is guaranteed to be > 0 */ + eeprom->offset = buf[0]; + buf++; + len--; + + for (; len > 0; len--) { + data[eeprom->offset] = *buf++; + eeprom->offset = (eeprom->offset + 1) % SMBUS_EEPROM_SIZE; + } + + return 0; } -static uint8_t eeprom_read_data(SMBusDevice *dev, uint8_t cmd, int n) +static bool smbus_eeprom_vmstate_needed(void *opaque) { - SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev; - /* If this is the first byte then set the current position. */ - if (n == 0) - eeprom->offset = cmd; - /* As with writes, we implement block reads without the - SMBus length byte. */ - return eeprom_receive_byte(dev); + MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); + SMBusEEPROMDevice *eeprom = opaque; + + return (eeprom->accessed || smbus_vmstate_needed(&eeprom->smbusdev)) && + !mc->smbus_no_migration_support; } -static void smbus_eeprom_realize(DeviceState *dev, Error **errp) +static const VMStateDescription vmstate_smbus_eeprom = { + .name = "smbus-eeprom", + .version_id = 1, + .minimum_version_id = 1, + .needed = smbus_eeprom_vmstate_needed, + .fields = (VMStateField[]) { + VMSTATE_SMBUS_DEVICE(smbusdev, SMBusEEPROMDevice), + VMSTATE_UINT8_ARRAY(data, SMBusEEPROMDevice, SMBUS_EEPROM_SIZE), + VMSTATE_UINT8(offset, SMBusEEPROMDevice), + VMSTATE_BOOL(accessed, SMBusEEPROMDevice), + VMSTATE_END_OF_LIST() + } +}; + +/* + * Reset the EEPROM contents to the initial state on a reset. This + * isn't really how an EEPROM works, of course, but the general + * principle of QEMU is to restore function on reset to what it would + * be if QEMU was stopped and started. + * + * The proper thing to do would be to have a backing blockdev to hold + * the contents and restore that on startup, and not do this on reset. + * But until that time, act as if we had been stopped and restarted. + */ +static void smbus_eeprom_reset(DeviceState *dev) { - SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *)dev; + SMBusEEPROMDevice *eeprom = SMBUS_EEPROM(dev); + memcpy(eeprom->data, eeprom->init_data, SMBUS_EEPROM_SIZE); eeprom->offset = 0; } +static void smbus_eeprom_realize(DeviceState *dev, Error **errp) +{ + smbus_eeprom_reset(dev); +} + static Property smbus_eeprom_properties[] = { - DEFINE_PROP_PTR("data", SMBusEEPROMDevice, data), + DEFINE_PROP_PTR("data", SMBusEEPROMDevice, init_data), DEFINE_PROP_END_OF_LIST(), }; @@ -117,18 +142,17 @@ static void smbus_eeprom_class_initfn(ObjectClass *klass, void *data) SMBusDeviceClass *sc = SMBUS_DEVICE_CLASS(klass); dc->realize = smbus_eeprom_realize; - sc->quick_cmd = eeprom_quick_cmd; - sc->send_byte = eeprom_send_byte; + dc->reset = smbus_eeprom_reset; sc->receive_byte = eeprom_receive_byte; sc->write_data = eeprom_write_data; - sc->read_data = eeprom_read_data; dc->props = smbus_eeprom_properties; + dc->vmsd = &vmstate_smbus_eeprom; /* Reason: pointer property "data" */ dc->user_creatable = false; } static const TypeInfo smbus_eeprom_info = { - .name = "smbus-eeprom", + .name = TYPE_SMBUS_EEPROM, .parent = TYPE_SMBUS_DEVICE, .instance_size = sizeof(SMBusEEPROMDevice), .class_init = smbus_eeprom_class_initfn, @@ -145,7 +169,7 @@ void smbus_eeprom_init_one(I2CBus *smbus, uint8_t address, uint8_t *eeprom_buf) { DeviceState *dev; - dev = qdev_create((BusState *) smbus, "smbus-eeprom"); + dev = qdev_create((BusState *) smbus, TYPE_SMBUS_EEPROM); qdev_prop_set_uint8(dev, "address", address); qdev_prop_set_ptr(dev, "data", eeprom_buf); qdev_init_nofail(dev); @@ -155,13 +179,17 @@ void smbus_eeprom_init(I2CBus *smbus, int nb_eeprom, const uint8_t *eeprom_spd, int eeprom_spd_size) { int i; - uint8_t *eeprom_buf = g_malloc0(8 * 256); /* XXX: make this persistent */ + /* XXX: make this persistent */ + + assert(nb_eeprom <= 8); + uint8_t *eeprom_buf = g_malloc0(8 * SMBUS_EEPROM_SIZE); if (eeprom_spd_size > 0) { memcpy(eeprom_buf, eeprom_spd, eeprom_spd_size); } for (i = 0; i < nb_eeprom; i++) { - smbus_eeprom_init_one(smbus, 0x50 + i, eeprom_buf + (i * 256)); + smbus_eeprom_init_one(smbus, 0x50 + i, + eeprom_buf + (i * SMBUS_EEPROM_SIZE)); } } diff --git a/hw/i2c/smbus_ich9.c b/hw/i2c/smbus_ich9.c index 2a8b49e02f..7b24be8256 100644 --- a/hw/i2c/smbus_ich9.c +++ b/hw/i2c/smbus_ich9.c @@ -29,8 +29,6 @@ #include "hw/i2c/pm_smbus.h" #include "hw/pci/pci.h" #include "sysemu/sysemu.h" -#include "hw/i2c/i2c.h" -#include "hw/i2c/smbus.h" #include "hw/i386/ich9.h" @@ -45,12 +43,20 @@ typedef struct ICH9SMBState { PMSMBus smb; } ICH9SMBState; +static bool ich9_vmstate_need_smbus(void *opaque, int version_id) +{ + return pm_smbus_vmstate_needed(); +} + static const VMStateDescription vmstate_ich9_smbus = { .name = "ich9_smb", .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(dev, struct ICH9SMBState), + VMSTATE_PCI_DEVICE(dev, ICH9SMBState), + VMSTATE_BOOL_TEST(irq_enabled, ICH9SMBState, ich9_vmstate_need_smbus), + VMSTATE_STRUCT_TEST(smb, ICH9SMBState, ich9_vmstate_need_smbus, 1, + pmsmb_vmstate, PMSMBus), VMSTATE_END_OF_LIST() } }; diff --git a/hw/i2c/smbus_master.c b/hw/i2c/smbus_master.c new file mode 100644 index 0000000000..0a6223744c --- /dev/null +++ b/hw/i2c/smbus_master.c @@ -0,0 +1,165 @@ +/* + * QEMU SMBus host (master) emulation. + * + * This code emulates SMBus transactions from the master point of view, + * it runs the individual I2C transaction to do the SMBus protocol + * over I2C. + * + * Copyright (c) 2007 CodeSourcery. + * Written by Paul Brook + * + * This code is licensed under the LGPL. + */ + +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "hw/i2c/i2c.h" +#include "hw/i2c/smbus_master.h" + +/* Master device commands. */ +int smbus_quick_command(I2CBus *bus, uint8_t addr, int read) +{ + if (i2c_start_transfer(bus, addr, read)) { + return -1; + } + i2c_end_transfer(bus); + return 0; +} + +int smbus_receive_byte(I2CBus *bus, uint8_t addr) +{ + uint8_t data; + + if (i2c_start_transfer(bus, addr, 1)) { + return -1; + } + data = i2c_recv(bus); + i2c_nack(bus); + i2c_end_transfer(bus); + return data; +} + +int smbus_send_byte(I2CBus *bus, uint8_t addr, uint8_t data) +{ + if (i2c_start_transfer(bus, addr, 0)) { + return -1; + } + i2c_send(bus, data); + i2c_end_transfer(bus); + return 0; +} + +int smbus_read_byte(I2CBus *bus, uint8_t addr, uint8_t command) +{ + uint8_t data; + if (i2c_start_transfer(bus, addr, 0)) { + return -1; + } + i2c_send(bus, command); + if (i2c_start_transfer(bus, addr, 1)) { + i2c_end_transfer(bus); + return -1; + } + data = i2c_recv(bus); + i2c_nack(bus); + i2c_end_transfer(bus); + return data; +} + +int smbus_write_byte(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t data) +{ + if (i2c_start_transfer(bus, addr, 0)) { + return -1; + } + i2c_send(bus, command); + i2c_send(bus, data); + i2c_end_transfer(bus); + return 0; +} + +int smbus_read_word(I2CBus *bus, uint8_t addr, uint8_t command) +{ + uint16_t data; + if (i2c_start_transfer(bus, addr, 0)) { + return -1; + } + i2c_send(bus, command); + if (i2c_start_transfer(bus, addr, 1)) { + i2c_end_transfer(bus); + return -1; + } + data = i2c_recv(bus); + data |= i2c_recv(bus) << 8; + i2c_nack(bus); + i2c_end_transfer(bus); + return data; +} + +int smbus_write_word(I2CBus *bus, uint8_t addr, uint8_t command, uint16_t data) +{ + if (i2c_start_transfer(bus, addr, 0)) { + return -1; + } + i2c_send(bus, command); + i2c_send(bus, data & 0xff); + i2c_send(bus, data >> 8); + i2c_end_transfer(bus); + return 0; +} + +int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data, + int len, bool recv_len, bool send_cmd) +{ + int rlen; + int i; + + if (send_cmd) { + if (i2c_start_transfer(bus, addr, 0)) { + return -1; + } + i2c_send(bus, command); + } + if (i2c_start_transfer(bus, addr, 1)) { + if (send_cmd) { + i2c_end_transfer(bus); + } + return -1; + } + if (recv_len) { + rlen = i2c_recv(bus); + } else { + rlen = len; + } + if (rlen > len) { + rlen = 0; + } + for (i = 0; i < rlen; i++) { + data[i] = i2c_recv(bus); + } + i2c_nack(bus); + i2c_end_transfer(bus); + return rlen; +} + +int smbus_write_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data, + int len, bool send_len) +{ + int i; + + if (len > 32) { + len = 32; + } + + if (i2c_start_transfer(bus, addr, 0)) { + return -1; + } + i2c_send(bus, command); + if (send_len) { + i2c_send(bus, len); + } + for (i = 0; i < len; i++) { + i2c_send(bus, data[i]); + } + i2c_end_transfer(bus); + return 0; +} diff --git a/hw/i2c/smbus_slave.c b/hw/i2c/smbus_slave.c new file mode 100644 index 0000000000..9a2d314d1a --- /dev/null +++ b/hw/i2c/smbus_slave.c @@ -0,0 +1,236 @@ +/* + * QEMU SMBus device emulation. + * + * This code is a helper for SMBus device emulation. It implements an + * I2C device inteface and runs the SMBus protocol from the device + * point of view and maps those to simple calls to emulate. + * + * Copyright (c) 2007 CodeSourcery. + * Written by Paul Brook + * + * This code is licensed under the LGPL. + */ + +/* TODO: Implement PEC. */ + +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "hw/i2c/i2c.h" +#include "hw/i2c/smbus_slave.h" + +//#define DEBUG_SMBUS 1 + +#ifdef DEBUG_SMBUS +#define DPRINTF(fmt, ...) \ +do { printf("smbus(%02x): " fmt , dev->i2c.address, ## __VA_ARGS__); } while (0) +#define BADF(fmt, ...) \ +do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__); exit(1);} while (0) +#else +#define DPRINTF(fmt, ...) do {} while(0) +#define BADF(fmt, ...) \ +do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__);} while (0) +#endif + +enum { + SMBUS_IDLE, + SMBUS_WRITE_DATA, + SMBUS_READ_DATA, + SMBUS_DONE, + SMBUS_CONFUSED = -1 +}; + +static void smbus_do_quick_cmd(SMBusDevice *dev, int recv) +{ + SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev); + + DPRINTF("Quick Command %d\n", recv); + if (sc->quick_cmd) { + sc->quick_cmd(dev, recv); + } +} + +static void smbus_do_write(SMBusDevice *dev) +{ + SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev); + + DPRINTF("Command %d len %d\n", dev->data_buf[0], dev->data_len); + if (sc->write_data) { + sc->write_data(dev, dev->data_buf, dev->data_len); + } +} + +static int smbus_i2c_event(I2CSlave *s, enum i2c_event event) +{ + SMBusDevice *dev = SMBUS_DEVICE(s); + + switch (event) { + case I2C_START_SEND: + switch (dev->mode) { + case SMBUS_IDLE: + DPRINTF("Incoming data\n"); + dev->mode = SMBUS_WRITE_DATA; + break; + + default: + BADF("Unexpected send start condition in state %d\n", dev->mode); + dev->mode = SMBUS_CONFUSED; + break; + } + break; + + case I2C_START_RECV: + switch (dev->mode) { + case SMBUS_IDLE: + DPRINTF("Read mode\n"); + dev->mode = SMBUS_READ_DATA; + break; + + case SMBUS_WRITE_DATA: + if (dev->data_len == 0) { + BADF("Read after write with no data\n"); + dev->mode = SMBUS_CONFUSED; + } else { + smbus_do_write(dev); + DPRINTF("Read mode\n"); + dev->mode = SMBUS_READ_DATA; + } + break; + + default: + BADF("Unexpected recv start condition in state %d\n", dev->mode); + dev->mode = SMBUS_CONFUSED; + break; + } + break; + + case I2C_FINISH: + if (dev->data_len == 0) { + if (dev->mode == SMBUS_WRITE_DATA || dev->mode == SMBUS_READ_DATA) { + smbus_do_quick_cmd(dev, dev->mode == SMBUS_READ_DATA); + } + } else { + switch (dev->mode) { + case SMBUS_WRITE_DATA: + smbus_do_write(dev); + break; + + case SMBUS_READ_DATA: + BADF("Unexpected stop during receive\n"); + break; + + default: + /* Nothing to do. */ + break; + } + } + dev->mode = SMBUS_IDLE; + dev->data_len = 0; + break; + + case I2C_NACK: + switch (dev->mode) { + case SMBUS_DONE: + /* Nothing to do. */ + break; + + case SMBUS_READ_DATA: + dev->mode = SMBUS_DONE; + break; + + default: + BADF("Unexpected NACK in state %d\n", dev->mode); + dev->mode = SMBUS_CONFUSED; + break; + } + } + + return 0; +} + +static uint8_t smbus_i2c_recv(I2CSlave *s) +{ + SMBusDevice *dev = SMBUS_DEVICE(s); + SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev); + uint8_t ret = 0xff; + + switch (dev->mode) { + case SMBUS_READ_DATA: + if (sc->receive_byte) { + ret = sc->receive_byte(dev); + } + DPRINTF("Read data %02x\n", ret); + break; + + default: + BADF("Unexpected read in state %d\n", dev->mode); + dev->mode = SMBUS_CONFUSED; + break; + } + + return ret; +} + +static int smbus_i2c_send(I2CSlave *s, uint8_t data) +{ + SMBusDevice *dev = SMBUS_DEVICE(s); + + switch (dev->mode) { + case SMBUS_WRITE_DATA: + DPRINTF("Write data %02x\n", data); + if (dev->data_len >= sizeof(dev->data_buf)) { + BADF("Too many bytes sent\n"); + } else { + dev->data_buf[dev->data_len++] = data; + } + break; + + default: + BADF("Unexpected write in state %d\n", dev->mode); + break; + } + + return 0; +} + +static void smbus_device_class_init(ObjectClass *klass, void *data) +{ + I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); + + sc->event = smbus_i2c_event; + sc->recv = smbus_i2c_recv; + sc->send = smbus_i2c_send; +} + +bool smbus_vmstate_needed(SMBusDevice *dev) +{ + return dev->mode != SMBUS_IDLE; +} + +const VMStateDescription vmstate_smbus_device = { + .name = TYPE_SMBUS_DEVICE, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_I2C_SLAVE(i2c, SMBusDevice), + VMSTATE_INT32(mode, SMBusDevice), + VMSTATE_INT32(data_len, SMBusDevice), + VMSTATE_UINT8_ARRAY(data_buf, SMBusDevice, SMBUS_DATA_MAX_LEN), + VMSTATE_END_OF_LIST() + } +}; + +static const TypeInfo smbus_device_type_info = { + .name = TYPE_SMBUS_DEVICE, + .parent = TYPE_I2C_SLAVE, + .instance_size = sizeof(SMBusDevice), + .abstract = true, + .class_size = sizeof(SMBusDeviceClass), + .class_init = smbus_device_class_init, +}; + +static void smbus_device_register_types(void) +{ + type_register_static(&smbus_device_type_info); +} + +type_init(smbus_device_register_types) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index fd0f2c268f..8770ecada9 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -42,7 +42,7 @@ #include "sysemu/sysemu.h" #include "hw/sysbus.h" #include "sysemu/arch_init.h" -#include "hw/i2c/smbus.h" +#include "hw/i2c/smbus_eeprom.h" #include "hw/xen/xen.h" #include "exec/memory.h" #include "exec/address-spaces.h" @@ -444,6 +444,7 @@ static void pc_i440fx_3_1_machine_options(MachineClass *m) pc_i440fx_4_0_machine_options(m); m->is_default = 0; + m->smbus_no_migration_support = true; m->alias = NULL; pcmc->pvh_enabled = false; compat_props_add(m->compat_props, hw_compat_3_1, hw_compat_3_1_len); diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 4a175ea50e..cfb9043e12 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -33,7 +33,7 @@ #include "hw/hw.h" #include "hw/loader.h" #include "sysemu/arch_init.h" -#include "hw/i2c/smbus.h" +#include "hw/i2c/smbus_eeprom.h" #include "hw/boards.h" #include "hw/timer/mc146818rtc.h" #include "hw/xen/xen.h" @@ -380,6 +380,7 @@ static void pc_q35_3_1_machine_options(MachineClass *m) pc_q35_4_0_machine_options(m); m->default_kernel_irqchip_split = false; + m->smbus_no_migration_support = true; m->alias = NULL; pcmc->pvh_enabled = false; compat_props_add(m->compat_props, hw_compat_3_1, hw_compat_3_1_len); diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c index 39e473f9c2..1b0f66cc08 100644 --- a/hw/ide/atapi.c +++ b/hw/ide/atapi.c @@ -174,16 +174,15 @@ static void cd_read_sector_cb(void *opaque, int ret) static int cd_read_sector(IDEState *s) { + void *buf; + if (s->cd_sector_size != 2048 && s->cd_sector_size != 2352) { block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_READ); return -EINVAL; } - s->iov.iov_base = (s->cd_sector_size == 2352) ? - s->io_buffer + 16 : s->io_buffer; - - s->iov.iov_len = ATAPI_SECTOR_SIZE; - qemu_iovec_init_external(&s->qiov, &s->iov, 1); + buf = (s->cd_sector_size == 2352) ? s->io_buffer + 16 : s->io_buffer; + qemu_iovec_init_buf(&s->qiov, buf, ATAPI_SECTOR_SIZE); trace_cd_read_sector(s->lba); @@ -421,9 +420,8 @@ static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret) data_offset = 0; } trace_ide_atapi_cmd_read_dma_cb_aio(s, s->lba, n); - s->bus->dma->iov.iov_base = (void *)(s->io_buffer + data_offset); - s->bus->dma->iov.iov_len = n * ATAPI_SECTOR_SIZE; - qemu_iovec_init_external(&s->bus->dma->qiov, &s->bus->dma->iov, 1); + qemu_iovec_init_buf(&s->bus->dma->qiov, s->io_buffer + data_offset, + n * ATAPI_SECTOR_SIZE); s->bus->dma->aiocb = ide_buffered_readv(s, (int64_t)s->lba << 2, &s->bus->dma->qiov, n * 4, diff --git a/hw/ide/core.c b/hw/ide/core.c index 84832008b8..6afadf894f 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -629,13 +629,15 @@ static void ide_buffered_readv_cb(void *opaque, int ret) IDEBufferedRequest *req = opaque; if (!req->orphaned) { if (!ret) { - qemu_iovec_from_buf(req->original_qiov, 0, req->iov.iov_base, + assert(req->qiov.size == req->original_qiov->size); + qemu_iovec_from_buf(req->original_qiov, 0, + req->qiov.local_iov.iov_base, req->original_qiov->size); } req->original_cb(req->original_opaque, ret); } QLIST_REMOVE(req, list); - qemu_vfree(req->iov.iov_base); + qemu_vfree(qemu_iovec_buf(&req->qiov)); g_free(req); } @@ -660,9 +662,8 @@ BlockAIOCB *ide_buffered_readv(IDEState *s, int64_t sector_num, req->original_qiov = iov; req->original_cb = cb; req->original_opaque = opaque; - req->iov.iov_base = qemu_blockalign(blk_bs(s->blk), iov->size); - req->iov.iov_len = iov->size; - qemu_iovec_init_external(&req->qiov, &req->iov, 1); + qemu_iovec_init_buf(&req->qiov, blk_blockalign(s->blk, iov->size), + iov->size); aioreq = blk_aio_preadv(s->blk, sector_num << BDRV_SECTOR_BITS, &req->qiov, 0, ide_buffered_readv_cb, req); @@ -774,9 +775,7 @@ static void ide_sector_read(IDEState *s) return; } - s->iov.iov_base = s->io_buffer; - s->iov.iov_len = n * BDRV_SECTOR_SIZE; - qemu_iovec_init_external(&s->qiov, &s->iov, 1); + qemu_iovec_init_buf(&s->qiov, s->io_buffer, n * BDRV_SECTOR_SIZE); block_acct_start(blk_get_stats(s->blk), &s->acct, n * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ); @@ -1045,9 +1044,7 @@ static void ide_sector_write(IDEState *s) return; } - s->iov.iov_base = s->io_buffer; - s->iov.iov_len = n * BDRV_SECTOR_SIZE; - qemu_iovec_init_external(&s->qiov, &s->iov, 1); + qemu_iovec_init_buf(&s->qiov, s->io_buffer, n * BDRV_SECTOR_SIZE); block_acct_start(blk_get_stats(s->blk), &s->acct, n * BDRV_SECTOR_SIZE, BLOCK_ACCT_WRITE); diff --git a/hw/input/lm832x.c b/hw/input/lm832x.c index cffbf586d4..1fc7b86f19 100644 --- a/hw/input/lm832x.c +++ b/hw/input/lm832x.c @@ -401,7 +401,7 @@ static int lm_i2c_event(I2CSlave *i2c, enum i2c_event event) return 0; } -static int lm_i2c_rx(I2CSlave *i2c) +static uint8_t lm_i2c_rx(I2CSlave *i2c) { LM823KbdState *s = LM8323(i2c); diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c index 290a290e43..e0e5cb5d8e 100644 --- a/hw/intc/spapr_xive.c +++ b/hw/intc/spapr_xive.c @@ -244,13 +244,12 @@ static void spapr_xive_instance_init(Object *obj) { sPAPRXive *xive = SPAPR_XIVE(obj); - object_initialize(&xive->source, sizeof(xive->source), TYPE_XIVE_SOURCE); - object_property_add_child(obj, "source", OBJECT(&xive->source), NULL); + object_initialize_child(obj, "source", &xive->source, sizeof(xive->source), + TYPE_XIVE_SOURCE, &error_abort, NULL); - object_initialize(&xive->end_source, sizeof(xive->end_source), - TYPE_XIVE_END_SOURCE); - object_property_add_child(obj, "end_source", OBJECT(&xive->end_source), - NULL); + object_initialize_child(obj, "end_source", &xive->end_source, + sizeof(xive->end_source), TYPE_XIVE_END_SOURCE, + &error_abort, NULL); } static void spapr_xive_realize(DeviceState *dev, Error **errp) @@ -317,6 +316,9 @@ static void spapr_xive_realize(DeviceState *dev, Error **errp) /* Map all regions */ spapr_xive_map_mmio(xive); + xive->nodename = g_strdup_printf("interrupt-controller@%" PRIx64, + xive->tm_base + XIVE_TM_USER_PAGE * (1 << TM_SHIFT)); + qemu_register_reset(spapr_xive_reset, dev); } @@ -1448,7 +1450,6 @@ void spapr_dt_xive(sPAPRMachineState *spapr, uint32_t nr_servers, void *fdt, cpu_to_be32(7), /* start */ cpu_to_be32(0xf8), /* count */ }; - gchar *nodename; /* Thread Interrupt Management Area : User (ring 3) and OS (ring 2) */ timas[0] = cpu_to_be64(xive->tm_base + @@ -1458,10 +1459,7 @@ void spapr_dt_xive(sPAPRMachineState *spapr, uint32_t nr_servers, void *fdt, XIVE_TM_OS_PAGE * (1ull << TM_SHIFT)); timas[3] = cpu_to_be64(1ull << TM_SHIFT); - nodename = g_strdup_printf("interrupt-controller@%" PRIx64, - xive->tm_base + XIVE_TM_USER_PAGE * (1 << TM_SHIFT)); - _FDT(node = fdt_add_subnode(fdt, 0, nodename)); - g_free(nodename); + _FDT(node = fdt_add_subnode(fdt, 0, xive->nodename)); _FDT(fdt_setprop_string(fdt, node, "device_type", "power-ivpe")); _FDT(fdt_setprop(fdt, node, "reg", timas, sizeof(timas))); diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 3009fa7472..af7dc709ab 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -338,6 +338,9 @@ static void icp_realize(DeviceState *dev, Error **errp) case PPC_FLAGS_INPUT_POWER7: icp->output = env->irq_inputs[POWER7_INPUT_INT]; break; + case PPC_FLAGS_INPUT_POWER9: /* For SPAPR xics emulation */ + icp->output = env->irq_inputs[POWER9_INPUT_INT]; + break; case PPC_FLAGS_INPUT_970: icp->output = env->irq_inputs[PPC970_INPUT_INT]; @@ -755,6 +758,10 @@ void ics_set_irq_type(ICSState *ics, int srcno, bool lsi) ics->irqs[srcno].flags |= lsi ? XICS_FLAGS_IRQ_LSI : XICS_FLAGS_IRQ_MSI; + + if (kvm_irqchip_in_kernel()) { + ics_set_kvm_state_one(ics, srcno); + } } static void xics_register_types(void) diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c index a00d0a7962..c6e1b630a4 100644 --- a/hw/intc/xics_kvm.c +++ b/hw/intc/xics_kvm.c @@ -213,45 +213,57 @@ void ics_synchronize_state(ICSState *ics) ics_get_kvm_state(ics); } -int ics_set_kvm_state(ICSState *ics) +int ics_set_kvm_state_one(ICSState *ics, int srcno) { uint64_t state; - int i; Error *local_err = NULL; + ICSIRQState *irq = &ics->irqs[srcno]; + int ret; - for (i = 0; i < ics->nr_irqs; i++) { - ICSIRQState *irq = &ics->irqs[i]; - int ret; - - state = irq->server; - state |= (uint64_t)(irq->saved_priority & KVM_XICS_PRIORITY_MASK) - << KVM_XICS_PRIORITY_SHIFT; - if (irq->priority != irq->saved_priority) { - assert(irq->priority == 0xff); - state |= KVM_XICS_MASKED; - } + state = irq->server; + state |= (uint64_t)(irq->saved_priority & KVM_XICS_PRIORITY_MASK) + << KVM_XICS_PRIORITY_SHIFT; + if (irq->priority != irq->saved_priority) { + assert(irq->priority == 0xff); + state |= KVM_XICS_MASKED; + } - if (ics->irqs[i].flags & XICS_FLAGS_IRQ_LSI) { - state |= KVM_XICS_LEVEL_SENSITIVE; - if (irq->status & XICS_STATUS_ASSERTED) { - state |= KVM_XICS_PENDING; - } - } else { - if (irq->status & XICS_STATUS_MASKED_PENDING) { - state |= KVM_XICS_PENDING; - } + if (irq->flags & XICS_FLAGS_IRQ_LSI) { + state |= KVM_XICS_LEVEL_SENSITIVE; + if (irq->status & XICS_STATUS_ASSERTED) { + state |= KVM_XICS_PENDING; } - if (irq->status & XICS_STATUS_PRESENTED) { - state |= KVM_XICS_PRESENTED; - } - if (irq->status & XICS_STATUS_QUEUED) { - state |= KVM_XICS_QUEUED; + } else { + if (irq->status & XICS_STATUS_MASKED_PENDING) { + state |= KVM_XICS_PENDING; } + } + if (irq->status & XICS_STATUS_PRESENTED) { + state |= KVM_XICS_PRESENTED; + } + if (irq->status & XICS_STATUS_QUEUED) { + state |= KVM_XICS_QUEUED; + } + + ret = kvm_device_access(kernel_xics_fd, KVM_DEV_XICS_GRP_SOURCES, + srcno + ics->offset, &state, true, &local_err); + if (local_err) { + error_report_err(local_err); + return ret; + } + + return 0; +} + +int ics_set_kvm_state(ICSState *ics) +{ + int i; + + for (i = 0; i < ics->nr_irqs; i++) { + int ret; - ret = kvm_device_access(kernel_xics_fd, KVM_DEV_XICS_GRP_SOURCES, - i + ics->offset, &state, true, &local_err); - if (local_err) { - error_report_err(local_err); + ret = ics_set_kvm_state_one(ics, i); + if (ret) { return ret; } } diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c index e2d8b38183..53bda6661b 100644 --- a/hw/intc/xics_spapr.c +++ b/hw/intc/xics_spapr.c @@ -254,7 +254,7 @@ void spapr_dt_xics(sPAPRMachineState *spapr, uint32_t nr_servers, void *fdt, }; int node; - _FDT(node = fdt_add_subnode(fdt, 0, "interrupt-controller")); + _FDT(node = fdt_add_subnode(fdt, 0, XICS_NODENAME)); _FDT(fdt_setprop_string(fdt, node, "device_type", "PowerPC-External-Interrupt-Presentation")); diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 2e9b8efd43..daa7badc84 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -481,8 +481,8 @@ static void xive_tctx_realize(DeviceState *dev, Error **errp) env = &cpu->env; switch (PPC_INPUT(env)) { - case PPC_FLAGS_INPUT_POWER7: - tctx->output = env->irq_inputs[POWER7_INPUT_INT]; + case PPC_FLAGS_INPUT_POWER9: + tctx->output = env->irq_inputs[POWER9_INPUT_INT]; break; default: diff --git a/hw/isa/vt82c686.c b/hw/isa/vt82c686.c index 7302f6d74b..85d0532dd5 100644 --- a/hw/isa/vt82c686.c +++ b/hw/isa/vt82c686.c @@ -14,7 +14,6 @@ #include "hw/hw.h" #include "hw/isa/vt82c686.h" #include "hw/i2c/i2c.h" -#include "hw/i2c/smbus.h" #include "hw/pci/pci.h" #include "hw/isa/isa.h" #include "hw/isa/superio.h" diff --git a/hw/m68k/mcf5208.c b/hw/m68k/mcf5208.c index 8531e07e5b..6f6efae9fc 100644 --- a/hw/m68k/mcf5208.c +++ b/hw/m68k/mcf5208.c @@ -27,6 +27,8 @@ #define SYS_FREQ 166666666 +#define ROM_SIZE 0x200000 + #define PCSR_EN 0x0001 #define PCSR_RLD 0x0002 #define PCSR_PIF 0x0004 @@ -227,6 +229,7 @@ static void mcf5208evb_init(MachineState *machine) hwaddr entry; qemu_irq *pic; MemoryRegion *address_space_mem = get_system_memory(); + MemoryRegion *rom = g_new(MemoryRegion, 1); MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *sram = g_new(MemoryRegion, 1); @@ -237,6 +240,10 @@ static void mcf5208evb_init(MachineState *machine) env->vbr = 0; /* TODO: Configure BARs. */ + /* ROM at 0x00000000 */ + memory_region_init_rom(rom, NULL, "mcf5208.rom", ROM_SIZE, &error_fatal); + memory_region_add_subregion(address_space_mem, 0x00000000, rom); + /* DRAM at 0x40000000 */ memory_region_allocate_system_memory(ram, NULL, "mcf5208.ram", ram_size); memory_region_add_subregion(address_space_mem, 0x40000000, ram); @@ -285,9 +292,30 @@ static void mcf5208evb_init(MachineState *machine) /* 0xfc0a4000 GPIO. */ /* 0xfc0a8000 SDRAM controller. */ + /* Load firmware */ + if (bios_name) { + char *fn; + uint8_t *ptr; + + fn = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + if (!fn) { + error_report("Could not find ROM image '%s'", bios_name); + exit(1); + } + if (load_image_targphys(fn, 0x0, ROM_SIZE) < 8) { + error_report("Could not load ROM image '%s'", bios_name); + exit(1); + } + g_free(fn); + /* Initial PC is always at offset 4 in firmware binaries */ + ptr = rom_ptr(0x4, 4); + assert(ptr != NULL); + env->pc = ldl_p(ptr); + } + /* Load kernel. */ if (!kernel_filename) { - if (qtest_enabled()) { + if (qtest_enabled() || bios_name) { return; } error_report("Kernel image must be specified"); diff --git a/hw/mips/mips_fulong2e.c b/hw/mips/mips_fulong2e.c index 02549d5c7e..fbbc543eed 100644 --- a/hw/mips/mips_fulong2e.c +++ b/hw/mips/mips_fulong2e.c @@ -21,13 +21,14 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qapi/error.h" +#include "cpu.h" #include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/dma/i8257.h" #include "hw/isa/superio.h" #include "net/net.h" #include "hw/boards.h" -#include "hw/i2c/smbus.h" +#include "hw/i2c/smbus_eeprom.h" #include "hw/block/flash.h" #include "hw/mips/mips.h" #include "hw/mips/cpudevs.h" @@ -35,7 +36,6 @@ #include "audio/audio.h" #include "qemu/log.h" #include "hw/loader.h" -#include "hw/mips/bios.h" #include "hw/ide.h" #include "elf.h" #include "hw/isa/vt82c686.h" @@ -51,6 +51,8 @@ #define ENVP_NB_ENTRIES 16 #define ENVP_ENTRY_SIZE 256 +/* fulong 2e has a 512k flash: Winbond W39L040AP70Z */ +#define BIOS_SIZE (512 * KiB) #define MAX_IDE_BUS 2 /* @@ -212,20 +214,6 @@ static void main_cpu_reset(void *opaque) } } -static const uint8_t eeprom_spd[0x80] = { - 0x80,0x08,0x07,0x0d,0x09,0x02,0x40,0x00,0x04,0x70, - 0x70,0x00,0x82,0x10,0x00,0x01,0x0e,0x04,0x0c,0x01, - 0x02,0x20,0x80,0x75,0x70,0x00,0x00,0x50,0x3c,0x50, - 0x2d,0x20,0xb0,0xb0,0x50,0x50,0x00,0x00,0x00,0x00, - 0x00,0x41,0x48,0x3c,0x32,0x75,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x9c,0x7b,0x07,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x48,0x42,0x35,0x34,0x41,0x32, - 0x35,0x36,0x38,0x4b,0x4e,0x2d,0x41,0x37,0x35,0x42, - 0x20,0x30,0x20 -}; - static void vt82c686b_southbridge_init(PCIBus *pci_bus, int slot, qemu_irq intc, I2CBus **i2c_bus, ISABus **p_isa_bus) { @@ -282,7 +270,6 @@ static void network_init (PCIBus *pci_bus) static void mips_fulong2e_init(MachineState *machine) { - ram_addr_t ram_size = machine->ram_size; const char *kernel_filename = machine->kernel_filename; const char *kernel_cmdline = machine->kernel_cmdline; const char *initrd_filename = machine->initrd_filename; @@ -290,7 +277,10 @@ static void mips_fulong2e_init(MachineState *machine) MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *bios = g_new(MemoryRegion, 1); + ram_addr_t ram_size = machine->ram_size; long bios_size; + uint8_t *spd_data; + Error *err = NULL; int64_t kernel_entry; PCIBus *pci_bus; ISABus *isa_bus; @@ -304,15 +294,12 @@ static void mips_fulong2e_init(MachineState *machine) qemu_register_reset(main_cpu_reset, cpu); - /* fulong 2e has 256M ram. */ + /* TODO: support more than 256M RAM as highmem */ ram_size = 256 * MiB; - /* fulong 2e has a 1M flash.Winbond W39L040AP70Z */ - bios_size = 1 * MiB; - /* allocate RAM */ memory_region_allocate_system_memory(ram, NULL, "fulong2e.ram", ram_size); - memory_region_init_ram(bios, NULL, "fulong2e.bios", bios_size, + memory_region_init_ram(bios, NULL, "fulong2e.bios", BIOS_SIZE, &error_fatal); memory_region_set_readonly(bios, true); @@ -360,8 +347,14 @@ static void mips_fulong2e_init(MachineState *machine) vt82c686b_southbridge_init(pci_bus, FULONG2E_VIA_SLOT, env->irq[5], &smbus, &isa_bus); - /* TODO: Populate SPD eeprom data. */ - smbus_eeprom_init(smbus, 1, eeprom_spd, sizeof(eeprom_spd)); + /* Populate SPD eeprom data */ + spd_data = spd_data_generate(DDR, ram_size, &err); + if (err) { + warn_report_err(err); + } + if (spd_data) { + smbus_eeprom_init_one(smbus, 0x50, spd_data); + } mc146818_rtc_init(isa_bus, 2000, NULL); @@ -375,6 +368,7 @@ static void mips_fulong2e_machine_init(MachineClass *mc) mc->init = mips_fulong2e_init; mc->block_default_type = IF_IDE; mc->default_cpu_type = MIPS_CPU_TYPE_NAME("Loongson-2E"); + mc->default_ram_size = 256 * MiB; } DEFINE_MACHINE("fulong2e", mips_fulong2e_machine_init) diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index 7a403ef1ce..39aef4b6db 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -33,7 +33,7 @@ #include "hw/char/serial.h" #include "net/net.h" #include "hw/boards.h" -#include "hw/i2c/smbus.h" +#include "hw/i2c/smbus_eeprom.h" #include "hw/block/flash.h" #include "hw/mips/mips.h" #include "hw/mips/cpudevs.h" diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 74c91d250c..c71e07ae35 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -70,6 +70,7 @@ obj-$(CONFIG_IOTKIT_SECCTL) += iotkit-secctl.o obj-$(CONFIG_IOTKIT_SYSCTL) += iotkit-sysctl.o obj-$(CONFIG_IOTKIT_SYSINFO) += iotkit-sysinfo.o obj-$(CONFIG_ARMSSE_CPUID) += armsse-cpuid.o +obj-$(CONFIG_ARMSSE_MHU) += armsse-mhu.o obj-$(CONFIG_PVPANIC) += pvpanic.o obj-$(CONFIG_AUX) += auxbus.o diff --git a/hw/misc/armsse-mhu.c b/hw/misc/armsse-mhu.c new file mode 100644 index 0000000000..9ebca32e9a --- /dev/null +++ b/hw/misc/armsse-mhu.c @@ -0,0 +1,198 @@ +/* + * ARM SSE-200 Message Handling Unit (MHU) + * + * Copyright (c) 2019 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* + * This is a model of the Message Handling Unit (MHU) which is part of the + * Arm SSE-200 and documented in + * http://infocenter.arm.com/help/topic/com.arm.doc.101104_0100_00_en/corelink_sse200_subsystem_for_embedded_technical_reference_manual_101104_0100_00_en.pdf + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "trace.h" +#include "qapi/error.h" +#include "sysemu/sysemu.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/misc/armsse-mhu.h" + +REG32(CPU0INTR_STAT, 0x0) +REG32(CPU0INTR_SET, 0x4) +REG32(CPU0INTR_CLR, 0x8) +REG32(CPU1INTR_STAT, 0x10) +REG32(CPU1INTR_SET, 0x14) +REG32(CPU1INTR_CLR, 0x18) +REG32(PID4, 0xfd0) +REG32(PID5, 0xfd4) +REG32(PID6, 0xfd8) +REG32(PID7, 0xfdc) +REG32(PID0, 0xfe0) +REG32(PID1, 0xfe4) +REG32(PID2, 0xfe8) +REG32(PID3, 0xfec) +REG32(CID0, 0xff0) +REG32(CID1, 0xff4) +REG32(CID2, 0xff8) +REG32(CID3, 0xffc) + +/* Valid bits in the interrupt registers. If any are set the IRQ is raised */ +#define INTR_MASK 0xf + +/* PID/CID values */ +static const int armsse_mhu_id[] = { + 0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */ + 0x56, 0xb8, 0x0b, 0x00, /* PID0..PID3 */ + 0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */ +}; + +static void armsse_mhu_update(ARMSSEMHU *s) +{ + qemu_set_irq(s->cpu0irq, s->cpu0intr != 0); + qemu_set_irq(s->cpu1irq, s->cpu1intr != 0); +} + +static uint64_t armsse_mhu_read(void *opaque, hwaddr offset, unsigned size) +{ + ARMSSEMHU *s = ARMSSE_MHU(opaque); + uint64_t r; + + switch (offset) { + case A_CPU0INTR_STAT: + r = s->cpu0intr; + break; + + case A_CPU1INTR_STAT: + r = s->cpu1intr; + break; + + case A_PID4 ... A_CID3: + r = armsse_mhu_id[(offset - A_PID4) / 4]; + break; + + case A_CPU0INTR_SET: + case A_CPU0INTR_CLR: + case A_CPU1INTR_SET: + case A_CPU1INTR_CLR: + qemu_log_mask(LOG_GUEST_ERROR, + "SSE MHU: read of write-only register at offset 0x%x\n", + (int)offset); + r = 0; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "SSE MHU read: bad offset 0x%x\n", (int)offset); + r = 0; + break; + } + trace_armsse_mhu_read(offset, r, size); + return r; +} + +static void armsse_mhu_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + ARMSSEMHU *s = ARMSSE_MHU(opaque); + + trace_armsse_mhu_write(offset, value, size); + + switch (offset) { + case A_CPU0INTR_SET: + s->cpu0intr |= (value & INTR_MASK); + break; + case A_CPU0INTR_CLR: + s->cpu0intr &= ~(value & INTR_MASK); + break; + case A_CPU1INTR_SET: + s->cpu1intr |= (value & INTR_MASK); + break; + case A_CPU1INTR_CLR: + s->cpu1intr &= ~(value & INTR_MASK); + break; + + case A_CPU0INTR_STAT: + case A_CPU1INTR_STAT: + case A_PID4 ... A_CID3: + qemu_log_mask(LOG_GUEST_ERROR, + "SSE MHU: write to read-only register at offset 0x%x\n", + (int)offset); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "SSE MHU write: bad offset 0x%x\n", (int)offset); + break; + } + + armsse_mhu_update(s); +} + +static const MemoryRegionOps armsse_mhu_ops = { + .read = armsse_mhu_read, + .write = armsse_mhu_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, +}; + +static void armsse_mhu_reset(DeviceState *dev) +{ + ARMSSEMHU *s = ARMSSE_MHU(dev); + + s->cpu0intr = 0; + s->cpu1intr = 0; +} + +static const VMStateDescription armsse_mhu_vmstate = { + .name = "armsse-mhu", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(cpu0intr, ARMSSEMHU), + VMSTATE_UINT32(cpu1intr, ARMSSEMHU), + VMSTATE_END_OF_LIST() + }, +}; + +static void armsse_mhu_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + ARMSSEMHU *s = ARMSSE_MHU(obj); + + memory_region_init_io(&s->iomem, obj, &armsse_mhu_ops, + s, "armsse-mhu", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->cpu0irq); + sysbus_init_irq(sbd, &s->cpu1irq); +} + +static void armsse_mhu_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = armsse_mhu_reset; + dc->vmsd = &armsse_mhu_vmstate; +} + +static const TypeInfo armsse_mhu_info = { + .name = TYPE_ARMSSE_MHU, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(ARMSSEMHU), + .instance_init = armsse_mhu_init, + .class_init = armsse_mhu_class_init, +}; + +static void armsse_mhu_register_types(void) +{ + type_register_static(&armsse_mhu_info); +} + +type_init(armsse_mhu_register_types); diff --git a/hw/misc/iotkit-sysctl.c b/hw/misc/iotkit-sysctl.c index a21d8bd678..54064a31ef 100644 --- a/hw/misc/iotkit-sysctl.c +++ b/hw/misc/iotkit-sysctl.c @@ -17,6 +17,7 @@ */ #include "qemu/osdep.h" +#include "qemu/bitops.h" #include "qemu/log.h" #include "trace.h" #include "qapi/error.h" @@ -24,19 +25,32 @@ #include "hw/sysbus.h" #include "hw/registerfields.h" #include "hw/misc/iotkit-sysctl.h" +#include "target/arm/arm-powerctl.h" +#include "target/arm/cpu.h" REG32(SECDBGSTAT, 0x0) REG32(SECDBGSET, 0x4) REG32(SECDBGCLR, 0x8) +REG32(SCSECCTRL, 0xc) +REG32(FCLK_DIV, 0x10) +REG32(SYSCLK_DIV, 0x14) +REG32(CLOCK_FORCE, 0x18) REG32(RESET_SYNDROME, 0x100) REG32(RESET_MASK, 0x104) REG32(SWRESET, 0x108) FIELD(SWRESET, SWRESETREQ, 9, 1) REG32(GRETREG, 0x10c) -REG32(INITSVRTOR0, 0x110) +REG32(INITSVTOR0, 0x110) +REG32(INITSVTOR1, 0x114) REG32(CPUWAIT, 0x118) -REG32(BUSWAIT, 0x11c) +REG32(NMI_ENABLE, 0x11c) /* BUSWAIT in IoTKit */ REG32(WICCTRL, 0x120) +REG32(EWCTRL, 0x124) +REG32(PDCM_PD_SYS_SENSE, 0x200) +REG32(PDCM_PD_SRAM0_SENSE, 0x20c) +REG32(PDCM_PD_SRAM1_SENSE, 0x210) +REG32(PDCM_PD_SRAM2_SENSE, 0x214) +REG32(PDCM_PD_SRAM3_SENSE, 0x218) REG32(PID4, 0xfd0) REG32(PID5, 0xfd4) REG32(PID6, 0xfd8) @@ -57,6 +71,21 @@ static const int sysctl_id[] = { 0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */ }; +/* + * Set the initial secure vector table offset address for the core. + * This will take effect when the CPU next resets. + */ +static void set_init_vtor(uint64_t cpuid, uint32_t vtor) +{ + Object *cpuobj = OBJECT(arm_get_cpu_by_id(cpuid)); + + if (cpuobj) { + if (object_property_find(cpuobj, "init-svtor", NULL)) { + object_property_set_uint(cpuobj, vtor, "init-svtor", &error_abort); + } + } +} + static uint64_t iotkit_sysctl_read(void *opaque, hwaddr offset, unsigned size) { @@ -67,6 +96,30 @@ static uint64_t iotkit_sysctl_read(void *opaque, hwaddr offset, case A_SECDBGSTAT: r = s->secure_debug; break; + case A_SCSECCTRL: + if (!s->is_sse200) { + goto bad_offset; + } + r = s->scsecctrl; + break; + case A_FCLK_DIV: + if (!s->is_sse200) { + goto bad_offset; + } + r = s->fclk_div; + break; + case A_SYSCLK_DIV: + if (!s->is_sse200) { + goto bad_offset; + } + r = s->sysclk_div; + break; + case A_CLOCK_FORCE: + if (!s->is_sse200) { + goto bad_offset; + } + r = s->clock_force; + break; case A_RESET_SYNDROME: r = s->reset_syndrome; break; @@ -76,19 +129,65 @@ static uint64_t iotkit_sysctl_read(void *opaque, hwaddr offset, case A_GRETREG: r = s->gretreg; break; - case A_INITSVRTOR0: - r = s->initsvrtor0; + case A_INITSVTOR0: + r = s->initsvtor0; + break; + case A_INITSVTOR1: + if (!s->is_sse200) { + goto bad_offset; + } + r = s->initsvtor1; break; case A_CPUWAIT: r = s->cpuwait; break; - case A_BUSWAIT: - /* In IoTKit BUSWAIT is reserved, R/O, zero */ - r = 0; + case A_NMI_ENABLE: + /* In IoTKit this is named BUSWAIT but is marked reserved, R/O, zero */ + if (!s->is_sse200) { + r = 0; + break; + } + r = s->nmi_enable; break; case A_WICCTRL: r = s->wicctrl; break; + case A_EWCTRL: + if (!s->is_sse200) { + goto bad_offset; + } + r = s->ewctrl; + break; + case A_PDCM_PD_SYS_SENSE: + if (!s->is_sse200) { + goto bad_offset; + } + r = s->pdcm_pd_sys_sense; + break; + case A_PDCM_PD_SRAM0_SENSE: + if (!s->is_sse200) { + goto bad_offset; + } + r = s->pdcm_pd_sram0_sense; + break; + case A_PDCM_PD_SRAM1_SENSE: + if (!s->is_sse200) { + goto bad_offset; + } + r = s->pdcm_pd_sram1_sense; + break; + case A_PDCM_PD_SRAM2_SENSE: + if (!s->is_sse200) { + goto bad_offset; + } + r = s->pdcm_pd_sram2_sense; + break; + case A_PDCM_PD_SRAM3_SENSE: + if (!s->is_sse200) { + goto bad_offset; + } + r = s->pdcm_pd_sram3_sense; + break; case A_PID4 ... A_CID3: r = sysctl_id[(offset - A_PID4) / 4]; break; @@ -101,6 +200,7 @@ static uint64_t iotkit_sysctl_read(void *opaque, hwaddr offset, r = 0; break; default: + bad_offset: qemu_log_mask(LOG_GUEST_ERROR, "IoTKit SysCtl read: bad offset %x\n", (int)offset); r = 0; @@ -145,12 +245,19 @@ static void iotkit_sysctl_write(void *opaque, hwaddr offset, */ s->gretreg = value; break; - case A_INITSVRTOR0: - qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl INITSVRTOR0 unimplemented\n"); - s->initsvrtor0 = value; + case A_INITSVTOR0: + s->initsvtor0 = value; + set_init_vtor(0, s->initsvtor0); break; case A_CPUWAIT: - qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl CPUWAIT unimplemented\n"); + if ((s->cpuwait & 1) && !(value & 1)) { + /* Powering up CPU 0 */ + arm_set_cpu_on_and_reset(0); + } + if ((s->cpuwait & 2) && !(value & 2)) { + /* Powering up CPU 1 */ + arm_set_cpu_on_and_reset(1); + } s->cpuwait = value; break; case A_WICCTRL: @@ -172,14 +279,105 @@ static void iotkit_sysctl_write(void *opaque, hwaddr offset, qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); } break; - case A_BUSWAIT: /* In IoTKit BUSWAIT is reserved, R/O, zero */ + case A_SCSECCTRL: + if (!s->is_sse200) { + goto bad_offset; + } + qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl SCSECCTRL unimplemented\n"); + s->scsecctrl = value; + break; + case A_FCLK_DIV: + if (!s->is_sse200) { + goto bad_offset; + } + qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl FCLK_DIV unimplemented\n"); + s->fclk_div = value; + break; + case A_SYSCLK_DIV: + if (!s->is_sse200) { + goto bad_offset; + } + qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl SYSCLK_DIV unimplemented\n"); + s->sysclk_div = value; + break; + case A_CLOCK_FORCE: + if (!s->is_sse200) { + goto bad_offset; + } + qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl CLOCK_FORCE unimplemented\n"); + s->clock_force = value; + break; + case A_INITSVTOR1: + if (!s->is_sse200) { + goto bad_offset; + } + s->initsvtor1 = value; + set_init_vtor(1, s->initsvtor1); + break; + case A_EWCTRL: + if (!s->is_sse200) { + goto bad_offset; + } + qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl EWCTRL unimplemented\n"); + s->ewctrl = value; + break; + case A_PDCM_PD_SYS_SENSE: + if (!s->is_sse200) { + goto bad_offset; + } + qemu_log_mask(LOG_UNIMP, + "IoTKit SysCtl PDCM_PD_SYS_SENSE unimplemented\n"); + s->pdcm_pd_sys_sense = value; + break; + case A_PDCM_PD_SRAM0_SENSE: + if (!s->is_sse200) { + goto bad_offset; + } + qemu_log_mask(LOG_UNIMP, + "IoTKit SysCtl PDCM_PD_SRAM0_SENSE unimplemented\n"); + s->pdcm_pd_sram0_sense = value; + break; + case A_PDCM_PD_SRAM1_SENSE: + if (!s->is_sse200) { + goto bad_offset; + } + qemu_log_mask(LOG_UNIMP, + "IoTKit SysCtl PDCM_PD_SRAM1_SENSE unimplemented\n"); + s->pdcm_pd_sram1_sense = value; + break; + case A_PDCM_PD_SRAM2_SENSE: + if (!s->is_sse200) { + goto bad_offset; + } + qemu_log_mask(LOG_UNIMP, + "IoTKit SysCtl PDCM_PD_SRAM2_SENSE unimplemented\n"); + s->pdcm_pd_sram2_sense = value; + break; + case A_PDCM_PD_SRAM3_SENSE: + if (!s->is_sse200) { + goto bad_offset; + } + qemu_log_mask(LOG_UNIMP, + "IoTKit SysCtl PDCM_PD_SRAM3_SENSE unimplemented\n"); + s->pdcm_pd_sram3_sense = value; + break; + case A_NMI_ENABLE: + /* In IoTKit this is BUSWAIT: reserved, R/O, zero */ + if (!s->is_sse200) { + goto ro_offset; + } + qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl NMI_ENABLE unimplemented\n"); + s->nmi_enable = value; + break; case A_SECDBGSTAT: case A_PID4 ... A_CID3: + ro_offset: qemu_log_mask(LOG_GUEST_ERROR, "IoTKit SysCtl write: write of RO offset %x\n", (int)offset); break; default: + bad_offset: qemu_log_mask(LOG_GUEST_ERROR, "IoTKit SysCtl write: bad offset %x\n", (int)offset); break; @@ -206,9 +404,21 @@ static void iotkit_sysctl_reset(DeviceState *dev) s->reset_syndrome = 1; s->reset_mask = 0; s->gretreg = 0; - s->initsvrtor0 = 0x10000000; - s->cpuwait = 0; + s->initsvtor0 = s->initsvtor0_rst; + s->initsvtor1 = s->initsvtor1_rst; + s->cpuwait = s->cpuwait_rst; s->wicctrl = 0; + s->scsecctrl = 0; + s->fclk_div = 0; + s->sysclk_div = 0; + s->clock_force = 0; + s->nmi_enable = 0; + s->ewctrl = 0; + s->pdcm_pd_sys_sense = 0x7f; + s->pdcm_pd_sram0_sense = 0; + s->pdcm_pd_sram1_sense = 0; + s->pdcm_pd_sram2_sense = 0; + s->pdcm_pd_sram3_sense = 0; } static void iotkit_sysctl_init(Object *obj) @@ -221,6 +431,44 @@ static void iotkit_sysctl_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); } +static void iotkit_sysctl_realize(DeviceState *dev, Error **errp) +{ + IoTKitSysCtl *s = IOTKIT_SYSCTL(dev); + + /* The top 4 bits of the SYS_VERSION register tell us if we're an SSE-200 */ + if (extract32(s->sys_version, 28, 4) == 2) { + s->is_sse200 = true; + } +} + +static bool sse200_needed(void *opaque) +{ + IoTKitSysCtl *s = IOTKIT_SYSCTL(opaque); + + return s->is_sse200; +} + +static const VMStateDescription iotkit_sysctl_sse200_vmstate = { + .name = "iotkit-sysctl/sse-200", + .version_id = 1, + .minimum_version_id = 1, + .needed = sse200_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT32(scsecctrl, IoTKitSysCtl), + VMSTATE_UINT32(fclk_div, IoTKitSysCtl), + VMSTATE_UINT32(sysclk_div, IoTKitSysCtl), + VMSTATE_UINT32(clock_force, IoTKitSysCtl), + VMSTATE_UINT32(initsvtor1, IoTKitSysCtl), + VMSTATE_UINT32(nmi_enable, IoTKitSysCtl), + VMSTATE_UINT32(pdcm_pd_sys_sense, IoTKitSysCtl), + VMSTATE_UINT32(pdcm_pd_sram0_sense, IoTKitSysCtl), + VMSTATE_UINT32(pdcm_pd_sram1_sense, IoTKitSysCtl), + VMSTATE_UINT32(pdcm_pd_sram2_sense, IoTKitSysCtl), + VMSTATE_UINT32(pdcm_pd_sram3_sense, IoTKitSysCtl), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription iotkit_sysctl_vmstate = { .name = "iotkit-sysctl", .version_id = 1, @@ -230,19 +478,35 @@ static const VMStateDescription iotkit_sysctl_vmstate = { VMSTATE_UINT32(reset_syndrome, IoTKitSysCtl), VMSTATE_UINT32(reset_mask, IoTKitSysCtl), VMSTATE_UINT32(gretreg, IoTKitSysCtl), - VMSTATE_UINT32(initsvrtor0, IoTKitSysCtl), + VMSTATE_UINT32(initsvtor0, IoTKitSysCtl), VMSTATE_UINT32(cpuwait, IoTKitSysCtl), VMSTATE_UINT32(wicctrl, IoTKitSysCtl), VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription*[]) { + &iotkit_sysctl_sse200_vmstate, + NULL } }; +static Property iotkit_sysctl_props[] = { + DEFINE_PROP_UINT32("SYS_VERSION", IoTKitSysCtl, sys_version, 0), + DEFINE_PROP_UINT32("CPUWAIT_RST", IoTKitSysCtl, cpuwait_rst, 0), + DEFINE_PROP_UINT32("INITSVTOR0_RST", IoTKitSysCtl, initsvtor0_rst, + 0x10000000), + DEFINE_PROP_UINT32("INITSVTOR1_RST", IoTKitSysCtl, initsvtor1_rst, + 0x10000000), + DEFINE_PROP_END_OF_LIST() +}; + static void iotkit_sysctl_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &iotkit_sysctl_vmstate; dc->reset = iotkit_sysctl_reset; + dc->props = iotkit_sysctl_props; + dc->realize = iotkit_sysctl_realize; } static const TypeInfo iotkit_sysctl_info = { diff --git a/hw/misc/mips_itu.c b/hw/misc/mips_itu.c index 1257d8fce6..3afdbe69c6 100644 --- a/hw/misc/mips_itu.c +++ b/hw/misc/mips_itu.c @@ -94,7 +94,7 @@ void itc_reconfigure(MIPSITUState *tag) if (tag->saar_present) { address = ((*(uint64_t *) tag->saar) & 0xFFFFFFFFE000ULL) << 4; - size = 1 << ((*(uint64_t *) tag->saar >> 1) & 0x1f); + size = 1ULL << ((*(uint64_t *) tag->saar >> 1) & 0x1f); is_enabled = *(uint64_t *) tag->saar & 1; } diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c index 9775d5274a..7325d3f287 100644 --- a/hw/misc/pca9552.c +++ b/hw/misc/pca9552.c @@ -115,7 +115,7 @@ static void pca9552_autoinc(PCA9552State *s) } } -static int pca9552_recv(I2CSlave *i2c) +static uint8_t pca9552_recv(I2CSlave *i2c) { PCA9552State *s = PCA9552(i2c); uint8_t ret; diff --git a/hw/misc/tmp105.c b/hw/misc/tmp105.c index f6d7163273..0c32f6f8b6 100644 --- a/hw/misc/tmp105.c +++ b/hw/misc/tmp105.c @@ -147,7 +147,7 @@ static void tmp105_write(TMP105State *s) } } -static int tmp105_rx(I2CSlave *i2c) +static uint8_t tmp105_rx(I2CSlave *i2c) { TMP105State *s = TMP105(i2c); diff --git a/hw/misc/tmp421.c b/hw/misc/tmp421.c index eeb11000f0..ce6d40ac9c 100644 --- a/hw/misc/tmp421.c +++ b/hw/misc/tmp421.c @@ -249,7 +249,7 @@ static void tmp421_write(TMP421State *s) } } -static int tmp421_rx(I2CSlave *i2c) +static uint8_t tmp421_rx(I2CSlave *i2c) { TMP421State *s = TMP421(i2c); diff --git a/hw/misc/trace-events b/hw/misc/trace-events index b0701bddd3..c1795bb54b 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -136,3 +136,7 @@ iotkit_sysctl_reset(void) "IoTKit SysCtl: reset" # hw/misc/armsse-cpuid.c armsse_cpuid_read(uint64_t offset, uint64_t data, unsigned size) "SSE-200 CPU_IDENTITY read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" armsse_cpuid_write(uint64_t offset, uint64_t data, unsigned size) "SSE-200 CPU_IDENTITY write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" + +# hw/misc/armsse-mhu.c +armsse_mhu_read(uint64_t offset, uint64_t data, unsigned size) "SSE-200 MHU read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +armsse_mhu_write(uint64_t offset, uint64_t data, unsigned size) "SSE-200 MHU write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" diff --git a/hw/misc/tz-ppc.c b/hw/misc/tz-ppc.c index 3dd045c15f..2e04837bea 100644 --- a/hw/misc/tz-ppc.c +++ b/hw/misc/tz-ppc.c @@ -181,6 +181,21 @@ static const MemoryRegionOps tz_ppc_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static bool tz_ppc_dummy_accepts(void *opaque, hwaddr addr, + unsigned size, bool is_write, + MemTxAttrs attrs) +{ + /* + * Board code should never map the upstream end of an unused port, + * so we should never try to make a memory access to it. + */ + g_assert_not_reached(); +} + +static const MemoryRegionOps tz_ppc_dummy_ops = { + .valid.accepts = tz_ppc_dummy_accepts, +}; + static void tz_ppc_reset(DeviceState *dev) { TZPPC *s = TZ_PPC(dev); @@ -210,16 +225,33 @@ static void tz_ppc_realize(DeviceState *dev, Error **errp) SysBusDevice *sbd = SYS_BUS_DEVICE(dev); TZPPC *s = TZ_PPC(dev); int i; + int max_port = 0; /* We can't create the upstream end of the port until realize, * as we don't know the size of the MR used as the downstream until then. */ for (i = 0; i < TZ_NUM_PORTS; i++) { + if (s->port[i].downstream) { + max_port = i; + } + } + + for (i = 0; i <= max_port; i++) { TZPPCPort *port = &s->port[i]; char *name; uint64_t size; if (!port->downstream) { + /* + * Create dummy sysbus MMIO region so the sysbus region + * numbering doesn't get out of sync with the port numbers. + * The size is entirely arbitrary. + */ + name = g_strdup_printf("tz-ppc-dummy-port[%d]", i); + memory_region_init_io(&port->upstream, obj, &tz_ppc_dummy_ops, + port, name, 0x10000); + sysbus_init_mmio(sbd, &port->upstream); + g_free(name); continue; } diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 3f319ef723..6e6b146022 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -82,29 +82,17 @@ static inline __virtio16 *virtio_net_rsc_ext_num_dupacks( #endif -/* - * Calculate the number of bytes up to and including the given 'field' of - * 'container'. - */ -#define endof(container, field) \ - (offsetof(container, field) + sizeof_field(container, field)) - -typedef struct VirtIOFeature { - uint64_t flags; - size_t end; -} VirtIOFeature; - static VirtIOFeature feature_sizes[] = { {.flags = 1ULL << VIRTIO_NET_F_MAC, - .end = endof(struct virtio_net_config, mac)}, + .end = virtio_endof(struct virtio_net_config, mac)}, {.flags = 1ULL << VIRTIO_NET_F_STATUS, - .end = endof(struct virtio_net_config, status)}, + .end = virtio_endof(struct virtio_net_config, status)}, {.flags = 1ULL << VIRTIO_NET_F_MQ, - .end = endof(struct virtio_net_config, max_virtqueue_pairs)}, + .end = virtio_endof(struct virtio_net_config, max_virtqueue_pairs)}, {.flags = 1ULL << VIRTIO_NET_F_MTU, - .end = endof(struct virtio_net_config, mtu)}, + .end = virtio_endof(struct virtio_net_config, mtu)}, {.flags = 1ULL << VIRTIO_NET_F_SPEED_DUPLEX, - .end = endof(struct virtio_net_config, duplex)}, + .end = virtio_endof(struct virtio_net_config, duplex)}, {} }; @@ -2580,15 +2568,10 @@ static void virtio_net_guest_notifier_mask(VirtIODevice *vdev, int idx, static void virtio_net_set_config_size(VirtIONet *n, uint64_t host_features) { - int i, config_size = 0; virtio_add_feature(&host_features, VIRTIO_NET_F_MAC); - for (i = 0; feature_sizes[i].flags != 0; i++) { - if (host_features & feature_sizes[i].flags) { - config_size = MAX(feature_sizes[i].end, config_size); - } - } - n->config_size = config_size; + n->config_size = virtio_feature_get_config_size(feature_sizes, + host_features); } void virtio_net_set_netclient_name(VirtIONet *n, const char *name, diff --git a/hw/nvram/eeprom_at24c.c b/hw/nvram/eeprom_at24c.c index 27cd01e615..d1456dafbd 100644 --- a/hw/nvram/eeprom_at24c.c +++ b/hw/nvram/eeprom_at24c.c @@ -74,10 +74,10 @@ int at24c_eeprom_event(I2CSlave *s, enum i2c_event event) } static -int at24c_eeprom_recv(I2CSlave *s) +uint8_t at24c_eeprom_recv(I2CSlave *s) { EEPROMState *ee = AT24C_EE(s); - int ret; + uint8_t ret; ret = ee->mem[ee->cur]; diff --git a/hw/pci-host/bonito.c b/hw/pci-host/bonito.c index 9f33582706..dde4437595 100644 --- a/hw/pci-host/bonito.c +++ b/hw/pci-host/bonito.c @@ -217,6 +217,7 @@ struct BonitoState { PCIHostState parent_obj; qemu_irq *pic; PCIBonitoState *pci_dev; + MemoryRegion pci_mem; }; #define TYPE_BONITO_PCI_HOST_BRIDGE "Bonito-pcihost" @@ -598,11 +599,15 @@ static const VMStateDescription vmstate_bonito = { static void bonito_pcihost_realize(DeviceState *dev, Error **errp) { PCIHostState *phb = PCI_HOST_BRIDGE(dev); + BonitoState *bs = BONITO_PCI_HOST_BRIDGE(dev); + memory_region_init(&bs->pci_mem, OBJECT(dev), "pci.mem", BONITO_PCILO_SIZE); phb->bus = pci_register_root_bus(DEVICE(dev), "pci", pci_bonito_set_irq, pci_bonito_map_irq, - dev, get_system_memory(), get_system_io(), + dev, &bs->pci_mem, get_system_io(), 0x28, 32, TYPE_PCI_BUS); + memory_region_add_subregion(get_system_memory(), BONITO_PCILO_BASE, + &bs->pci_mem); } static void bonito_realize(PCIDevice *dev, Error **errp) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index da540860a2..3d5dfef220 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -47,14 +47,16 @@ #include <libfdt.h> -#define FDT_MAX_SIZE 0x00100000 +#define FDT_MAX_SIZE (1 * MiB) #define FW_FILE_NAME "skiboot.lid" #define FW_LOAD_ADDR 0x0 -#define FW_MAX_SIZE 0x00400000 +#define FW_MAX_SIZE (4 * MiB) #define KERNEL_LOAD_ADDR 0x20000000 +#define KERNEL_MAX_SIZE (256 * MiB) #define INITRD_LOAD_ADDR 0x60000000 +#define INITRD_MAX_SIZE (256 * MiB) static const char *pnv_chip_core_typename(const PnvChip *o) { @@ -588,7 +590,7 @@ static void pnv_init(MachineState *machine) long kernel_size; kernel_size = load_image_targphys(machine->kernel_filename, - KERNEL_LOAD_ADDR, 0x2000000); + KERNEL_LOAD_ADDR, KERNEL_MAX_SIZE); if (kernel_size < 0) { error_report("Could not load kernel '%s'", machine->kernel_filename); @@ -600,7 +602,7 @@ static void pnv_init(MachineState *machine) if (machine->initrd_filename) { pnv->initrd_base = INITRD_LOAD_ADDR; pnv->initrd_size = load_image_targphys(machine->initrd_filename, - pnv->initrd_base, 0x10000000); /* 128MB max */ + pnv->initrd_base, INITRD_MAX_SIZE); if (pnv->initrd_size < 0) { error_report("Could not load initial ram disk '%s'", machine->initrd_filename); @@ -736,18 +738,18 @@ static void pnv_chip_power8_instance_init(Object *obj) { Pnv8Chip *chip8 = PNV8_CHIP(obj); - object_initialize(&chip8->psi, sizeof(chip8->psi), TYPE_PNV_PSI); - object_property_add_child(obj, "psi", OBJECT(&chip8->psi), NULL); + object_initialize_child(obj, "psi", &chip8->psi, sizeof(chip8->psi), + TYPE_PNV_PSI, &error_abort, NULL); object_property_add_const_link(OBJECT(&chip8->psi), "xics", OBJECT(qdev_get_machine()), &error_abort); - object_initialize(&chip8->lpc, sizeof(chip8->lpc), TYPE_PNV_LPC); - object_property_add_child(obj, "lpc", OBJECT(&chip8->lpc), NULL); + object_initialize_child(obj, "lpc", &chip8->lpc, sizeof(chip8->lpc), + TYPE_PNV_LPC, &error_abort, NULL); object_property_add_const_link(OBJECT(&chip8->lpc), "psi", OBJECT(&chip8->psi), &error_abort); - object_initialize(&chip8->occ, sizeof(chip8->occ), TYPE_PNV_OCC); - object_property_add_child(obj, "occ", OBJECT(&chip8->occ), NULL); + object_initialize_child(obj, "occ", &chip8->occ, sizeof(chip8->occ), + TYPE_PNV_OCC, &error_abort, NULL); object_property_add_const_link(OBJECT(&chip8->occ), "psi", OBJECT(&chip8->psi), &error_abort); } diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c index 8ced095063..44bc0cbf58 100644 --- a/hw/ppc/pnv_psi.c +++ b/hw/ppc/pnv_psi.c @@ -444,8 +444,8 @@ static void pnv_psi_init(Object *obj) { PnvPsi *psi = PNV_PSI(obj); - object_initialize(&psi->ics, sizeof(psi->ics), TYPE_ICS_SIMPLE); - object_property_add_child(obj, "ics-psi", OBJECT(&psi->ics), NULL); + object_initialize_child(obj, "ics-psi", &psi->ics, sizeof(psi->ics), + TYPE_ICS_SIMPLE, &error_abort, NULL); } static const uint8_t irq_to_xivr[] = { diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index cffdc3914a..d1e3d4cd20 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -306,6 +306,48 @@ void ppcPOWER7_irq_init(PowerPCCPU *cpu) env->irq_inputs = (void **)qemu_allocate_irqs(&power7_set_irq, cpu, POWER7_INPUT_NB); } + +/* POWER9 internal IRQ controller */ +static void power9_set_irq(void *opaque, int pin, int level) +{ + PowerPCCPU *cpu = opaque; + CPUPPCState *env = &cpu->env; + + LOG_IRQ("%s: env %p pin %d level %d\n", __func__, + env, pin, level); + + switch (pin) { + case POWER9_INPUT_INT: + /* Level sensitive - active high */ + LOG_IRQ("%s: set the external IRQ state to %d\n", + __func__, level); + ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level); + break; + case POWER9_INPUT_HINT: + /* Level sensitive - active high */ + LOG_IRQ("%s: set the external IRQ state to %d\n", + __func__, level); + ppc_set_irq(cpu, PPC_INTERRUPT_HVIRT, level); + break; + default: + /* Unknown pin - do nothing */ + LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin); + return; + } + if (level) { + env->irq_input_state |= 1 << pin; + } else { + env->irq_input_state &= ~(1 << pin); + } +} + +void ppcPOWER9_irq_init(PowerPCCPU *cpu) +{ + CPUPPCState *env = &cpu->env; + + env->irq_inputs = (void **)qemu_allocate_irqs(&power9_set_irq, cpu, + POWER9_INPUT_NB); +} #endif /* defined(TARGET_PPC64) */ void ppc40x_core_reset(PowerPCCPU *cpu) @@ -776,7 +818,7 @@ static inline void cpu_ppc_hdecr_excp(PowerPCCPU *cpu) * interrupts in a PM state. Not only they don't cause a * wakeup but they also get effectively discarded. */ - if (!env->in_pm_state) { + if (!env->resume_as_sreset) { ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 1); } } diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c index 75250d49e4..d455c4bd07 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -34,7 +34,7 @@ #include "hw/sysbus.h" #include "hw/char/serial.h" #include "hw/i2c/ppc4xx_i2c.h" -#include "hw/i2c/smbus.h" +#include "hw/i2c/smbus_eeprom.h" #include "hw/usb/hcd-ehci.h" #include "hw/ppc/fdt.h" diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index abf9ebce59..b6a571b6f1 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1247,13 +1247,30 @@ static void *spapr_build_fdt(sPAPRMachineState *spapr) * Add info to guest to indentify which host is it being run on * and what is the uuid of the guest */ - if (kvmppc_get_host_model(&buf)) { - _FDT(fdt_setprop_string(fdt, 0, "host-model", buf)); - g_free(buf); + if (spapr->host_model && !g_str_equal(spapr->host_model, "none")) { + if (g_str_equal(spapr->host_model, "passthrough")) { + /* -M host-model=passthrough */ + if (kvmppc_get_host_model(&buf)) { + _FDT(fdt_setprop_string(fdt, 0, "host-model", buf)); + g_free(buf); + } + } else { + /* -M host-model=<user-string> */ + _FDT(fdt_setprop_string(fdt, 0, "host-model", spapr->host_model)); + } } - if (kvmppc_get_host_serial(&buf)) { - _FDT(fdt_setprop_string(fdt, 0, "host-serial", buf)); - g_free(buf); + + if (spapr->host_serial && !g_str_equal(spapr->host_serial, "none")) { + if (g_str_equal(spapr->host_serial, "passthrough")) { + /* -M host-serial=passthrough */ + if (kvmppc_get_host_serial(&buf)) { + _FDT(fdt_setprop_string(fdt, 0, "host-serial", buf)); + g_free(buf); + } + } else { + /* -M host-serial=<user-string> */ + _FDT(fdt_setprop_string(fdt, 0, "host-serial", spapr->host_serial)); + } } buf = qemu_uuid_unparse_strdup(&qemu_uuid); @@ -1295,7 +1312,7 @@ static void *spapr_build_fdt(sPAPRMachineState *spapr) QLIST_FOREACH(phb, &spapr->phbs, list) { ret = spapr_populate_pci_dt(phb, PHANDLE_INTC, fdt, - spapr->irq->nr_msis); + spapr->irq->nr_msis, NULL); if (ret < 0) { error_report("couldn't setup PCI devices in fdt"); exit(1); @@ -1348,6 +1365,14 @@ static void *spapr_build_fdt(sPAPRMachineState *spapr) exit(1); } + if (smc->dr_phb_enabled) { + ret = spapr_drc_populate_dt(fdt, 0, NULL, SPAPR_DR_CONNECTOR_TYPE_PHB); + if (ret < 0) { + error_report("Couldn't set up PHB DR device tree properties"); + exit(1); + } + } + return fdt; } @@ -1372,11 +1397,44 @@ static void emulate_spapr_hypercall(PPCVirtualHypervisor *vhyp, } } -static uint64_t spapr_get_patbe(PPCVirtualHypervisor *vhyp) +struct LPCRSyncState { + target_ulong value; + target_ulong mask; +}; + +static void do_lpcr_sync(CPUState *cs, run_on_cpu_data arg) +{ + struct LPCRSyncState *s = arg.host_ptr; + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + target_ulong lpcr; + + cpu_synchronize_state(cs); + lpcr = env->spr[SPR_LPCR]; + lpcr &= ~s->mask; + lpcr |= s->value; + ppc_store_lpcr(cpu, lpcr); +} + +void spapr_set_all_lpcrs(target_ulong value, target_ulong mask) +{ + CPUState *cs; + struct LPCRSyncState s = { + .value = value, + .mask = mask + }; + CPU_FOREACH(cs) { + run_on_cpu(cs, do_lpcr_sync, RUN_ON_CPU_HOST_PTR(&s)); + } +} + +static void spapr_get_pate(PPCVirtualHypervisor *vhyp, ppc_v3_pate_t *entry) { sPAPRMachineState *spapr = SPAPR_MACHINE(vhyp); - return spapr->patb_entry; + /* Copy PATE1:GR into PATE0:HR */ + entry->dw0 = spapr->patb_entry & PATE0_HR; + entry->dw1 = spapr->patb_entry; } #define HPTE(_table, _i) (void *)(((uint64_t *)(_table)) + ((_i) * 2)) @@ -1476,8 +1534,25 @@ static void spapr_store_hpte(PPCVirtualHypervisor *vhyp, hwaddr ptex, if (!spapr->htab) { kvmppc_write_hpte(ptex, pte0, pte1); } else { - stq_p(spapr->htab + offset, pte0); - stq_p(spapr->htab + offset + HASH_PTE_SIZE_64 / 2, pte1); + if (pte0 & HPTE64_V_VALID) { + stq_p(spapr->htab + offset + HASH_PTE_SIZE_64 / 2, pte1); + /* + * When setting valid, we write PTE1 first. This ensures + * proper synchronization with the reading code in + * ppc_hash64_pteg_search() + */ + smp_wmb(); + stq_p(spapr->htab + offset, pte0); + } else { + stq_p(spapr->htab + offset, pte0); + /* + * When clearing it we set PTE0 first. This ensures proper + * synchronization with the reading code in + * ppc_hash64_pteg_search() + */ + smp_wmb(); + stq_p(spapr->htab + offset + HASH_PTE_SIZE_64 / 2, pte1); + } } } @@ -1548,7 +1623,7 @@ void spapr_reallocate_hpt(sPAPRMachineState *spapr, int shift, } } /* We're setting up a hash table, so that means we're not radix */ - spapr->patb_entry = 0; + spapr_set_all_lpcrs(0, LPCR_HR | LPCR_UPRT); } void spapr_setup_hpt_and_vrma(sPAPRMachineState *spapr) @@ -1602,16 +1677,21 @@ static void spapr_machine_reset(void) if (kvm_enabled() && kvmppc_has_cap_mmu_radix() && ppc_type_check_compat(machine->cpu_type, CPU_POWERPC_LOGICAL_3_00, 0, spapr->max_compat_pvr)) { - /* If using KVM with radix mode available, VCPUs can be started + /* + * If using KVM with radix mode available, VCPUs can be started * without a HPT because KVM will start them in radix mode. - * Set the GR bit in PATB so that we know there is no HPT. */ - spapr->patb_entry = PATBE1_GR; + * Set the GR bit in PATE so that we know there is no HPT. + */ + spapr->patb_entry = PATE1_GR; + spapr_set_all_lpcrs(LPCR_HR | LPCR_UPRT, LPCR_HR | LPCR_UPRT); } else { spapr_setup_hpt_and_vrma(spapr); } - /* if this reset wasn't generated by CAS, we should reset our - * negotiated options and start from scratch */ + /* + * If this reset wasn't generated by CAS, we should reset our + * negotiated options and start from scratch + */ if (!spapr->cas_reboot) { spapr_ovec_cleanup(spapr->ov5_cas); spapr->ov5_cas = spapr_ovec_new(); @@ -1696,9 +1776,9 @@ static void spapr_create_nvram(sPAPRMachineState *spapr) static void spapr_rtc_create(sPAPRMachineState *spapr) { - object_initialize(&spapr->rtc, sizeof(spapr->rtc), TYPE_SPAPR_RTC); - object_property_add_child(OBJECT(spapr), "rtc", OBJECT(&spapr->rtc), - &error_fatal); + object_initialize_child(OBJECT(spapr), "rtc", + &spapr->rtc, sizeof(spapr->rtc), TYPE_SPAPR_RTC, + &error_fatal, NULL); object_property_set_bool(OBJECT(&spapr->rtc), true, "realized", &error_fatal); object_property_add_alias(OBJECT(spapr), "rtc-time", OBJECT(&spapr->rtc), @@ -1761,9 +1841,16 @@ static int spapr_post_load(void *opaque, int version_id) if (kvm_enabled() && spapr->patb_entry) { PowerPCCPU *cpu = POWERPC_CPU(first_cpu); - bool radix = !!(spapr->patb_entry & PATBE1_GR); + bool radix = !!(spapr->patb_entry & PATE1_GR); bool gtse = !!(cpu->env.spr[SPR_LPCR] & LPCR_GTSE); + /* + * Update LPCR:HR and UPRT as they may not be set properly in + * the stream + */ + spapr_set_all_lpcrs(radix ? (LPCR_HR | LPCR_UPRT) : 0, + LPCR_HR | LPCR_UPRT); + err = kvmppc_configure_v3_mmu(cpu, radix, gtse, spapr->patb_entry); if (err) { error_report("Process table config unsupported by the host"); @@ -2796,6 +2883,19 @@ static void spapr_machine_init(MachineState *machine) /* We always have at least the nvram device on VIO */ spapr_create_nvram(spapr); + /* + * Setup hotplug / dynamic-reconfiguration connectors. top-level + * connectors (described in root DT node's "ibm,drc-types" property) + * are pre-initialized here. additional child connectors (such as + * connectors for a PHBs PCI slots) are added as needed during their + * parent's realization. + */ + if (smc->dr_phb_enabled) { + for (i = 0; i < SPAPR_MAX_PHBS; i++) { + spapr_dr_connector_new(OBJECT(machine), TYPE_SPAPR_DRC_PHB, i); + } + } + /* Set up PCI */ spapr_pci_rtas_init(); @@ -2909,6 +3009,9 @@ static void spapr_machine_init(MachineState *machine) register_savevm_live(NULL, "spapr/htab", -1, 1, &savevm_htab_handlers, spapr); + qbus_set_hotplug_handler(sysbus_get_default(), OBJECT(machine), + &error_fatal); + qemu_register_boot_set(spapr_boot_set, spapr); if (kvm_enabled()) { @@ -3144,6 +3247,36 @@ static void spapr_set_ic_mode(Object *obj, const char *value, Error **errp) } } +static char *spapr_get_host_model(Object *obj, Error **errp) +{ + sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + + return g_strdup(spapr->host_model); +} + +static void spapr_set_host_model(Object *obj, const char *value, Error **errp) +{ + sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + + g_free(spapr->host_model); + spapr->host_model = g_strdup(value); +} + +static char *spapr_get_host_serial(Object *obj, Error **errp) +{ + sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + + return g_strdup(spapr->host_serial); +} + +static void spapr_set_host_serial(Object *obj, const char *value, Error **errp) +{ + sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + + g_free(spapr->host_serial); + spapr->host_serial = g_strdup(value); +} + static void spapr_instance_init(Object *obj) { sPAPRMachineState *spapr = SPAPR_MACHINE(obj); @@ -3189,6 +3322,17 @@ static void spapr_instance_init(Object *obj) object_property_set_description(obj, "ic-mode", "Specifies the interrupt controller mode (xics, xive, dual)", NULL); + + object_property_add_str(obj, "host-model", + spapr_get_host_model, spapr_set_host_model, + &error_abort); + object_property_set_description(obj, "host-model", + "Set host's model-id to use - none|passthrough|string", &error_abort); + object_property_add_str(obj, "host-serial", + spapr_get_host_serial, spapr_set_host_serial, + &error_abort); + object_property_set_description(obj, "host-serial", + "Set host's system-id to use - none|passthrough|string", &error_abort); } static void spapr_machine_finalizefn(Object *obj) @@ -3213,14 +3357,26 @@ static void spapr_nmi(NMIState *n, int cpu_index, Error **errp) } } +int spapr_lmb_dt_populate(sPAPRDRConnector *drc, sPAPRMachineState *spapr, + void *fdt, int *fdt_start_offset, Error **errp) +{ + uint64_t addr; + uint32_t node; + + addr = spapr_drc_index(drc) * SPAPR_MEMORY_BLOCK_SIZE; + node = object_property_get_uint(OBJECT(drc->dev), PC_DIMM_NODE_PROP, + &error_abort); + *fdt_start_offset = spapr_populate_memory_node(fdt, node, addr, + SPAPR_MEMORY_BLOCK_SIZE); + return 0; +} + static void spapr_add_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size, - uint32_t node, bool dedicated_hp_event_source, - Error **errp) + bool dedicated_hp_event_source, Error **errp) { sPAPRDRConnector *drc; uint32_t nr_lmbs = size/SPAPR_MEMORY_BLOCK_SIZE; - int i, fdt_offset, fdt_size; - void *fdt; + int i; uint64_t addr = addr_start; bool hotplugged = spapr_drc_hotplugged(dev); Error *local_err = NULL; @@ -3230,11 +3386,7 @@ static void spapr_add_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size, addr / SPAPR_MEMORY_BLOCK_SIZE); g_assert(drc); - fdt = create_device_tree(&fdt_size); - fdt_offset = spapr_populate_memory_node(fdt, node, addr, - SPAPR_MEMORY_BLOCK_SIZE); - - spapr_drc_attach(drc, dev, fdt, fdt_offset, &local_err); + spapr_drc_attach(drc, dev, &local_err); if (local_err) { while (addr > addr_start) { addr -= SPAPR_MEMORY_BLOCK_SIZE; @@ -3242,7 +3394,6 @@ static void spapr_add_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size, addr / SPAPR_MEMORY_BLOCK_SIZE); spapr_drc_detach(drc); } - g_free(fdt); error_propagate(errp, local_err); return; } @@ -3275,7 +3426,6 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, sPAPRMachineState *ms = SPAPR_MACHINE(hotplug_dev); PCDIMMDevice *dimm = PC_DIMM(dev); uint64_t size, addr; - uint32_t node; size = memory_device_get_region_size(MEMORY_DEVICE(dev), &error_abort); @@ -3290,10 +3440,7 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, goto out_unplug; } - node = object_property_get_uint(OBJECT(dev), PC_DIMM_NODE_PROP, - &error_abort); - spapr_add_lmbs(dev, addr, size, node, - spapr_ovec_test(ms->ov5_cas, OV5_HP_EVT), + spapr_add_lmbs(dev, addr, size, spapr_ovec_test(ms->ov5_cas, OV5_HP_EVT), &local_err); if (local_err) { goto out_unplug; @@ -3513,27 +3660,6 @@ out: error_propagate(errp, local_err); } -static void *spapr_populate_hotplug_cpu_dt(CPUState *cs, int *fdt_offset, - sPAPRMachineState *spapr) -{ - PowerPCCPU *cpu = POWERPC_CPU(cs); - DeviceClass *dc = DEVICE_GET_CLASS(cs); - int id = spapr_get_vcpu_id(cpu); - void *fdt; - int offset, fdt_size; - char *nodename; - - fdt = create_device_tree(&fdt_size); - nodename = g_strdup_printf("%s@%x", dc->fw_name, id); - offset = fdt_add_subnode(fdt, 0, nodename); - - spapr_populate_cpu_dt(cs, fdt, offset, spapr); - g_free(nodename); - - *fdt_offset = offset; - return fdt; -} - /* Callback to be called during DRC release. */ void spapr_core_release(DeviceState *dev) { @@ -3594,6 +3720,27 @@ void spapr_core_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev, spapr_hotplug_req_remove_by_index(drc); } +int spapr_core_dt_populate(sPAPRDRConnector *drc, sPAPRMachineState *spapr, + void *fdt, int *fdt_start_offset, Error **errp) +{ + sPAPRCPUCore *core = SPAPR_CPU_CORE(drc->dev); + CPUState *cs = CPU(core->threads[0]); + PowerPCCPU *cpu = POWERPC_CPU(cs); + DeviceClass *dc = DEVICE_GET_CLASS(cs); + int id = spapr_get_vcpu_id(cpu); + char *nodename; + int offset; + + nodename = g_strdup_printf("%s@%x", dc->fw_name, id); + offset = fdt_add_subnode(fdt, 0, nodename); + g_free(nodename); + + spapr_populate_cpu_dt(cs, fdt, offset, spapr); + + *fdt_start_offset = offset; + return 0; +} + static void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { @@ -3602,7 +3749,7 @@ static void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev, sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc); sPAPRCPUCore *core = SPAPR_CPU_CORE(OBJECT(dev)); CPUCore *cc = CPU_CORE(dev); - CPUState *cs = CPU(core->threads[0]); + CPUState *cs; sPAPRDRConnector *drc; Error *local_err = NULL; CPUArchId *core_slot; @@ -3621,14 +3768,8 @@ static void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev, g_assert(drc || !mc->has_hotpluggable_cpus); if (drc) { - void *fdt; - int fdt_offset; - - fdt = spapr_populate_hotplug_cpu_dt(cs, &fdt_offset, spapr); - - spapr_drc_attach(drc, dev, fdt, fdt_offset, &local_err); + spapr_drc_attach(drc, dev, &local_err); if (local_err) { - g_free(fdt); error_propagate(errp, local_err); return; } @@ -3712,6 +3853,115 @@ out: error_propagate(errp, local_err); } +int spapr_phb_dt_populate(sPAPRDRConnector *drc, sPAPRMachineState *spapr, + void *fdt, int *fdt_start_offset, Error **errp) +{ + sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(drc->dev); + int intc_phandle; + + intc_phandle = spapr_irq_get_phandle(spapr, spapr->fdt_blob, errp); + if (intc_phandle <= 0) { + return -1; + } + + if (spapr_populate_pci_dt(sphb, intc_phandle, fdt, spapr->irq->nr_msis, + fdt_start_offset)) { + error_setg(errp, "unable to create FDT node for PHB %d", sphb->index); + return -1; + } + + /* generally SLOF creates these, for hotplug it's up to QEMU */ + _FDT(fdt_setprop_string(fdt, *fdt_start_offset, "name", "pci")); + + return 0; +} + +static void spapr_phb_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) +{ + sPAPRMachineState *spapr = SPAPR_MACHINE(OBJECT(hotplug_dev)); + sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(dev); + sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); + const unsigned windows_supported = spapr_phb_windows_supported(sphb); + + if (dev->hotplugged && !smc->dr_phb_enabled) { + error_setg(errp, "PHB hotplug not supported for this machine"); + return; + } + + if (sphb->index == (uint32_t)-1) { + error_setg(errp, "\"index\" for PAPR PHB is mandatory"); + return; + } + + /* + * This will check that sphb->index doesn't exceed the maximum number of + * PHBs for the current machine type. + */ + smc->phb_placement(spapr, sphb->index, + &sphb->buid, &sphb->io_win_addr, + &sphb->mem_win_addr, &sphb->mem64_win_addr, + windows_supported, sphb->dma_liobn, errp); +} + +static void spapr_phb_plug(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) +{ + sPAPRMachineState *spapr = SPAPR_MACHINE(OBJECT(hotplug_dev)); + sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); + sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(dev); + sPAPRDRConnector *drc; + bool hotplugged = spapr_drc_hotplugged(dev); + Error *local_err = NULL; + + if (!smc->dr_phb_enabled) { + return; + } + + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_PHB, sphb->index); + /* hotplug hooks should check it's enabled before getting this far */ + assert(drc); + + spapr_drc_attach(drc, DEVICE(dev), &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + if (hotplugged) { + spapr_hotplug_req_add_by_index(drc); + } else { + spapr_drc_reset(drc); + } +} + +void spapr_phb_release(DeviceState *dev) +{ + HotplugHandler *hotplug_ctrl = qdev_get_hotplug_handler(dev); + + hotplug_handler_unplug(hotplug_ctrl, dev, &error_abort); +} + +static void spapr_phb_unplug(HotplugHandler *hotplug_dev, DeviceState *dev) +{ + object_unparent(OBJECT(dev)); +} + +static void spapr_phb_unplug_request(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(dev); + sPAPRDRConnector *drc; + + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_PHB, sphb->index); + assert(drc); + + if (!spapr_drc_unplug_requested(drc)) { + spapr_drc_detach(drc); + spapr_hotplug_req_remove_by_index(drc); + } +} + static void spapr_machine_device_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { @@ -3719,6 +3969,8 @@ static void spapr_machine_device_plug(HotplugHandler *hotplug_dev, spapr_memory_plug(hotplug_dev, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) { spapr_core_plug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_PCI_HOST_BRIDGE)) { + spapr_phb_plug(hotplug_dev, dev, errp); } } @@ -3729,6 +3981,8 @@ static void spapr_machine_device_unplug(HotplugHandler *hotplug_dev, spapr_memory_unplug(hotplug_dev, dev); } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) { spapr_core_unplug(hotplug_dev, dev); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_PCI_HOST_BRIDGE)) { + spapr_phb_unplug(hotplug_dev, dev); } } @@ -3737,6 +3991,7 @@ static void spapr_machine_device_unplug_request(HotplugHandler *hotplug_dev, { sPAPRMachineState *sms = SPAPR_MACHINE(OBJECT(hotplug_dev)); MachineClass *mc = MACHINE_GET_CLASS(sms); + sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc); if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { if (spapr_ovec_test(sms->ov5_cas, OV5_HP_EVT)) { @@ -3756,6 +4011,12 @@ static void spapr_machine_device_unplug_request(HotplugHandler *hotplug_dev, return; } spapr_core_unplug_request(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_PCI_HOST_BRIDGE)) { + if (!smc->dr_phb_enabled) { + error_setg(errp, "PHB hot unplug not supported on this machine"); + return; + } + spapr_phb_unplug_request(hotplug_dev, dev, errp); } } @@ -3766,6 +4027,8 @@ static void spapr_machine_device_pre_plug(HotplugHandler *hotplug_dev, spapr_memory_pre_plug(hotplug_dev, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) { spapr_core_pre_plug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_PCI_HOST_BRIDGE)) { + spapr_phb_pre_plug(hotplug_dev, dev, errp); } } @@ -3773,7 +4036,8 @@ static HotplugHandler *spapr_get_hotplug_handler(MachineState *machine, DeviceState *dev) { if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) || - object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) { + object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE) || + object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_PCI_HOST_BRIDGE)) { return HOTPLUG_HANDLER(machine); } return NULL; @@ -4004,7 +4268,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) vhc->map_hptes = spapr_map_hptes; vhc->unmap_hptes = spapr_unmap_hptes; vhc->store_hpte = spapr_store_hpte; - vhc->get_patbe = spapr_get_patbe; + vhc->get_pate = spapr_get_pate; vhc->encode_hpt_for_kvm_pr = spapr_encode_hpt_for_kvm_pr; xic->ics_get = spapr_ics_get; xic->ics_resend = spapr_ics_resend; @@ -4026,6 +4290,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) smc->default_caps.caps[SPAPR_CAP_NESTED_KVM_HV] = SPAPR_CAP_OFF; spapr_caps_add_properties(smc, &error_abort); smc->irq = &spapr_irq_xics; + smc->dr_phb_enabled = true; } static const TypeInfo spapr_machine_info = { @@ -4086,11 +4351,18 @@ DEFINE_SPAPR_MACHINE(4_0, "4.0", true); static void spapr_machine_3_1_class_options(MachineClass *mc) { sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc); + static GlobalProperty compat[] = { + { TYPE_SPAPR_MACHINE, "host-model", "passthrough" }, + { TYPE_SPAPR_MACHINE, "host-serial", "passthrough" }, + }; spapr_machine_4_0_class_options(mc); compat_props_add(mc->compat_props, hw_compat_3_1, hw_compat_3_1_len); + compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); + mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power8_v2.0"); smc->update_dt_enabled = false; + smc->dr_phb_enabled = false; } DEFINE_SPAPR_MACHINE(3_1, "3.1", false); diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c index 2edb7d1e9c..2943cf47d4 100644 --- a/hw/ppc/spapr_drc.c +++ b/hw/ppc/spapr_drc.c @@ -22,6 +22,7 @@ #include "qemu/error-report.h" #include "hw/ppc/spapr.h" /* for RTAS return codes */ #include "hw/pci-host/spapr.h" /* spapr_phb_remove_pci_device_cb callback */ +#include "sysemu/device_tree.h" #include "trace.h" #define DRC_CONTAINER_PATH "/dr-connector" @@ -373,8 +374,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name, } while (fdt_depth != 0); } -void spapr_drc_attach(sPAPRDRConnector *drc, DeviceState *d, void *fdt, - int fdt_start_offset, Error **errp) +void spapr_drc_attach(sPAPRDRConnector *drc, DeviceState *d, Error **errp) { trace_spapr_drc_attach(spapr_drc_index(drc)); @@ -384,11 +384,8 @@ void spapr_drc_attach(sPAPRDRConnector *drc, DeviceState *d, void *fdt, } g_assert((drc->state == SPAPR_DRC_STATE_LOGICAL_UNUSABLE) || (drc->state == SPAPR_DRC_STATE_PHYSICAL_POWERON)); - g_assert(fdt); drc->dev = d; - drc->fdt = fdt; - drc->fdt_start_offset = fdt_start_offset; object_property_add_link(OBJECT(drc), "device", object_get_typename(OBJECT(drc->dev)), @@ -674,6 +671,7 @@ static void spapr_drc_cpu_class_init(ObjectClass *k, void *data) drck->typename = "CPU"; drck->drc_name_prefix = "CPU "; drck->release = spapr_core_release; + drck->dt_populate = spapr_core_dt_populate; } static void spapr_drc_pci_class_init(ObjectClass *k, void *data) @@ -684,6 +682,7 @@ static void spapr_drc_pci_class_init(ObjectClass *k, void *data) drck->typename = "28"; drck->drc_name_prefix = "C"; drck->release = spapr_phb_remove_pci_device_cb; + drck->dt_populate = spapr_pci_dt_populate; } static void spapr_drc_lmb_class_init(ObjectClass *k, void *data) @@ -694,6 +693,18 @@ static void spapr_drc_lmb_class_init(ObjectClass *k, void *data) drck->typename = "MEM"; drck->drc_name_prefix = "LMB "; drck->release = spapr_lmb_release; + drck->dt_populate = spapr_lmb_dt_populate; +} + +static void spapr_drc_phb_class_init(ObjectClass *k, void *data) +{ + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); + + drck->typeshift = SPAPR_DR_CONNECTOR_TYPE_SHIFT_PHB; + drck->typename = "PHB"; + drck->drc_name_prefix = "PHB "; + drck->release = spapr_phb_release; + drck->dt_populate = spapr_phb_dt_populate; } static const TypeInfo spapr_dr_connector_info = { @@ -739,6 +750,13 @@ static const TypeInfo spapr_drc_lmb_info = { .class_init = spapr_drc_lmb_class_init, }; +static const TypeInfo spapr_drc_phb_info = { + .name = TYPE_SPAPR_DRC_PHB, + .parent = TYPE_SPAPR_DRC_LOGICAL, + .instance_size = sizeof(sPAPRDRConnector), + .class_init = spapr_drc_phb_class_init, +}; + /* helper functions for external users */ sPAPRDRConnector *spapr_drc_by_index(uint32_t index) @@ -1102,10 +1120,28 @@ static void rtas_ibm_configure_connector(PowerPCCPU *cpu, goto out; } - g_assert(drc->fdt); - drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + if (!drc->fdt) { + Error *local_err = NULL; + void *fdt; + int fdt_size; + + fdt = create_device_tree(&fdt_size); + + if (drck->dt_populate(drc, spapr, fdt, &drc->fdt_start_offset, + &local_err)) { + g_free(fdt); + error_free(local_err); + rc = SPAPR_DR_CC_RESPONSE_ERROR; + goto out; + } + + drc->fdt = fdt; + drc->ccs_offset = drc->fdt_start_offset; + drc->ccs_depth = 0; + } + do { uint32_t tag; const char *name; @@ -1189,6 +1225,7 @@ static void spapr_drc_register_types(void) type_register_static(&spapr_drc_cpu_info); type_register_static(&spapr_drc_pci_info); type_register_static(&spapr_drc_lmb_info); + type_register_static(&spapr_drc_phb_info); spapr_rtas_register(RTAS_SET_INDICATOR, "set-indicator", rtas_set_indicator); diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c index b9c7ecb9e9..ab9a1f0063 100644 --- a/hw/ppc/spapr_events.c +++ b/hw/ppc/spapr_events.c @@ -526,6 +526,9 @@ static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action, case SPAPR_DR_CONNECTOR_TYPE_CPU: hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_CPU; break; + case SPAPR_DR_CONNECTOR_TYPE_PHB: + hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_PHB; + break; default: /* we shouldn't be signaling hotplug events for resources * that don't support them diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 17bcaa3822..476bad6271 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -17,37 +17,6 @@ #include "mmu-book3s-v3.h" #include "hw/mem/memory-device.h" -struct LPCRSyncState { - target_ulong value; - target_ulong mask; -}; - -static void do_lpcr_sync(CPUState *cs, run_on_cpu_data arg) -{ - struct LPCRSyncState *s = arg.host_ptr; - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; - target_ulong lpcr; - - cpu_synchronize_state(cs); - lpcr = env->spr[SPR_LPCR]; - lpcr &= ~s->mask; - lpcr |= s->value; - ppc_store_lpcr(cpu, lpcr); -} - -static void set_all_lpcrs(target_ulong value, target_ulong mask) -{ - CPUState *cs; - struct LPCRSyncState s = { - .value = value, - .mask = mask - }; - CPU_FOREACH(cs) { - run_on_cpu(cs, do_lpcr_sync, RUN_ON_CPU_HOST_PTR(&s)); - } -} - static bool has_spr(PowerPCCPU *cpu, int spr) { /* We can test whether the SPR is defined by checking for a valid name */ @@ -1255,12 +1224,12 @@ static target_ulong h_set_mode_resource_le(PowerPCCPU *cpu, switch (mflags) { case H_SET_MODE_ENDIAN_BIG: - set_all_lpcrs(0, LPCR_ILE); + spapr_set_all_lpcrs(0, LPCR_ILE); spapr_pci_switch_vga(true); return H_SUCCESS; case H_SET_MODE_ENDIAN_LITTLE: - set_all_lpcrs(LPCR_ILE, LPCR_ILE); + spapr_set_all_lpcrs(LPCR_ILE, LPCR_ILE); spapr_pci_switch_vga(false); return H_SUCCESS; } @@ -1289,7 +1258,7 @@ static target_ulong h_set_mode_resource_addr_trans_mode(PowerPCCPU *cpu, return H_UNSUPPORTED_FLAG; } - set_all_lpcrs(mflags << LPCR_AIL_SHIFT, LPCR_AIL); + spapr_set_all_lpcrs(mflags << LPCR_AIL_SHIFT, LPCR_AIL); return H_SUCCESS; } @@ -1342,12 +1311,12 @@ static void spapr_check_setup_free_hpt(sPAPRMachineState *spapr, * later and so assumed radix and now it's called H_REG_PROC_TBL */ - if ((patbe_old & PATBE1_GR) == (patbe_new & PATBE1_GR)) { + if ((patbe_old & PATE1_GR) == (patbe_new & PATE1_GR)) { /* We assume RADIX, so this catches all the "Do Nothing" cases */ - } else if (!(patbe_old & PATBE1_GR)) { + } else if (!(patbe_old & PATE1_GR)) { /* HASH->RADIX : Free HPT */ spapr_free_hpt(spapr); - } else if (!(patbe_new & PATBE1_GR)) { + } else if (!(patbe_new & PATE1_GR)) { /* RADIX->HASH || NOTHING->HASH : Allocate HPT */ spapr_setup_hpt_and_vrma(spapr); } @@ -1385,7 +1354,7 @@ static target_ulong h_register_process_table(PowerPCCPU *cpu, } else if (table_size > 24) { return H_P4; } - cproc = PATBE1_GR | proc_tbl | table_size; + cproc = PATE1_GR | proc_tbl | table_size; } else { /* Register new HPT process table */ if (flags & FLAG_HASH_PROC_TBL) { /* Hash with Segment Tables */ /* TODO - Not Supported */ @@ -1404,13 +1373,15 @@ static target_ulong h_register_process_table(PowerPCCPU *cpu, } } else { /* Deregister current process table */ - /* Set to benign value: (current GR) | 0. This allows - * deregistration in KVM to succeed even if the radix bit in flags - * doesn't match the radix bit in the old PATB. */ - cproc = spapr->patb_entry & PATBE1_GR; + /* + * Set to benign value: (current GR) | 0. This allows + * deregistration in KVM to succeed even if the radix bit + * in flags doesn't match the radix bit in the old PATE. + */ + cproc = spapr->patb_entry & PATE1_GR; } } else { /* Maintain current registration */ - if (!(flags & FLAG_RADIX) != !(spapr->patb_entry & PATBE1_GR)) { + if (!(flags & FLAG_RADIX) != !(spapr->patb_entry & PATE1_GR)) { /* Technically caused by flag bits => H_PARAMETER */ return H_PARAMETER; /* Existing Process Table Mismatch */ } @@ -1422,10 +1393,11 @@ static target_ulong h_register_process_table(PowerPCCPU *cpu, spapr->patb_entry = cproc; /* Save new process table */ - /* Update the UPRT and GTSE bits in the LPCR for all cpus */ - set_all_lpcrs(((flags & (FLAG_RADIX | FLAG_HASH_PROC_TBL)) ? LPCR_UPRT : 0) | - ((flags & FLAG_GTSE) ? LPCR_GTSE : 0), - LPCR_UPRT | LPCR_GTSE); + /* Update the UPRT, HR and GTSE bits in the LPCR for all cpus */ + spapr_set_all_lpcrs(((flags & (FLAG_RADIX | FLAG_HASH_PROC_TBL)) ? + (LPCR_UPRT | LPCR_HR) : 0) | + ((flags & FLAG_GTSE) ? LPCR_GTSE : 0), + LPCR_UPRT | LPCR_HR | LPCR_GTSE); if (kvm_enabled()) { return kvmppc_configure_v3_mmu(cpu, flags & FLAG_RADIX, @@ -1646,7 +1618,7 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, if (!spapr->cas_reboot) { /* If spapr_machine_reset() did not set up a HPT but one is necessary * (because the guest isn't going to use radix) then set it up here. */ - if ((spapr->patb_entry & PATBE1_GR) && !guest_radix) { + if ((spapr->patb_entry & PATE1_GR) && !guest_radix) { /* legacy hash or new hash: */ spapr_setup_hpt_and_vrma(spapr); } diff --git a/hw/ppc/spapr_irq.c b/hw/ppc/spapr_irq.c index 4297eed600..4145079d7f 100644 --- a/hw/ppc/spapr_irq.c +++ b/hw/ppc/spapr_irq.c @@ -230,6 +230,11 @@ static void spapr_irq_reset_xics(sPAPRMachineState *spapr, Error **errp) /* TODO: create the KVM XICS device */ } +static const char *spapr_irq_get_nodename_xics(sPAPRMachineState *spapr) +{ + return XICS_NODENAME; +} + #define SPAPR_IRQ_XICS_NR_IRQS 0x1000 #define SPAPR_IRQ_XICS_NR_MSIS \ (XICS_IRQ_BASE + SPAPR_IRQ_XICS_NR_IRQS - SPAPR_IRQ_MSI) @@ -249,6 +254,7 @@ sPAPRIrq spapr_irq_xics = { .post_load = spapr_irq_post_load_xics, .reset = spapr_irq_reset_xics, .set_irq = spapr_irq_set_irq_xics, + .get_nodename = spapr_irq_get_nodename_xics, }; /* @@ -384,6 +390,11 @@ static void spapr_irq_set_irq_xive(void *opaque, int srcno, int val) xive_source_set_irq(&spapr->xive->source, srcno, val); } +static const char *spapr_irq_get_nodename_xive(sPAPRMachineState *spapr) +{ + return spapr->xive->nodename; +} + /* * XIVE uses the full IRQ number space. Set it to 8K to be compatible * with XICS. @@ -407,6 +418,7 @@ sPAPRIrq spapr_irq_xive = { .post_load = spapr_irq_post_load_xive, .reset = spapr_irq_reset_xive, .set_irq = spapr_irq_set_irq_xive, + .get_nodename = spapr_irq_get_nodename_xive, }; /* @@ -541,6 +553,11 @@ static void spapr_irq_set_irq_dual(void *opaque, int srcno, int val) spapr_irq_current(spapr)->set_irq(spapr, srcno, val); } +static const char *spapr_irq_get_nodename_dual(sPAPRMachineState *spapr) +{ + return spapr_irq_current(spapr)->get_nodename(spapr); +} + /* * Define values in sync with the XIVE and XICS backend */ @@ -561,7 +578,8 @@ sPAPRIrq spapr_irq_dual = { .cpu_intc_create = spapr_irq_cpu_intc_create_dual, .post_load = spapr_irq_post_load_dual, .reset = spapr_irq_reset_dual, - .set_irq = spapr_irq_set_irq_dual + .set_irq = spapr_irq_set_irq_dual, + .get_nodename = spapr_irq_get_nodename_dual, }; /* @@ -620,6 +638,27 @@ void spapr_irq_reset(sPAPRMachineState *spapr, Error **errp) } } +int spapr_irq_get_phandle(sPAPRMachineState *spapr, void *fdt, Error **errp) +{ + const char *nodename = spapr->irq->get_nodename(spapr); + int offset, phandle; + + offset = fdt_subnode_offset(fdt, 0, nodename); + if (offset < 0) { + error_setg(errp, "Can't find node \"%s\": %s", nodename, + fdt_strerror(offset)); + return -1; + } + + phandle = fdt_get_phandle(fdt, offset); + if (!phandle) { + error_setg(errp, "Can't get phandle of node \"%s\"", nodename); + return -1; + } + + return phandle; +} + /* * XICS legacy routines - to deprecate one day */ @@ -691,4 +730,5 @@ sPAPRIrq spapr_irq_xics_legacy = { .cpu_intc_create = spapr_irq_cpu_intc_create_xics, .post_load = spapr_irq_post_load_xics, .set_irq = spapr_irq_set_irq_xics, + .get_nodename = spapr_irq_get_nodename_xics, }; diff --git a/hw/ppc/spapr_ovec.c b/hw/ppc/spapr_ovec.c index 318bf33de4..12510b236a 100644 --- a/hw/ppc/spapr_ovec.c +++ b/hw/ppc/spapr_ovec.c @@ -16,6 +16,7 @@ #include "qemu/bitmap.h" #include "exec/address-spaces.h" #include "qemu/error-report.h" +#include "sysemu/qtest.h" #include "trace.h" #include <libfdt.h> @@ -131,6 +132,11 @@ bool spapr_ovec_test(sPAPROptionVector *ov, long bitnr) g_assert(ov); g_assert(bitnr < OV_MAXBITS); + /* support memory unplug for qtest */ + if (qtest_enabled() && bitnr == OV5_HP_EVT) { + return true; + } + return test_bit(bitnr, ov->bitmap) ? true : false; } diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 60777b2355..06a5ffd281 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -1408,6 +1408,17 @@ static uint32_t spapr_phb_get_pci_drc_index(sPAPRPHBState *phb, return spapr_drc_index(drc); } +int spapr_pci_dt_populate(sPAPRDRConnector *drc, sPAPRMachineState *spapr, + void *fdt, int *fdt_start_offset, Error **errp) +{ + HotplugHandler *plug_handler = qdev_get_hotplug_handler(drc->dev); + sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(plug_handler); + PCIDevice *pdev = PCI_DEVICE(drc->dev); + + *fdt_start_offset = spapr_create_pci_child_dt(sphb, pdev, fdt, 0); + return 0; +} + static void spapr_pci_plug(HotplugHandler *plug_handler, DeviceState *plugged_dev, Error **errp) { @@ -1417,8 +1428,6 @@ static void spapr_pci_plug(HotplugHandler *plug_handler, Error *local_err = NULL; PCIBus *bus = PCI_BUS(qdev_get_parent_bus(DEVICE(pdev))); uint32_t slotnr = PCI_SLOT(pdev->devfn); - void *fdt = NULL; - int fdt_start_offset, fdt_size; /* if DR is disabled we don't need to do anything in the case of * hotplug or coldplug callbacks @@ -1448,10 +1457,7 @@ static void spapr_pci_plug(HotplugHandler *plug_handler, goto out; } - fdt = create_device_tree(&fdt_size); - fdt_start_offset = spapr_create_pci_child_dt(phb, pdev, fdt, 0); - - spapr_drc_attach(drc, DEVICE(pdev), fdt, fdt_start_offset, &local_err); + spapr_drc_attach(drc, DEVICE(pdev), &local_err); if (local_err) { goto out; } @@ -1483,7 +1489,6 @@ static void spapr_pci_plug(HotplugHandler *plug_handler, out: if (local_err) { error_propagate(errp, local_err); - g_free(fdt); } } @@ -1565,6 +1570,75 @@ static void spapr_pci_unplug_request(HotplugHandler *plug_handler, } } +static void spapr_phb_finalizefn(Object *obj) +{ + sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(obj); + + g_free(sphb->dtbusname); + sphb->dtbusname = NULL; +} + +static void spapr_phb_unrealize(DeviceState *dev, Error **errp) +{ + sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); + SysBusDevice *s = SYS_BUS_DEVICE(dev); + PCIHostState *phb = PCI_HOST_BRIDGE(s); + sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(phb); + sPAPRTCETable *tcet; + int i; + const unsigned windows_supported = spapr_phb_windows_supported(sphb); + + if (sphb->msi) { + g_hash_table_unref(sphb->msi); + sphb->msi = NULL; + } + + /* + * Remove IO/MMIO subregions and aliases, rest should get cleaned + * via PHB's unrealize->object_finalize + */ + for (i = windows_supported - 1; i >= 0; i--) { + tcet = spapr_tce_find_by_liobn(sphb->dma_liobn[i]); + if (tcet) { + memory_region_del_subregion(&sphb->iommu_root, + spapr_tce_get_iommu(tcet)); + } + } + + if (sphb->dr_enabled) { + for (i = PCI_SLOT_MAX * 8 - 1; i >= 0; i--) { + sPAPRDRConnector *drc = spapr_drc_by_id(TYPE_SPAPR_DRC_PCI, + (sphb->index << 16) | i); + + if (drc) { + object_unparent(OBJECT(drc)); + } + } + } + + for (i = PCI_NUM_PINS - 1; i >= 0; i--) { + if (sphb->lsi_table[i].irq) { + spapr_irq_free(spapr, sphb->lsi_table[i].irq, 1); + sphb->lsi_table[i].irq = 0; + } + } + + QLIST_REMOVE(sphb, list); + + memory_region_del_subregion(&sphb->iommu_root, &sphb->msiwindow); + + address_space_destroy(&sphb->iommu_as); + + qbus_set_hotplug_handler(BUS(phb->bus), NULL, &error_abort); + pci_unregister_root_bus(phb->bus); + + memory_region_del_subregion(get_system_memory(), &sphb->iowindow); + if (sphb->mem64_win_pciaddr != (hwaddr)-1) { + memory_region_del_subregion(get_system_memory(), &sphb->mem64window); + } + memory_region_del_subregion(get_system_memory(), &sphb->mem32window); +} + static void spapr_phb_realize(DeviceState *dev, Error **errp) { /* We don't use SPAPR_MACHINE() in order to exit gracefully if the user @@ -1582,29 +1656,14 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) PCIBus *bus; uint64_t msi_window_size = 4096; sPAPRTCETable *tcet; - const unsigned windows_supported = - sphb->ddw_enabled ? SPAPR_PCI_DMA_MAX_WINDOWS : 1; + const unsigned windows_supported = spapr_phb_windows_supported(sphb); if (!spapr) { error_setg(errp, TYPE_SPAPR_PCI_HOST_BRIDGE " needs a pseries machine"); return; } - if (sphb->index != (uint32_t)-1) { - Error *local_err = NULL; - - smc->phb_placement(spapr, sphb->index, - &sphb->buid, &sphb->io_win_addr, - &sphb->mem_win_addr, &sphb->mem64_win_addr, - windows_supported, sphb->dma_liobn, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - } else { - error_setg(errp, "\"index\" for PAPR PHB is mandatory"); - return; - } + assert(sphb->index != (uint32_t)-1); /* checked in spapr_phb_pre_plug() */ if (sphb->mem64_win_size != 0) { if (sphb->mem_win_size > SPAPR_PCI_MEM32_WIN_SIZE) { @@ -1740,6 +1799,10 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) if (local_err) { error_propagate_prepend(errp, local_err, "can't allocate LSIs: "); + /* + * Older machines will never support PHB hotplug, ie, this is an + * init only path and QEMU will terminate. No need to rollback. + */ return; } } @@ -1747,7 +1810,7 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) spapr_irq_claim(spapr, irq, true, &local_err); if (local_err) { error_propagate_prepend(errp, local_err, "can't allocate LSIs: "); - return; + goto unrealize; } sphb->lsi_table[i].irq = irq; @@ -1767,13 +1830,17 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) if (!tcet) { error_setg(errp, "Creating window#%d failed for %s", i, sphb->dtbusname); - return; + goto unrealize; } memory_region_add_subregion(&sphb->iommu_root, 0, spapr_tce_get_iommu(tcet)); } sphb->msi = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free); + return; + +unrealize: + spapr_phb_unrealize(dev, NULL); } static int spapr_phb_children_reset(Object *child, void *opaque) @@ -1972,6 +2039,7 @@ static void spapr_phb_class_init(ObjectClass *klass, void *data) hc->root_bus_path = spapr_phb_root_bus_path; dc->realize = spapr_phb_realize; + dc->unrealize = spapr_phb_unrealize; dc->props = spapr_phb_properties; dc->reset = spapr_phb_reset; dc->vmsd = &vmstate_spapr_pci; @@ -1987,6 +2055,7 @@ static const TypeInfo spapr_phb_info = { .name = TYPE_SPAPR_PCI_HOST_BRIDGE, .parent = TYPE_PCI_HOST_BRIDGE, .instance_size = sizeof(sPAPRPHBState), + .instance_finalize = spapr_phb_finalizefn, .class_init = spapr_phb_class_init, .interfaces = (InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, @@ -2070,7 +2139,7 @@ static void spapr_phb_pci_enumerate(sPAPRPHBState *phb) } int spapr_populate_pci_dt(sPAPRPHBState *phb, uint32_t intc_phandle, void *fdt, - uint32_t nr_msis) + uint32_t nr_msis, int *node_offset) { int bus_off, i, j, ret; gchar *nodename; @@ -2120,11 +2189,15 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, uint32_t intc_phandle, void *fdt, sPAPRTCETable *tcet; PCIBus *bus = PCI_HOST_BRIDGE(phb)->bus; sPAPRFDT s_fdt; + sPAPRDRConnector *drc; /* Start populating the FDT */ nodename = g_strdup_printf("pci@%" PRIx64, phb->buid); _FDT(bus_off = fdt_add_subnode(fdt, 0, nodename)); g_free(nodename); + if (node_offset) { + *node_offset = bus_off; + } /* Write PHB properties */ _FDT(fdt_setprop_string(fdt, bus_off, "device_type", "pci")); @@ -2183,6 +2256,14 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, uint32_t intc_phandle, void *fdt, tcet->liobn, tcet->bus_offset, tcet->nb_table << tcet->page_shift); + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_PHB, phb->index); + if (drc) { + uint32_t drc_index = cpu_to_be32(spapr_drc_index(drc)); + + _FDT(fdt_setprop(fdt, bus_off, "ibm,my-drc-index", &drc_index, + sizeof(drc_index))); + } + /* Walk the bridges and program the bus numbers*/ spapr_phb_pci_enumerate(phb); _FDT(fdt_setprop_cell(fdt, bus_off, "qemu,phb-enumerated", 0x1)); diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index d6a0952154..7a2cb786a3 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -172,10 +172,10 @@ static void rtas_start_cpu(PowerPCCPU *callcpu, sPAPRMachineState *spapr, * New cpus are expected to start in the same radix/hash mode * as the existing CPUs */ - if (ppc64_radix_guest(callcpu)) { - lpcr |= LPCR_UPRT | LPCR_GTSE; + if (ppc64_v3_radix(callcpu)) { + lpcr |= LPCR_UPRT | LPCR_GTSE | LPCR_HR; } else { - lpcr &= ~(LPCR_UPRT | LPCR_GTSE); + lpcr &= ~(LPCR_UPRT | LPCR_GTSE | LPCR_HR); } } ppc_store_lpcr(newcpu, lpcr); diff --git a/hw/timer/ds1338.c b/hw/timer/ds1338.c index 3849b74a68..03da75486b 100644 --- a/hw/timer/ds1338.c +++ b/hw/timer/ds1338.c @@ -117,7 +117,7 @@ static int ds1338_event(I2CSlave *i2c, enum i2c_event event) return 0; } -static int ds1338_recv(I2CSlave *i2c) +static uint8_t ds1338_recv(I2CSlave *i2c) { DS1338State *s = DS1338(i2c); uint8_t res; diff --git a/hw/timer/m41t80.c b/hw/timer/m41t80.c index 734d7d95fc..c45b9297d8 100644 --- a/hw/timer/m41t80.c +++ b/hw/timer/m41t80.c @@ -40,7 +40,7 @@ static int m41t80_send(I2CSlave *i2c, uint8_t data) return 0; } -static int m41t80_recv(I2CSlave *i2c) +static uint8_t m41t80_recv(I2CSlave *i2c) { M41t80State *s = M41T80(i2c); struct tm now; diff --git a/hw/timer/pl031.c b/hw/timer/pl031.c index d3aacce80d..274ad47a33 100644 --- a/hw/timer/pl031.c +++ b/hw/timer/pl031.c @@ -12,20 +12,13 @@ */ #include "qemu/osdep.h" +#include "hw/timer/pl031.h" #include "hw/sysbus.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" #include "qemu/cutils.h" #include "qemu/log.h" - -//#define DEBUG_PL031 - -#ifdef DEBUG_PL031 -#define DPRINTF(fmt, ...) \ -do { printf("pl031: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#endif +#include "trace.h" #define RTC_DR 0x00 /* Data read register */ #define RTC_MR 0x04 /* Match register */ @@ -36,30 +29,6 @@ do { printf("pl031: " fmt , ## __VA_ARGS__); } while (0) #define RTC_MIS 0x18 /* Masked interrupt status register */ #define RTC_ICR 0x1c /* Interrupt clear register */ -#define TYPE_PL031 "pl031" -#define PL031(obj) OBJECT_CHECK(PL031State, (obj), TYPE_PL031) - -typedef struct PL031State { - SysBusDevice parent_obj; - - MemoryRegion iomem; - QEMUTimer *timer; - qemu_irq irq; - - /* Needed to preserve the tick_count across migration, even if the - * absolute value of the rtc_clock is different on the source and - * destination. - */ - uint32_t tick_offset_vmstate; - uint32_t tick_offset; - - uint32_t mr; - uint32_t lr; - uint32_t cr; - uint32_t im; - uint32_t is; -} PL031State; - static const unsigned char pl031_id[] = { 0x31, 0x10, 0x14, 0x00, /* Device ID */ 0x0d, 0xf0, 0x05, 0xb1 /* Cell ID */ @@ -67,7 +36,10 @@ static const unsigned char pl031_id[] = { static void pl031_update(PL031State *s) { - qemu_set_irq(s->irq, s->is & s->im); + uint32_t flags = s->is & s->im; + + trace_pl031_irq_state(flags); + qemu_set_irq(s->irq, flags); } static void pl031_interrupt(void * opaque) @@ -75,7 +47,7 @@ static void pl031_interrupt(void * opaque) PL031State *s = (PL031State *)opaque; s->is = 1; - DPRINTF("Alarm raised\n"); + trace_pl031_alarm_raised(); pl031_update(s); } @@ -92,7 +64,7 @@ static void pl031_set_alarm(PL031State *s) /* The timer wraps around. This subtraction also wraps in the same way, and gives correct results when alarm < now_ticks. */ ticks = s->mr - pl031_get_count(s); - DPRINTF("Alarm set in %ud ticks\n", ticks); + trace_pl031_set_alarm(ticks); if (ticks == 0) { timer_del(s->timer); pl031_interrupt(s); @@ -106,38 +78,49 @@ static uint64_t pl031_read(void *opaque, hwaddr offset, unsigned size) { PL031State *s = (PL031State *)opaque; - - if (offset >= 0xfe0 && offset < 0x1000) - return pl031_id[(offset - 0xfe0) >> 2]; + uint64_t r; switch (offset) { case RTC_DR: - return pl031_get_count(s); + r = pl031_get_count(s); + break; case RTC_MR: - return s->mr; + r = s->mr; + break; case RTC_IMSC: - return s->im; + r = s->im; + break; case RTC_RIS: - return s->is; + r = s->is; + break; case RTC_LR: - return s->lr; + r = s->lr; + break; case RTC_CR: /* RTC is permanently enabled. */ - return 1; + r = 1; + break; case RTC_MIS: - return s->is & s->im; + r = s->is & s->im; + break; + case 0xfe0 ... 0xfff: + r = pl031_id[(offset - 0xfe0) >> 2]; + break; case RTC_ICR: qemu_log_mask(LOG_GUEST_ERROR, "pl031: read of write-only register at offset 0x%x\n", (int)offset); + r = 0; break; default: qemu_log_mask(LOG_GUEST_ERROR, "pl031_read: Bad offset 0x%x\n", (int)offset); + r = 0; break; } - return 0; + trace_pl031_read(offset, r); + return r; } static void pl031_write(void * opaque, hwaddr offset, @@ -145,6 +128,7 @@ static void pl031_write(void * opaque, hwaddr offset, { PL031State *s = (PL031State *)opaque; + trace_pl031_write(offset, value); switch (offset) { case RTC_LR: @@ -157,7 +141,6 @@ static void pl031_write(void * opaque, hwaddr offset, break; case RTC_IMSC: s->im = value & 1; - DPRINTF("Interrupt mask %d\n", s->im); pl031_update(s); break; case RTC_ICR: @@ -165,7 +148,6 @@ static void pl031_write(void * opaque, hwaddr offset, cleared when bit 0 of the written value is set. However the arm926e documentation (DDI0287B) states that the interrupt is cleared when any value is written. */ - DPRINTF("Interrupt cleared"); s->is = 0; pl031_update(s); break; diff --git a/hw/timer/trace-events b/hw/timer/trace-events index 0144a68951..12eb505fee 100644 --- a/hw/timer/trace-events +++ b/hw/timer/trace-events @@ -77,3 +77,9 @@ xlnx_zynqmp_rtc_gettime(int year, int month, int day, int hour, int min, int sec nrf51_timer_read(uint64_t addr, uint32_t value, unsigned size) "read addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u" nrf51_timer_write(uint64_t addr, uint32_t value, unsigned size) "write addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u" +# hw/timer/pl031.c +pl031_irq_state(int level) "irq state %d" +pl031_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" +pl031_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" +pl031_alarm_raised(void) "alarm raised" +pl031_set_alarm(uint32_t ticks) "alarm set for %u ticks" diff --git a/hw/timer/twl92230.c b/hw/timer/twl92230.c index 51ec355f3f..c83d803dd8 100644 --- a/hw/timer/twl92230.c +++ b/hw/timer/twl92230.c @@ -737,7 +737,7 @@ static int menelaus_tx(I2CSlave *i2c, uint8_t data) return 0; } -static int menelaus_rx(I2CSlave *i2c) +static uint8_t menelaus_rx(I2CSlave *i2c) { MenelausState *s = TWL92230(i2c); diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c index f1d20fa1b9..4ee4fc5a89 100644 --- a/hw/usb/dev-mtp.c +++ b/hw/usb/dev-mtp.c @@ -11,17 +11,16 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "qemu/error-report.h" #include <wchar.h> #include <dirent.h> #include <sys/statvfs.h> -#ifdef CONFIG_INOTIFY1 -#include <sys/inotify.h> -#include "qemu/main-loop.h" -#endif + #include "qemu-common.h" #include "qemu/iov.h" +#include "qemu/filemonitor.h" #include "trace.h" #include "hw/usb.h" #include "desc.h" @@ -132,7 +131,6 @@ enum { EP_EVENT, }; -#ifdef CONFIG_INOTIFY1 typedef struct MTPMonEntry MTPMonEntry; struct MTPMonEntry { @@ -141,7 +139,6 @@ struct MTPMonEntry { QTAILQ_ENTRY(MTPMonEntry) next; }; -#endif struct MTPControl { uint16_t code; @@ -172,10 +169,8 @@ struct MTPObject { char *name; char *path; struct stat stat; -#ifdef CONFIG_INOTIFY1 - /* inotify watch cookie */ - int watchfd; -#endif + /* file monitor watch id */ + int watchid; MTPObject *parent; uint32_t nchildren; QLIST_HEAD(, MTPObject) children; @@ -198,11 +193,8 @@ struct MTPState { bool readonly; QTAILQ_HEAD(, MTPObject) objects; -#ifdef CONFIG_INOTIFY1 - /* inotify descriptor */ - int inotifyfd; + QFileMonitor *file_monitor; QTAILQ_HEAD(, MTPMonEntry) events; -#endif /* Responder is expecting a write operation */ bool write_pending; struct { @@ -383,7 +375,7 @@ static const USBDesc desc = { /* ----------------------------------------------------------------------- */ static MTPObject *usb_mtp_object_alloc(MTPState *s, uint32_t handle, - MTPObject *parent, char *name) + MTPObject *parent, const char *name) { MTPObject *o = g_new0(MTPObject, 1); @@ -391,6 +383,7 @@ static MTPObject *usb_mtp_object_alloc(MTPState *s, uint32_t handle, goto ignore; } + o->watchid = -1; o->handle = handle; o->parent = parent; o->name = g_strdup(name); @@ -437,6 +430,10 @@ static void usb_mtp_object_free(MTPState *s, MTPObject *o) trace_usb_mtp_object_free(s->dev.addr, o->handle, o->path); + if (o->watchid != -1 && s->file_monitor) { + qemu_file_monitor_remove_watch(s->file_monitor, o->path, o->watchid); + } + QTAILQ_REMOVE(&s->objects, o, next); if (o->parent) { QLIST_REMOVE(o, list); @@ -465,7 +462,7 @@ static MTPObject *usb_mtp_object_lookup(MTPState *s, uint32_t handle) } static MTPObject *usb_mtp_add_child(MTPState *s, MTPObject *o, - char *name) + const char *name) { MTPObject *child = usb_mtp_object_alloc(s, s->next_handle++, o, name); @@ -484,10 +481,14 @@ static MTPObject *usb_mtp_add_child(MTPState *s, MTPObject *o, } static MTPObject *usb_mtp_object_lookup_name(MTPObject *parent, - char *name, int len) + const char *name, int len) { MTPObject *iter; + if (len == -1) { + len = strlen(name); + } + QLIST_FOREACH(iter, &parent->children, list) { if (strncmp(iter->name, name, len) == 0) { return iter; @@ -497,13 +498,12 @@ static MTPObject *usb_mtp_object_lookup_name(MTPObject *parent, return NULL; } -#ifdef CONFIG_INOTIFY1 -static MTPObject *usb_mtp_object_lookup_wd(MTPState *s, int wd) +static MTPObject *usb_mtp_object_lookup_id(MTPState *s, int id) { MTPObject *iter; QTAILQ_FOREACH(iter, &s->objects, next) { - if (iter->watchfd == wd) { + if (iter->watchid == id) { return iter; } } @@ -511,160 +511,103 @@ static MTPObject *usb_mtp_object_lookup_wd(MTPState *s, int wd) return NULL; } -static void inotify_watchfn(void *arg) +static void file_monitor_event(int id, + QFileMonitorEvent ev, + const char *name, + void *opaque) { - MTPState *s = arg; - ssize_t bytes; - /* From the man page: atleast one event can be read */ - int pos; - char buf[sizeof(struct inotify_event) + NAME_MAX + 1]; - - for (;;) { - bytes = read(s->inotifyfd, buf, sizeof(buf)); - pos = 0; - - if (bytes <= 0) { - /* Better luck next time */ + MTPState *s = opaque; + MTPObject *parent = usb_mtp_object_lookup_id(s, id); + MTPMonEntry *entry = NULL; + MTPObject *o; + + if (!parent) { + return; + } + + switch (ev) { + case QFILE_MONITOR_EVENT_CREATED: + if (usb_mtp_object_lookup_name(parent, name, -1)) { + /* Duplicate create event */ return; } + entry = g_new0(MTPMonEntry, 1); + entry->handle = s->next_handle; + entry->event = EVT_OBJ_ADDED; + o = usb_mtp_add_child(s, parent, name); + if (!o) { + g_free(entry); + return; + } + trace_usb_mtp_file_monitor_event(s->dev.addr, name, "Obj Added"); + break; + case QFILE_MONITOR_EVENT_DELETED: /* - * TODO: Ignore initiator initiated events. - * For now we are good because the store is RO + * The kernel issues a IN_IGNORED event + * when a dir containing a watchpoint is + * deleted, so we don't have to delete the + * watchpoint */ - while (bytes > 0) { - char *p = buf + pos; - struct inotify_event *event = (struct inotify_event *)p; - int watchfd = 0; - uint32_t mask = event->mask & (IN_CREATE | IN_DELETE | - IN_MODIFY | IN_IGNORED); - MTPObject *parent = usb_mtp_object_lookup_wd(s, event->wd); - MTPMonEntry *entry = NULL; - MTPObject *o; - - pos = pos + sizeof(struct inotify_event) + event->len; - bytes = bytes - pos; - - if (!parent) { - continue; - } - - switch (mask) { - case IN_CREATE: - if (usb_mtp_object_lookup_name - (parent, event->name, event->len)) { - /* Duplicate create event */ - continue; - } - entry = g_new0(MTPMonEntry, 1); - entry->handle = s->next_handle; - entry->event = EVT_OBJ_ADDED; - o = usb_mtp_add_child(s, parent, event->name); - if (!o) { - g_free(entry); - continue; - } - o->watchfd = watchfd; - trace_usb_mtp_inotify_event(s->dev.addr, event->name, - event->mask, "Obj Added"); - break; - - case IN_DELETE: - /* - * The kernel issues a IN_IGNORED event - * when a dir containing a watchpoint is - * deleted, so we don't have to delete the - * watchpoint - */ - o = usb_mtp_object_lookup_name(parent, event->name, event->len); - if (!o) { - continue; - } - entry = g_new0(MTPMonEntry, 1); - entry->handle = o->handle; - entry->event = EVT_OBJ_REMOVED; - trace_usb_mtp_inotify_event(s->dev.addr, o->path, - event->mask, "Obj Deleted"); - usb_mtp_object_free(s, o); - break; - - case IN_MODIFY: - o = usb_mtp_object_lookup_name(parent, event->name, event->len); - if (!o) { - continue; - } - entry = g_new0(MTPMonEntry, 1); - entry->handle = o->handle; - entry->event = EVT_OBJ_INFO_CHANGED; - trace_usb_mtp_inotify_event(s->dev.addr, o->path, - event->mask, "Obj Modified"); - break; - - case IN_IGNORED: - trace_usb_mtp_inotify_event(s->dev.addr, parent->path, - event->mask, "Obj parent dir ignored"); - break; - - default: - fprintf(stderr, "usb-mtp: failed to parse inotify event\n"); - continue; - } - - if (entry) { - QTAILQ_INSERT_HEAD(&s->events, entry, next); - } + o = usb_mtp_object_lookup_name(parent, name, -1); + if (!o) { + return; } - } -} + entry = g_new0(MTPMonEntry, 1); + entry->handle = o->handle; + entry->event = EVT_OBJ_REMOVED; + trace_usb_mtp_file_monitor_event(s->dev.addr, o->path, "Obj Deleted"); + usb_mtp_object_free(s, o); + break; -static int usb_mtp_inotify_init(MTPState *s) -{ - int fd; + case QFILE_MONITOR_EVENT_MODIFIED: + o = usb_mtp_object_lookup_name(parent, name, -1); + if (!o) { + return; + } + entry = g_new0(MTPMonEntry, 1); + entry->handle = o->handle; + entry->event = EVT_OBJ_INFO_CHANGED; + trace_usb_mtp_file_monitor_event(s->dev.addr, o->path, "Obj Modified"); + break; - fd = inotify_init1(IN_NONBLOCK); - if (fd == -1) { - return 1; - } + case QFILE_MONITOR_EVENT_IGNORED: + trace_usb_mtp_file_monitor_event(s->dev.addr, parent->path, + "Obj parent dir ignored"); + break; - QTAILQ_INIT(&s->events); - s->inotifyfd = fd; + case QFILE_MONITOR_EVENT_ATTRIBUTES: + break; - qemu_set_fd_handler(fd, inotify_watchfn, NULL, s); + default: + g_assert_not_reached(); + } - return 0; + if (entry) { + QTAILQ_INSERT_HEAD(&s->events, entry, next); + } } -static void usb_mtp_inotify_cleanup(MTPState *s) +static void usb_mtp_file_monitor_cleanup(MTPState *s) { MTPMonEntry *e, *p; - if (!s->inotifyfd) { - return; - } - - qemu_set_fd_handler(s->inotifyfd, NULL, NULL, s); - close(s->inotifyfd); - QTAILQ_FOREACH_SAFE(e, &s->events, next, p) { QTAILQ_REMOVE(&s->events, e, next); g_free(e); } -} -static int usb_mtp_add_watch(int inotifyfd, char *path) -{ - uint32_t mask = IN_CREATE | IN_DELETE | IN_MODIFY | - IN_ISDIR; - - return inotify_add_watch(inotifyfd, path, mask); + qemu_file_monitor_free(s->file_monitor); + s->file_monitor = NULL; } -#endif + static void usb_mtp_object_readdir(MTPState *s, MTPObject *o) { struct dirent *entry; DIR *dir; int fd; + Error *err = NULL; if (o->have_children) { return; @@ -680,16 +623,21 @@ static void usb_mtp_object_readdir(MTPState *s, MTPObject *o) close(fd); return; } -#ifdef CONFIG_INOTIFY1 - int watchfd = usb_mtp_add_watch(s->inotifyfd, o->path); - if (watchfd == -1) { - fprintf(stderr, "usb-mtp: failed to add watch for %s\n", o->path); - } else { - trace_usb_mtp_inotify_event(s->dev.addr, o->path, - 0, "Watch Added"); - o->watchfd = watchfd; + + if (s->file_monitor) { + int id = qemu_file_monitor_add_watch(s->file_monitor, o->path, NULL, + file_monitor_event, s, &err); + if (id == -1) { + error_report("usb-mtp: failed to add watch for %s: %s", o->path, + error_get_pretty(err)); + error_free(err); + } else { + trace_usb_mtp_file_monitor_event(s->dev.addr, o->path, + "Watch Added"); + o->watchid = id; + } } -#endif + while ((entry = readdir(dir)) != NULL) { usb_mtp_add_child(s, o, entry->d_name); } @@ -1197,13 +1145,11 @@ enum { /* Assumes that children, if any, have been already freed */ static void usb_mtp_object_free_one(MTPState *s, MTPObject *o) { -#ifndef CONFIG_INOTIFY1 assert(o->nchildren == 0); QTAILQ_REMOVE(&s->objects, o, next); g_free(o->name); g_free(o->path); g_free(o); -#endif } static int usb_mtp_deletefn(MTPState *s, MTPObject *o, uint32_t trans) @@ -1302,6 +1248,7 @@ static void usb_mtp_command(MTPState *s, MTPControl *c) MTPData *data_in = NULL; MTPObject *o = NULL; uint32_t nres = 0, res0 = 0; + Error *err = NULL; /* sanity checks */ if (c->code >= CMD_CLOSE_SESSION && s->session == 0) { @@ -1329,19 +1276,21 @@ static void usb_mtp_command(MTPState *s, MTPControl *c) trace_usb_mtp_op_open_session(s->dev.addr); s->session = c->argv[0]; usb_mtp_object_alloc(s, s->next_handle++, NULL, s->root); -#ifdef CONFIG_INOTIFY1 - if (usb_mtp_inotify_init(s)) { - fprintf(stderr, "usb-mtp: file monitoring init failed\n"); + + s->file_monitor = qemu_file_monitor_new(&err); + if (err) { + error_report("usb-mtp: file monitoring init failed: %s", + error_get_pretty(err)); + error_free(err); + } else { + QTAILQ_INIT(&s->events); } -#endif break; case CMD_CLOSE_SESSION: trace_usb_mtp_op_close_session(s->dev.addr); s->session = 0; s->next_handle = 0; -#ifdef CONFIG_INOTIFY1 - usb_mtp_inotify_cleanup(s); -#endif + usb_mtp_file_monitor_cleanup(s); usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects)); assert(QTAILQ_EMPTY(&s->objects)); break; @@ -1554,9 +1503,7 @@ static void usb_mtp_handle_reset(USBDevice *dev) trace_usb_mtp_reset(s->dev.addr); -#ifdef CONFIG_INOTIFY1 - usb_mtp_inotify_cleanup(s); -#endif + usb_mtp_file_monitor_cleanup(s); usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects)); s->session = 0; usb_mtp_data_free(s->data_in); @@ -2027,7 +1974,6 @@ static void usb_mtp_handle_data(USBDevice *dev, USBPacket *p) } break; case EP_EVENT: -#ifdef CONFIG_INOTIFY1 if (!QTAILQ_EMPTY(&s->events)) { struct MTPMonEntry *e = QTAILQ_LAST(&s->events); uint32_t handle; @@ -2051,7 +1997,6 @@ static void usb_mtp_handle_data(USBDevice *dev, USBPacket *p) g_free(e); return; } -#endif p->status = USB_RET_NAK; return; default: diff --git a/hw/usb/trace-events b/hw/usb/trace-events index 2c18770ca5..99b1e8b8ce 100644 --- a/hw/usb/trace-events +++ b/hw/usb/trace-events @@ -237,7 +237,7 @@ usb_mtp_op_unknown(int dev, uint32_t code) "dev %d, command code 0x%x" usb_mtp_object_alloc(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s" usb_mtp_object_free(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s" usb_mtp_add_child(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s" -usb_mtp_inotify_event(int dev, const char *path, uint32_t mask, const char *s) "dev %d, path %s mask 0x%x event %s" +usb_mtp_file_monitor_event(int dev, const char *path, const char *s) "dev %d, path %s event %s" # hw/usb/host-libusb.c usb_host_open_started(int bus, int addr) "dev %d:%d" diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 4262b80c44..df2b4721bf 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -220,7 +220,25 @@ static int vfio_dma_unmap(VFIOContainer *container, .size = size, }; - if (ioctl(container->fd, VFIO_IOMMU_UNMAP_DMA, &unmap)) { + while (ioctl(container->fd, VFIO_IOMMU_UNMAP_DMA, &unmap)) { + /* + * The type1 backend has an off-by-one bug in the kernel (71a7d3d78e3c + * v4.15) where an overflow in its wrap-around check prevents us from + * unmapping the last page of the address space. Test for the error + * condition and re-try the unmap excluding the last page. The + * expectation is that we've never mapped the last page anyway and this + * unmap request comes via vIOMMU support which also makes it unlikely + * that this page is used. This bug was introduced well after type1 v2 + * support was introduced, so we shouldn't need to test for v1. A fix + * is queued for kernel v5.0 so this workaround can be removed once + * affected kernels are sufficiently deprecated. + */ + if (errno == EINVAL && unmap.size && !(unmap.iova + unmap.size) && + container->iommu_type == VFIO_TYPE1v2_IOMMU) { + trace_vfio_dma_unmap_overflow_workaround(); + unmap.size -= 1ULL << ctz64(container->pgsizes); + continue; + } error_report("VFIO_UNMAP_DMA: %d", -errno); return -errno; } @@ -1036,6 +1054,60 @@ static void vfio_put_address_space(VFIOAddressSpace *space) } } +/* + * vfio_get_iommu_type - selects the richest iommu_type (v2 first) + */ +static int vfio_get_iommu_type(VFIOContainer *container, + Error **errp) +{ + int iommu_types[] = { VFIO_TYPE1v2_IOMMU, VFIO_TYPE1_IOMMU, + VFIO_SPAPR_TCE_v2_IOMMU, VFIO_SPAPR_TCE_IOMMU }; + int i; + + for (i = 0; i < ARRAY_SIZE(iommu_types); i++) { + if (ioctl(container->fd, VFIO_CHECK_EXTENSION, iommu_types[i])) { + return iommu_types[i]; + } + } + error_setg(errp, "No available IOMMU models"); + return -EINVAL; +} + +static int vfio_init_container(VFIOContainer *container, int group_fd, + Error **errp) +{ + int iommu_type, ret; + + iommu_type = vfio_get_iommu_type(container, errp); + if (iommu_type < 0) { + return iommu_type; + } + + ret = ioctl(group_fd, VFIO_GROUP_SET_CONTAINER, &container->fd); + if (ret) { + error_setg_errno(errp, errno, "Failed to set group container"); + return -errno; + } + + while (ioctl(container->fd, VFIO_SET_IOMMU, iommu_type)) { + if (iommu_type == VFIO_SPAPR_TCE_v2_IOMMU) { + /* + * On sPAPR, despite the IOMMU subdriver always advertises v1 and + * v2, the running platform may not support v2 and there is no + * way to guess it until an IOMMU group gets added to the container. + * So in case it fails with v2, try v1 as a fallback. + */ + iommu_type = VFIO_SPAPR_TCE_IOMMU; + continue; + } + error_setg_errno(errp, errno, "Failed to set iommu for container"); + return -errno; + } + + container->iommu_type = iommu_type; + return 0; +} + static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, Error **errp) { @@ -1101,25 +1173,17 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, container->fd = fd; QLIST_INIT(&container->giommu_list); QLIST_INIT(&container->hostwin_list); - if (ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_TYPE1_IOMMU) || - ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_TYPE1v2_IOMMU)) { - bool v2 = !!ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_TYPE1v2_IOMMU); - struct vfio_iommu_type1_info info; - ret = ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &fd); - if (ret) { - error_setg_errno(errp, errno, "failed to set group container"); - ret = -errno; - goto free_container_exit; - } + ret = vfio_init_container(container, group->fd, errp); + if (ret) { + goto free_container_exit; + } - container->iommu_type = v2 ? VFIO_TYPE1v2_IOMMU : VFIO_TYPE1_IOMMU; - ret = ioctl(fd, VFIO_SET_IOMMU, container->iommu_type); - if (ret) { - error_setg_errno(errp, errno, "failed to set iommu for container"); - ret = -errno; - goto free_container_exit; - } + switch (container->iommu_type) { + case VFIO_TYPE1v2_IOMMU: + case VFIO_TYPE1_IOMMU: + { + struct vfio_iommu_type1_info info; /* * FIXME: This assumes that a Type1 IOMMU can map any 64-bit @@ -1137,30 +1201,13 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, } vfio_host_win_add(container, 0, (hwaddr)-1, info.iova_pgsizes); container->pgsizes = info.iova_pgsizes; - } else if (ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_SPAPR_TCE_IOMMU) || - ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_SPAPR_TCE_v2_IOMMU)) { + break; + } + case VFIO_SPAPR_TCE_v2_IOMMU: + case VFIO_SPAPR_TCE_IOMMU: + { struct vfio_iommu_spapr_tce_info info; - bool v2 = !!ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_SPAPR_TCE_v2_IOMMU); - - ret = ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &fd); - if (ret) { - error_setg_errno(errp, errno, "failed to set group container"); - ret = -errno; - goto free_container_exit; - } - container->iommu_type = - v2 ? VFIO_SPAPR_TCE_v2_IOMMU : VFIO_SPAPR_TCE_IOMMU; - ret = ioctl(fd, VFIO_SET_IOMMU, container->iommu_type); - if (ret) { - container->iommu_type = VFIO_SPAPR_TCE_IOMMU; - v2 = false; - ret = ioctl(fd, VFIO_SET_IOMMU, container->iommu_type); - } - if (ret) { - error_setg_errno(errp, errno, "failed to set iommu for container"); - ret = -errno; - goto free_container_exit; - } + bool v2 = container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU; /* * The host kernel code implementing VFIO_IOMMU_DISABLE is called @@ -1222,10 +1269,7 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, info.dma32_window_size - 1, 0x1000); } - } else { - error_setg(errp, "No available IOMMU models"); - ret = -EINVAL; - goto free_container_exit; + } } vfio_kvm_device_add_group(group); diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index f41ca96160..ed2f333ad7 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -110,6 +110,7 @@ vfio_region_mmaps_set_enabled(const char *name, bool enabled) "Region %s mmaps e vfio_region_sparse_mmap_header(const char *name, int index, int nr_areas) "Device %s region %d: %d sparse mmap entries" vfio_region_sparse_mmap_entry(int i, unsigned long start, unsigned long end) "sparse entry %d [0x%lx - 0x%lx]" vfio_get_dev_region(const char *name, int index, uint32_t type, uint32_t subtype) "%s index %d, %08x/%0x8" +vfio_dma_unmap_overflow_workaround(void) "" # hw/vfio/platform.c vfio_platform_base_device_init(char *name, int groupid) "%s belongs to group #%d" diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index a1ff647a66..2626a895cb 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -2036,6 +2036,21 @@ int virtio_set_features(VirtIODevice *vdev, uint64_t val) return ret; } +size_t virtio_feature_get_config_size(VirtIOFeature *feature_sizes, + uint64_t host_features) +{ + size_t config_size = 0; + int i; + + for (i = 0; feature_sizes[i].flags != 0; i++) { + if (host_features & feature_sizes[i].flags) { + config_size = MAX(feature_sizes[i].end, config_size); + } + } + + return config_size; +} + int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) { int i, ret; |