diff options
Diffstat (limited to 'hw')
99 files changed, 3394 insertions, 3074 deletions
diff --git a/hw/Kconfig b/hw/Kconfig index ecf491bf04..62f9ebdc22 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -55,6 +55,7 @@ source nios2/Kconfig source openrisc/Kconfig source ppc/Kconfig source riscv/Kconfig +source rx/Kconfig source s390x/Kconfig source sh4/Kconfig source sparc/Kconfig diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 2cb7b991ef..f6fbc9b95d 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -1878,48 +1878,61 @@ build_hdr: "FACP", tbl->len - fadt_start, f->rev, oem_id, oem_table_id); } +/* + * build_tpm2 - Build the TPM2 table as specified in + * table 7: TCG Hardware Interface Description Table Format for TPM 2.0 + * of TCG ACPI Specification, Family “1.2” and “2.0”, Version 1.2, Rev 8 + */ void build_tpm2(GArray *table_data, BIOSLinker *linker, GArray *tcpalog) { - Acpi20TPM2 *tpm2_ptr = acpi_data_push(table_data, sizeof(AcpiTableHeader)); - unsigned log_addr_size = sizeof(tpm2_ptr->log_area_start_address); - unsigned log_addr_offset = - (char *)&tpm2_ptr->log_area_start_address - table_data->data; uint8_t start_method_params[12] = {}; + unsigned log_addr_offset, tpm2_start; + uint64_t control_area_start_address; TPMIf *tpmif = tpm_find(); + uint32_t start_method; + void *tpm2_ptr; + + tpm2_start = table_data->len; + tpm2_ptr = acpi_data_push(table_data, sizeof(AcpiTableHeader)); - /* platform class */ + /* Platform Class */ build_append_int_noprefix(table_data, TPM2_ACPI_CLASS_CLIENT, 2); - /* reserved */ + /* Reserved */ build_append_int_noprefix(table_data, 0, 2); if (TPM_IS_TIS_ISA(tpmif) || TPM_IS_TIS_SYSBUS(tpmif)) { - /* address of control area */ - build_append_int_noprefix(table_data, 0, 8); - /* start method */ - build_append_int_noprefix(table_data, TPM2_START_METHOD_MMIO, 4); + control_area_start_address = 0; + start_method = TPM2_START_METHOD_MMIO; } else if (TPM_IS_CRB(tpmif)) { - build_append_int_noprefix(table_data, TPM_CRB_ADDR_CTRL, 8); - build_append_int_noprefix(table_data, TPM2_START_METHOD_CRB, 4); + control_area_start_address = TPM_CRB_ADDR_CTRL; + start_method = TPM2_START_METHOD_CRB; } else { - g_warn_if_reached(); + g_assert_not_reached(); } + /* Address of Control Area */ + build_append_int_noprefix(table_data, control_area_start_address, 8); + /* Start Method */ + build_append_int_noprefix(table_data, start_method, 4); - /* platform specific parameters */ - g_array_append_vals(table_data, &start_method_params, 12); + /* Platform Specific Parameters */ + g_array_append_vals(table_data, &start_method_params, + ARRAY_SIZE(start_method_params)); - /* log area minimum length */ + /* Log Area Minimum Length */ build_append_int_noprefix(table_data, TPM_LOG_AREA_MINIMUM_SIZE, 4); acpi_data_push(tcpalog, TPM_LOG_AREA_MINIMUM_SIZE); bios_linker_loader_alloc(linker, ACPI_BUILD_TPMLOG_FILE, tcpalog, 1, false); - /* log area start address to be filled by Guest linker */ + log_addr_offset = table_data->len; + + /* Log Area Start Address to be filled by Guest linker */ build_append_int_noprefix(table_data, 0, 8); bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE, - log_addr_offset, log_addr_size, + log_addr_offset, 8, ACPI_BUILD_TPMLOG_FILE, 0); build_header(linker, table_data, - (void *)tpm2_ptr, "TPM2", sizeof(*tpm2_ptr), 4, NULL, NULL); + tpm2_ptr, "TPM2", table_data->len - tpm2_start, 4, NULL, NULL); } /* ACPI 5.0: 6.4.3.8.2 Serial Bus Connection Descriptors */ diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c index 1cb34111e5..b8abdefa1c 100644 --- a/hw/acpi/generic_event_device.c +++ b/hw/acpi/generic_event_device.c @@ -193,6 +193,33 @@ static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev, } } +static void acpi_ged_unplug_request_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + AcpiGedState *s = ACPI_GED(hotplug_dev); + + if ((object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) && + !(object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)))) { + acpi_memory_unplug_request_cb(hotplug_dev, &s->memhp_state, dev, errp); + } else { + error_setg(errp, "acpi: device unplug request for unsupported device" + " type: %s", object_get_typename(OBJECT(dev))); + } +} + +static void acpi_ged_unplug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + AcpiGedState *s = ACPI_GED(hotplug_dev); + + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + acpi_memory_unplug_cb(&s->memhp_state, dev, errp); + } else { + error_setg(errp, "acpi: device unplug for unsupported device" + " type: %s", object_get_typename(OBJECT(dev))); + } +} + static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev) { AcpiGedState *s = ACPI_GED(adev); @@ -318,6 +345,8 @@ static void acpi_ged_class_init(ObjectClass *class, void *data) dc->vmsd = &vmstate_acpi_ged; hc->plug = acpi_ged_device_plug_cb; + hc->unplug_request = acpi_ged_unplug_request_cb; + hc->unplug = acpi_ged_unplug_cb; adevc->send_event = acpi_ged_send_event; } diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 1262abc77a..630ca6ec87 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -77,7 +77,7 @@ typedef struct PIIX4PMState { Notifier powerdown_notifier; AcpiPciHpState acpi_pci_hotplug; - bool use_acpi_pci_hotplug; + bool use_acpi_hotplug_bridge; uint8_t disable_s3; uint8_t disable_s4; @@ -204,16 +204,17 @@ static const VMStateDescription vmstate_pci_status = { } }; -static bool vmstate_test_use_acpi_pci_hotplug(void *opaque, int version_id) +static bool vmstate_test_use_acpi_hotplug_bridge(void *opaque, int version_id) { PIIX4PMState *s = opaque; - return s->use_acpi_pci_hotplug; + return s->use_acpi_hotplug_bridge; } -static bool vmstate_test_no_use_acpi_pci_hotplug(void *opaque, int version_id) +static bool vmstate_test_no_use_acpi_hotplug_bridge(void *opaque, + int version_id) { PIIX4PMState *s = opaque; - return !s->use_acpi_pci_hotplug; + return !s->use_acpi_hotplug_bridge; } static bool vmstate_test_use_memhp(void *opaque) @@ -290,11 +291,11 @@ static const VMStateDescription vmstate_acpi = { VMSTATE_STRUCT_TEST( acpi_pci_hotplug.acpi_pcihp_pci_status[ACPI_PCIHP_BSEL_DEFAULT], PIIX4PMState, - vmstate_test_no_use_acpi_pci_hotplug, + vmstate_test_no_use_acpi_hotplug_bridge, 2, vmstate_pci_status, struct AcpiPciHpPciStatus), VMSTATE_PCI_HOTPLUG(acpi_pci_hotplug, PIIX4PMState, - vmstate_test_use_acpi_pci_hotplug), + vmstate_test_use_acpi_hotplug_bridge), VMSTATE_END_OF_LIST() }, .subsections = (const VMStateDescription*[]) { @@ -530,7 +531,7 @@ I2CBus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base, s->smi_irq = smi_irq; s->smm_enabled = smm_enabled; if (xen_enabled()) { - s->use_acpi_pci_hotplug = false; + s->use_acpi_hotplug_bridge = false; } pci_realize_and_unref(pci_dev, bus, &error_fatal); @@ -595,7 +596,7 @@ static void piix4_acpi_system_hot_add_init(MemoryRegion *parent, memory_region_add_subregion(parent, GPE_BASE, &s->io_gpe); acpi_pcihp_init(OBJECT(s), &s->acpi_pci_hotplug, bus, parent, - s->use_acpi_pci_hotplug); + s->use_acpi_hotplug_bridge); s->cpu_hotplug_legacy = true; object_property_add_bool(OBJECT(s), "cpu-hotplug-legacy", @@ -633,7 +634,7 @@ static Property piix4_pm_properties[] = { DEFINE_PROP_UINT8(ACPI_PM_PROP_S4_DISABLED, PIIX4PMState, disable_s4, 0), DEFINE_PROP_UINT8(ACPI_PM_PROP_S4_VAL, PIIX4PMState, s4_val, 2), DEFINE_PROP_BOOL("acpi-pci-hotplug-with-bridge-support", PIIX4PMState, - use_acpi_pci_hotplug, true), + use_acpi_hotplug_bridge, true), DEFINE_PROP_BOOL("memory-hotplug-support", PIIX4PMState, acpi_memory_hotplug.is_enabled, true), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 9afa6eee79..4a224a6351 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -59,7 +59,7 @@ config HIGHBANK select ARM_TIMER # sp804 select ARM_V7M select PL011 # UART - select PL022 # Serial port + select PL022 # SPI select PL031 # RTC select PL061 # GPIO select PL310 # cache controller @@ -222,7 +222,7 @@ config STELLARIS select CMSDK_APB_WATCHDOG select I2C select PL011 # UART - select PL022 # Serial port + select PL022 # SPI select PL061 # GPIO select SSD0303 # OLED display select SSD0323 # OLED display @@ -401,10 +401,12 @@ config MPS2 select MPS2_FPGAIO select MPS2_SCC select OR_IRQ - select PL022 # Serial port + select PL022 # SPI select PL080 # DMA controller select SPLIT_IRQ select UNIMP + select CMSDK_APB_WATCHDOG + select VERSATILE_I2C config FSL_IMX7 bool diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 0ad08a2b4c..863757e1f0 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -215,8 +215,8 @@ static void write_boot_rom(DriveInfo *dinfo, hwaddr addr, size_t rom_size, g_free(storage); } -static void aspeed_board_init_flashes(AspeedSMCState *s, const char *flashtype, - Error **errp) +static void aspeed_board_init_flashes(AspeedSMCState *s, + const char *flashtype) { int i ; @@ -227,8 +227,8 @@ static void aspeed_board_init_flashes(AspeedSMCState *s, const char *flashtype, fl->flash = qdev_new(flashtype); if (dinfo) { - qdev_prop_set_drive(fl->flash, "drive", blk_by_legacy_dinfo(dinfo), - errp); + qdev_prop_set_drive(fl->flash, "drive", + blk_by_legacy_dinfo(dinfo)); } qdev_realize_and_unref(fl->flash, BUS(s->spi), &error_fatal); @@ -243,8 +243,8 @@ static void sdhci_attach_drive(SDHCIState *sdhci, DriveInfo *dinfo) card = qdev_new(TYPE_SD_CARD); if (dinfo) { - qdev_prop_set_drive(card, "drive", blk_by_legacy_dinfo(dinfo), - &error_fatal); + qdev_prop_set_drive_err(card, "drive", blk_by_legacy_dinfo(dinfo), + &error_fatal); } qdev_realize_and_unref(card, qdev_get_child_bus(DEVICE(sdhci), "sd-bus"), @@ -314,8 +314,8 @@ static void aspeed_machine_init(MachineState *machine) "max_ram", max_ram_size - ram_size); memory_region_add_subregion(&bmc->ram_container, ram_size, &bmc->max_ram); - aspeed_board_init_flashes(&bmc->soc.fmc, amc->fmc_model, &error_abort); - aspeed_board_init_flashes(&bmc->soc.spi[0], amc->spi_model, &error_abort); + aspeed_board_init_flashes(&bmc->soc.fmc, amc->fmc_model); + aspeed_board_init_flashes(&bmc->soc.spi[0], amc->spi_model); /* Install first FMC flash content as a boot rom. */ if (drive0) { diff --git a/hw/arm/cubieboard.c b/hw/arm/cubieboard.c index a96c860575..5cbd115c53 100644 --- a/hw/arm/cubieboard.c +++ b/hw/arm/cubieboard.c @@ -93,7 +93,7 @@ static void cubieboard_init(MachineState *machine) /* Plug in SD card */ carddev = qdev_new(TYPE_SD_CARD); - qdev_prop_set_drive(carddev, "drive", blk, &error_fatal); + qdev_prop_set_drive_err(carddev, "drive", blk, &error_fatal); qdev_realize_and_unref(carddev, bus, &error_fatal); memory_region_add_subregion(get_system_memory(), AW_A10_SDRAM_BASE, diff --git a/hw/arm/exynos4210.c b/hw/arm/exynos4210.c index b888a5c9ab..fa639806ec 100644 --- a/hw/arm/exynos4210.c +++ b/hw/arm/exynos4210.c @@ -434,7 +434,7 @@ static void exynos4210_realize(DeviceState *socdev, Error **errp) di = drive_get(IF_SD, 0, n); blk = di ? blk_by_legacy_dinfo(di) : NULL; carddev = qdev_new(TYPE_SD_CARD); - qdev_prop_set_drive(carddev, "drive", blk, &error_abort); + qdev_prop_set_drive(carddev, "drive", blk); qdev_realize_and_unref(carddev, qdev_get_child_bus(dev, "sd-bus"), &error_fatal); } diff --git a/hw/arm/imx25_pdk.c b/hw/arm/imx25_pdk.c index af8f7f969c..1c201d0d8e 100644 --- a/hw/arm/imx25_pdk.c +++ b/hw/arm/imx25_pdk.c @@ -130,7 +130,7 @@ static void imx25_pdk_init(MachineState *machine) blk = di ? blk_by_legacy_dinfo(di) : NULL; bus = qdev_get_child_bus(DEVICE(&s->soc.esdhc[i]), "sd-bus"); carddev = qdev_new(TYPE_SD_CARD); - qdev_prop_set_drive(carddev, "drive", blk, &error_fatal); + qdev_prop_set_drive_err(carddev, "drive", blk, &error_fatal); qdev_realize_and_unref(carddev, bus, &error_fatal); } diff --git a/hw/arm/mcimx6ul-evk.c b/hw/arm/mcimx6ul-evk.c index 3d1d2e3c04..2f845cedfc 100644 --- a/hw/arm/mcimx6ul-evk.c +++ b/hw/arm/mcimx6ul-evk.c @@ -55,7 +55,7 @@ static void mcimx6ul_evk_init(MachineState *machine) blk = di ? blk_by_legacy_dinfo(di) : NULL; bus = qdev_get_child_bus(DEVICE(&s->usdhc[i]), "sd-bus"); carddev = qdev_new(TYPE_SD_CARD); - qdev_prop_set_drive(carddev, "drive", blk, &error_fatal); + qdev_prop_set_drive_err(carddev, "drive", blk, &error_fatal); qdev_realize_and_unref(carddev, bus, &error_fatal); } diff --git a/hw/arm/mcimx7d-sabre.c b/hw/arm/mcimx7d-sabre.c index 365f8183bc..e57d52b344 100644 --- a/hw/arm/mcimx7d-sabre.c +++ b/hw/arm/mcimx7d-sabre.c @@ -57,7 +57,7 @@ static void mcimx7d_sabre_init(MachineState *machine) blk = di ? blk_by_legacy_dinfo(di) : NULL; bus = qdev_get_child_bus(DEVICE(&s->usdhc[i]), "sd-bus"); carddev = qdev_new(TYPE_SD_CARD); - qdev_prop_set_drive(carddev, "drive", blk, &error_fatal); + qdev_prop_set_drive_err(carddev, "drive", blk, &error_fatal); qdev_realize_and_unref(carddev, bus, &error_fatal); } diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index 8155c35418..a4fd5ddede 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -58,6 +58,7 @@ #include "hw/arm/armsse.h" #include "hw/dma/pl080.h" #include "hw/ssi/pl022.h" +#include "hw/i2c/arm_sbcon_i2c.h" #include "hw/net/lan9118.h" #include "net/net.h" #include "hw/core/split-irq.h" @@ -87,7 +88,7 @@ typedef struct { TZPPC ppc[5]; TZMPC ssram_mpc[3]; PL022State spi[5]; - UnimplementedDeviceState i2c[4]; + ArmSbconI2CState i2c[4]; UnimplementedDeviceState i2s_audio; UnimplementedDeviceState gpio[4]; UnimplementedDeviceState gfx; @@ -365,6 +366,18 @@ static MemoryRegion *make_spi(MPS2TZMachineState *mms, void *opaque, return sysbus_mmio_get_region(s, 0); } +static MemoryRegion *make_i2c(MPS2TZMachineState *mms, void *opaque, + const char *name, hwaddr size) +{ + ArmSbconI2CState *i2c = opaque; + SysBusDevice *s; + + object_initialize_child(OBJECT(mms), name, i2c, TYPE_ARM_SBCON_I2C); + s = SYS_BUS_DEVICE(i2c); + sysbus_realize(s, &error_fatal); + return sysbus_mmio_get_region(s, 0); +} + static void mps2tz_common_init(MachineState *machine) { MPS2TZMachineState *mms = MPS2TZ_MACHINE(machine); @@ -499,10 +512,10 @@ static void mps2tz_common_init(MachineState *machine) { "uart2", make_uart, &mms->uart[2], 0x40202000, 0x1000 }, { "uart3", make_uart, &mms->uart[3], 0x40203000, 0x1000 }, { "uart4", make_uart, &mms->uart[4], 0x40204000, 0x1000 }, - { "i2c0", make_unimp_dev, &mms->i2c[0], 0x40207000, 0x1000 }, - { "i2c1", make_unimp_dev, &mms->i2c[1], 0x40208000, 0x1000 }, - { "i2c2", make_unimp_dev, &mms->i2c[2], 0x4020c000, 0x1000 }, - { "i2c3", make_unimp_dev, &mms->i2c[3], 0x4020d000, 0x1000 }, + { "i2c0", make_i2c, &mms->i2c[0], 0x40207000, 0x1000 }, + { "i2c1", make_i2c, &mms->i2c[1], 0x40208000, 0x1000 }, + { "i2c2", make_i2c, &mms->i2c[2], 0x4020c000, 0x1000 }, + { "i2c3", make_i2c, &mms->i2c[3], 0x4020d000, 0x1000 }, }, }, { .name = "apb_ppcexp2", diff --git a/hw/arm/mps2.c b/hw/arm/mps2.c index daa55f730b..d1653a7e6e 100644 --- a/hw/arm/mps2.c +++ b/hw/arm/mps2.c @@ -38,8 +38,12 @@ #include "hw/timer/cmsdk-apb-timer.h" #include "hw/timer/cmsdk-apb-dualtimer.h" #include "hw/misc/mps2-scc.h" +#include "hw/misc/mps2-fpgaio.h" +#include "hw/ssi/pl022.h" +#include "hw/i2c/arm_sbcon_i2c.h" #include "hw/net/lan9118.h" #include "net/net.h" +#include "hw/watchdog/cmsdk-apb-watchdog.h" typedef enum MPS2FPGAType { FPGA_AN385, @@ -65,8 +69,12 @@ typedef struct { MemoryRegion blockram_m2; MemoryRegion blockram_m3; MemoryRegion sram; + /* FPGA APB subsystem */ MPS2SCC scc; + MPS2FPGAIO fpgaio; + /* CMSDK APB subsystem */ CMSDKAPBDualTimer dualtimer; + CMSDKAPBWatchdog watchdog; } MPS2MachineState; #define TYPE_MPS2_MACHINE "mps2" @@ -111,6 +119,7 @@ static void mps2_common_init(MachineState *machine) MemoryRegion *system_memory = get_system_memory(); MachineClass *mc = MACHINE_GET_CLASS(machine); DeviceState *armv7m, *sccdev; + int i; if (strcmp(machine->cpu_type, mc->default_cpu_type) != 0) { error_report("This board can only be used with CPU %s", @@ -210,10 +219,11 @@ static void mps2_common_init(MachineState *machine) */ create_unimplemented_device("CMSDK APB peripheral region @0x40000000", 0x40000000, 0x00010000); - create_unimplemented_device("CMSDK peripheral region @0x40010000", + create_unimplemented_device("CMSDK AHB peripheral region @0x40010000", 0x40010000, 0x00010000); create_unimplemented_device("Extra peripheral region @0x40020000", 0x40020000, 0x00010000); + create_unimplemented_device("RESERVED 4", 0x40030000, 0x001D0000); create_unimplemented_device("VGA", 0x41000000, 0x0200000); @@ -225,7 +235,6 @@ static void mps2_common_init(MachineState *machine) */ Object *orgate; DeviceState *orgate_dev; - int i; orgate = object_new(TYPE_OR_IRQ); object_property_set_int(orgate, 6, "num-lines", &error_fatal); @@ -262,7 +271,6 @@ static void mps2_common_init(MachineState *machine) */ Object *orgate; DeviceState *orgate_dev; - int i; orgate = object_new(TYPE_OR_IRQ); object_property_set_int(orgate, 10, "num-lines", &error_fatal); @@ -298,10 +306,15 @@ static void mps2_common_init(MachineState *machine) default: g_assert_not_reached(); } + for (i = 0; i < 4; i++) { + static const hwaddr gpiobase[] = {0x40010000, 0x40011000, + 0x40012000, 0x40013000}; + create_unimplemented_device("cmsdk-ahb-gpio", gpiobase[i], 0x1000); + } + /* CMSDK APB subsystem */ cmsdk_apb_timer_create(0x40000000, qdev_get_gpio_in(armv7m, 8), SYSCLK_FRQ); cmsdk_apb_timer_create(0x40001000, qdev_get_gpio_in(armv7m, 9), SYSCLK_FRQ); - object_initialize_child(OBJECT(mms), "dualtimer", &mms->dualtimer, TYPE_CMSDK_APB_DUALTIMER); qdev_prop_set_uint32(DEVICE(&mms->dualtimer), "pclk-frq", SYSCLK_FRQ); @@ -309,7 +322,15 @@ static void mps2_common_init(MachineState *machine) sysbus_connect_irq(SYS_BUS_DEVICE(&mms->dualtimer), 0, qdev_get_gpio_in(armv7m, 10)); sysbus_mmio_map(SYS_BUS_DEVICE(&mms->dualtimer), 0, 0x40002000); - + object_initialize_child(OBJECT(mms), "watchdog", &mms->watchdog, + TYPE_CMSDK_APB_WATCHDOG); + qdev_prop_set_uint32(DEVICE(&mms->watchdog), "wdogclk-frq", SYSCLK_FRQ); + sysbus_realize(SYS_BUS_DEVICE(&mms->watchdog), &error_fatal); + sysbus_connect_irq(SYS_BUS_DEVICE(&mms->watchdog), 0, + qdev_get_gpio_in_named(armv7m, "NMI", 0)); + sysbus_mmio_map(SYS_BUS_DEVICE(&mms->watchdog), 0, 0x40008000); + + /* FPGA APB subsystem */ object_initialize_child(OBJECT(mms), "scc", &mms->scc, TYPE_MPS2_SCC); sccdev = DEVICE(&mms->scc); qdev_prop_set_uint32(sccdev, "scc-cfg4", 0x2); @@ -317,6 +338,42 @@ static void mps2_common_init(MachineState *machine) qdev_prop_set_uint32(sccdev, "scc-id", mmc->scc_id); sysbus_realize(SYS_BUS_DEVICE(&mms->scc), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(sccdev), 0, 0x4002f000); + object_initialize_child(OBJECT(mms), "fpgaio", + &mms->fpgaio, TYPE_MPS2_FPGAIO); + qdev_prop_set_uint32(DEVICE(&mms->fpgaio), "prescale-clk", 25000000); + sysbus_realize(SYS_BUS_DEVICE(&mms->fpgaio), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&mms->fpgaio), 0, 0x40028000); + sysbus_create_simple(TYPE_PL022, 0x40025000, /* External ADC */ + qdev_get_gpio_in(armv7m, 22)); + for (i = 0; i < 2; i++) { + static const int spi_irqno[] = {11, 24}; + static const hwaddr spibase[] = {0x40020000, /* APB */ + 0x40021000, /* LCD */ + 0x40026000, /* Shield0 */ + 0x40027000}; /* Shield1 */ + DeviceState *orgate_dev; + Object *orgate; + int j; + + orgate = object_new(TYPE_OR_IRQ); + object_property_set_int(orgate, 2, "num-lines", &error_fatal); + orgate_dev = DEVICE(orgate); + qdev_realize(orgate_dev, NULL, &error_fatal); + qdev_connect_gpio_out(orgate_dev, 0, + qdev_get_gpio_in(armv7m, spi_irqno[i])); + for (j = 0; j < 2; j++) { + sysbus_create_simple(TYPE_PL022, spibase[2 * i + j], + qdev_get_gpio_in(orgate_dev, j)); + } + } + for (i = 0; i < 4; i++) { + static const hwaddr i2cbase[] = {0x40022000, /* Touch */ + 0x40023000, /* Audio */ + 0x40029000, /* Shield0 */ + 0x4002a000}; /* Shield1 */ + sysbus_create_simple(TYPE_ARM_SBCON_I2C, i2cbase[i], NULL); + } + create_unimplemented_device("i2s", 0x40024000, 0x400); /* In hardware this is a LAN9220; the LAN9118 is software compatible * except that it doesn't support the checksum-offload feature. diff --git a/hw/arm/msf2-som.c b/hw/arm/msf2-som.c index 355966c073..f9b61c36dd 100644 --- a/hw/arm/msf2-som.c +++ b/hw/arm/msf2-som.c @@ -86,8 +86,8 @@ static void emcraft_sf2_s2s010_init(MachineState *machine) spi_flash = qdev_new("s25sl12801"); qdev_prop_set_uint8(spi_flash, "spansion-cr2nv", 1); if (dinfo) { - qdev_prop_set_drive(spi_flash, "drive", blk_by_legacy_dinfo(dinfo), - &error_fatal); + qdev_prop_set_drive_err(spi_flash, "drive", + blk_by_legacy_dinfo(dinfo), &error_fatal); } qdev_realize_and_unref(spi_flash, spi_bus, &error_fatal); cs_line = qdev_get_gpio_in_named(spi_flash, SSI_GPIO_CS, 0); diff --git a/hw/arm/nseries.c b/hw/arm/nseries.c index 02678dda2d..428a2a2c5a 100644 --- a/hw/arm/nseries.c +++ b/hw/arm/nseries.c @@ -182,8 +182,8 @@ static void n8x0_nand_setup(struct n800_s *s) qdev_prop_set_int32(s->nand, "shift", 1); dinfo = drive_get(IF_MTD, 0, 0); if (dinfo) { - qdev_prop_set_drive(s->nand, "drive", blk_by_legacy_dinfo(dinfo), - &error_fatal); + qdev_prop_set_drive_err(s->nand, "drive", blk_by_legacy_dinfo(dinfo), + &error_fatal); } sysbus_realize_and_unref(SYS_BUS_DEVICE(s->nand), &error_fatal); sysbus_connect_irq(SYS_BUS_DEVICE(s->nand), 0, diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c index 678c93033e..843dcbbd62 100644 --- a/hw/arm/orangepi.c +++ b/hw/arm/orangepi.c @@ -95,7 +95,7 @@ static void orangepi_init(MachineState *machine) /* Plug in SD card */ carddev = qdev_new(TYPE_SD_CARD); - qdev_prop_set_drive(carddev, "drive", blk, &error_fatal); + qdev_prop_set_drive_err(carddev, "drive", blk, &error_fatal); qdev_realize_and_unref(carddev, bus, &error_fatal); /* SDRAM */ diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c index 380978fc27..09bf02ec9c 100644 --- a/hw/arm/raspi.c +++ b/hw/arm/raspi.c @@ -298,7 +298,7 @@ static void raspi_machine_init(MachineState *machine) exit(1); } carddev = qdev_new(TYPE_SD_CARD); - qdev_prop_set_drive(carddev, "drive", blk, &error_fatal); + qdev_prop_set_drive_err(carddev, "drive", blk, &error_fatal); qdev_realize_and_unref(carddev, bus, &error_fatal); vcram_size = object_property_get_uint(OBJECT(&s->soc), "vcram-size", diff --git a/hw/arm/realview.c b/hw/arm/realview.c index f3c00fe00c..b6c0a1adb9 100644 --- a/hw/arm/realview.c +++ b/hw/arm/realview.c @@ -26,6 +26,7 @@ #include "hw/cpu/a9mpcore.h" #include "hw/intc/realview_gic.h" #include "hw/irq.h" +#include "hw/i2c/arm_sbcon_i2c.h" #define SMP_BOOT_ADDR 0xe0000000 #define SMP_BOOTREG_ADDR 0x10000030 @@ -282,7 +283,7 @@ static void realview_init(MachineState *machine, } } - dev = sysbus_create_simple("versatile_i2c", 0x10002000, NULL); + dev = sysbus_create_simple(TYPE_VERSATILE_I2C, 0x10002000, NULL); i2c = (I2CBus *)qdev_get_child_bus(dev, "i2c"); i2c_create_slave(i2c, "ds1338", 0x68); diff --git a/hw/arm/sabrelite.c b/hw/arm/sabrelite.c index a27e5baf60..91d8c43a7e 100644 --- a/hw/arm/sabrelite.c +++ b/hw/arm/sabrelite.c @@ -77,9 +77,9 @@ static void sabrelite_init(MachineState *machine) flash_dev = qdev_new("sst25vf016b"); if (dinfo) { - qdev_prop_set_drive(flash_dev, "drive", - blk_by_legacy_dinfo(dinfo), - &error_fatal); + qdev_prop_set_drive_err(flash_dev, "drive", + blk_by_legacy_dinfo(dinfo), + &error_fatal); } qdev_realize_and_unref(flash_dev, BUS(spi_bus), &error_fatal); diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c index 2ebdcbd8ac..e596b8170f 100644 --- a/hw/arm/versatilepb.c +++ b/hw/arm/versatilepb.c @@ -18,6 +18,7 @@ #include "sysemu/sysemu.h" #include "hw/pci/pci.h" #include "hw/i2c/i2c.h" +#include "hw/i2c/arm_sbcon_i2c.h" #include "hw/irq.h" #include "hw/boards.h" #include "exec/address-spaces.h" @@ -314,7 +315,7 @@ static void versatile_init(MachineState *machine, int board_id) /* Add PL031 Real Time Clock. */ sysbus_create_simple("pl031", 0x101e8000, pic[10]); - dev = sysbus_create_simple("versatile_i2c", 0x10002000, NULL); + dev = sysbus_create_simple(TYPE_VERSATILE_I2C, 0x10002000, NULL); i2c = (I2CBus *)qdev_get_child_bus(dev, "i2c"); i2c_create_slave(i2c, "ds1338", 0x68); diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index 7ca5d523a4..5bf9cff8a8 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -42,6 +42,7 @@ #include "hw/char/pl011.h" #include "hw/cpu/a9mpcore.h" #include "hw/cpu/a15mpcore.h" +#include "hw/i2c/arm_sbcon_i2c.h" #define VEXPRESS_BOARD_ID 0x8e0 #define VEXPRESS_FLASH_SIZE (64 * 1024 * 1024) @@ -517,8 +518,7 @@ static PFlashCFI01 *ve_pflash_cfi01_register(hwaddr base, const char *name, DeviceState *dev = qdev_new(TYPE_PFLASH_CFI01); if (di) { - qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(di), - &error_abort); + qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(di)); } qdev_prop_set_uint32(dev, "num-blocks", @@ -640,7 +640,7 @@ static void vexpress_common_init(MachineState *machine) sysbus_create_simple("sp804", map[VE_TIMER01], pic[2]); sysbus_create_simple("sp804", map[VE_TIMER23], pic[3]); - dev = sysbus_create_simple("versatile_i2c", map[VE_SERIALDVI], NULL); + dev = sysbus_create_simple(TYPE_VERSATILE_I2C, map[VE_SERIALDVI], NULL); i2c = (I2CBus *)qdev_get_child_bus(dev, "i2c"); i2c_create_slave(i2c, "sii9022", 0x39); diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index ca31f70f7f..1384a2cf2a 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -46,6 +46,7 @@ #include "hw/pci/pci.h" #include "hw/arm/virt.h" #include "hw/mem/nvdimm.h" +#include "hw/platform-bus.h" #include "sysemu/numa.h" #include "sysemu/reset.h" #include "sysemu/tpm.h" @@ -364,6 +365,38 @@ static void acpi_dsdt_add_power_button(Aml *scope) aml_append(scope, dev); } +static void acpi_dsdt_add_tpm(Aml *scope, VirtMachineState *vms) +{ + PlatformBusDevice *pbus = PLATFORM_BUS_DEVICE(vms->platform_bus_dev); + hwaddr pbus_base = vms->memmap[VIRT_PLATFORM_BUS].base; + SysBusDevice *sbdev = SYS_BUS_DEVICE(tpm_find()); + MemoryRegion *sbdev_mr; + hwaddr tpm_base; + + if (!sbdev) { + return; + } + + tpm_base = platform_bus_get_mmio_addr(pbus, sbdev, 0); + assert(tpm_base != -1); + + tpm_base += pbus_base; + + sbdev_mr = sysbus_mmio_get_region(sbdev, 0); + + Aml *dev = aml_device("TPM0"); + aml_append(dev, aml_name_decl("_HID", aml_string("MSFT0101"))); + aml_append(dev, aml_name_decl("_UID", aml_int(0))); + + Aml *crs = aml_resource_template(); + aml_append(crs, + aml_memory32_fixed(tpm_base, + (uint32_t)memory_region_size(sbdev_mr), + AML_READ_WRITE)); + aml_append(dev, aml_name_decl("_CRS", crs)); + aml_append(scope, dev); +} + static void build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { @@ -762,6 +795,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) } acpi_dsdt_add_power_button(scope); + acpi_dsdt_add_tpm(scope, vms); aml_append(dsdt, scope); diff --git a/hw/arm/virt.c b/hw/arm/virt.c index caceb1e4a0..402c362c14 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -2177,11 +2177,68 @@ static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev, } } +static void virt_dimm_unplug_request(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + VirtMachineState *vms = VIRT_MACHINE(hotplug_dev); + Error *local_err = NULL; + + if (!vms->acpi_dev) { + error_setg(&local_err, + "memory hotplug is not enabled: missing acpi-ged device"); + goto out; + } + + if (object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)) { + error_setg(&local_err, + "nvdimm device hot unplug is not supported yet."); + goto out; + } + + hotplug_handler_unplug_request(HOTPLUG_HANDLER(vms->acpi_dev), dev, + &local_err); +out: + error_propagate(errp, local_err); +} + +static void virt_dimm_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + VirtMachineState *vms = VIRT_MACHINE(hotplug_dev); + Error *local_err = NULL; + + hotplug_handler_unplug(HOTPLUG_HANDLER(vms->acpi_dev), dev, &local_err); + if (local_err) { + goto out; + } + + pc_dimm_unplug(PC_DIMM(dev), MACHINE(vms)); + qdev_unrealize(dev); + +out: + error_propagate(errp, local_err); +} + static void virt_machine_device_unplug_request_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - error_setg(errp, "device unplug request for unsupported device" - " type: %s", object_get_typename(OBJECT(dev))); + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + virt_dimm_unplug_request(hotplug_dev, dev, errp); + } else { + error_setg(errp, "device unplug request for unsupported device" + " type: %s", object_get_typename(OBJECT(dev))); + } +} + +static void virt_machine_device_unplug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + virt_dimm_unplug(hotplug_dev, dev, errp); + } else { + error_setg(errp, "virt: device unplug for unsupported device" + " type: %s", object_get_typename(OBJECT(dev))); + } } static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine, @@ -2262,6 +2319,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) hc->pre_plug = virt_machine_device_pre_plug_cb; hc->plug = virt_machine_device_plug_cb; hc->unplug_request = virt_machine_device_unplug_request_cb; + hc->unplug = virt_machine_device_unplug_cb; mc->numa_mem_supported = true; mc->nvdimm_supported = true; mc->auto_enable_numa_with_memhp = true; @@ -2375,6 +2433,7 @@ DEFINE_VIRT_MACHINE_AS_LATEST(5, 1) static void virt_machine_5_0_options(MachineClass *mc) { virt_machine_5_1_options(mc); + compat_props_add(mc->compat_props, hw_compat_5_0, hw_compat_5_0_len); } DEFINE_VIRT_MACHINE(5, 0) diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index 4247c4dbd8..ed970273f3 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -159,8 +159,9 @@ static inline void zynq_init_spi_flashes(uint32_t base_addr, qemu_irq irq, DriveInfo *dinfo = drive_get_next(IF_MTD); flash_dev = qdev_new("n25q128"); if (dinfo) { - qdev_prop_set_drive(flash_dev, "drive", - blk_by_legacy_dinfo(dinfo), &error_fatal); + qdev_prop_set_drive_err(flash_dev, "drive", + blk_by_legacy_dinfo(dinfo), + &error_fatal); } qdev_realize_and_unref(flash_dev, BUS(spi), &error_fatal); @@ -290,7 +291,7 @@ static void zynq_init(MachineState *machine) di = drive_get_next(IF_SD); blk = di ? blk_by_legacy_dinfo(di) : NULL; carddev = qdev_new(TYPE_SD_CARD); - qdev_prop_set_drive(carddev, "drive", blk, &error_fatal); + qdev_prop_set_drive_err(carddev, "drive", blk, &error_fatal); qdev_realize_and_unref(carddev, qdev_get_child_bus(dev, "sd-bus"), &error_fatal); } diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index 5bcca7f95b..a3b1ce9c7c 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -465,7 +465,7 @@ static void sd_plugin_card(SDHCIState *sd, DriveInfo *di) card = qdev_new(TYPE_SD_CARD); object_property_add_child(OBJECT(sd), "card[*]", OBJECT(card)); - qdev_prop_set_drive(card, "drive", blk, &error_fatal); + qdev_prop_set_drive_err(card, "drive", blk, &error_fatal); qdev_realize_and_unref(card, qdev_get_child_bus(DEVICE(sd), "sd-bus"), &error_fatal); } diff --git a/hw/arm/xlnx-zcu102.c b/hw/arm/xlnx-zcu102.c index b920bcee94..77449759c6 100644 --- a/hw/arm/xlnx-zcu102.c +++ b/hw/arm/xlnx-zcu102.c @@ -143,7 +143,7 @@ static void xlnx_zcu102_init(MachineState *machine) exit(1); } carddev = qdev_new(TYPE_SD_CARD); - qdev_prop_set_drive(carddev, "drive", blk, &error_fatal); + qdev_prop_set_drive_err(carddev, "drive", blk, &error_fatal); qdev_realize_and_unref(carddev, bus, &error_fatal); } @@ -159,8 +159,8 @@ static void xlnx_zcu102_init(MachineState *machine) flash_dev = qdev_new("sst25wf080"); if (dinfo) { - qdev_prop_set_drive(flash_dev, "drive", blk_by_legacy_dinfo(dinfo), - &error_fatal); + qdev_prop_set_drive_err(flash_dev, "drive", + blk_by_legacy_dinfo(dinfo), &error_fatal); } qdev_realize_and_unref(flash_dev, spi_bus, &error_fatal); @@ -182,8 +182,8 @@ static void xlnx_zcu102_init(MachineState *machine) flash_dev = qdev_new("n25q512a11"); if (dinfo) { - qdev_prop_set_drive(flash_dev, "drive", blk_by_legacy_dinfo(dinfo), - &error_fatal); + qdev_prop_set_drive_err(flash_dev, "drive", + blk_by_legacy_dinfo(dinfo), &error_fatal); } qdev_realize_and_unref(flash_dev, spi_bus, &error_fatal); diff --git a/hw/block/fdc.c b/hw/block/fdc.c index be0674e4aa..3425d56e2a 100644 --- a/hw/block/fdc.c +++ b/hw/block/fdc.c @@ -32,6 +32,7 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/timer.h" +#include "hw/acpi/aml-build.h" #include "hw/irq.h" #include "hw/isa/isa.h" #include "hw/qdev-properties.h" @@ -2497,6 +2498,29 @@ static void fdctrl_result_timer(void *opaque) } /* Init functions */ + +static void fdctrl_init_drives(FloppyBus *bus, DriveInfo **fds) +{ + DeviceState *dev; + int i; + + for (i = 0; i < MAX_FD; i++) { + if (fds[i]) { + dev = qdev_new("floppy"); + qdev_prop_set_uint32(dev, "unit", i); + qdev_prop_set_enum(dev, "drive-type", FLOPPY_DRIVE_TYPE_AUTO); + qdev_prop_set_drive_err(dev, "drive", blk_by_legacy_dinfo(fds[i]), + &error_fatal); + qdev_realize_and_unref(dev, &bus->bus, &error_fatal); + } + } +} + +void isa_fdc_init_drives(ISADevice *fdc, DriveInfo **fds) +{ + fdctrl_init_drives(&ISA_FDC(fdc)->state.bus, fds); +} + static void fdctrl_connect_drives(FDCtrl *fdctrl, DeviceState *fdc_dev, Error **errp) { @@ -2505,6 +2529,7 @@ static void fdctrl_connect_drives(FDCtrl *fdctrl, DeviceState *fdc_dev, DeviceState *dev; BlockBackend *blk; Error *local_err = NULL; + const char *fdc_name, *drive_suffix; for (i = 0; i < MAX_FD; i++) { drive = &fdctrl->drives[i]; @@ -2519,14 +2544,30 @@ static void fdctrl_connect_drives(FDCtrl *fdctrl, DeviceState *fdc_dev, continue; } + fdc_name = object_get_typename(OBJECT(fdc_dev)); + drive_suffix = !strcmp(fdc_name, "SUNW,fdtwo") ? "" : i ? "B" : "A"; + warn_report("warning: property %s.drive%s is deprecated", + fdc_name, drive_suffix); + error_printf("Use -device floppy,unit=%d,drive=... instead.\n", i); + dev = qdev_new("floppy"); qdev_prop_set_uint32(dev, "unit", i); qdev_prop_set_enum(dev, "drive-type", fdctrl->qdev_for_drives[i].type); + /* + * Hack alert: we move the backend from the floppy controller + * device to the floppy device. We first need to detach the + * controller, or else floppy_create()'s qdev_prop_set_drive() + * will die when it attaches floppy device. We also need to + * take another reference so that blk_detach_dev() doesn't + * free blk while we still need it. + * + * The hack is probably a bad idea. + */ blk_ref(blk); blk_detach_dev(blk, fdc_dev); fdctrl->qdev_for_drives[i].blk = NULL; - qdev_prop_set_drive(dev, "drive", blk, &local_err); + qdev_prop_set_drive_err(dev, "drive", blk, &local_err); blk_unref(blk); if (local_err) { @@ -2542,30 +2583,6 @@ static void fdctrl_connect_drives(FDCtrl *fdctrl, DeviceState *fdc_dev, } } -ISADevice *fdctrl_init_isa(ISABus *bus, DriveInfo **fds) -{ - DeviceState *dev; - ISADevice *isadev; - - isadev = isa_try_new(TYPE_ISA_FDC); - if (!isadev) { - return NULL; - } - dev = DEVICE(isadev); - - if (fds[0]) { - qdev_prop_set_drive(dev, "driveA", blk_by_legacy_dinfo(fds[0]), - &error_fatal); - } - if (fds[1]) { - qdev_prop_set_drive(dev, "driveB", blk_by_legacy_dinfo(fds[1]), - &error_fatal); - } - isa_realize_and_unref(isadev, bus, &error_fatal); - - return isadev; -} - void fdctrl_init_sysbus(qemu_irq irq, int dma_chann, hwaddr mmio_base, DriveInfo **fds) { @@ -2578,18 +2595,12 @@ void fdctrl_init_sysbus(qemu_irq irq, int dma_chann, sys = SYSBUS_FDC(dev); fdctrl = &sys->state; fdctrl->dma_chann = dma_chann; /* FIXME */ - if (fds[0]) { - qdev_prop_set_drive(dev, "driveA", blk_by_legacy_dinfo(fds[0]), - &error_fatal); - } - if (fds[1]) { - qdev_prop_set_drive(dev, "driveB", blk_by_legacy_dinfo(fds[1]), - &error_fatal); - } sbd = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(sbd, &error_fatal); sysbus_connect_irq(sbd, 0, irq); sysbus_mmio_map(sbd, 0, mmio_base); + + fdctrl_init_drives(&sys->state.bus, fds); } void sun4m_fdctrl_init(qemu_irq irq, hwaddr io_base, @@ -2599,15 +2610,13 @@ void sun4m_fdctrl_init(qemu_irq irq, hwaddr io_base, FDCtrlSysBus *sys; dev = qdev_new("SUNW,fdtwo"); - if (fds[0]) { - qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(fds[0]), - &error_fatal); - } sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); sys = SYSBUS_FDC(dev); sysbus_connect_irq(SYS_BUS_DEVICE(sys), 0, irq); sysbus_mmio_map(SYS_BUS_DEVICE(sys), 0, io_base); *fdc_tc = qdev_get_gpio_in(dev, 0); + + fdctrl_init_drives(&sys->state.bus, fds); } static void fdctrl_realize_common(DeviceState *dev, FDCtrl *fdctrl, @@ -2745,8 +2754,8 @@ FloppyDriveType isa_fdc_get_drive_type(ISADevice *fdc, int i) return isa->state.drives[i].drive; } -void isa_fdc_get_drive_max_chs(FloppyDriveType type, - uint8_t *maxc, uint8_t *maxh, uint8_t *maxs) +static void isa_fdc_get_drive_max_chs(FloppyDriveType type, uint8_t *maxc, + uint8_t *maxh, uint8_t *maxs) { const FDFormat *fdf; @@ -2768,6 +2777,110 @@ void isa_fdc_get_drive_max_chs(FloppyDriveType type, (*maxc)--; } +static Aml *build_fdinfo_aml(int idx, FloppyDriveType type) +{ + Aml *dev, *fdi; + uint8_t maxc, maxh, maxs; + + isa_fdc_get_drive_max_chs(type, &maxc, &maxh, &maxs); + + dev = aml_device("FLP%c", 'A' + idx); + + aml_append(dev, aml_name_decl("_ADR", aml_int(idx))); + + fdi = aml_package(16); + aml_append(fdi, aml_int(idx)); /* Drive Number */ + aml_append(fdi, + aml_int(cmos_get_fd_drive_type(type))); /* Device Type */ + /* + * the values below are the limits of the drive, and are thus independent + * of the inserted media + */ + aml_append(fdi, aml_int(maxc)); /* Maximum Cylinder Number */ + aml_append(fdi, aml_int(maxs)); /* Maximum Sector Number */ + aml_append(fdi, aml_int(maxh)); /* Maximum Head Number */ + /* + * SeaBIOS returns the below values for int 0x13 func 0x08 regardless of + * the drive type, so shall we + */ + aml_append(fdi, aml_int(0xAF)); /* disk_specify_1 */ + aml_append(fdi, aml_int(0x02)); /* disk_specify_2 */ + aml_append(fdi, aml_int(0x25)); /* disk_motor_wait */ + aml_append(fdi, aml_int(0x02)); /* disk_sector_siz */ + aml_append(fdi, aml_int(0x12)); /* disk_eot */ + aml_append(fdi, aml_int(0x1B)); /* disk_rw_gap */ + aml_append(fdi, aml_int(0xFF)); /* disk_dtl */ + aml_append(fdi, aml_int(0x6C)); /* disk_formt_gap */ + aml_append(fdi, aml_int(0xF6)); /* disk_fill */ + aml_append(fdi, aml_int(0x0F)); /* disk_head_sttl */ + aml_append(fdi, aml_int(0x08)); /* disk_motor_strt */ + + aml_append(dev, aml_name_decl("_FDI", fdi)); + return dev; +} + +int cmos_get_fd_drive_type(FloppyDriveType fd0) +{ + int val; + + switch (fd0) { + case FLOPPY_DRIVE_TYPE_144: + /* 1.44 Mb 3"5 drive */ + val = 4; + break; + case FLOPPY_DRIVE_TYPE_288: + /* 2.88 Mb 3"5 drive */ + val = 5; + break; + case FLOPPY_DRIVE_TYPE_120: + /* 1.2 Mb 5"5 drive */ + val = 2; + break; + case FLOPPY_DRIVE_TYPE_NONE: + default: + val = 0; + break; + } + return val; +} + +static void fdc_isa_build_aml(ISADevice *isadev, Aml *scope) +{ + Aml *dev; + Aml *crs; + int i; + +#define ACPI_FDE_MAX_FD 4 + uint32_t fde_buf[5] = { + 0, 0, 0, 0, /* presence of floppy drives #0 - #3 */ + cpu_to_le32(2) /* tape presence (2 == never present) */ + }; + + crs = aml_resource_template(); + aml_append(crs, aml_io(AML_DECODE16, 0x03F2, 0x03F2, 0x00, 0x04)); + aml_append(crs, aml_io(AML_DECODE16, 0x03F7, 0x03F7, 0x00, 0x01)); + aml_append(crs, aml_irq_no_flags(6)); + aml_append(crs, + aml_dma(AML_COMPATIBILITY, AML_NOTBUSMASTER, AML_TRANSFER8, 2)); + + dev = aml_device("FDC0"); + aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0700"))); + aml_append(dev, aml_name_decl("_CRS", crs)); + + for (i = 0; i < MIN(MAX_FD, ACPI_FDE_MAX_FD); i++) { + FloppyDriveType type = isa_fdc_get_drive_type(isadev, i); + + if (type < FLOPPY_DRIVE_TYPE_NONE) { + fde_buf[i] = cpu_to_le32(1); /* drive present */ + aml_append(dev, build_fdinfo_aml(i, type)); + } + } + aml_append(dev, aml_name_decl("_FDE", + aml_buffer(sizeof(fde_buf), (uint8_t *)fde_buf))); + + aml_append(scope, dev); +} + static const VMStateDescription vmstate_isa_fdc ={ .name = "fdc", .version_id = 2, @@ -2801,11 +2914,13 @@ static Property isa_fdc_properties[] = { static void isabus_fdc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ISADeviceClass *isa = ISA_DEVICE_CLASS(klass); dc->realize = isabus_fdc_realize; dc->fw_name = "fdc"; dc->reset = fdctrl_external_reset_isa; dc->vmsd = &vmstate_isa_fdc; + isa->build_aml = fdc_isa_build_aml; device_class_set_props(dc, isa_fdc_properties); set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } diff --git a/hw/block/nand.c b/hw/block/nand.c index 7e25681d59..654e0cb5d1 100644 --- a/hw/block/nand.c +++ b/hw/block/nand.c @@ -648,7 +648,7 @@ DeviceState *nand_init(BlockBackend *blk, int manf_id, int chip_id) qdev_prop_set_uint8(dev, "manufacturer_id", manf_id); qdev_prop_set_uint8(dev, "chip_id", chip_id); if (blk) { - qdev_prop_set_drive(dev, "drive", blk, &error_fatal); + qdev_prop_set_drive_err(dev, "drive", blk, &error_fatal); } qdev_realize(dev, NULL, &error_fatal); diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index 9f0c1d61ca..cddc3a5a0c 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -962,7 +962,7 @@ PFlashCFI01 *pflash_cfi01_register(hwaddr base, DeviceState *dev = qdev_new(TYPE_PFLASH_CFI01); if (blk) { - qdev_prop_set_drive(dev, "drive", blk, &error_abort); + qdev_prop_set_drive(dev, "drive", blk); } assert(QEMU_IS_ALIGNED(size, sector_len)); qdev_prop_set_uint32(dev, "num-blocks", size / sector_len); @@ -1010,8 +1010,8 @@ void pflash_cfi01_legacy_drive(PFlashCFI01 *fl, DriveInfo *dinfo) error_report("clashes with -machine"); exit(1); } - qdev_prop_set_drive(DEVICE(fl), "drive", - blk_by_legacy_dinfo(dinfo), &error_fatal); + qdev_prop_set_drive_err(DEVICE(fl), "drive", blk_by_legacy_dinfo(dinfo), + &error_fatal); loc_pop(&loc); } diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c index 6eb66e7bb0..b40ce2335a 100644 --- a/hw/block/pflash_cfi02.c +++ b/hw/block/pflash_cfi02.c @@ -1001,7 +1001,7 @@ PFlashCFI02 *pflash_cfi02_register(hwaddr base, DeviceState *dev = qdev_new(TYPE_PFLASH_CFI02); if (blk) { - qdev_prop_set_drive(dev, "drive", blk, &error_abort); + qdev_prop_set_drive(dev, "drive", blk); } assert(QEMU_IS_ALIGNED(size, sector_len)); qdev_prop_set_uint32(dev, "num-blocks", size / sector_len); diff --git a/hw/char/Kconfig b/hw/char/Kconfig index 40e7a8b8bb..874627520c 100644 --- a/hw/char/Kconfig +++ b/hw/char/Kconfig @@ -46,3 +46,6 @@ config SCLPCONSOLE config TERMINAL3270 bool + +config RENESAS_SCI + bool diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs index 633996be5b..8306c4a789 100644 --- a/hw/char/Makefile.objs +++ b/hw/char/Makefile.objs @@ -21,6 +21,7 @@ common-obj-$(CONFIG_SH4) += sh_serial.o common-obj-$(CONFIG_DIGIC) += digic-uart.o common-obj-$(CONFIG_STM32F2XX_USART) += stm32f2xx_usart.o common-obj-$(CONFIG_RASPI) += bcm2835_aux.o +common-obj-$(CONFIG_RENESAS_SCI) += renesas_sci.o common-obj-$(CONFIG_CMSDK_APB_UART) += cmsdk-apb-uart.o common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o diff --git a/hw/char/renesas_sci.c b/hw/char/renesas_sci.c new file mode 100644 index 0000000000..5d7c6e6523 --- /dev/null +++ b/hw/char/renesas_sci.c @@ -0,0 +1,350 @@ +/* + * Renesas Serial Communication Interface + * + * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware + * (Rev.1.40 R01UH0033EJ0140) + * + * Copyright (c) 2019 Yoshinori Sato + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/irq.h" +#include "hw/registerfields.h" +#include "hw/qdev-properties.h" +#include "hw/char/renesas_sci.h" +#include "migration/vmstate.h" + +/* SCI register map */ +REG8(SMR, 0) + FIELD(SMR, CKS, 0, 2) + FIELD(SMR, MP, 2, 1) + FIELD(SMR, STOP, 3, 1) + FIELD(SMR, PM, 4, 1) + FIELD(SMR, PE, 5, 1) + FIELD(SMR, CHR, 6, 1) + FIELD(SMR, CM, 7, 1) +REG8(BRR, 1) +REG8(SCR, 2) + FIELD(SCR, CKE, 0, 2) + FIELD(SCR, TEIE, 2, 1) + FIELD(SCR, MPIE, 3, 1) + FIELD(SCR, RE, 4, 1) + FIELD(SCR, TE, 5, 1) + FIELD(SCR, RIE, 6, 1) + FIELD(SCR, TIE, 7, 1) +REG8(TDR, 3) +REG8(SSR, 4) + FIELD(SSR, MPBT, 0, 1) + FIELD(SSR, MPB, 1, 1) + FIELD(SSR, TEND, 2, 1) + FIELD(SSR, ERR, 3, 3) + FIELD(SSR, PER, 3, 1) + FIELD(SSR, FER, 4, 1) + FIELD(SSR, ORER, 5, 1) + FIELD(SSR, RDRF, 6, 1) + FIELD(SSR, TDRE, 7, 1) +REG8(RDR, 5) +REG8(SCMR, 6) + FIELD(SCMR, SMIF, 0, 1) + FIELD(SCMR, SINV, 2, 1) + FIELD(SCMR, SDIR, 3, 1) + FIELD(SCMR, BCP2, 7, 1) +REG8(SEMR, 7) + FIELD(SEMR, ACS0, 0, 1) + FIELD(SEMR, ABCS, 4, 1) + +static int can_receive(void *opaque) +{ + RSCIState *sci = RSCI(opaque); + if (sci->rx_next > qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)) { + return 0; + } else { + return FIELD_EX8(sci->scr, SCR, RE); + } +} + +static void receive(void *opaque, const uint8_t *buf, int size) +{ + RSCIState *sci = RSCI(opaque); + sci->rx_next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + sci->trtime; + if (FIELD_EX8(sci->ssr, SSR, RDRF) || size > 1) { + sci->ssr = FIELD_DP8(sci->ssr, SSR, ORER, 1); + if (FIELD_EX8(sci->scr, SCR, RIE)) { + qemu_set_irq(sci->irq[ERI], 1); + } + } else { + sci->rdr = buf[0]; + sci->ssr = FIELD_DP8(sci->ssr, SSR, RDRF, 1); + if (FIELD_EX8(sci->scr, SCR, RIE)) { + qemu_irq_pulse(sci->irq[RXI]); + } + } +} + +static void send_byte(RSCIState *sci) +{ + if (qemu_chr_fe_backend_connected(&sci->chr)) { + qemu_chr_fe_write_all(&sci->chr, &sci->tdr, 1); + } + timer_mod(&sci->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + sci->trtime); + sci->ssr = FIELD_DP8(sci->ssr, SSR, TEND, 0); + sci->ssr = FIELD_DP8(sci->ssr, SSR, TDRE, 1); + qemu_set_irq(sci->irq[TEI], 0); + if (FIELD_EX8(sci->scr, SCR, TIE)) { + qemu_irq_pulse(sci->irq[TXI]); + } +} + +static void txend(void *opaque) +{ + RSCIState *sci = RSCI(opaque); + if (!FIELD_EX8(sci->ssr, SSR, TDRE)) { + send_byte(sci); + } else { + sci->ssr = FIELD_DP8(sci->ssr, SSR, TEND, 1); + if (FIELD_EX8(sci->scr, SCR, TEIE)) { + qemu_set_irq(sci->irq[TEI], 1); + } + } +} + +static void update_trtime(RSCIState *sci) +{ + /* char per bits */ + sci->trtime = 8 - FIELD_EX8(sci->smr, SMR, CHR); + sci->trtime += FIELD_EX8(sci->smr, SMR, PE); + sci->trtime += FIELD_EX8(sci->smr, SMR, STOP) + 1; + /* x bit transmit time (32 * divrate * brr) / base freq */ + sci->trtime *= 32 * sci->brr; + sci->trtime *= 1 << (2 * FIELD_EX8(sci->smr, SMR, CKS)); + sci->trtime *= NANOSECONDS_PER_SECOND; + sci->trtime /= sci->input_freq; +} + +static bool sci_is_tr_enabled(RSCIState *sci) +{ + return FIELD_EX8(sci->scr, SCR, TE) || FIELD_EX8(sci->scr, SCR, RE); +} + +static void sci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) +{ + RSCIState *sci = RSCI(opaque); + + switch (offset) { + case A_SMR: + if (!sci_is_tr_enabled(sci)) { + sci->smr = val; + update_trtime(sci); + } + break; + case A_BRR: + if (!sci_is_tr_enabled(sci)) { + sci->brr = val; + update_trtime(sci); + } + break; + case A_SCR: + sci->scr = val; + if (FIELD_EX8(sci->scr, SCR, TE)) { + sci->ssr = FIELD_DP8(sci->ssr, SSR, TDRE, 1); + sci->ssr = FIELD_DP8(sci->ssr, SSR, TEND, 1); + if (FIELD_EX8(sci->scr, SCR, TIE)) { + qemu_irq_pulse(sci->irq[TXI]); + } + } + if (!FIELD_EX8(sci->scr, SCR, TEIE)) { + qemu_set_irq(sci->irq[TEI], 0); + } + if (!FIELD_EX8(sci->scr, SCR, RIE)) { + qemu_set_irq(sci->irq[ERI], 0); + } + break; + case A_TDR: + sci->tdr = val; + if (FIELD_EX8(sci->ssr, SSR, TEND)) { + send_byte(sci); + } else { + sci->ssr = FIELD_DP8(sci->ssr, SSR, TDRE, 0); + } + break; + case A_SSR: + sci->ssr = FIELD_DP8(sci->ssr, SSR, MPBT, + FIELD_EX8(val, SSR, MPBT)); + sci->ssr = FIELD_DP8(sci->ssr, SSR, ERR, + FIELD_EX8(val, SSR, ERR) & 0x07); + if (FIELD_EX8(sci->read_ssr, SSR, ERR) && + FIELD_EX8(sci->ssr, SSR, ERR) == 0) { + qemu_set_irq(sci->irq[ERI], 0); + } + break; + case A_RDR: + qemu_log_mask(LOG_GUEST_ERROR, "reneas_sci: RDR is read only.\n"); + break; + case A_SCMR: + sci->scmr = val; break; + case A_SEMR: /* SEMR */ + sci->semr = val; break; + default: + qemu_log_mask(LOG_UNIMP, "renesas_sci: Register 0x%" HWADDR_PRIX " " + "not implemented\n", + offset); + } +} + +static uint64_t sci_read(void *opaque, hwaddr offset, unsigned size) +{ + RSCIState *sci = RSCI(opaque); + + switch (offset) { + case A_SMR: + return sci->smr; + case A_BRR: + return sci->brr; + case A_SCR: + return sci->scr; + case A_TDR: + return sci->tdr; + case A_SSR: + sci->read_ssr = sci->ssr; + return sci->ssr; + case A_RDR: + sci->ssr = FIELD_DP8(sci->ssr, SSR, RDRF, 0); + return sci->rdr; + case A_SCMR: + return sci->scmr; + case A_SEMR: + return sci->semr; + default: + qemu_log_mask(LOG_UNIMP, "renesas_sci: Register 0x%" HWADDR_PRIX + " not implemented.\n", offset); + } + return UINT64_MAX; +} + +static const MemoryRegionOps sci_ops = { + .write = sci_write, + .read = sci_read, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl.max_access_size = 1, + .valid.max_access_size = 1, +}; + +static void rsci_reset(DeviceState *dev) +{ + RSCIState *sci = RSCI(dev); + sci->smr = sci->scr = 0x00; + sci->brr = 0xff; + sci->tdr = 0xff; + sci->rdr = 0x00; + sci->ssr = 0x84; + sci->scmr = 0x00; + sci->semr = 0x00; + sci->rx_next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); +} + +static void sci_event(void *opaque, QEMUChrEvent event) +{ + RSCIState *sci = RSCI(opaque); + if (event == CHR_EVENT_BREAK) { + sci->ssr = FIELD_DP8(sci->ssr, SSR, FER, 1); + if (FIELD_EX8(sci->scr, SCR, RIE)) { + qemu_set_irq(sci->irq[ERI], 1); + } + } +} + +static void rsci_realize(DeviceState *dev, Error **errp) +{ + RSCIState *sci = RSCI(dev); + + if (sci->input_freq == 0) { + qemu_log_mask(LOG_GUEST_ERROR, + "renesas_sci: input-freq property must be set."); + return; + } + qemu_chr_fe_set_handlers(&sci->chr, can_receive, receive, + sci_event, NULL, sci, NULL, true); +} + +static void rsci_init(Object *obj) +{ + SysBusDevice *d = SYS_BUS_DEVICE(obj); + RSCIState *sci = RSCI(obj); + int i; + + memory_region_init_io(&sci->memory, OBJECT(sci), &sci_ops, + sci, "renesas-sci", 0x8); + sysbus_init_mmio(d, &sci->memory); + + for (i = 0; i < SCI_NR_IRQ; i++) { + sysbus_init_irq(d, &sci->irq[i]); + } + timer_init_ns(&sci->timer, QEMU_CLOCK_VIRTUAL, txend, sci); +} + +static const VMStateDescription vmstate_rsci = { + .name = "renesas-sci", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_INT64(trtime, RSCIState), + VMSTATE_INT64(rx_next, RSCIState), + VMSTATE_UINT8(smr, RSCIState), + VMSTATE_UINT8(brr, RSCIState), + VMSTATE_UINT8(scr, RSCIState), + VMSTATE_UINT8(tdr, RSCIState), + VMSTATE_UINT8(ssr, RSCIState), + VMSTATE_UINT8(rdr, RSCIState), + VMSTATE_UINT8(scmr, RSCIState), + VMSTATE_UINT8(semr, RSCIState), + VMSTATE_UINT8(read_ssr, RSCIState), + VMSTATE_TIMER(timer, RSCIState), + VMSTATE_END_OF_LIST() + } +}; + +static Property rsci_properties[] = { + DEFINE_PROP_UINT64("input-freq", RSCIState, input_freq, 0), + DEFINE_PROP_CHR("chardev", RSCIState, chr), + DEFINE_PROP_END_OF_LIST(), +}; + +static void rsci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = rsci_realize; + dc->vmsd = &vmstate_rsci; + dc->reset = rsci_reset; + device_class_set_props(dc, rsci_properties); +} + +static const TypeInfo rsci_info = { + .name = TYPE_RENESAS_SCI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(RSCIState), + .instance_init = rsci_init, + .class_init = rsci_class_init, +}; + +static void rsci_register_types(void) +{ + type_register_static(&rsci_info); +} + +type_init(rsci_register_types) diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index 70bfd4809b..38b0c9f09b 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -25,29 +25,67 @@ #include "sysemu/iothread.h" #include "sysemu/tpm_backend.h" -static void get_pointer(Object *obj, Visitor *v, Property *prop, - char *(*print)(void *ptr), - const char *name, Error **errp) +static bool check_prop_still_unset(DeviceState *dev, const char *name, + const void *old_val, const char *new_val, + Error **errp) +{ + const GlobalProperty *prop = qdev_find_global_prop(dev, name); + + if (!old_val) { + return true; + } + + if (prop) { + error_setg(errp, "-global %s.%s=... conflicts with %s=%s", + prop->driver, prop->property, name, new_val); + } else { + /* Error message is vague, but a better one would be hard */ + error_setg(errp, "%s=%s conflicts, and override is not implemented", + name, new_val); + } + return false; +} + + +/* --- drive --- */ + +static void get_drive(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) { DeviceState *dev = DEVICE(obj); + Property *prop = opaque; void **ptr = qdev_get_prop_ptr(dev, prop); + const char *value; char *p; - p = *ptr ? print(*ptr) : g_strdup(""); + if (*ptr) { + value = blk_name(*ptr); + if (!*value) { + BlockDriverState *bs = blk_bs(*ptr); + if (bs) { + value = bdrv_get_node_name(bs); + } + } + } else { + value = ""; + } + + p = g_strdup(value); visit_type_str(v, name, &p, errp); g_free(p); } -static void set_pointer(Object *obj, Visitor *v, Property *prop, - void (*parse)(DeviceState *dev, const char *str, - void **ptr, const char *propname, - Error **errp), - const char *name, Error **errp) +static void set_drive_helper(Object *obj, Visitor *v, const char *name, + void *opaque, bool iothread, Error **errp) { DeviceState *dev = DEVICE(obj); + Property *prop = opaque; Error *local_err = NULL; void **ptr = qdev_get_prop_ptr(dev, prop); char *str; + BlockBackend *blk; + bool blk_created = false; + int ret; if (dev->realized) { qdev_prop_set_after_realize(dev, name, errp); @@ -59,23 +97,20 @@ static void set_pointer(Object *obj, Visitor *v, Property *prop, error_propagate(errp, local_err); return; } + + /* + * TODO Should this really be an error? If no, the old value + * needs to be released before we store the new one. + */ + if (!check_prop_still_unset(dev, name, *ptr, str, errp)) { + return; + } + if (!*str) { g_free(str); *ptr = NULL; return; } - parse(dev, str, ptr, prop->name, errp); - g_free(str); -} - -/* --- drive --- */ - -static void do_parse_drive(DeviceState *dev, const char *str, void **ptr, - const char *propname, bool iothread, Error **errp) -{ - BlockBackend *blk; - bool blk_created = false; - int ret; blk = blk_by_name(str); if (!blk) { @@ -101,7 +136,7 @@ static void do_parse_drive(DeviceState *dev, const char *str, void **ptr, } if (!blk) { error_setg(errp, "Property '%s.%s' can't find value '%s'", - object_get_typename(OBJECT(dev)), propname, str); + object_get_typename(OBJECT(dev)), prop->name, str); goto fail; } if (blk_attach_dev(blk, dev) < 0) { @@ -126,18 +161,20 @@ fail: /* If we need to keep a reference, blk_attach_dev() took it */ blk_unref(blk); } + + g_free(str); } -static void parse_drive(DeviceState *dev, const char *str, void **ptr, - const char *propname, Error **errp) +static void set_drive(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) { - do_parse_drive(dev, str, ptr, propname, false, errp); + set_drive_helper(obj, v, name, opaque, false, errp); } -static void parse_drive_iothread(DeviceState *dev, const char *str, void **ptr, - const char *propname, Error **errp) +static void set_drive_iothread(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { - do_parse_drive(dev, str, ptr, propname, true, errp); + set_drive_helper(obj, v, name, opaque, true, errp); } static void release_drive(Object *obj, const char *name, void *opaque) @@ -156,38 +193,6 @@ static void release_drive(Object *obj, const char *name, void *opaque) } } -static char *print_drive(void *ptr) -{ - const char *name; - - name = blk_name(ptr); - if (!*name) { - BlockDriverState *bs = blk_bs(ptr); - if (bs) { - name = bdrv_get_node_name(bs); - } - } - return g_strdup(name); -} - -static void get_drive(Object *obj, Visitor *v, const char *name, void *opaque, - Error **errp) -{ - get_pointer(obj, v, opaque, print_drive, name, errp); -} - -static void set_drive(Object *obj, Visitor *v, const char *name, void *opaque, - Error **errp) -{ - set_pointer(obj, v, opaque, parse_drive, name, errp); -} - -static void set_drive_iothread(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - set_pointer(obj, v, opaque, parse_drive_iothread, name, errp); -} - const PropertyInfo qdev_prop_drive = { .name = "str", .description = "Node name or ID of a block device to use as a backend", @@ -239,6 +244,14 @@ static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque, return; } + /* + * TODO Should this really be an error? If no, the old value + * needs to be released before we store the new one. + */ + if (!check_prop_still_unset(dev, name, be->chr, str, errp)) { + return; + } + if (!*str) { g_free(str); be->chr = NULL; @@ -324,14 +337,16 @@ static void set_netdev(Object *obj, Visitor *v, const char *name, } for (i = 0; i < queues; i++) { - if (peers[i]->peer) { err = -EEXIST; goto out; } - if (ncs[i]) { - err = -EINVAL; + /* + * TODO Should this really be an error? If no, the old value + * needs to be released before we store the new one. + */ + if (!check_prop_still_unset(dev, name, ncs[i], str, errp)) { goto out; } @@ -410,8 +425,8 @@ const PropertyInfo qdev_prop_audiodev = { .set = set_audiodev, }; -void qdev_prop_set_drive(DeviceState *dev, const char *name, - BlockBackend *value, Error **errp) +void qdev_prop_set_drive_err(DeviceState *dev, const char *name, + BlockBackend *value, Error **errp) { const char *ref = ""; @@ -428,6 +443,12 @@ void qdev_prop_set_drive(DeviceState *dev, const char *name, object_property_set_str(OBJECT(dev), ref, name, errp); } +void qdev_prop_set_drive(DeviceState *dev, const char *name, + BlockBackend *value) +{ + qdev_prop_set_drive_err(dev, name, value, &error_abort); +} + void qdev_prop_set_chr(DeviceState *dev, const char *name, Chardev *value) { diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index ead35d7ffd..71f8aca7c6 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -1246,6 +1246,23 @@ void qdev_prop_register_global(GlobalProperty *prop) g_ptr_array_add(global_props(), prop); } +const GlobalProperty *qdev_find_global_prop(DeviceState *dev, + const char *name) +{ + GPtrArray *props = global_props(); + const GlobalProperty *p; + int i; + + for (i = 0; i < props->len; i++) { + p = g_ptr_array_index(props, i); + if (object_dynamic_cast(OBJECT(dev), p->driver) + && !strcmp(p->property, name)) { + return p; + } + } + return NULL; +} + int qdev_prop_check_globals(void) { int i, ret = 0; diff --git a/hw/i2c/versatile_i2c.c b/hw/i2c/versatile_i2c.c index 1ac2a6f59a..da8cda2ec1 100644 --- a/hw/i2c/versatile_i2c.c +++ b/hw/i2c/versatile_i2c.c @@ -1,5 +1,6 @@ /* - * ARM Versatile I2C controller + * ARM SBCon two-wire serial bus interface (I2C bitbang) + * a.k.a. ARM Versatile I2C controller * * Copyright (c) 2006-2007 CodeSourcery. * Copyright (c) 2012 Oskar Andero <oskar.andero@gmail.com> @@ -22,32 +23,33 @@ */ #include "qemu/osdep.h" -#include "hw/sysbus.h" -#include "hw/i2c/bitbang_i2c.h" +#include "hw/i2c/arm_sbcon_i2c.h" +#include "hw/registerfields.h" #include "qemu/log.h" #include "qemu/module.h" -#define TYPE_VERSATILE_I2C "versatile_i2c" #define VERSATILE_I2C(obj) \ OBJECT_CHECK(VersatileI2CState, (obj), TYPE_VERSATILE_I2C) -typedef struct VersatileI2CState { - SysBusDevice parent_obj; +typedef ArmSbconI2CState VersatileI2CState; - MemoryRegion iomem; - bitbang_i2c_interface bitbang; - int out; - int in; -} VersatileI2CState; + +REG32(CONTROL_GET, 0) +REG32(CONTROL_SET, 0) +REG32(CONTROL_CLR, 4) + +#define SCL BIT(0) +#define SDA BIT(1) static uint64_t versatile_i2c_read(void *opaque, hwaddr offset, unsigned size) { VersatileI2CState *s = (VersatileI2CState *)opaque; - if (offset == 0) { + switch (offset) { + case A_CONTROL_SET: return (s->out & 1) | (s->in << 1); - } else { + default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%x\n", __func__, (int)offset); return -1; @@ -60,18 +62,18 @@ static void versatile_i2c_write(void *opaque, hwaddr offset, VersatileI2CState *s = (VersatileI2CState *)opaque; switch (offset) { - case 0: + case A_CONTROL_SET: s->out |= value & 3; break; - case 4: + case A_CONTROL_CLR: s->out &= ~value; break; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%x\n", __func__, (int)offset); } - bitbang_i2c_set(&s->bitbang, BITBANG_I2C_SCL, (s->out & 1) != 0); - s->in = bitbang_i2c_set(&s->bitbang, BITBANG_I2C_SDA, (s->out & 2) != 0); + bitbang_i2c_set(&s->bitbang, BITBANG_I2C_SCL, (s->out & SCL) != 0); + s->in = bitbang_i2c_set(&s->bitbang, BITBANG_I2C_SDA, (s->out & SDA) != 0); } static const MemoryRegionOps versatile_i2c_ops = { @@ -90,7 +92,7 @@ static void versatile_i2c_init(Object *obj) bus = i2c_init_bus(dev, "i2c"); bitbang_i2c_init(&s->bitbang, bus); memory_region_init_io(&s->iomem, obj, &versatile_i2c_ops, s, - "versatile_i2c", 0x1000); + "arm_sbcon_i2c", 0x1000); sysbus_init_mmio(sbd, &s->iomem); } diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 900f786d08..378515df66 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -938,121 +938,6 @@ static void build_hpet_aml(Aml *table) aml_append(table, scope); } -static Aml *build_fdinfo_aml(int idx, FloppyDriveType type) -{ - Aml *dev, *fdi; - uint8_t maxc, maxh, maxs; - - isa_fdc_get_drive_max_chs(type, &maxc, &maxh, &maxs); - - dev = aml_device("FLP%c", 'A' + idx); - - aml_append(dev, aml_name_decl("_ADR", aml_int(idx))); - - fdi = aml_package(16); - aml_append(fdi, aml_int(idx)); /* Drive Number */ - aml_append(fdi, - aml_int(cmos_get_fd_drive_type(type))); /* Device Type */ - /* - * the values below are the limits of the drive, and are thus independent - * of the inserted media - */ - aml_append(fdi, aml_int(maxc)); /* Maximum Cylinder Number */ - aml_append(fdi, aml_int(maxs)); /* Maximum Sector Number */ - aml_append(fdi, aml_int(maxh)); /* Maximum Head Number */ - /* - * SeaBIOS returns the below values for int 0x13 func 0x08 regardless of - * the drive type, so shall we - */ - aml_append(fdi, aml_int(0xAF)); /* disk_specify_1 */ - aml_append(fdi, aml_int(0x02)); /* disk_specify_2 */ - aml_append(fdi, aml_int(0x25)); /* disk_motor_wait */ - aml_append(fdi, aml_int(0x02)); /* disk_sector_siz */ - aml_append(fdi, aml_int(0x12)); /* disk_eot */ - aml_append(fdi, aml_int(0x1B)); /* disk_rw_gap */ - aml_append(fdi, aml_int(0xFF)); /* disk_dtl */ - aml_append(fdi, aml_int(0x6C)); /* disk_formt_gap */ - aml_append(fdi, aml_int(0xF6)); /* disk_fill */ - aml_append(fdi, aml_int(0x0F)); /* disk_head_sttl */ - aml_append(fdi, aml_int(0x08)); /* disk_motor_strt */ - - aml_append(dev, aml_name_decl("_FDI", fdi)); - return dev; -} - -static Aml *build_fdc_device_aml(ISADevice *fdc) -{ - int i; - Aml *dev; - Aml *crs; - -#define ACPI_FDE_MAX_FD 4 - uint32_t fde_buf[5] = { - 0, 0, 0, 0, /* presence of floppy drives #0 - #3 */ - cpu_to_le32(2) /* tape presence (2 == never present) */ - }; - - dev = aml_device("FDC0"); - aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0700"))); - - crs = aml_resource_template(); - aml_append(crs, aml_io(AML_DECODE16, 0x03F2, 0x03F2, 0x00, 0x04)); - aml_append(crs, aml_io(AML_DECODE16, 0x03F7, 0x03F7, 0x00, 0x01)); - aml_append(crs, aml_irq_no_flags(6)); - aml_append(crs, - aml_dma(AML_COMPATIBILITY, AML_NOTBUSMASTER, AML_TRANSFER8, 2)); - aml_append(dev, aml_name_decl("_CRS", crs)); - - for (i = 0; i < MIN(MAX_FD, ACPI_FDE_MAX_FD); i++) { - FloppyDriveType type = isa_fdc_get_drive_type(fdc, i); - - if (type < FLOPPY_DRIVE_TYPE_NONE) { - fde_buf[i] = cpu_to_le32(1); /* drive present */ - aml_append(dev, build_fdinfo_aml(i, type)); - } - } - aml_append(dev, aml_name_decl("_FDE", - aml_buffer(sizeof(fde_buf), (uint8_t *)fde_buf))); - - return dev; -} - -static Aml *build_kbd_device_aml(void) -{ - Aml *dev; - Aml *crs; - - dev = aml_device("KBD"); - aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0303"))); - - aml_append(dev, aml_name_decl("_STA", aml_int(0xf))); - - crs = aml_resource_template(); - aml_append(crs, aml_io(AML_DECODE16, 0x0060, 0x0060, 0x01, 0x01)); - aml_append(crs, aml_io(AML_DECODE16, 0x0064, 0x0064, 0x01, 0x01)); - aml_append(crs, aml_irq_no_flags(1)); - aml_append(dev, aml_name_decl("_CRS", crs)); - - return dev; -} - -static Aml *build_mouse_device_aml(void) -{ - Aml *dev; - Aml *crs; - - dev = aml_device("MOU"); - aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0F13"))); - - aml_append(dev, aml_name_decl("_STA", aml_int(0xf))); - - crs = aml_resource_template(); - aml_append(crs, aml_irq_no_flags(12)); - aml_append(dev, aml_name_decl("_CRS", crs)); - - return dev; -} - static Aml *build_vmbus_device_aml(VMBusBridge *vmbus_bridge) { Aml *dev; @@ -1092,27 +977,16 @@ static Aml *build_vmbus_device_aml(VMBusBridge *vmbus_bridge) static void build_isa_devices_aml(Aml *table) { - ISADevice *fdc = pc_find_fdc0(); VMBusBridge *vmbus_bridge = vmbus_bridge_find(); bool ambiguous; - - Aml *scope = aml_scope("_SB.PCI0.ISA"); Object *obj = object_resolve_path_type("", TYPE_ISA_BUS, &ambiguous); + Aml *scope; - aml_append(scope, build_kbd_device_aml()); - aml_append(scope, build_mouse_device_aml()); - if (fdc) { - aml_append(scope, build_fdc_device_aml(fdc)); - } + assert(obj && !ambiguous); - if (ambiguous) { - error_report("Multiple ISA busses, unable to define IPMI ACPI data"); - } else if (!obj) { - error_report("No ISA bus, unable to define IPMI ACPI data"); - } else { - build_acpi_ipmi_devices(scope, BUS(obj), "\\_SB.PCI0.ISA"); - isa_build_aml(ISA_BUS(obj), scope); - } + scope = aml_scope("_SB.PCI0.ISA"); + build_acpi_ipmi_devices(scope, BUS(obj), "\\_SB.PCI0.ISA"); + isa_build_aml(ISA_BUS(obj), scope); if (vmbus_bridge) { aml_append(scope, build_vmbus_device_aml(vmbus_bridge)); @@ -1466,7 +1340,6 @@ static void build_q35_isa_bridge(Aml *table) { Aml *dev; Aml *scope; - Aml *field; scope = aml_scope("_SB.PCI0"); dev = aml_device("ISA"); @@ -1476,40 +1349,6 @@ static void build_q35_isa_bridge(Aml *table) aml_append(dev, aml_operation_region("PIRQ", AML_PCI_CONFIG, aml_int(0x60), 0x0C)); - aml_append(dev, aml_operation_region("LPCD", AML_PCI_CONFIG, - aml_int(0x80), 0x02)); - field = aml_field("LPCD", AML_ANY_ACC, AML_NOLOCK, AML_PRESERVE); - aml_append(field, aml_named_field("COMA", 3)); - aml_append(field, aml_reserved_field(1)); - aml_append(field, aml_named_field("COMB", 3)); - aml_append(field, aml_reserved_field(1)); - aml_append(field, aml_named_field("LPTD", 2)); - aml_append(dev, field); - - aml_append(dev, aml_operation_region("LPCE", AML_PCI_CONFIG, - aml_int(0x82), 0x02)); - /* enable bits */ - field = aml_field("LPCE", AML_ANY_ACC, AML_NOLOCK, AML_PRESERVE); - aml_append(field, aml_named_field("CAEN", 1)); - aml_append(field, aml_named_field("CBEN", 1)); - aml_append(field, aml_named_field("LPEN", 1)); - aml_append(dev, field); - - aml_append(scope, dev); - aml_append(table, scope); -} - -static void build_piix4_pm(Aml *table) -{ - Aml *dev; - Aml *scope; - - scope = aml_scope("_SB.PCI0"); - dev = aml_device("PX13"); - aml_append(dev, aml_name_decl("_ADR", aml_int(0x00010003))); - - aml_append(dev, aml_operation_region("P13C", AML_PCI_CONFIG, - aml_int(0x00), 0xff)); aml_append(scope, dev); aml_append(table, scope); } @@ -1518,7 +1357,6 @@ static void build_piix4_isa_bridge(Aml *table) { Aml *dev; Aml *scope; - Aml *field; scope = aml_scope("_SB.PCI0"); dev = aml_device("ISA"); @@ -1527,19 +1365,6 @@ static void build_piix4_isa_bridge(Aml *table) /* PIIX PCI to ISA irq remapping */ aml_append(dev, aml_operation_region("P40C", AML_PCI_CONFIG, aml_int(0x60), 0x04)); - /* enable bits */ - field = aml_field("^PX13.P13C", AML_ANY_ACC, AML_NOLOCK, AML_PRESERVE); - /* Offset(0x5f),, 7, */ - aml_append(field, aml_reserved_field(0x2f8)); - aml_append(field, aml_reserved_field(7)); - aml_append(field, aml_named_field("LPEN", 1)); - /* Offset(0x67),, 3, */ - aml_append(field, aml_reserved_field(0x38)); - aml_append(field, aml_reserved_field(3)); - aml_append(field, aml_named_field("CAEN", 1)); - aml_append(field, aml_reserved_field(3)); - aml_append(field, aml_named_field("CBEN", 1)); - aml_append(dev, field); aml_append(scope, dev); aml_append(table, scope); @@ -1679,7 +1504,6 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(dsdt, sb_scope); build_hpet_aml(dsdt); - build_piix4_pm(dsdt); build_piix4_isa_bridge(dsdt); build_isa_devices_aml(dsdt); build_piix4_pci_hotplug(dsdt); @@ -1924,30 +1748,8 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, /* create fw_cfg node, unconditionally */ { - /* when using port i/o, the 8-bit data register *always* overlaps - * with half of the 16-bit control register. Hence, the total size - * of the i/o region used is FW_CFG_CTL_SIZE; when using DMA, the - * DMA control register is located at FW_CFG_DMA_IO_BASE + 4 */ - uint8_t io_size = object_property_get_bool(OBJECT(x86ms->fw_cfg), - "dma_enabled", NULL) ? - ROUND_UP(FW_CFG_CTL_SIZE, 4) + sizeof(dma_addr_t) : - FW_CFG_CTL_SIZE; - scope = aml_scope("\\_SB.PCI0"); - dev = aml_device("FWCF"); - - aml_append(dev, aml_name_decl("_HID", aml_string("QEMU0002"))); - - /* device present, functioning, decoding, not shown in UI */ - aml_append(dev, aml_name_decl("_STA", aml_int(0xB))); - - crs = aml_resource_template(); - aml_append(crs, - aml_io(AML_DECODE16, FW_CFG_IO_BASE, FW_CFG_IO_BASE, 0x01, io_size) - ); - aml_append(dev, aml_name_decl("_CRS", crs)); - - aml_append(scope, dev); + fw_cfg_add_acpi_dsdt(scope, x86ms->fw_cfg); aml_append(dsdt, scope); } diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c index da60ada594..c55abfb01a 100644 --- a/hw/i386/fw_cfg.c +++ b/hw/i386/fw_cfg.c @@ -15,6 +15,7 @@ #include "qemu/osdep.h" #include "sysemu/numa.h" #include "hw/acpi/acpi.h" +#include "hw/acpi/aml-build.h" #include "hw/firmware/smbios.h" #include "hw/i386/fw_cfg.h" #include "hw/timer/hpet.h" @@ -179,3 +180,30 @@ void fw_cfg_build_feature_control(MachineState *ms, FWCfgState *fw_cfg) *val = cpu_to_le64(feature_control_bits | FEATURE_CONTROL_LOCKED); fw_cfg_add_file(fw_cfg, "etc/msr_feature_control", val, sizeof(*val)); } + +void fw_cfg_add_acpi_dsdt(Aml *scope, FWCfgState *fw_cfg) +{ + /* + * when using port i/o, the 8-bit data register *always* overlaps + * with half of the 16-bit control register. Hence, the total size + * of the i/o region used is FW_CFG_CTL_SIZE; when using DMA, the + * DMA control register is located at FW_CFG_DMA_IO_BASE + 4 + */ + Object *obj = OBJECT(fw_cfg); + uint8_t io_size = object_property_get_bool(obj, "dma_enabled", NULL) ? + ROUND_UP(FW_CFG_CTL_SIZE, 4) + sizeof(dma_addr_t) : + FW_CFG_CTL_SIZE; + Aml *dev = aml_device("FWCF"); + Aml *crs = aml_resource_template(); + + aml_append(dev, aml_name_decl("_HID", aml_string("QEMU0002"))); + + /* device present, functioning, decoding, not shown in UI */ + aml_append(dev, aml_name_decl("_STA", aml_int(0xB))); + + aml_append(crs, + aml_io(AML_DECODE16, FW_CFG_IO_BASE, FW_CFG_IO_BASE, 0x01, io_size)); + + aml_append(dev, aml_name_decl("_CRS", crs)); + aml_append(scope, dev); +} diff --git a/hw/i386/fw_cfg.h b/hw/i386/fw_cfg.h index 9e74278779..275f15c1c5 100644 --- a/hw/i386/fw_cfg.h +++ b/hw/i386/fw_cfg.h @@ -25,5 +25,6 @@ FWCfgState *fw_cfg_arch_create(MachineState *ms, uint16_t apic_id_limit); void fw_cfg_build_smbios(MachineState *ms, FWCfgState *fw_cfg); void fw_cfg_build_feature_control(MachineState *ms, FWCfgState *fw_cfg); +void fw_cfg_add_acpi_dsdt(Aml *scope, FWCfgState *fw_cfg); #endif diff --git a/hw/i386/pc.c b/hw/i386/pc.c index d103b8c0ab..803bd52ca4 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -386,31 +386,6 @@ static uint64_t ioportF0_read(void *opaque, hwaddr addr, unsigned size) #define REG_EQUIPMENT_BYTE 0x14 -int cmos_get_fd_drive_type(FloppyDriveType fd0) -{ - int val; - - switch (fd0) { - case FLOPPY_DRIVE_TYPE_144: - /* 1.44 Mb 3"5 drive */ - val = 4; - break; - case FLOPPY_DRIVE_TYPE_288: - /* 2.88 Mb 3"5 drive */ - val = 5; - break; - case FLOPPY_DRIVE_TYPE_120: - /* 1.2 Mb 5"5 drive */ - val = 2; - break; - case FLOPPY_DRIVE_TYPE_NONE: - default: - val = 0; - break; - } - return val; -} - static void cmos_init_hd(ISADevice *s, int type_ofs, int info_ofs, int16_t cylinders, int8_t heads, int8_t sectors) { @@ -1142,7 +1117,7 @@ static void pc_superio_init(ISABus *isa_bus, bool create_fdctrl, bool no_vmport) int i; DriveInfo *fd[MAX_FD]; qemu_irq *a20_line; - ISADevice *i8042, *port92, *vmmouse; + ISADevice *fdc, *i8042, *port92, *vmmouse; serial_hds_isa_init(isa_bus, 0, MAX_ISA_SERIAL_PORTS); parallel_hds_isa_init(isa_bus, MAX_PARALLEL_PORTS); @@ -1152,7 +1127,11 @@ static void pc_superio_init(ISABus *isa_bus, bool create_fdctrl, bool no_vmport) create_fdctrl |= !!fd[i]; } if (create_fdctrl) { - fdctrl_init_isa(isa_bus, fd); + fdc = isa_new(TYPE_ISA_FDC); + if (fdc) { + isa_realize_and_unref(fdc, isa_bus, &error_fatal); + isa_fdc_init_drives(fdc, fd); + } } i8042 = isa_create_simple(isa_bus, "i8042"); diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c index 3ccb5e2529..f68fbee93d 100644 --- a/hw/ide/qdev.c +++ b/hw/ide/qdev.c @@ -129,8 +129,8 @@ IDEDevice *ide_create_drive(IDEBus *bus, int unit, DriveInfo *drive) dev = qdev_new(drive->media_cd ? "ide-cd" : "ide-hd"); qdev_prop_set_uint32(dev, "unit", unit); - qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(drive), - &error_fatal); + qdev_prop_set_drive_err(dev, "drive", blk_by_legacy_dinfo(drive), + &error_fatal); qdev_realize_and_unref(dev, &bus->qbus, &error_fatal); return DO_UPCAST(IDEDevice, qdev, dev); } diff --git a/hw/input/adb-kbd.c b/hw/input/adb-kbd.c index a6d5c9b7c9..3cfb6a7a20 100644 --- a/hw/input/adb-kbd.c +++ b/hw/input/adb-kbd.c @@ -243,7 +243,7 @@ static int adb_kbd_request(ADBDevice *d, uint8_t *obuf, olen = 0; switch (cmd) { case ADB_WRITEREG: - trace_adb_kbd_writereg(reg, buf[1]); + trace_adb_device_kbd_writereg(reg, buf[1]); switch (reg) { case 2: /* LED status */ @@ -256,24 +256,22 @@ static int adb_kbd_request(ADBDevice *d, uint8_t *obuf, case ADB_CMD_CHANGE_ID_AND_ACT: case ADB_CMD_CHANGE_ID_AND_ENABLE: d->devaddr = buf[1] & 0xf; - trace_adb_kbd_request_change_addr(d->devaddr); + trace_adb_device_kbd_request_change_addr(d->devaddr); break; default: - if (!d->disable_direct_reg3_writes) { - d->devaddr = buf[1] & 0xf; - - /* we support handlers: - * 1: Apple Standard Keyboard - * 2: Apple Extended Keyboard (LShift = RShift) - * 3: Apple Extended Keyboard (LShift != RShift) - */ - if (buf[2] == 1 || buf[2] == 2 || buf[2] == 3) { - d->handler = buf[2]; - } - - trace_adb_kbd_request_change_addr_and_handler(d->devaddr, - d->handler); + d->devaddr = buf[1] & 0xf; + /* + * we support handlers: + * 1: Apple Standard Keyboard + * 2: Apple Extended Keyboard (LShift = RShift) + * 3: Apple Extended Keyboard (LShift != RShift) + */ + if (buf[2] == 1 || buf[2] == 2 || buf[2] == 3) { + d->handler = buf[2]; } + + trace_adb_device_kbd_request_change_addr_and_handler( + d->devaddr, d->handler); break; } } @@ -296,12 +294,19 @@ static int adb_kbd_request(ADBDevice *d, uint8_t *obuf, olen = 2; break; } - trace_adb_kbd_readreg(reg, obuf[0], obuf[1]); + trace_adb_device_kbd_readreg(reg, obuf[0], obuf[1]); break; } return olen; } +static bool adb_kbd_has_data(ADBDevice *d) +{ + KBDState *s = ADB_KEYBOARD(d); + + return s->count > 0; +} + /* This is where keyboard events enter this file */ static void adb_keyboard_event(DeviceState *dev, QemuConsole *src, InputEvent *evt) @@ -316,7 +321,7 @@ static void adb_keyboard_event(DeviceState *dev, QemuConsole *src, /* FIXME: take handler into account when translating qcode */ keycode = qcode_to_adb_keycode[qcode]; if (keycode == NO_KEY) { /* We don't want to send this to the guest */ - trace_adb_kbd_no_key(); + trace_adb_device_kbd_no_key(); return; } if (evt->u.key.data->down == false) { /* if key release event */ @@ -384,6 +389,7 @@ static void adb_kbd_class_init(ObjectClass *oc, void *data) set_bit(DEVICE_CATEGORY_INPUT, dc->categories); adc->devreq = adb_kbd_request; + adc->devhasdata = adb_kbd_has_data; dc->reset = adb_kbd_reset; dc->vmsd = &vmstate_adb_kbd; } diff --git a/hw/input/adb-mouse.c b/hw/input/adb-mouse.c index aeba41bddd..577a38ff2e 100644 --- a/hw/input/adb-mouse.c +++ b/hw/input/adb-mouse.c @@ -121,7 +121,7 @@ static int adb_mouse_request(ADBDevice *d, uint8_t *obuf, s->dx = 0; s->dy = 0; s->dz = 0; - trace_adb_mouse_flush(); + trace_adb_device_mouse_flush(); return 0; } @@ -130,11 +130,21 @@ static int adb_mouse_request(ADBDevice *d, uint8_t *obuf, olen = 0; switch (cmd) { case ADB_WRITEREG: - trace_adb_mouse_writereg(reg, buf[1]); + trace_adb_device_mouse_writereg(reg, buf[1]); switch (reg) { case 2: break; case 3: + /* + * MacOS 9 has a bug in its ADB driver whereby after configuring + * the ADB bus devices it sends another write of invalid length + * to reg 3. Make sure we ignore it to prevent an address clash + * with the previous device. + */ + if (len != 3) { + return 0; + } + switch (buf[2]) { case ADB_CMD_SELF_TEST: break; @@ -142,30 +152,28 @@ static int adb_mouse_request(ADBDevice *d, uint8_t *obuf, case ADB_CMD_CHANGE_ID_AND_ACT: case ADB_CMD_CHANGE_ID_AND_ENABLE: d->devaddr = buf[1] & 0xf; - trace_adb_mouse_request_change_addr(d->devaddr); + trace_adb_device_mouse_request_change_addr(d->devaddr); break; default: - if (!d->disable_direct_reg3_writes) { - d->devaddr = buf[1] & 0xf; - - /* we support handlers: - * 0x01: Classic Apple Mouse Protocol / 100 cpi operations - * 0x02: Classic Apple Mouse Protocol / 200 cpi operations - * we don't support handlers (at least): - * 0x03: Mouse systems A3 trackball - * 0x04: Extended Apple Mouse Protocol - * 0x2f: Microspeed mouse - * 0x42: Macally - * 0x5f: Microspeed mouse - * 0x66: Microspeed mouse - */ - if (buf[2] == 1 || buf[2] == 2) { - d->handler = buf[2]; - } - - trace_adb_mouse_request_change_addr_and_handler( - d->devaddr, d->handler); + d->devaddr = buf[1] & 0xf; + /* + * we support handlers: + * 0x01: Classic Apple Mouse Protocol / 100 cpi operations + * 0x02: Classic Apple Mouse Protocol / 200 cpi operations + * we don't support handlers (at least): + * 0x03: Mouse systems A3 trackball + * 0x04: Extended Apple Mouse Protocol + * 0x2f: Microspeed mouse + * 0x42: Macally + * 0x5f: Microspeed mouse + * 0x66: Microspeed mouse + */ + if (buf[2] == 1 || buf[2] == 2) { + d->handler = buf[2]; } + + trace_adb_device_mouse_request_change_addr_and_handler( + d->devaddr, d->handler); break; } } @@ -183,12 +191,20 @@ static int adb_mouse_request(ADBDevice *d, uint8_t *obuf, olen = 2; break; } - trace_adb_mouse_readreg(reg, obuf[0], obuf[1]); + trace_adb_device_mouse_readreg(reg, obuf[0], obuf[1]); break; } return olen; } +static bool adb_mouse_has_data(ADBDevice *d) +{ + MouseState *s = ADB_MOUSE(d); + + return !(s->last_buttons_state == s->buttons_state && + s->dx == 0 && s->dy == 0); +} + static void adb_mouse_reset(DeviceState *dev) { ADBDevice *d = ADB_DEVICE(dev); @@ -244,6 +260,7 @@ static void adb_mouse_class_init(ObjectClass *oc, void *data) set_bit(DEVICE_CATEGORY_INPUT, dc->categories); adc->devreq = adb_mouse_request; + adc->devhasdata = adb_mouse_has_data; dc->reset = adb_mouse_reset; dc->vmsd = &vmstate_adb_mouse; } diff --git a/hw/input/adb.c b/hw/input/adb.c index b1ac4a3852..013fcc9c54 100644 --- a/hw/input/adb.c +++ b/hw/input/adb.c @@ -27,41 +27,85 @@ #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "qemu/module.h" +#include "qemu/timer.h" #include "adb-internal.h" +#include "trace.h" /* error codes */ #define ADB_RET_NOTPRESENT (-2) +static const char *adb_commands[] = { + "RESET", "FLUSH", "(Reserved 0x2)", "(Reserved 0x3)", + "Reserved (0x4)", "(Reserved 0x5)", "(Reserved 0x6)", "(Reserved 0x7)", + "LISTEN r0", "LISTEN r1", "LISTEN r2", "LISTEN r3", + "TALK r0", "TALK r1", "TALK r2", "TALK r3", +}; + static void adb_device_reset(ADBDevice *d) { qdev_reset_all(DEVICE(d)); } -int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len) +static int do_adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, + int len) { ADBDevice *d; - int devaddr, cmd, i; + ADBDeviceClass *adc; + int devaddr, cmd, olen, i; cmd = buf[0] & 0xf; if (cmd == ADB_BUSRESET) { - for(i = 0; i < s->nb_devices; i++) { + for (i = 0; i < s->nb_devices; i++) { d = s->devices[i]; adb_device_reset(d); } + s->status = 0; return 0; } + + s->pending = 0; + for (i = 0; i < s->nb_devices; i++) { + d = s->devices[i]; + adc = ADB_DEVICE_GET_CLASS(d); + + if (adc->devhasdata(d)) { + s->pending |= (1 << d->devaddr); + } + } + + s->status = 0; devaddr = buf[0] >> 4; - for(i = 0; i < s->nb_devices; i++) { + for (i = 0; i < s->nb_devices; i++) { d = s->devices[i]; + adc = ADB_DEVICE_GET_CLASS(d); + if (d->devaddr == devaddr) { - ADBDeviceClass *adc = ADB_DEVICE_GET_CLASS(d); - return adc->devreq(d, obuf, buf, len); + olen = adc->devreq(d, obuf, buf, len); + if (!olen) { + s->status |= ADB_STATUS_BUSTIMEOUT; + } + return olen; } } + + s->status |= ADB_STATUS_BUSTIMEOUT; return ADB_RET_NOTPRESENT; } -/* XXX: move that to cuda ? */ +int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len) +{ + int ret; + + trace_adb_bus_request(buf[0] >> 4, adb_commands[buf[0] & 0xf], len); + + assert(s->autopoll_blocked); + + ret = do_adb_request(s, obuf, buf, len); + + trace_adb_bus_request_done(buf[0] >> 4, adb_commands[buf[0] & 0xf], ret); + return ret; +} + int adb_poll(ADBBusState *s, uint8_t *obuf, uint16_t poll_mask) { ADBDevice *d; @@ -69,18 +113,20 @@ int adb_poll(ADBBusState *s, uint8_t *obuf, uint16_t poll_mask) uint8_t buf[1]; olen = 0; - for(i = 0; i < s->nb_devices; i++) { - if (s->poll_index >= s->nb_devices) + for (i = 0; i < s->nb_devices; i++) { + if (s->poll_index >= s->nb_devices) { s->poll_index = 0; + } d = s->devices[s->poll_index]; if ((1 << d->devaddr) & poll_mask) { buf[0] = ADB_READREG | (d->devaddr << 4); - olen = adb_request(s, obuf + 1, buf, 1); + olen = do_adb_request(s, obuf + 1, buf, 1); /* if there is data, we poll again the same device */ if (olen > 0) { + s->status |= ADB_STATUS_POLLREPLY; obuf[0] = buf[0]; olen++; - break; + return olen; } } s->poll_index++; @@ -88,10 +134,145 @@ int adb_poll(ADBBusState *s, uint8_t *obuf, uint16_t poll_mask) return olen; } +void adb_set_autopoll_enabled(ADBBusState *s, bool enabled) +{ + if (s->autopoll_enabled != enabled) { + s->autopoll_enabled = enabled; + if (s->autopoll_enabled) { + timer_mod(s->autopoll_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + + s->autopoll_rate_ms); + } else { + timer_del(s->autopoll_timer); + } + } +} + +void adb_set_autopoll_rate_ms(ADBBusState *s, int rate_ms) +{ + s->autopoll_rate_ms = rate_ms; + + if (s->autopoll_enabled) { + timer_mod(s->autopoll_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + + s->autopoll_rate_ms); + } +} + +void adb_set_autopoll_mask(ADBBusState *s, uint16_t mask) +{ + if (s->autopoll_mask != mask) { + s->autopoll_mask = mask; + if (s->autopoll_enabled && s->autopoll_mask) { + timer_mod(s->autopoll_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + + s->autopoll_rate_ms); + } else { + timer_del(s->autopoll_timer); + } + } +} + +void adb_autopoll_block(ADBBusState *s) +{ + s->autopoll_blocked = true; + trace_adb_bus_autopoll_block(s->autopoll_blocked); + + if (s->autopoll_enabled) { + timer_del(s->autopoll_timer); + } +} + +void adb_autopoll_unblock(ADBBusState *s) +{ + s->autopoll_blocked = false; + trace_adb_bus_autopoll_block(s->autopoll_blocked); + + if (s->autopoll_enabled) { + timer_mod(s->autopoll_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + + s->autopoll_rate_ms); + } +} + +static void adb_autopoll(void *opaque) +{ + ADBBusState *s = opaque; + + if (!s->autopoll_blocked) { + trace_adb_bus_autopoll_cb(s->autopoll_mask); + s->autopoll_cb(s->autopoll_cb_opaque); + trace_adb_bus_autopoll_cb_done(s->autopoll_mask); + } + + timer_mod(s->autopoll_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + + s->autopoll_rate_ms); +} + +void adb_register_autopoll_callback(ADBBusState *s, void (*cb)(void *opaque), + void *opaque) +{ + s->autopoll_cb = cb; + s->autopoll_cb_opaque = opaque; +} + +static const VMStateDescription vmstate_adb_bus = { + .name = "adb_bus", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_TIMER_PTR(autopoll_timer, ADBBusState), + VMSTATE_BOOL(autopoll_enabled, ADBBusState), + VMSTATE_UINT8(autopoll_rate_ms, ADBBusState), + VMSTATE_UINT16(autopoll_mask, ADBBusState), + VMSTATE_BOOL(autopoll_blocked, ADBBusState), + VMSTATE_END_OF_LIST() + } +}; + +static void adb_bus_reset(BusState *qbus) +{ + ADBBusState *adb_bus = ADB_BUS(qbus); + + adb_bus->autopoll_enabled = false; + adb_bus->autopoll_mask = 0xffff; + adb_bus->autopoll_rate_ms = 20; +} + +static void adb_bus_realize(BusState *qbus, Error **errp) +{ + ADBBusState *adb_bus = ADB_BUS(qbus); + + adb_bus->autopoll_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, adb_autopoll, + adb_bus); + + vmstate_register(NULL, -1, &vmstate_adb_bus, adb_bus); +} + +static void adb_bus_unrealize(BusState *qbus) +{ + ADBBusState *adb_bus = ADB_BUS(qbus); + + timer_del(adb_bus->autopoll_timer); + + vmstate_unregister(NULL, &vmstate_adb_bus, adb_bus); +} + +static void adb_bus_class_init(ObjectClass *klass, void *data) +{ + BusClass *k = BUS_CLASS(klass); + + k->realize = adb_bus_realize; + k->unrealize = adb_bus_unrealize; + k->reset = adb_bus_reset; +} + static const TypeInfo adb_bus_type_info = { .name = TYPE_ADB_BUS, .parent = TYPE_BUS, .instance_size = sizeof(ADBBusState), + .class_init = adb_bus_class_init, }; const VMStateDescription vmstate_adb_device = { @@ -117,18 +298,11 @@ static void adb_device_realizefn(DeviceState *dev, Error **errp) bus->devices[bus->nb_devices++] = d; } -static Property adb_device_properties[] = { - DEFINE_PROP_BOOL("disable-direct-reg3-writes", ADBDevice, - disable_direct_reg3_writes, false), - DEFINE_PROP_END_OF_LIST(), -}; - static void adb_device_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = adb_device_realizefn; - device_class_set_props(dc, adb_device_properties); dc->bus_type = TYPE_ADB_BUS; } diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c index 60a4130320..29d633ca94 100644 --- a/hw/input/pckbd.c +++ b/hw/input/pckbd.c @@ -26,6 +26,7 @@ #include "qemu/log.h" #include "hw/isa/isa.h" #include "migration/vmstate.h" +#include "hw/acpi/aml-build.h" #include "hw/input/ps2.h" #include "hw/irq.h" #include "hw/input/i8042.h" @@ -561,12 +562,42 @@ static void i8042_realizefn(DeviceState *dev, Error **errp) qemu_register_reset(kbd_reset, s); } +static void i8042_build_aml(ISADevice *isadev, Aml *scope) +{ + Aml *kbd; + Aml *mou; + Aml *crs; + + crs = aml_resource_template(); + aml_append(crs, aml_io(AML_DECODE16, 0x0060, 0x0060, 0x01, 0x01)); + aml_append(crs, aml_io(AML_DECODE16, 0x0064, 0x0064, 0x01, 0x01)); + aml_append(crs, aml_irq_no_flags(1)); + + kbd = aml_device("KBD"); + aml_append(kbd, aml_name_decl("_HID", aml_eisaid("PNP0303"))); + aml_append(kbd, aml_name_decl("_STA", aml_int(0xf))); + aml_append(kbd, aml_name_decl("_CRS", crs)); + + crs = aml_resource_template(); + aml_append(crs, aml_irq_no_flags(12)); + + mou = aml_device("MOU"); + aml_append(mou, aml_name_decl("_HID", aml_eisaid("PNP0F13"))); + aml_append(mou, aml_name_decl("_STA", aml_int(0xf))); + aml_append(mou, aml_name_decl("_CRS", crs)); + + aml_append(scope, kbd); + aml_append(scope, mou); +} + static void i8042_class_initfn(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ISADeviceClass *isa = ISA_DEVICE_CLASS(klass); dc->realize = i8042_realizefn; dc->vmsd = &vmstate_kbd_isa; + isa->build_aml = i8042_build_aml; set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } diff --git a/hw/input/trace-events b/hw/input/trace-events index a2888fd10c..1dd8ad6018 100644 --- a/hw/input/trace-events +++ b/hw/input/trace-events @@ -1,18 +1,25 @@ # See docs/devel/tracing.txt for syntax documentation. # adb-kbd.c -adb_kbd_no_key(void) "Ignoring NO_KEY" -adb_kbd_writereg(int reg, uint8_t val) "reg %d val 0x%2.2x" -adb_kbd_readreg(int reg, uint8_t val0, uint8_t val1) "reg %d obuf[0] 0x%2.2x obuf[1] 0x%2.2x" -adb_kbd_request_change_addr(int devaddr) "change addr to 0x%x" -adb_kbd_request_change_addr_and_handler(int devaddr, int handler) "change addr and handler to 0x%x, 0x%x" +adb_device_kbd_no_key(void) "Ignoring NO_KEY" +adb_device_kbd_writereg(int reg, uint8_t val) "reg %d val 0x%2.2x" +adb_device_kbd_readreg(int reg, uint8_t val0, uint8_t val1) "reg %d obuf[0] 0x%2.2x obuf[1] 0x%2.2x" +adb_device_kbd_request_change_addr(int devaddr) "change addr to 0x%x" +adb_device_kbd_request_change_addr_and_handler(int devaddr, int handler) "change addr and handler to 0x%x, 0x%x" # adb-mouse.c -adb_mouse_flush(void) "flush" -adb_mouse_writereg(int reg, uint8_t val) "reg %d val 0x%2.2x" -adb_mouse_readreg(int reg, uint8_t val0, uint8_t val1) "reg %d obuf[0] 0x%2.2x obuf[1] 0x%2.2x" -adb_mouse_request_change_addr(int devaddr) "change addr to 0x%x" -adb_mouse_request_change_addr_and_handler(int devaddr, int handler) "change addr and handler to 0x%x, 0x%x" +adb_device_mouse_flush(void) "flush" +adb_device_mouse_writereg(int reg, uint8_t val) "reg %d val 0x%2.2x" +adb_device_mouse_readreg(int reg, uint8_t val0, uint8_t val1) "reg %d obuf[0] 0x%2.2x obuf[1] 0x%2.2x" +adb_device_mouse_request_change_addr(int devaddr) "change addr to 0x%x" +adb_device_mouse_request_change_addr_and_handler(int devaddr, int handler) "change addr and handler to 0x%x, 0x%x" + +# adb.c +adb_bus_request(uint8_t addr, const char *cmd, int size) "device 0x%x %s cmdsize=%d" +adb_bus_request_done(uint8_t addr, const char *cmd, int size) "device 0x%x %s replysize=%d" +adb_bus_autopoll_block(bool blocked) "blocked: %d" +adb_bus_autopoll_cb(uint16_t mask) "executing autopoll_cb with autopoll mask 0x%x" +adb_bus_autopoll_cb_done(uint16_t mask) "done executing autopoll_cb with autopoll mask 0x%x" # pckbd.c pckbd_kbd_read_data(uint32_t val) "0x%02x" diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig index a189d6fedd..f562342bab 100644 --- a/hw/intc/Kconfig +++ b/hw/intc/Kconfig @@ -61,3 +61,6 @@ config S390_FLIC_KVM config OMPIC bool + +config RX_ICU + bool diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs index a61e6728fe..a4202636d4 100644 --- a/hw/intc/Makefile.objs +++ b/hw/intc/Makefile.objs @@ -20,6 +20,7 @@ common-obj-$(CONFIG_ARM_GIC) += arm_gicv3_dist.o common-obj-$(CONFIG_ARM_GIC) += arm_gicv3_redist.o common-obj-$(CONFIG_ARM_GIC) += arm_gicv3_its_common.o common-obj-$(CONFIG_OPENPIC) += openpic.o +common-obj-$(CONFIG_RX_ICU) += rx_icu.o common-obj-y += intc.o obj-$(CONFIG_APIC) += apic.o apic_common.o diff --git a/hw/intc/rx_icu.c b/hw/intc/rx_icu.c new file mode 100644 index 0000000000..df4b6a8d22 --- /dev/null +++ b/hw/intc/rx_icu.c @@ -0,0 +1,397 @@ +/* + * RX Interrupt Control Unit + * + * Warning: Only ICUa is supported. + * + * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware + * (Rev.1.40 R01UH0033EJ0140) + * + * Copyright (c) 2019 Yoshinori Sato + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "hw/irq.h" +#include "hw/registerfields.h" +#include "hw/qdev-properties.h" +#include "hw/intc/rx_icu.h" +#include "migration/vmstate.h" + +REG8(IR, 0) + FIELD(IR, IR, 0, 1) +REG8(DTCER, 0x100) + FIELD(DTCER, DTCE, 0, 1) +REG8(IER, 0x200) +REG8(SWINTR, 0x2e0) + FIELD(SWINTR, SWINT, 0, 1) +REG16(FIR, 0x2f0) + FIELD(FIR, FVCT, 0, 8) + FIELD(FIR, FIEN, 15, 1) +REG8(IPR, 0x300) + FIELD(IPR, IPR, 0, 4) +REG8(DMRSR, 0x400) +REG8(IRQCR, 0x500) + FIELD(IRQCR, IRQMD, 2, 2) +REG8(NMISR, 0x580) + FIELD(NMISR, NMIST, 0, 1) + FIELD(NMISR, LVDST, 1, 1) + FIELD(NMISR, OSTST, 2, 1) +REG8(NMIER, 0x581) + FIELD(NMIER, NMIEN, 0, 1) + FIELD(NMIER, LVDEN, 1, 1) + FIELD(NMIER, OSTEN, 2, 1) +REG8(NMICLR, 0x582) + FIELD(NMICLR, NMICLR, 0, 1) + FIELD(NMICLR, OSTCLR, 2, 1) +REG8(NMICR, 0x583) + FIELD(NMICR, NMIMD, 3, 1) + +static void set_irq(RXICUState *icu, int n_IRQ, int req) +{ + if ((icu->fir & R_FIR_FIEN_MASK) && + (icu->fir & R_FIR_FVCT_MASK) == n_IRQ) { + qemu_set_irq(icu->_fir, req); + } else { + qemu_set_irq(icu->_irq, req); + } +} + +static uint16_t rxicu_level(RXICUState *icu, unsigned n) +{ + return (icu->ipr[icu->map[n]] << 8) | n; +} + +static void rxicu_request(RXICUState *icu, int n_IRQ) +{ + int enable; + + enable = icu->ier[n_IRQ / 8] & (1 << (n_IRQ & 7)); + if (n_IRQ > 0 && enable != 0 && atomic_read(&icu->req_irq) < 0) { + atomic_set(&icu->req_irq, n_IRQ); + set_irq(icu, n_IRQ, rxicu_level(icu, n_IRQ)); + } +} + +static void rxicu_set_irq(void *opaque, int n_IRQ, int level) +{ + RXICUState *icu = opaque; + struct IRQSource *src; + int issue; + + if (n_IRQ >= NR_IRQS) { + error_report("%s: IRQ %d out of range", __func__, n_IRQ); + return; + } + + src = &icu->src[n_IRQ]; + + level = (level != 0); + switch (src->sense) { + case TRG_LEVEL: + /* level-sensitive irq */ + issue = level; + src->level = level; + break; + case TRG_NEDGE: + issue = (level == 0 && src->level == 1); + src->level = level; + break; + case TRG_PEDGE: + issue = (level == 1 && src->level == 0); + src->level = level; + break; + case TRG_BEDGE: + issue = ((level ^ src->level) & 1); + src->level = level; + break; + default: + g_assert_not_reached(); + } + if (issue == 0 && src->sense == TRG_LEVEL) { + icu->ir[n_IRQ] = 0; + if (atomic_read(&icu->req_irq) == n_IRQ) { + /* clear request */ + set_irq(icu, n_IRQ, 0); + atomic_set(&icu->req_irq, -1); + } + return; + } + if (issue) { + icu->ir[n_IRQ] = 1; + rxicu_request(icu, n_IRQ); + } +} + +static void rxicu_ack_irq(void *opaque, int no, int level) +{ + RXICUState *icu = opaque; + int i; + int n_IRQ; + int max_pri; + + n_IRQ = atomic_read(&icu->req_irq); + if (n_IRQ < 0) { + return; + } + atomic_set(&icu->req_irq, -1); + if (icu->src[n_IRQ].sense != TRG_LEVEL) { + icu->ir[n_IRQ] = 0; + } + + max_pri = 0; + n_IRQ = -1; + for (i = 0; i < NR_IRQS; i++) { + if (icu->ir[i]) { + if (max_pri < icu->ipr[icu->map[i]]) { + n_IRQ = i; + max_pri = icu->ipr[icu->map[i]]; + } + } + } + + if (n_IRQ >= 0) { + rxicu_request(icu, n_IRQ); + } +} + +static uint64_t icu_read(void *opaque, hwaddr addr, unsigned size) +{ + RXICUState *icu = opaque; + int reg = addr & 0xff; + + if ((addr != A_FIR && size != 1) || + (addr == A_FIR && size != 2)) { + qemu_log_mask(LOG_GUEST_ERROR, "rx_icu: Invalid read size 0x%" + HWADDR_PRIX "\n", + addr); + return UINT64_MAX; + } + switch (addr) { + case A_IR ... A_IR + 0xff: + return icu->ir[reg] & R_IR_IR_MASK; + case A_DTCER ... A_DTCER + 0xff: + return icu->dtcer[reg] & R_DTCER_DTCE_MASK; + case A_IER ... A_IER + 0x1f: + return icu->ier[reg]; + case A_SWINTR: + return 0; + case A_FIR: + return icu->fir & (R_FIR_FIEN_MASK | R_FIR_FVCT_MASK); + case A_IPR ... A_IPR + 0x8f: + return icu->ipr[reg] & R_IPR_IPR_MASK; + case A_DMRSR: + case A_DMRSR + 4: + case A_DMRSR + 8: + case A_DMRSR + 12: + return icu->dmasr[reg >> 2]; + case A_IRQCR ... A_IRQCR + 0x1f: + return icu->src[64 + reg].sense << R_IRQCR_IRQMD_SHIFT; + case A_NMISR: + case A_NMICLR: + return 0; + case A_NMIER: + return icu->nmier; + case A_NMICR: + return icu->nmicr; + default: + qemu_log_mask(LOG_UNIMP, "rx_icu: Register 0x%" HWADDR_PRIX " " + "not implemented.\n", + addr); + break; + } + return UINT64_MAX; +} + +static void icu_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) +{ + RXICUState *icu = opaque; + int reg = addr & 0xff; + + if ((addr != A_FIR && size != 1) || + (addr == A_FIR && size != 2)) { + qemu_log_mask(LOG_GUEST_ERROR, "rx_icu: Invalid write size at " + "0x%" HWADDR_PRIX "\n", + addr); + return; + } + switch (addr) { + case A_IR ... A_IR + 0xff: + if (icu->src[reg].sense != TRG_LEVEL && val == 0) { + icu->ir[reg] = 0; + } + break; + case A_DTCER ... A_DTCER + 0xff: + icu->dtcer[reg] = val & R_DTCER_DTCE_MASK; + qemu_log_mask(LOG_UNIMP, "rx_icu: DTC not implemented\n"); + break; + case A_IER ... A_IER + 0x1f: + icu->ier[reg] = val; + break; + case A_SWINTR: + if (val & R_SWINTR_SWINT_MASK) { + qemu_irq_pulse(icu->_swi); + } + break; + case A_FIR: + icu->fir = val & (R_FIR_FIEN_MASK | R_FIR_FVCT_MASK); + break; + case A_IPR ... A_IPR + 0x8f: + icu->ipr[reg] = val & R_IPR_IPR_MASK; + break; + case A_DMRSR: + case A_DMRSR + 4: + case A_DMRSR + 8: + case A_DMRSR + 12: + icu->dmasr[reg >> 2] = val; + qemu_log_mask(LOG_UNIMP, "rx_icu: DMAC not implemented\n"); + break; + case A_IRQCR ... A_IRQCR + 0x1f: + icu->src[64 + reg].sense = val >> R_IRQCR_IRQMD_SHIFT; + break; + case A_NMICLR: + break; + case A_NMIER: + icu->nmier |= val & (R_NMIER_NMIEN_MASK | + R_NMIER_LVDEN_MASK | + R_NMIER_OSTEN_MASK); + break; + case A_NMICR: + if ((icu->nmier & R_NMIER_NMIEN_MASK) == 0) { + icu->nmicr = val & R_NMICR_NMIMD_MASK; + } + break; + default: + qemu_log_mask(LOG_UNIMP, "rx_icu: Register 0x%" HWADDR_PRIX " " + "not implemented\n", + addr); + break; + } +} + +static const MemoryRegionOps icu_ops = { + .write = icu_write, + .read = icu_read, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 1, + .max_access_size = 2, + }, + .valid = { + .min_access_size = 1, + .max_access_size = 2, + }, +}; + +static void rxicu_realize(DeviceState *dev, Error **errp) +{ + RXICUState *icu = RX_ICU(dev); + int i, j; + + if (icu->init_sense == NULL) { + qemu_log_mask(LOG_GUEST_ERROR, + "rx_icu: trigger-level property must be set."); + return; + } + for (i = j = 0; i < NR_IRQS; i++) { + if (icu->init_sense[j] == i) { + icu->src[i].sense = TRG_LEVEL; + if (j < icu->nr_sense) { + j++; + } + } else { + icu->src[i].sense = TRG_PEDGE; + } + } + icu->req_irq = -1; +} + +static void rxicu_init(Object *obj) +{ + SysBusDevice *d = SYS_BUS_DEVICE(obj); + RXICUState *icu = RX_ICU(obj); + + memory_region_init_io(&icu->memory, OBJECT(icu), &icu_ops, + icu, "rx-icu", 0x600); + sysbus_init_mmio(d, &icu->memory); + + qdev_init_gpio_in(DEVICE(d), rxicu_set_irq, NR_IRQS); + qdev_init_gpio_in_named(DEVICE(d), rxicu_ack_irq, "ack", 1); + sysbus_init_irq(d, &icu->_irq); + sysbus_init_irq(d, &icu->_fir); + sysbus_init_irq(d, &icu->_swi); +} + +static void rxicu_fini(Object *obj) +{ + RXICUState *icu = RX_ICU(obj); + g_free(icu->map); + g_free(icu->init_sense); +} + +static const VMStateDescription vmstate_rxicu = { + .name = "rx-icu", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8_ARRAY(ir, RXICUState, NR_IRQS), + VMSTATE_UINT8_ARRAY(dtcer, RXICUState, NR_IRQS), + VMSTATE_UINT8_ARRAY(ier, RXICUState, NR_IRQS / 8), + VMSTATE_UINT8_ARRAY(ipr, RXICUState, 142), + VMSTATE_UINT8_ARRAY(dmasr, RXICUState, 4), + VMSTATE_UINT16(fir, RXICUState), + VMSTATE_UINT8(nmisr, RXICUState), + VMSTATE_UINT8(nmier, RXICUState), + VMSTATE_UINT8(nmiclr, RXICUState), + VMSTATE_UINT8(nmicr, RXICUState), + VMSTATE_INT16(req_irq, RXICUState), + VMSTATE_END_OF_LIST() + } +}; + +static Property rxicu_properties[] = { + DEFINE_PROP_ARRAY("ipr-map", RXICUState, nr_irqs, map, + qdev_prop_uint8, uint8_t), + DEFINE_PROP_ARRAY("trigger-level", RXICUState, nr_sense, init_sense, + qdev_prop_uint8, uint8_t), + DEFINE_PROP_END_OF_LIST(), +}; + +static void rxicu_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = rxicu_realize; + dc->vmsd = &vmstate_rxicu; + device_class_set_props(dc, rxicu_properties); +} + +static const TypeInfo rxicu_info = { + .name = TYPE_RX_ICU, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(RXICUState), + .instance_init = rxicu_init, + .instance_finalize = rxicu_fini, + .class_init = rxicu_class_init, +}; + +static void rxicu_register_types(void) +{ + type_register_static(&rxicu_info); +} + +type_init(rxicu_register_types) diff --git a/hw/isa/isa-superio.c b/hw/isa/isa-superio.c index d3d58f9f16..e2e47d8fd9 100644 --- a/hw/isa/isa-superio.c +++ b/hw/isa/isa-superio.c @@ -17,6 +17,7 @@ #include "sysemu/sysemu.h" #include "sysemu/blockdev.h" #include "chardev/char.h" +#include "hw/block/fdc.h" #include "hw/isa/superio.h" #include "hw/qdev-properties.h" #include "hw/input/i8042.h" @@ -31,7 +32,7 @@ static void isa_superio_realize(DeviceState *dev, Error **errp) ISADevice *isa; DeviceState *d; Chardev *chr; - DriveInfo *drive; + DriveInfo *fd[MAX_FD]; char *name; int i; @@ -115,7 +116,7 @@ static void isa_superio_realize(DeviceState *dev, Error **errp) /* Floppy disc */ if (!k->floppy.is_enabled || k->floppy.is_enabled(sio, 0)) { - isa = isa_new("isa-fdc"); + isa = isa_new(TYPE_ISA_FDC); d = DEVICE(isa); if (k->floppy.get_iobase) { qdev_prop_set_uint32(d, "iobase", k->floppy.get_iobase(sio, 0)); @@ -124,19 +125,12 @@ static void isa_superio_realize(DeviceState *dev, Error **errp) qdev_prop_set_uint32(d, "irq", k->floppy.get_irq(sio, 0)); } /* FIXME use a qdev drive property instead of drive_get() */ - drive = drive_get(IF_FLOPPY, 0, 0); - if (drive != NULL) { - qdev_prop_set_drive(d, "driveA", blk_by_legacy_dinfo(drive), - &error_fatal); - } - /* FIXME use a qdev drive property instead of drive_get() */ - drive = drive_get(IF_FLOPPY, 0, 1); - if (drive != NULL) { - qdev_prop_set_drive(d, "driveB", blk_by_legacy_dinfo(drive), - &error_fatal); + for (i = 0; i < MAX_FD; i++) { + fd[i] = drive_get(IF_FLOPPY, 0, i); } object_property_add_child(OBJECT(sio), "isa-fdc", OBJECT(isa)); isa_realize_and_unref(isa, bus, &error_fatal); + isa_fdc_init_drives(isa, fd); sio->floppy = isa; trace_superio_create_floppy(0, k->floppy.get_iobase ? diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index 503ec54f5d..459d326af0 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -221,8 +221,7 @@ static void q800_init(MachineState *machine) via_dev = qdev_new(TYPE_MAC_VIA); dinfo = drive_get(IF_MTD, 0, 0); if (dinfo) { - qdev_prop_set_drive(via_dev, "drive", blk_by_legacy_dinfo(dinfo), - &error_abort); + qdev_prop_set_drive(via_dev, "drive", blk_by_legacy_dinfo(dinfo)); } sysbus = SYS_BUS_DEVICE(via_dev); sysbus_realize_and_unref(sysbus, &error_fatal); diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c index 23420028f5..fff2c578ef 100644 --- a/hw/microblaze/petalogix_ml605_mmu.c +++ b/hw/microblaze/petalogix_ml605_mmu.c @@ -188,8 +188,9 @@ petalogix_ml605_init(MachineState *machine) dev = qdev_new("n25q128"); if (dinfo) { - qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(dinfo), - &error_fatal); + qdev_prop_set_drive_err(dev, "drive", + blk_by_legacy_dinfo(dinfo), + &error_fatal); } qdev_realize_and_unref(dev, BUS(spi), &error_fatal); diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c index 9cd313c812..d76d7b28d3 100644 --- a/hw/misc/mac_via.c +++ b/hw/misc/mac_via.c @@ -599,179 +599,310 @@ static void via1_rtc_update(MacVIAState *m) m->cmd = REG_EMPTY; } -static int adb_via_poll(MacVIAState *s, int state, uint8_t *data) +static void adb_via_poll(void *opaque) { - if (state != ADB_STATE_IDLE) { - return 0; - } + MacVIAState *m = opaque; + MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(&m->mos6522_via1); + MOS6522State *s = MOS6522(v1s); + ADBBusState *adb_bus = &m->adb_bus; + uint8_t obuf[9]; + uint8_t *data = &s->sr; + int olen; + uint16_t pending; - if (s->adb_data_in_size < s->adb_data_in_index) { - return 0; - } + /* + * Setting vADBInt below indicates that an autopoll reply has been + * received, however we must block autopoll until the point where + * the entire reply has been read back to the host + */ + adb_autopoll_block(adb_bus); + + m->adb_data_in_index = 0; + m->adb_data_out_index = 0; + olen = adb_poll(adb_bus, obuf, adb_bus->autopoll_mask); + + if (olen > 0) { + /* Autopoll response */ + *data = obuf[0]; + olen--; + memcpy(m->adb_data_in, &obuf[1], olen); + m->adb_data_in_size = olen; + + s->b &= ~VIA1B_vADBInt; + qemu_irq_raise(m->adb_data_ready); + } else if (olen < 0) { + /* Bus timeout (device does not exist) */ + *data = 0xff; + s->b |= VIA1B_vADBInt; + adb_autopoll_unblock(adb_bus); + } else { + pending = adb_bus->pending & ~(1 << (m->adb_autopoll_cmd >> 4)); + + if (pending) { + /* + * Bus timeout (device exists but another device has data). Block + * autopoll so the OS can read out the first EVEN and first ODD + * byte to determine bus timeout and SRQ status + */ + *data = m->adb_autopoll_cmd; + s->b &= ~VIA1B_vADBInt; - if (s->adb_data_out_index != 0) { - return 0; - } + obuf[0] = 0xff; + obuf[1] = 0xff; + olen = 2; - s->adb_data_in_index = 0; - s->adb_data_out_index = 0; - s->adb_data_in_size = adb_poll(&s->adb_bus, s->adb_data_in, 0xffff); + memcpy(m->adb_data_in, obuf, olen); + m->adb_data_in_size = olen; - if (s->adb_data_in_size) { - *data = s->adb_data_in[s->adb_data_in_index++]; - qemu_irq_raise(s->adb_data_ready); + qemu_irq_raise(m->adb_data_ready); + } else { + /* Bus timeout (device exists but no other device has data) */ + *data = 0; + s->b |= VIA1B_vADBInt; + adb_autopoll_unblock(adb_bus); + } } - return s->adb_data_in_size; + trace_via1_adb_poll(*data, (s->b & VIA1B_vADBInt) ? "+" : "-", + adb_bus->status, m->adb_data_in_index, olen); } -static int adb_via_send(MacVIAState *s, int state, uint8_t data) +static int adb_via_send_len(uint8_t data) { - switch (state) { - case ADB_STATE_NEW: - s->adb_data_out_index = 0; - break; - case ADB_STATE_EVEN: - if ((s->adb_data_out_index & 1) == 0) { - return 0; - } - break; - case ADB_STATE_ODD: - if (s->adb_data_out_index & 1) { - return 0; + /* Determine the send length from the given ADB command */ + uint8_t cmd = data & 0xc; + uint8_t reg = data & 0x3; + + switch (cmd) { + case 0x8: + /* Listen command */ + switch (reg) { + case 2: + /* Register 2 is only used for the keyboard */ + return 3; + case 3: + /* + * Fortunately our devices only implement writes + * to register 3 which is fixed at 2 bytes + */ + return 3; + default: + qemu_log_mask(LOG_UNIMP, "ADB unknown length for register %d\n", + reg); + return 1; } - break; - case ADB_STATE_IDLE: - return 0; + default: + /* Talk, BusReset */ + return 1; } - - assert(s->adb_data_out_index < sizeof(s->adb_data_out) - 1); - - s->adb_data_out[s->adb_data_out_index++] = data; - qemu_irq_raise(s->adb_data_ready); - return 1; } -static int adb_via_receive(MacVIAState *s, int state, uint8_t *data) +static void adb_via_send(MacVIAState *s, int state, uint8_t data) { + MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(&s->mos6522_via1); + MOS6522State *ms = MOS6522(v1s); + ADBBusState *adb_bus = &s->adb_bus; + uint16_t autopoll_mask; + switch (state) { case ADB_STATE_NEW: - return 0; - - case ADB_STATE_EVEN: - if (s->adb_data_in_size <= 0) { - qemu_irq_raise(s->adb_data_ready); - return 0; - } - - if (s->adb_data_in_index >= s->adb_data_in_size) { - *data = 0; - qemu_irq_raise(s->adb_data_ready); - return 1; - } - - if ((s->adb_data_in_index & 1) == 0) { - return 0; + /* + * Command byte: vADBInt tells host autopoll data already present + * in VIA shift register and ADB transceiver + */ + adb_autopoll_block(adb_bus); + + if (adb_bus->status & ADB_STATUS_POLLREPLY) { + /* Tell the host the existing data is from autopoll */ + ms->b &= ~VIA1B_vADBInt; + } else { + ms->b |= VIA1B_vADBInt; + s->adb_data_out_index = 0; + s->adb_data_out[s->adb_data_out_index++] = data; } + trace_via1_adb_send(" NEW", data, (ms->b & VIA1B_vADBInt) ? "+" : "-"); + qemu_irq_raise(s->adb_data_ready); break; + case ADB_STATE_EVEN: case ADB_STATE_ODD: - if (s->adb_data_in_size <= 0) { - qemu_irq_raise(s->adb_data_ready); - return 0; - } - - if (s->adb_data_in_index >= s->adb_data_in_size) { - *data = 0; - qemu_irq_raise(s->adb_data_ready); - return 1; - } - - if (s->adb_data_in_index & 1) { - return 0; - } + ms->b |= VIA1B_vADBInt; + s->adb_data_out[s->adb_data_out_index++] = data; + trace_via1_adb_send(state == ADB_STATE_EVEN ? "EVEN" : " ODD", + data, (ms->b & VIA1B_vADBInt) ? "+" : "-"); + qemu_irq_raise(s->adb_data_ready); break; case ADB_STATE_IDLE: - if (s->adb_data_out_index == 0) { - return 0; - } + return; + } - s->adb_data_in_size = adb_request(&s->adb_bus, s->adb_data_in, + /* If the command is complete, execute it */ + if (s->adb_data_out_index == adb_via_send_len(s->adb_data_out[0])) { + s->adb_data_in_size = adb_request(adb_bus, s->adb_data_in, s->adb_data_out, s->adb_data_out_index); - s->adb_data_out_index = 0; s->adb_data_in_index = 0; - if (s->adb_data_in_size < 0) { - *data = 0xff; - qemu_irq_raise(s->adb_data_ready); - return -1; - } - if (s->adb_data_in_size == 0) { - return 0; + if (adb_bus->status & ADB_STATUS_BUSTIMEOUT) { + /* + * Bus timeout (but allow first EVEN and ODD byte to indicate + * timeout via vADBInt and SRQ status) + */ + s->adb_data_in[0] = 0xff; + s->adb_data_in[1] = 0xff; + s->adb_data_in_size = 2; } - break; - } - - assert(s->adb_data_in_index < sizeof(s->adb_data_in) - 1); + /* + * If last command is TALK, store it for use by autopoll and adjust + * the autopoll mask accordingly + */ + if ((s->adb_data_out[0] & 0xc) == 0xc) { + s->adb_autopoll_cmd = s->adb_data_out[0]; - *data = s->adb_data_in[s->adb_data_in_index++]; - qemu_irq_raise(s->adb_data_ready); - if (*data == 0xff || *data == 0) { - return 0; + autopoll_mask = 1 << (s->adb_autopoll_cmd >> 4); + adb_set_autopoll_mask(adb_bus, autopoll_mask); + } } - return 1; } -static void via1_adb_update(MacVIAState *m) +static void adb_via_receive(MacVIAState *s, int state, uint8_t *data) { - MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(&m->mos6522_via1); - MOS6522State *s = MOS6522(v1s); - int state; - int ret; + MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(&s->mos6522_via1); + MOS6522State *ms = MOS6522(v1s); + ADBBusState *adb_bus = &s->adb_bus; + uint16_t pending; - state = (s->b & VIA1B_vADB_StateMask) >> VIA1B_vADB_StateShift; + switch (state) { + case ADB_STATE_NEW: + ms->b |= VIA1B_vADBInt; + return; - if (s->acr & VIA1ACR_vShiftOut) { - /* output mode */ - ret = adb_via_send(m, state, s->sr); - if (ret > 0) { - s->b &= ~VIA1B_vADBInt; + case ADB_STATE_IDLE: + /* + * Since adb_request() will have already consumed the data from the + * device, we must detect this extra state change and re-inject the + * reponse as either a "fake" autopoll reply or bus timeout + * accordingly + */ + if (s->adb_data_in_index == 0) { + if (adb_bus->status & ADB_STATUS_BUSTIMEOUT) { + *data = 0xff; + ms->b |= VIA1B_vADBInt; + qemu_irq_raise(s->adb_data_ready); + } else if (s->adb_data_in_size > 0) { + adb_bus->status = ADB_STATUS_POLLREPLY; + *data = s->adb_autopoll_cmd; + ms->b &= ~VIA1B_vADBInt; + qemu_irq_raise(s->adb_data_ready); + } } else { - s->b |= VIA1B_vADBInt; + ms->b |= VIA1B_vADBInt; + adb_autopoll_unblock(adb_bus); } - } else { - /* input mode */ - ret = adb_via_receive(m, state, &s->sr); - if (ret > 0 && s->sr != 0xff) { - s->b &= ~VIA1B_vADBInt; - } else { - s->b |= VIA1B_vADBInt; + + trace_via1_adb_receive("IDLE", *data, + (ms->b & VIA1B_vADBInt) ? "+" : "-", adb_bus->status, + s->adb_data_in_index, s->adb_data_in_size); + + break; + + case ADB_STATE_EVEN: + case ADB_STATE_ODD: + switch (s->adb_data_in_index) { + case 0: + /* First EVEN byte: vADBInt indicates bus timeout */ + trace_via1_adb_receive(state == ADB_STATE_EVEN ? "EVEN" : " ODD", + *data, (ms->b & VIA1B_vADBInt) ? "+" : "-", + adb_bus->status, s->adb_data_in_index, + s->adb_data_in_size); + + *data = s->adb_data_in[s->adb_data_in_index++]; + if (adb_bus->status & ADB_STATUS_BUSTIMEOUT) { + ms->b &= ~VIA1B_vADBInt; + } else { + ms->b |= VIA1B_vADBInt; + } + break; + + case 1: + /* First ODD byte: vADBInt indicates SRQ */ + trace_via1_adb_receive(state == ADB_STATE_EVEN ? "EVEN" : " ODD", + *data, (ms->b & VIA1B_vADBInt) ? "+" : "-", + adb_bus->status, s->adb_data_in_index, + s->adb_data_in_size); + + *data = s->adb_data_in[s->adb_data_in_index++]; + pending = adb_bus->pending & ~(1 << (s->adb_autopoll_cmd >> 4)); + if (pending) { + ms->b &= ~VIA1B_vADBInt; + } else { + ms->b |= VIA1B_vADBInt; + } + break; + + default: + /* + * Otherwise vADBInt indicates end of data. Note that Linux + * specifically checks for the sequence 0x0 0xff to confirm the + * end of the poll reply, so provide these extra bytes below to + * keep it happy + */ + trace_via1_adb_receive(state == ADB_STATE_EVEN ? "EVEN" : " ODD", + *data, (ms->b & VIA1B_vADBInt) ? "+" : "-", + adb_bus->status, s->adb_data_in_index, + s->adb_data_in_size); + + if (s->adb_data_in_index < s->adb_data_in_size) { + /* Next data byte */ + *data = s->adb_data_in[s->adb_data_in_index++]; + ms->b |= VIA1B_vADBInt; + } else if (s->adb_data_in_index == s->adb_data_in_size) { + if (adb_bus->status & ADB_STATUS_BUSTIMEOUT) { + /* Bus timeout (no more data) */ + *data = 0xff; + } else { + /* Return 0x0 after reply */ + *data = 0; + } + s->adb_data_in_index++; + ms->b &= ~VIA1B_vADBInt; + } else { + /* Bus timeout (no more data) */ + *data = 0xff; + ms->b &= ~VIA1B_vADBInt; + adb_bus->status = 0; + adb_autopoll_unblock(adb_bus); + } + break; } + + qemu_irq_raise(s->adb_data_ready); + break; } } -static void via_adb_poll(void *opaque) +static void via1_adb_update(MacVIAState *m) { - MacVIAState *m = opaque; MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(&m->mos6522_via1); MOS6522State *s = MOS6522(v1s); - int state; + int oldstate, state; - if (s->b & VIA1B_vADBInt) { - state = (s->b & VIA1B_vADB_StateMask) >> VIA1B_vADB_StateShift; - if (adb_via_poll(m, state, &s->sr)) { - s->b &= ~VIA1B_vADBInt; + oldstate = (v1s->last_b & VIA1B_vADB_StateMask) >> VIA1B_vADB_StateShift; + state = (s->b & VIA1B_vADB_StateMask) >> VIA1B_vADB_StateShift; + + if (state != oldstate) { + if (s->acr & VIA1ACR_vShiftOut) { + /* output mode */ + adb_via_send(m, state, s->sr); + } else { + /* input mode */ + adb_via_receive(m, state, &s->sr); } } - - timer_mod(m->adb_poll_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (NANOSECONDS_PER_SECOND / VIA_ADB_POLL_FREQ)); } static uint64_t mos6522_q800_via1_read(void *opaque, hwaddr addr, unsigned size) @@ -802,11 +933,21 @@ static void mos6522_q800_via1_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(opaque); + MacVIAState *m = container_of(v1s, MacVIAState, mos6522_via1); MOS6522State *ms = MOS6522(v1s); addr = (addr >> 9) & 0xf; mos6522_write(ms, addr, val, size); + switch (addr) { + case VIA_REG_B: + via1_rtc_update(m); + via1_adb_update(m); + + v1s->last_b = ms->b; + break; + } + via1_one_second_update(v1s); via1_VBL_update(v1s); } @@ -854,10 +995,9 @@ static void mac_via_reset(DeviceState *dev) { MacVIAState *m = MAC_VIA(dev); MOS6522Q800VIA1State *v1s = &m->mos6522_via1; + ADBBusState *adb_bus = &m->adb_bus; - timer_mod(m->adb_poll_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (NANOSECONDS_PER_SECOND / VIA_ADB_POLL_FREQ)); + adb_set_autopoll_enabled(adb_bus, true); timer_del(v1s->VBL_timer); v1s->next_VBL = 0; @@ -872,6 +1012,7 @@ static void mac_via_realize(DeviceState *dev, Error **errp) { MacVIAState *m = MAC_VIA(dev); MOS6522State *ms; + ADBBusState *adb_bus = &m->adb_bus; struct tm tm; int ret; @@ -907,7 +1048,7 @@ static void mac_via_realize(DeviceState *dev, Error **errp) qemu_get_timedate(&tm, 0); m->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET; - m->adb_poll_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, via_adb_poll, m); + adb_register_autopoll_callback(adb_bus, adb_via_poll, m); m->adb_data_ready = qdev_get_gpio_in_named(dev, "via1-irq", VIA1_IRQ_ADB_READY_BIT); @@ -980,8 +1121,8 @@ static int mac_via_post_load(void *opaque, int version_id) static const VMStateDescription vmstate_mac_via = { .name = "mac-via", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .post_load = mac_via_post_load, .fields = (VMStateField[]) { /* VIAs */ @@ -1005,12 +1146,12 @@ static const VMStateDescription vmstate_mac_via = { VMSTATE_INT32(wprotect, MacVIAState), VMSTATE_INT32(alt, MacVIAState), /* ADB */ - VMSTATE_TIMER_PTR(adb_poll_timer, MacVIAState), VMSTATE_INT32(adb_data_in_size, MacVIAState), VMSTATE_INT32(adb_data_in_index, MacVIAState), VMSTATE_INT32(adb_data_out_index, MacVIAState), VMSTATE_BUFFER(adb_data_in, MacVIAState), VMSTATE_BUFFER(adb_data_out, MacVIAState), + VMSTATE_UINT8(adb_autopoll_cmd, MacVIAState), VMSTATE_END_OF_LIST() } }; @@ -1039,18 +1180,6 @@ static TypeInfo mac_via_info = { }; /* VIA 1 */ -static void mos6522_q800_via1_portB_write(MOS6522State *s) -{ - MOS6522Q800VIA1State *v1s = container_of(s, MOS6522Q800VIA1State, - parent_obj); - MacVIAState *m = container_of(v1s, MacVIAState, mos6522_via1); - - via1_rtc_update(m); - via1_adb_update(m); - - v1s->last_b = s->b; -} - static void mos6522_q800_via1_reset(DeviceState *dev) { MOS6522State *ms = MOS6522(dev); @@ -1073,10 +1202,8 @@ static void mos6522_q800_via1_init(Object *obj) static void mos6522_q800_via1_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); - MOS6522DeviceClass *mdc = MOS6522_DEVICE_CLASS(oc); dc->reset = mos6522_q800_via1_reset; - mdc->portB_write = mos6522_q800_via1_portB_write; } static const TypeInfo mos6522_q800_via1_type_info = { diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c index 47aa3b0552..5bbc7770fa 100644 --- a/hw/misc/macio/cuda.c +++ b/hw/misc/macio/cuda.c @@ -116,6 +116,7 @@ static void cuda_update(CUDAState *s) { MOS6522CUDAState *mcs = &s->mos6522_cuda; MOS6522State *ms = MOS6522(mcs); + ADBBusState *adb_bus = &s->adb_bus; int packet_received, len; packet_received = 0; @@ -126,6 +127,9 @@ static void cuda_update(CUDAState *s) /* data output */ if ((ms->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) { if (s->data_out_index < sizeof(s->data_out)) { + if (s->data_out_index == 0) { + adb_autopoll_block(adb_bus); + } trace_cuda_data_send(ms->sr); s->data_out[s->data_out_index++] = ms->sr; cuda_delay_set_sr_int(s); @@ -140,6 +144,7 @@ static void cuda_update(CUDAState *s) /* indicate end of transfer */ if (s->data_in_index >= s->data_in_size) { ms->b = (ms->b | TREQ); + adb_autopoll_unblock(adb_bus); } cuda_delay_set_sr_int(s); } @@ -201,17 +206,16 @@ static void cuda_send_packet_to_host(CUDAState *s, static void cuda_adb_poll(void *opaque) { CUDAState *s = opaque; + ADBBusState *adb_bus = &s->adb_bus; uint8_t obuf[ADB_MAX_OUT_LEN + 2]; int olen; - olen = adb_poll(&s->adb_bus, obuf + 2, s->adb_poll_mask); + olen = adb_poll(adb_bus, obuf + 2, adb_bus->autopoll_mask); if (olen > 0) { obuf[0] = ADB_PACKET; obuf[1] = 0x40; /* polled data */ cuda_send_packet_to_host(s, obuf, olen + 2); } - timer_mod(s->adb_poll_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (NANOSECONDS_PER_SECOND / (1000 / s->autopoll_rate_ms))); } /* description of commands */ @@ -227,23 +231,16 @@ static bool cuda_cmd_autopoll(CUDAState *s, const uint8_t *in_data, int in_len, uint8_t *out_data, int *out_len) { - int autopoll; + ADBBusState *adb_bus = &s->adb_bus; + bool autopoll; if (in_len != 1) { return false; } - autopoll = (in_data[0] != 0); - if (autopoll != s->autopoll) { - s->autopoll = autopoll; - if (autopoll) { - timer_mod(s->adb_poll_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (NANOSECONDS_PER_SECOND / (1000 / s->autopoll_rate_ms))); - } else { - timer_del(s->adb_poll_timer); - } - } + autopoll = (in_data[0] != 0) ? true : false; + + adb_set_autopoll_enabled(adb_bus, autopoll); return true; } @@ -251,6 +248,8 @@ static bool cuda_cmd_set_autorate(CUDAState *s, const uint8_t *in_data, int in_len, uint8_t *out_data, int *out_len) { + ADBBusState *adb_bus = &s->adb_bus; + if (in_len != 1) { return false; } @@ -261,12 +260,7 @@ static bool cuda_cmd_set_autorate(CUDAState *s, return false; } - s->autopoll_rate_ms = in_data[0]; - if (s->autopoll) { - timer_mod(s->adb_poll_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (NANOSECONDS_PER_SECOND / (1000 / s->autopoll_rate_ms))); - } + adb_set_autopoll_rate_ms(adb_bus, in_data[0]); return true; } @@ -274,11 +268,16 @@ static bool cuda_cmd_set_device_list(CUDAState *s, const uint8_t *in_data, int in_len, uint8_t *out_data, int *out_len) { + ADBBusState *adb_bus = &s->adb_bus; + uint16_t mask; + if (in_len != 2) { return false; } - s->adb_poll_mask = (((uint16_t)in_data[0]) << 8) | in_data[1]; + mask = (((uint16_t)in_data[0]) << 8) | in_data[1]; + + adb_set_autopoll_mask(adb_bus, mask); return true; } @@ -489,8 +488,8 @@ static const MemoryRegionOps mos6522_cuda_ops = { static const VMStateDescription vmstate_cuda = { .name = "cuda", - .version_id = 5, - .minimum_version_id = 5, + .version_id = 6, + .minimum_version_id = 6, .fields = (VMStateField[]) { VMSTATE_STRUCT(mos6522_cuda.parent_obj, CUDAState, 0, vmstate_mos6522, MOS6522State), @@ -499,13 +498,9 @@ static const VMStateDescription vmstate_cuda = { VMSTATE_INT32(data_in_size, CUDAState), VMSTATE_INT32(data_in_index, CUDAState), VMSTATE_INT32(data_out_index, CUDAState), - VMSTATE_UINT8(autopoll, CUDAState), - VMSTATE_UINT8(autopoll_rate_ms, CUDAState), - VMSTATE_UINT16(adb_poll_mask, CUDAState), VMSTATE_BUFFER(data_in, CUDAState), VMSTATE_BUFFER(data_out, CUDAState), VMSTATE_UINT32(tick_offset, CUDAState), - VMSTATE_TIMER_PTR(adb_poll_timer, CUDAState), VMSTATE_TIMER_PTR(sr_delay_timer, CUDAState), VMSTATE_END_OF_LIST() } @@ -514,11 +509,13 @@ static const VMStateDescription vmstate_cuda = { static void cuda_reset(DeviceState *dev) { CUDAState *s = CUDA(dev); + ADBBusState *adb_bus = &s->adb_bus; s->data_in_size = 0; s->data_in_index = 0; s->data_out_index = 0; - s->autopoll = 0; + + adb_set_autopoll_enabled(adb_bus, false); } static void cuda_realize(DeviceState *dev, Error **errp) @@ -526,6 +523,7 @@ static void cuda_realize(DeviceState *dev, Error **errp) CUDAState *s = CUDA(dev); Error *err = NULL; SysBusDevice *sbd; + ADBBusState *adb_bus = &s->adb_bus; struct tm tm; sysbus_realize(SYS_BUS_DEVICE(&s->mos6522_cuda), &err); @@ -544,9 +542,7 @@ static void cuda_realize(DeviceState *dev, Error **errp) s->sr_delay_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_set_sr_int, s); s->sr_delay_ns = 20 * SCALE_US; - s->adb_poll_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_adb_poll, s); - s->adb_poll_mask = 0xffff; - s->autopoll_rate_ms = 20; + adb_register_autopoll_callback(adb_bus, cuda_adb_poll, s); } static void cuda_init(Object *obj) diff --git a/hw/misc/macio/pmu.c b/hw/misc/macio/pmu.c index 41b626c46c..598d8e7517 100644 --- a/hw/misc/macio/pmu.c +++ b/hw/misc/macio/pmu.c @@ -92,10 +92,11 @@ static void pmu_update_extirq(PMUState *s) static void pmu_adb_poll(void *opaque) { PMUState *s = opaque; + ADBBusState *adb_bus = &s->adb_bus; int olen; if (!(s->intbits & PMU_INT_ADB)) { - olen = adb_poll(&s->adb_bus, s->adb_reply, s->adb_poll_mask); + olen = adb_poll(adb_bus, s->adb_reply, adb_bus->autopoll_mask); trace_pmu_adb_poll(olen); if (olen > 0) { @@ -104,9 +105,6 @@ static void pmu_adb_poll(void *opaque) pmu_update_extirq(s); } } - - timer_mod(s->adb_poll_timer, - qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 30); } static void pmu_one_sec_timer(void *opaque) @@ -173,18 +171,15 @@ static void pmu_cmd_set_int_mask(PMUState *s, static void pmu_cmd_set_adb_autopoll(PMUState *s, uint16_t mask) { - trace_pmu_cmd_set_adb_autopoll(mask); + ADBBusState *adb_bus = &s->adb_bus; - if (s->autopoll_mask == mask) { - return; - } + trace_pmu_cmd_set_adb_autopoll(mask); - s->autopoll_mask = mask; if (mask) { - timer_mod(s->adb_poll_timer, - qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 30); + adb_set_autopoll_mask(adb_bus, mask); + adb_set_autopoll_enabled(adb_bus, true); } else { - timer_del(s->adb_poll_timer); + adb_set_autopoll_enabled(adb_bus, false); } } @@ -267,6 +262,8 @@ static void pmu_cmd_adb_poll_off(PMUState *s, const uint8_t *in_data, uint8_t in_len, uint8_t *out_data, uint8_t *out_len) { + ADBBusState *adb_bus = &s->adb_bus; + if (in_len != 0) { qemu_log_mask(LOG_GUEST_ERROR, "PMU: ADB POLL OFF command, invalid len: %d want: 0\n", @@ -274,9 +271,8 @@ static void pmu_cmd_adb_poll_off(PMUState *s, return; } - if (s->has_adb && s->autopoll_mask) { - timer_del(s->adb_poll_timer); - s->autopoll_mask = false; + if (s->has_adb) { + adb_set_autopoll_enabled(adb_bus, false); } } @@ -521,6 +517,7 @@ static void pmu_update(PMUState *s) { MOS6522PMUState *mps = &s->mos6522_pmu; MOS6522State *ms = MOS6522(mps); + ADBBusState *adb_bus = &s->adb_bus; /* Only react to changes in reg B */ if (ms->b == s->last_b) { @@ -582,6 +579,7 @@ static void pmu_update(PMUState *s) s->cmd_rsp_pos = 0; s->cmd_state = pmu_state_cmd; + adb_autopoll_block(adb_bus); trace_pmu_debug_protocol_cmd(s->cmd, s->cmdlen, s->rsplen); break; @@ -640,6 +638,7 @@ static void pmu_update(PMUState *s) if (s->cmd_state == pmu_state_rsp && s->rsplen == s->cmd_rsp_pos) { trace_pmu_debug_protocol_cmd_resp_complete(ms->ier); + adb_autopoll_unblock(adb_bus); s->cmd_state = pmu_state_idle; } } @@ -684,12 +683,10 @@ static bool pmu_adb_state_needed(void *opaque) static const VMStateDescription vmstate_pmu_adb = { .name = "pmu/adb", - .version_id = 0, - .minimum_version_id = 0, + .version_id = 1, + .minimum_version_id = 1, .needed = pmu_adb_state_needed, .fields = (VMStateField[]) { - VMSTATE_UINT16(adb_poll_mask, PMUState), - VMSTATE_TIMER_PTR(adb_poll_timer, PMUState), VMSTATE_UINT8(adb_reply_size, PMUState), VMSTATE_BUFFER(adb_reply, PMUState), VMSTATE_END_OF_LIST() @@ -698,8 +695,8 @@ static const VMStateDescription vmstate_pmu_adb = { static const VMStateDescription vmstate_pmu = { .name = "pmu", - .version_id = 0, - .minimum_version_id = 0, + .version_id = 1, + .minimum_version_id = 1, .fields = (VMStateField[]) { VMSTATE_STRUCT(mos6522_pmu.parent_obj, PMUState, 0, vmstate_mos6522, MOS6522State), @@ -714,8 +711,6 @@ static const VMStateDescription vmstate_pmu = { VMSTATE_BUFFER(cmd_rsp, PMUState), VMSTATE_UINT8(intbits, PMUState), VMSTATE_UINT8(intmask, PMUState), - VMSTATE_UINT8(autopoll_rate_ms, PMUState), - VMSTATE_UINT8(autopoll_mask, PMUState), VMSTATE_UINT32(tick_offset, PMUState), VMSTATE_TIMER_PTR(one_sec_timer, PMUState), VMSTATE_INT64(one_sec_target, PMUState), @@ -735,7 +730,6 @@ static void pmu_reset(DeviceState *dev) s->intbits = 0; s->cmd_state = pmu_state_idle; - s->autopoll_mask = 0; } static void pmu_realize(DeviceState *dev, Error **errp) @@ -743,6 +737,7 @@ static void pmu_realize(DeviceState *dev, Error **errp) PMUState *s = VIA_PMU(dev); Error *err = NULL; SysBusDevice *sbd; + ADBBusState *adb_bus = &s->adb_bus; struct tm tm; sysbus_realize(SYS_BUS_DEVICE(&s->mos6522_pmu), &err); @@ -764,9 +759,7 @@ static void pmu_realize(DeviceState *dev, Error **errp) if (s->has_adb) { qbus_create_inplace(&s->adb_bus, sizeof(s->adb_bus), TYPE_ADB_BUS, dev, "adb.0"); - s->adb_poll_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, pmu_adb_poll, s); - s->adb_poll_mask = 0xffff; - s->autopoll_rate_ms = 20; + adb_register_autopoll_callback(adb_bus, pmu_adb_poll, s); } } diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 5561746866..68a6d9f2ab 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -202,6 +202,9 @@ via1_rtc_cmd_pram_read(int addr, int value) "addr=%u value=0x%02x" via1_rtc_cmd_pram_write(int addr, int value) "addr=%u value=0x%02x" via1_rtc_cmd_pram_sect_read(int sector, int offset, int addr, int value) "sector=%u offset=%u addr=%d value=0x%02x" via1_rtc_cmd_pram_sect_write(int sector, int offset, int addr, int value) "sector=%u offset=%u addr=%d value=0x%02x" +via1_adb_send(const char *state, uint8_t data, const char *vadbint) "state %s data=0x%02x vADBInt=%s" +via1_adb_receive(const char *state, uint8_t data, const char *vadbint, int status, int index, int size) "state %s data=0x%02x vADBInt=%s status=0x%x index=%d size=%d" +via1_adb_poll(uint8_t data, const char *vadbint, int status, int index, int size) "data=0x%02x vADBInt=%s status=0x%x index=%d size=%d" # grlib_ahb_apb_pnp.c grlib_ahb_pnp_read(uint64_t addr, uint32_t value) "AHB PnP read addr:0x%03"PRIx64" data:0x%08x" diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 5f3a028e6a..828c5992ae 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -404,11 +404,9 @@ static void ppc_core99_init(MachineState *machine) adb_bus = qdev_get_child_bus(dev, "adb.0"); dev = qdev_new(TYPE_ADB_KEYBOARD); - qdev_prop_set_bit(dev, "disable-direct-reg3-writes", true); qdev_realize_and_unref(dev, adb_bus, &error_fatal); dev = qdev_new(TYPE_ADB_MOUSE); - qdev_prop_set_bit(dev, "disable-direct-reg3-writes", true); qdev_realize_and_unref(dev, adb_bus, &error_fatal); } diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 80b4afd211..643098ad5f 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -21,6 +21,7 @@ #include "qemu-common.h" #include "qemu/units.h" #include "qapi/error.h" +#include "sysemu/qtest.h" #include "sysemu/sysemu.h" #include "sysemu/numa.h" #include "sysemu/reset.h" @@ -587,9 +588,11 @@ static void pnv_reset(MachineState *machine) bmc = pnv_bmc_find(&error_fatal); if (!pnv->bmc) { if (!bmc) { - warn_report("machine has no BMC device. Use '-device " - "ipmi-bmc-sim,id=bmc0 -device isa-ipmi-bt,bmc=bmc0,irq=10' " - "to define one"); + if (!qtest_enabled()) { + warn_report("machine has no BMC device. Use '-device " + "ipmi-bmc-sim,id=bmc0 -device isa-ipmi-bt,bmc=bmc0,irq=10' " + "to define one"); + } } else { pnv_bmc_set_pnor(bmc, pnv->pnor); pnv->bmc = bmc; @@ -730,8 +733,7 @@ static void pnv_init(MachineState *machine) */ dev = qdev_new(TYPE_PNV_PNOR); if (pnor) { - qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(pnor), - &error_abort); + qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(pnor)); } sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); pnv->pnor = PNV_PNOR(dev); diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 8d630baa5d..bd9345cdac 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1716,8 +1716,8 @@ static void spapr_create_nvram(SpaprMachineState *spapr) DriveInfo *dinfo = drive_get(IF_PFLASH, 0, 0); if (dinfo) { - qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(dinfo), - &error_fatal); + qdev_prop_set_drive_err(dev, "drive", blk_by_legacy_dinfo(dinfo), + &error_fatal); } qdev_realize_and_unref(dev, &spapr->vio_bus->bus, &error_fatal); diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index efdc0dbbcf..0c2bc8e06e 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -248,23 +248,18 @@ SpaprCapPossible cap_cfpc_possible = { static void cap_safe_cache_apply(SpaprMachineState *spapr, uint8_t val, Error **errp) { - Error *local_err = NULL; uint8_t kvm_val = kvmppc_get_cap_safe_cache(); if (tcg_enabled() && val) { /* TCG only supports broken, allow other values and print a warning */ - error_setg(&local_err, - "TCG doesn't support requested feature, cap-cfpc=%s", - cap_cfpc_possible.vals[val]); + warn_report("TCG doesn't support requested feature, cap-cfpc=%s", + cap_cfpc_possible.vals[val]); } else if (kvm_enabled() && (val > kvm_val)) { error_setg(errp, "Requested safe cache capability level not supported by kvm," " try appending -machine cap-cfpc=%s", cap_cfpc_possible.vals[kvm_val]); } - - if (local_err != NULL) - warn_report_err(local_err); } SpaprCapPossible cap_sbbc_possible = { @@ -277,23 +272,18 @@ SpaprCapPossible cap_sbbc_possible = { static void cap_safe_bounds_check_apply(SpaprMachineState *spapr, uint8_t val, Error **errp) { - Error *local_err = NULL; uint8_t kvm_val = kvmppc_get_cap_safe_bounds_check(); if (tcg_enabled() && val) { /* TCG only supports broken, allow other values and print a warning */ - error_setg(&local_err, - "TCG doesn't support requested feature, cap-sbbc=%s", - cap_sbbc_possible.vals[val]); + warn_report("TCG doesn't support requested feature, cap-sbbc=%s", + cap_sbbc_possible.vals[val]); } else if (kvm_enabled() && (val > kvm_val)) { error_setg(errp, "Requested safe bounds check capability level not supported by kvm," " try appending -machine cap-sbbc=%s", cap_sbbc_possible.vals[kvm_val]); } - - if (local_err != NULL) - warn_report_err(local_err); } SpaprCapPossible cap_ibs_possible = { @@ -309,24 +299,18 @@ SpaprCapPossible cap_ibs_possible = { static void cap_safe_indirect_branch_apply(SpaprMachineState *spapr, uint8_t val, Error **errp) { - Error *local_err = NULL; uint8_t kvm_val = kvmppc_get_cap_safe_indirect_branch(); if (tcg_enabled() && val) { /* TCG only supports broken, allow other values and print a warning */ - error_setg(&local_err, - "TCG doesn't support requested feature, cap-ibs=%s", - cap_ibs_possible.vals[val]); + warn_report("TCG doesn't support requested feature, cap-ibs=%s", + cap_ibs_possible.vals[val]); } else if (kvm_enabled() && (val > kvm_val)) { error_setg(errp, "Requested safe indirect branch capability level not supported by kvm," " try appending -machine cap-ibs=%s", cap_ibs_possible.vals[kvm_val]); } - - if (local_err != NULL) { - warn_report_err(local_err); - } } #define VALUE_DESC_TRISTATE " (broken, workaround, fixed)" diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c index 4318ed9638..731080d989 100644 --- a/hw/ppc/spapr_vio.c +++ b/hw/ppc/spapr_vio.c @@ -420,7 +420,7 @@ static void spapr_vio_busdev_reset(DeviceState *qdev) } /* - * The register property of a VIO device is defined in livirt using + * The register property of a VIO device is defined in libvirt using * 0x1000 as a base register number plus a 0x1000 increment. For the * VIO tty device, the base number is changed to 0x30000000. QEMU uses * a base register number of 0x71000000 and then a simple increment. @@ -450,7 +450,7 @@ static inline uint32_t spapr_vio_reg_to_irq(uint32_t reg) } else if (reg >= 0x30000000) { /* - * VIO tty devices register values, when allocated by livirt, + * VIO tty devices register values, when allocated by libvirt, * are mapped in range [0xf0 - 0xff], gives us a maximum of 16 * vtys. */ @@ -459,7 +459,7 @@ static inline uint32_t spapr_vio_reg_to_irq(uint32_t reg) } else { /* * Other VIO devices register values, when allocated by - * livirt, should be mapped in range [0x00 - 0xef]. Conflicts + * libvirt, should be mapped in range [0x00 - 0xef]. Conflicts * will be detected when IRQ is claimed. */ irq = (reg >> 12) & 0xff; diff --git a/hw/rx/Kconfig b/hw/rx/Kconfig new file mode 100644 index 0000000000..2b297c5a6a --- /dev/null +++ b/hw/rx/Kconfig @@ -0,0 +1,10 @@ +config RX62N_MCU + bool + select RX_ICU + select RENESAS_TMR + select RENESAS_CMT + select RENESAS_SCI + +config RX_GDBSIM + bool + select RX62N_MCU diff --git a/hw/rx/Makefile.objs b/hw/rx/Makefile.objs new file mode 100644 index 0000000000..4ef6b9e5b1 --- /dev/null +++ b/hw/rx/Makefile.objs @@ -0,0 +1,2 @@ +obj-$(CONFIG_RX62N_MCU) += rx62n.o +obj-$(CONFIG_RX_GDBSIM) += rx-gdbsim.o diff --git a/hw/rx/rx-gdbsim.c b/hw/rx/rx-gdbsim.c new file mode 100644 index 0000000000..b8a56fa7af --- /dev/null +++ b/hw/rx/rx-gdbsim.c @@ -0,0 +1,198 @@ +/* + * RX QEMU GDB simulator + * + * Copyright (c) 2019 Yoshinori Sato + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/cutils.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "hw/loader.h" +#include "hw/rx/rx62n.h" +#include "sysemu/sysemu.h" +#include "sysemu/qtest.h" +#include "sysemu/device_tree.h" +#include "hw/boards.h" + +/* Same address of GDB integrated simulator */ +#define SDRAM_BASE EXT_CS_BASE + +typedef struct RxGdbSimMachineClass { + /*< private >*/ + MachineClass parent_class; + /*< public >*/ + const char *mcu_name; + uint32_t xtal_freq_hz; +} RxGdbSimMachineClass; + +typedef struct RxGdbSimMachineState { + /*< private >*/ + MachineState parent_obj; + /*< public >*/ + RX62NState mcu; +} RxGdbSimMachineState; + +#define TYPE_RX_GDBSIM_MACHINE MACHINE_TYPE_NAME("rx62n-common") + +#define RX_GDBSIM_MACHINE(obj) \ + OBJECT_CHECK(RxGdbSimMachineState, (obj), TYPE_RX_GDBSIM_MACHINE) + +#define RX_GDBSIM_MACHINE_CLASS(klass) \ + OBJECT_CLASS_CHECK(RxGdbSimMachineClass, (klass), TYPE_RX_GDBSIM_MACHINE) +#define RX_GDBSIM_MACHINE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(RxGdbSimMachineClass, (obj), TYPE_RX_GDBSIM_MACHINE) + +static void rx_load_image(RXCPU *cpu, const char *filename, + uint32_t start, uint32_t size) +{ + static uint32_t extable[32]; + long kernel_size; + int i; + + kernel_size = load_image_targphys(filename, start, size); + if (kernel_size < 0) { + fprintf(stderr, "qemu: could not load kernel '%s'\n", filename); + exit(1); + } + cpu->env.pc = start; + + /* setup exception trap trampoline */ + /* linux kernel only works little-endian mode */ + for (i = 0; i < ARRAY_SIZE(extable); i++) { + extable[i] = cpu_to_le32(0x10 + i * 4); + } + rom_add_blob_fixed("extable", extable, sizeof(extable), VECTOR_TABLE_BASE); +} + +static void rx_gdbsim_init(MachineState *machine) +{ + MachineClass *mc = MACHINE_GET_CLASS(machine); + RxGdbSimMachineState *s = RX_GDBSIM_MACHINE(machine); + RxGdbSimMachineClass *rxc = RX_GDBSIM_MACHINE_GET_CLASS(machine); + MemoryRegion *sysmem = get_system_memory(); + const char *kernel_filename = machine->kernel_filename; + const char *dtb_filename = machine->dtb; + + if (machine->ram_size < mc->default_ram_size) { + char *sz = size_to_str(mc->default_ram_size); + error_report("Invalid RAM size, should be more than %s", sz); + g_free(sz); + } + + /* Allocate memory space */ + memory_region_add_subregion(sysmem, SDRAM_BASE, machine->ram); + + /* Initialize MCU */ + object_initialize_child(OBJECT(machine), "mcu", &s->mcu, rxc->mcu_name); + object_property_set_link(OBJECT(&s->mcu), OBJECT(sysmem), + "main-bus", &error_abort); + object_property_set_uint(OBJECT(&s->mcu), rxc->xtal_freq_hz, + "xtal-frequency-hz", &error_abort); + object_property_set_bool(OBJECT(&s->mcu), kernel_filename != NULL, + "load-kernel", &error_abort); + qdev_realize(DEVICE(&s->mcu), NULL, &error_abort); + + /* Load kernel and dtb */ + if (kernel_filename) { + ram_addr_t kernel_offset; + + /* + * The kernel image is loaded into + * the latter half of the SDRAM space. + */ + kernel_offset = machine->ram_size / 2; + rx_load_image(RXCPU(first_cpu), kernel_filename, + SDRAM_BASE + kernel_offset, kernel_offset); + if (dtb_filename) { + ram_addr_t dtb_offset; + int dtb_size; + void *dtb; + + dtb = load_device_tree(dtb_filename, &dtb_size); + if (dtb == NULL) { + error_report("Couldn't open dtb file %s", dtb_filename); + exit(1); + } + if (machine->kernel_cmdline && + qemu_fdt_setprop_string(dtb, "/chosen", "bootargs", + machine->kernel_cmdline) < 0) { + error_report("Couldn't set /chosen/bootargs"); + exit(1); + } + /* DTB is located at the end of SDRAM space. */ + dtb_offset = machine->ram_size - dtb_size; + rom_add_blob_fixed("dtb", dtb, dtb_size, + SDRAM_BASE + dtb_offset); + /* Set dtb address to R1 */ + RXCPU(first_cpu)->env.regs[1] = SDRAM_BASE + dtb_offset; + } + } +} + +static void rx_gdbsim_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->init = rx_gdbsim_init; + mc->default_cpu_type = TYPE_RX62N_CPU; + mc->default_ram_size = 16 * MiB; + mc->default_ram_id = "ext-sdram"; +} + +static void rx62n7_class_init(ObjectClass *oc, void *data) +{ + RxGdbSimMachineClass *rxc = RX_GDBSIM_MACHINE_CLASS(oc); + MachineClass *mc = MACHINE_CLASS(oc); + + rxc->mcu_name = TYPE_R5F562N7_MCU; + rxc->xtal_freq_hz = 12 * 1000 * 1000; + mc->desc = "gdb simulator (R5F562N7 MCU and external RAM)"; +}; + +static void rx62n8_class_init(ObjectClass *oc, void *data) +{ + RxGdbSimMachineClass *rxc = RX_GDBSIM_MACHINE_CLASS(oc); + MachineClass *mc = MACHINE_CLASS(oc); + + rxc->mcu_name = TYPE_R5F562N8_MCU; + rxc->xtal_freq_hz = 12 * 1000 * 1000; + mc->desc = "gdb simulator (R5F562N8 MCU and external RAM)"; +}; + +static const TypeInfo rx_gdbsim_types[] = { + { + .name = MACHINE_TYPE_NAME("gdbsim-r5f562n7"), + .parent = TYPE_RX_GDBSIM_MACHINE, + .class_init = rx62n7_class_init, + }, { + .name = MACHINE_TYPE_NAME("gdbsim-r5f562n8"), + .parent = TYPE_RX_GDBSIM_MACHINE, + .class_init = rx62n8_class_init, + }, { + .name = TYPE_RX_GDBSIM_MACHINE, + .parent = TYPE_MACHINE, + .instance_size = sizeof(RxGdbSimMachineState), + .class_size = sizeof(RxGdbSimMachineClass), + .class_init = rx_gdbsim_class_init, + .abstract = true, + } +}; + +DEFINE_TYPES(rx_gdbsim_types) diff --git a/hw/rx/rx62n.c b/hw/rx/rx62n.c new file mode 100644 index 0000000000..b9c217ebfa --- /dev/null +++ b/hw/rx/rx62n.c @@ -0,0 +1,323 @@ +/* + * RX62N Microcontroller + * + * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware + * (Rev.1.40 R01UH0033EJ0140) + * + * Copyright (c) 2019 Yoshinori Sato + * Copyright (c) 2020 Philippe Mathieu-Daudé + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "hw/hw.h" +#include "hw/rx/rx62n.h" +#include "hw/loader.h" +#include "hw/sysbus.h" +#include "hw/qdev-properties.h" +#include "sysemu/sysemu.h" +#include "sysemu/qtest.h" +#include "cpu.h" + +/* + * RX62N Internal Memory + */ +#define RX62N_IRAM_BASE 0x00000000 +#define RX62N_DFLASH_BASE 0x00100000 +#define RX62N_CFLASH_BASE 0xfff80000 + +/* + * RX62N Peripheral Address + * See users manual section 5 + */ +#define RX62N_ICU_BASE 0x00087000 +#define RX62N_TMR_BASE 0x00088200 +#define RX62N_CMT_BASE 0x00088000 +#define RX62N_SCI_BASE 0x00088240 + +/* + * RX62N Peripheral IRQ + * See users manual section 11 + */ +#define RX62N_TMR_IRQ 174 +#define RX62N_CMT_IRQ 28 +#define RX62N_SCI_IRQ 214 + +#define RX62N_XTAL_MIN_HZ (8 * 1000 * 1000) +#define RX62N_XTAL_MAX_HZ (14 * 1000 * 1000) +#define RX62N_PCLK_MAX_HZ (50 * 1000 * 1000) + +typedef struct RX62NClass { + /*< private >*/ + DeviceClass parent_class; + /*< public >*/ + const char *name; + uint64_t ram_size; + uint64_t rom_flash_size; + uint64_t data_flash_size; +} RX62NClass; + +#define RX62N_MCU_CLASS(klass) \ + OBJECT_CLASS_CHECK(RX62NClass, (klass), TYPE_RX62N_MCU) +#define RX62N_MCU_GET_CLASS(obj) \ + OBJECT_GET_CLASS(RX62NClass, (obj), TYPE_RX62N_MCU) + +/* + * IRQ -> IPR mapping table + * 0x00 - 0x91: IPR no (IPR00 to IPR91) + * 0xff: IPR not assigned + * See "11.3.1 Interrupt Vector Table" in hardware manual. + */ +static const uint8_t ipr_table[NR_IRQS] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 15 */ + 0x00, 0xff, 0xff, 0xff, 0xff, 0x01, 0xff, 0x02, + 0xff, 0xff, 0xff, 0x03, 0x04, 0x05, 0x06, 0x07, /* 31 */ + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x14, 0x14, 0x14, /* 47 */ + 0x15, 0x15, 0x15, 0x15, 0xff, 0xff, 0xff, 0xff, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1d, 0x1e, 0x1f, /* 63 */ + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 79 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x3a, 0x3b, 0x3c, 0xff, 0xff, 0xff, /* 95 */ + 0x40, 0xff, 0x44, 0x45, 0xff, 0xff, 0x48, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 111 */ + 0xff, 0xff, 0x51, 0x51, 0x51, 0x51, 0x52, 0x52, + 0x52, 0x53, 0x53, 0x54, 0x54, 0x55, 0x55, 0x56, /* 127 */ + 0x56, 0x57, 0x57, 0x57, 0x57, 0x58, 0x59, 0x59, + 0x59, 0x59, 0x5a, 0x5b, 0x5b, 0x5b, 0x5c, 0x5c, /* 143 */ + 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5e, 0x5e, 0x5f, + 0x5f, 0x60, 0x60, 0x61, 0x61, 0x62, 0x62, 0x62, /* 159 */ + 0x62, 0x63, 0x64, 0x64, 0x64, 0x64, 0x65, 0x66, + 0x66, 0x66, 0x67, 0x67, 0x67, 0x67, 0x68, 0x68, /* 175 */ + 0x68, 0x69, 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6b, + 0x6b, 0x6b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 191 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x70, 0x71, + 0x72, 0x73, 0x74, 0x75, 0xff, 0xff, 0xff, 0xff, /* 207 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x80, + 0x80, 0x80, 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, /* 223 */ + 0x82, 0x82, 0x83, 0x83, 0x83, 0x83, 0xff, 0xff, + 0xff, 0xff, 0x85, 0x85, 0x85, 0x85, 0x86, 0x86, /* 239 */ + 0x86, 0x86, 0xff, 0xff, 0xff, 0xff, 0x88, 0x89, + 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, /* 255 */ +}; + +/* + * Level triggerd IRQ list + * Not listed IRQ is Edge trigger. + * See "11.3.1 Interrupt Vector Table" in hardware manual. + */ +static const uint8_t levelirq[] = { + 16, 21, 32, 44, 47, 48, 51, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 90, 91, 170, 171, 172, 173, 214, + 217, 218, 221, 222, 225, 226, 229, 234, 237, 238, + 241, 246, 249, 250, 253, +}; + +static void register_icu(RX62NState *s) +{ + int i; + SysBusDevice *icu; + + object_initialize_child(OBJECT(s), "icu", &s->icu, TYPE_RX_ICU); + icu = SYS_BUS_DEVICE(&s->icu); + qdev_prop_set_uint32(DEVICE(icu), "len-ipr-map", NR_IRQS); + for (i = 0; i < NR_IRQS; i++) { + char propname[32]; + snprintf(propname, sizeof(propname), "ipr-map[%d]", i); + qdev_prop_set_uint32(DEVICE(icu), propname, ipr_table[i]); + } + qdev_prop_set_uint32(DEVICE(icu), "len-trigger-level", + ARRAY_SIZE(levelirq)); + for (i = 0; i < ARRAY_SIZE(levelirq); i++) { + char propname[32]; + snprintf(propname, sizeof(propname), "trigger-level[%d]", i); + qdev_prop_set_uint32(DEVICE(icu), propname, levelirq[i]); + } + + for (i = 0; i < NR_IRQS; i++) { + s->irq[i] = qdev_get_gpio_in(DEVICE(icu), i); + } + sysbus_realize(icu, &error_abort); + sysbus_connect_irq(icu, 0, qdev_get_gpio_in(DEVICE(&s->cpu), RX_CPU_IRQ)); + sysbus_connect_irq(icu, 1, qdev_get_gpio_in(DEVICE(&s->cpu), RX_CPU_FIR)); + sysbus_connect_irq(icu, 2, s->irq[SWI]); + sysbus_mmio_map(SYS_BUS_DEVICE(icu), 0, RX62N_ICU_BASE); +} + +static void register_tmr(RX62NState *s, int unit) +{ + SysBusDevice *tmr; + int i, irqbase; + + object_initialize_child(OBJECT(s), "tmr[*]", + &s->tmr[unit], TYPE_RENESAS_TMR); + tmr = SYS_BUS_DEVICE(&s->tmr[unit]); + qdev_prop_set_uint64(DEVICE(tmr), "input-freq", s->pclk_freq_hz); + sysbus_realize(tmr, &error_abort); + + irqbase = RX62N_TMR_IRQ + TMR_NR_IRQ * unit; + for (i = 0; i < TMR_NR_IRQ; i++) { + sysbus_connect_irq(tmr, i, s->irq[irqbase + i]); + } + sysbus_mmio_map(tmr, 0, RX62N_TMR_BASE + unit * 0x10); +} + +static void register_cmt(RX62NState *s, int unit) +{ + SysBusDevice *cmt; + int i, irqbase; + + object_initialize_child(OBJECT(s), "cmt[*]", + &s->cmt[unit], TYPE_RENESAS_CMT); + cmt = SYS_BUS_DEVICE(&s->cmt[unit]); + qdev_prop_set_uint64(DEVICE(cmt), "input-freq", s->pclk_freq_hz); + sysbus_realize(cmt, &error_abort); + + irqbase = RX62N_CMT_IRQ + CMT_NR_IRQ * unit; + for (i = 0; i < CMT_NR_IRQ; i++) { + sysbus_connect_irq(cmt, i, s->irq[irqbase + i]); + } + sysbus_mmio_map(cmt, 0, RX62N_CMT_BASE + unit * 0x10); +} + +static void register_sci(RX62NState *s, int unit) +{ + SysBusDevice *sci; + int i, irqbase; + + object_initialize_child(OBJECT(s), "sci[*]", + &s->sci[unit], TYPE_RENESAS_SCI); + sci = SYS_BUS_DEVICE(&s->sci[unit]); + qdev_prop_set_chr(DEVICE(sci), "chardev", serial_hd(unit)); + qdev_prop_set_uint64(DEVICE(sci), "input-freq", s->pclk_freq_hz); + sysbus_realize(sci, &error_abort); + + irqbase = RX62N_SCI_IRQ + SCI_NR_IRQ * unit; + for (i = 0; i < SCI_NR_IRQ; i++) { + sysbus_connect_irq(sci, i, s->irq[irqbase + i]); + } + sysbus_mmio_map(sci, 0, RX62N_SCI_BASE + unit * 0x08); +} + +static void rx62n_realize(DeviceState *dev, Error **errp) +{ + RX62NState *s = RX62N_MCU(dev); + RX62NClass *rxc = RX62N_MCU_GET_CLASS(dev); + + if (s->xtal_freq_hz == 0) { + error_setg(errp, "\"xtal-frequency-hz\" property must be provided."); + return; + } + /* XTAL range: 8-14 MHz */ + if (s->xtal_freq_hz < RX62N_XTAL_MIN_HZ + || s->xtal_freq_hz > RX62N_XTAL_MAX_HZ) { + error_setg(errp, "\"xtal-frequency-hz\" property in incorrect range."); + return; + } + /* Use a 4x fixed multiplier */ + s->pclk_freq_hz = 4 * s->xtal_freq_hz; + /* PCLK range: 8-50 MHz */ + assert(s->pclk_freq_hz <= RX62N_PCLK_MAX_HZ); + + memory_region_init_ram(&s->iram, OBJECT(dev), "iram", + rxc->ram_size, &error_abort); + memory_region_add_subregion(s->sysmem, RX62N_IRAM_BASE, &s->iram); + memory_region_init_rom(&s->d_flash, OBJECT(dev), "flash-data", + rxc->data_flash_size, &error_abort); + memory_region_add_subregion(s->sysmem, RX62N_DFLASH_BASE, &s->d_flash); + memory_region_init_rom(&s->c_flash, OBJECT(dev), "flash-code", + rxc->rom_flash_size, &error_abort); + memory_region_add_subregion(s->sysmem, RX62N_CFLASH_BASE, &s->c_flash); + + if (!s->kernel) { + if (bios_name) { + rom_add_file_fixed(bios_name, RX62N_CFLASH_BASE, 0); + } else if (!qtest_enabled()) { + error_report("No bios or kernel specified"); + exit(1); + } + } + + /* Initialize CPU */ + object_initialize_child(OBJECT(s), "cpu", &s->cpu, TYPE_RX62N_CPU); + qdev_realize(DEVICE(&s->cpu), NULL, &error_abort); + + register_icu(s); + s->cpu.env.ack = qdev_get_gpio_in_named(DEVICE(&s->icu), "ack", 0); + register_tmr(s, 0); + register_tmr(s, 1); + register_cmt(s, 0); + register_cmt(s, 1); + register_sci(s, 0); +} + +static Property rx62n_properties[] = { + DEFINE_PROP_LINK("main-bus", RX62NState, sysmem, TYPE_MEMORY_REGION, + MemoryRegion *), + DEFINE_PROP_BOOL("load-kernel", RX62NState, kernel, false), + DEFINE_PROP_UINT32("xtal-frequency-hz", RX62NState, xtal_freq_hz, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void rx62n_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = rx62n_realize; + device_class_set_props(dc, rx62n_properties); +} + +static void r5f562n7_class_init(ObjectClass *oc, void *data) +{ + RX62NClass *rxc = RX62N_MCU_CLASS(oc); + + rxc->ram_size = 64 * KiB; + rxc->rom_flash_size = 384 * KiB; + rxc->data_flash_size = 32 * KiB; +}; + +static void r5f562n8_class_init(ObjectClass *oc, void *data) +{ + RX62NClass *rxc = RX62N_MCU_CLASS(oc); + + rxc->ram_size = 96 * KiB; + rxc->rom_flash_size = 512 * KiB; + rxc->data_flash_size = 32 * KiB; +}; + +static const TypeInfo rx62n_types[] = { + { + .name = TYPE_R5F562N7_MCU, + .parent = TYPE_RX62N_MCU, + .class_init = r5f562n7_class_init, + }, { + .name = TYPE_R5F562N8_MCU, + .parent = TYPE_RX62N_MCU, + .class_init = r5f562n8_class_init, + }, { + .name = TYPE_RX62N_MCU, + .parent = TYPE_DEVICE, + .instance_size = sizeof(RX62NState), + .class_size = sizeof(RX62NClass), + .class_init = rx62n_class_init, + .abstract = true, + } +}; + +DEFINE_TYPES(rx62n_types) diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index 1a7320c0af..27843bb04b 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -277,7 +277,7 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockBackend *blk, if (serial && object_property_find(OBJECT(dev), "serial", NULL)) { qdev_prop_set_string(dev, "serial", serial); } - qdev_prop_set_drive(dev, "drive", blk, &err); + qdev_prop_set_drive_err(dev, "drive", blk, &err); if (err) { error_propagate(errp, err); object_unparent(OBJECT(dev)); diff --git a/hw/sd/milkymist-memcard.c b/hw/sd/milkymist-memcard.c index 4cfdf7b64c..482e97191e 100644 --- a/hw/sd/milkymist-memcard.c +++ b/hw/sd/milkymist-memcard.c @@ -279,7 +279,7 @@ static void milkymist_memcard_realize(DeviceState *dev, Error **errp) dinfo = drive_get_next(IF_SD); blk = dinfo ? blk_by_legacy_dinfo(dinfo) : NULL; carddev = qdev_new(TYPE_SD_CARD); - qdev_prop_set_drive(carddev, "drive", blk, &err); + qdev_prop_set_drive(carddev, "drive", blk); qdev_realize_and_unref(carddev, BUS(&s->sdbus), &err); if (err) { error_setg(errp, "failed to init SD card: %s", error_get_pretty(err)); diff --git a/hw/sd/pxa2xx_mmci.c b/hw/sd/pxa2xx_mmci.c index 623be70b26..68bed24480 100644 --- a/hw/sd/pxa2xx_mmci.c +++ b/hw/sd/pxa2xx_mmci.c @@ -18,7 +18,6 @@ #include "hw/arm/pxa.h" #include "hw/sd/sd.h" #include "hw/qdev-properties.h" -#include "qemu/error-report.h" #include "qemu/log.h" #include "qemu/module.h" #include "trace.h" @@ -483,7 +482,6 @@ PXA2xxMMCIState *pxa2xx_mmci_init(MemoryRegion *sysmem, DeviceState *dev, *carddev; SysBusDevice *sbd; PXA2xxMMCIState *s; - Error *err = NULL; dev = qdev_new(TYPE_PXA2XX_MMCI); s = PXA2XX_MMCI(dev); @@ -496,16 +494,9 @@ PXA2xxMMCIState *pxa2xx_mmci_init(MemoryRegion *sysmem, /* Create and plug in the sd card */ carddev = qdev_new(TYPE_SD_CARD); - qdev_prop_set_drive(carddev, "drive", blk, &err); - if (err) { - error_reportf_err(err, "failed to init SD card: "); - return NULL; - } - qdev_realize_and_unref(carddev, qdev_get_child_bus(dev, "sd-bus"), &err); - if (err) { - error_reportf_err(err, "failed to init SD card: "); - return NULL; - } + qdev_prop_set_drive_err(carddev, "drive", blk, &error_fatal); + qdev_realize_and_unref(carddev, qdev_get_child_bus(dev, "sd-bus"), + &error_fatal); return s; } diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 7070a116ea..97a9d32964 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -706,7 +706,7 @@ SDState *sd_init(BlockBackend *blk, bool is_spi) obj = object_new(TYPE_SD_CARD); dev = DEVICE(obj); - qdev_prop_set_drive(dev, "drive", blk, &err); + qdev_prop_set_drive_err(dev, "drive", blk, &err); if (err) { error_reportf_err(err, "sd_init failed: "); return NULL; diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c index f98a6f3ae1..25cec2ddea 100644 --- a/hw/sd/ssi-sd.c +++ b/hw/sd/ssi-sd.c @@ -254,7 +254,8 @@ static void ssi_sd_realize(SSISlave *d, Error **errp) dinfo = drive_get_next(IF_SD); carddev = qdev_new(TYPE_SD_CARD); if (dinfo) { - qdev_prop_set_drive(carddev, "drive", blk_by_legacy_dinfo(dinfo), &err); + qdev_prop_set_drive_err(carddev, "drive", blk_by_legacy_dinfo(dinfo), + &err); if (err) { goto fail; } diff --git a/hw/sh4/sh7750.c b/hw/sh4/sh7750.c index d660714443..f8ac3ec6e3 100644 --- a/hw/sh4/sh7750.c +++ b/hw/sh4/sh7750.c @@ -30,6 +30,7 @@ #include "sh7750_regs.h" #include "sh7750_regnames.h" #include "hw/sh4/sh_intc.h" +#include "hw/timer/tmu012.h" #include "cpu.h" #include "exec/exec-all.h" diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index 97e6d3a025..9c8655cffc 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -341,16 +341,9 @@ static void ebus_realize(PCIDevice *pci_dev, Error **errp) } isa_dev = isa_new(TYPE_ISA_FDC); dev = DEVICE(isa_dev); - if (fd[0]) { - qdev_prop_set_drive(dev, "driveA", blk_by_legacy_dinfo(fd[0]), - &error_abort); - } - if (fd[1]) { - qdev_prop_set_drive(dev, "driveB", blk_by_legacy_dinfo(fd[1]), - &error_abort); - } qdev_prop_set_uint32(dev, "dma", -1); isa_realize_and_unref(isa_dev, s->isa_bus, &error_fatal); + isa_fdc_init_drives(isa_dev, fd); /* Power */ dev = qdev_new(TYPE_SUN4U_POWER); diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig index 59b3f44d69..59a667c503 100644 --- a/hw/timer/Kconfig +++ b/hw/timer/Kconfig @@ -35,3 +35,9 @@ config CMSDK_APB_TIMER config CMSDK_APB_DUALTIMER bool select PTIMER + +config RENESAS_TMR + bool + +config RENESAS_CMT + bool diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs index dece235fd7..a39f6ec0c2 100644 --- a/hw/timer/Makefile.objs +++ b/hw/timer/Makefile.objs @@ -23,6 +23,8 @@ common-obj-$(CONFIG_OMAP) += omap_gptimer.o common-obj-$(CONFIG_OMAP) += omap_synctimer.o common-obj-$(CONFIG_PXA2XX) += pxa2xx_timer.o common-obj-$(CONFIG_SH4) += sh_timer.o +common-obj-$(CONFIG_RENESAS_TMR) += renesas_tmr.o +common-obj-$(CONFIG_RENESAS_CMT) += renesas_cmt.o common-obj-$(CONFIG_DIGIC) += digic-timer.o common-obj-$(CONFIG_MIPS_CPS) += mips_gictimer.o diff --git a/hw/timer/renesas_cmt.c b/hw/timer/renesas_cmt.c new file mode 100644 index 0000000000..2e0fd21a36 --- /dev/null +++ b/hw/timer/renesas_cmt.c @@ -0,0 +1,283 @@ +/* + * Renesas 16bit Compare-match timer + * + * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware + * (Rev.1.40 R01UH0033EJ0140) + * + * Copyright (c) 2019 Yoshinori Sato + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/irq.h" +#include "hw/registerfields.h" +#include "hw/qdev-properties.h" +#include "hw/timer/renesas_cmt.h" +#include "migration/vmstate.h" + +/* + * +0 CMSTR - common control + * +2 CMCR - ch0 + * +4 CMCNT - ch0 + * +6 CMCOR - ch0 + * +8 CMCR - ch1 + * +10 CMCNT - ch1 + * +12 CMCOR - ch1 + * If we think that the address of CH 0 has an offset of +2, + * we can treat it with the same address as CH 1, so define it like that. + */ +REG16(CMSTR, 0) + FIELD(CMSTR, STR0, 0, 1) + FIELD(CMSTR, STR1, 1, 1) + FIELD(CMSTR, STR, 0, 2) +/* This addeess is channel offset */ +REG16(CMCR, 0) + FIELD(CMCR, CKS, 0, 2) + FIELD(CMCR, CMIE, 6, 1) +REG16(CMCNT, 2) +REG16(CMCOR, 4) + +static void update_events(RCMTState *cmt, int ch) +{ + int64_t next_time; + + if ((cmt->cmstr & (1 << ch)) == 0) { + /* count disable, so not happened next event. */ + return ; + } + next_time = cmt->cmcor[ch] - cmt->cmcnt[ch]; + next_time *= NANOSECONDS_PER_SECOND; + next_time /= cmt->input_freq; + /* + * CKS -> div rate + * 0 -> 8 (1 << 3) + * 1 -> 32 (1 << 5) + * 2 -> 128 (1 << 7) + * 3 -> 512 (1 << 9) + */ + next_time *= 1 << (3 + FIELD_EX16(cmt->cmcr[ch], CMCR, CKS) * 2); + next_time += qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + timer_mod(&cmt->timer[ch], next_time); +} + +static int64_t read_cmcnt(RCMTState *cmt, int ch) +{ + int64_t delta, now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + + if (cmt->cmstr & (1 << ch)) { + delta = (now - cmt->tick[ch]); + delta /= NANOSECONDS_PER_SECOND; + delta /= cmt->input_freq; + delta /= 1 << (3 + FIELD_EX16(cmt->cmcr[ch], CMCR, CKS) * 2); + cmt->tick[ch] = now; + return cmt->cmcnt[ch] + delta; + } else { + return cmt->cmcnt[ch]; + } +} + +static uint64_t cmt_read(void *opaque, hwaddr offset, unsigned size) +{ + RCMTState *cmt = opaque; + int ch = offset / 0x08; + uint64_t ret; + + if (offset == A_CMSTR) { + ret = 0; + ret = FIELD_DP16(ret, CMSTR, STR, + FIELD_EX16(cmt->cmstr, CMSTR, STR)); + return ret; + } else { + offset &= 0x07; + if (ch == 0) { + offset -= 0x02; + } + switch (offset) { + case A_CMCR: + ret = 0; + ret = FIELD_DP16(ret, CMCR, CKS, + FIELD_EX16(cmt->cmstr, CMCR, CKS)); + ret = FIELD_DP16(ret, CMCR, CMIE, + FIELD_EX16(cmt->cmstr, CMCR, CMIE)); + return ret; + case A_CMCNT: + return read_cmcnt(cmt, ch); + case A_CMCOR: + return cmt->cmcor[ch]; + } + } + qemu_log_mask(LOG_UNIMP, "renesas_cmt: Register 0x%" HWADDR_PRIX " " + "not implemented\n", + offset); + return UINT64_MAX; +} + +static void start_stop(RCMTState *cmt, int ch, int st) +{ + if (st) { + update_events(cmt, ch); + } else { + timer_del(&cmt->timer[ch]); + } +} + +static void cmt_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) +{ + RCMTState *cmt = opaque; + int ch = offset / 0x08; + + if (offset == A_CMSTR) { + cmt->cmstr = FIELD_EX16(val, CMSTR, STR); + start_stop(cmt, 0, FIELD_EX16(cmt->cmstr, CMSTR, STR0)); + start_stop(cmt, 1, FIELD_EX16(cmt->cmstr, CMSTR, STR1)); + } else { + offset &= 0x07; + if (ch == 0) { + offset -= 0x02; + } + switch (offset) { + case A_CMCR: + cmt->cmcr[ch] = FIELD_DP16(cmt->cmcr[ch], CMCR, CKS, + FIELD_EX16(val, CMCR, CKS)); + cmt->cmcr[ch] = FIELD_DP16(cmt->cmcr[ch], CMCR, CMIE, + FIELD_EX16(val, CMCR, CMIE)); + break; + case 2: + cmt->cmcnt[ch] = val; + break; + case 4: + cmt->cmcor[ch] = val; + break; + default: + qemu_log_mask(LOG_UNIMP, "renesas_cmt: Register 0x%" HWADDR_PRIX " " + "not implemented\n", + offset); + return; + } + if (FIELD_EX16(cmt->cmstr, CMSTR, STR) & (1 << ch)) { + update_events(cmt, ch); + } + } +} + +static const MemoryRegionOps cmt_ops = { + .write = cmt_write, + .read = cmt_read, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 2, + .max_access_size = 2, + }, + .valid = { + .min_access_size = 2, + .max_access_size = 2, + }, +}; + +static void timer_events(RCMTState *cmt, int ch) +{ + cmt->cmcnt[ch] = 0; + cmt->tick[ch] = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + update_events(cmt, ch); + if (FIELD_EX16(cmt->cmcr[ch], CMCR, CMIE)) { + qemu_irq_pulse(cmt->cmi[ch]); + } +} + +static void timer_event0(void *opaque) +{ + RCMTState *cmt = opaque; + + timer_events(cmt, 0); +} + +static void timer_event1(void *opaque) +{ + RCMTState *cmt = opaque; + + timer_events(cmt, 1); +} + +static void rcmt_reset(DeviceState *dev) +{ + RCMTState *cmt = RCMT(dev); + cmt->cmstr = 0; + cmt->cmcr[0] = cmt->cmcr[1] = 0; + cmt->cmcnt[0] = cmt->cmcnt[1] = 0; + cmt->cmcor[0] = cmt->cmcor[1] = 0xffff; +} + +static void rcmt_init(Object *obj) +{ + SysBusDevice *d = SYS_BUS_DEVICE(obj); + RCMTState *cmt = RCMT(obj); + int i; + + memory_region_init_io(&cmt->memory, OBJECT(cmt), &cmt_ops, + cmt, "renesas-cmt", 0x10); + sysbus_init_mmio(d, &cmt->memory); + + for (i = 0; i < ARRAY_SIZE(cmt->cmi); i++) { + sysbus_init_irq(d, &cmt->cmi[i]); + } + timer_init_ns(&cmt->timer[0], QEMU_CLOCK_VIRTUAL, timer_event0, cmt); + timer_init_ns(&cmt->timer[1], QEMU_CLOCK_VIRTUAL, timer_event1, cmt); +} + +static const VMStateDescription vmstate_rcmt = { + .name = "rx-cmt", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT16(cmstr, RCMTState), + VMSTATE_UINT16_ARRAY(cmcr, RCMTState, CMT_CH), + VMSTATE_UINT16_ARRAY(cmcnt, RCMTState, CMT_CH), + VMSTATE_UINT16_ARRAY(cmcor, RCMTState, CMT_CH), + VMSTATE_INT64_ARRAY(tick, RCMTState, CMT_CH), + VMSTATE_TIMER_ARRAY(timer, RCMTState, CMT_CH), + VMSTATE_END_OF_LIST() + } +}; + +static Property rcmt_properties[] = { + DEFINE_PROP_UINT64("input-freq", RCMTState, input_freq, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void rcmt_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_rcmt; + dc->reset = rcmt_reset; + device_class_set_props(dc, rcmt_properties); +} + +static const TypeInfo rcmt_info = { + .name = TYPE_RENESAS_CMT, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(RCMTState), + .instance_init = rcmt_init, + .class_init = rcmt_class_init, +}; + +static void rcmt_register_types(void) +{ + type_register_static(&rcmt_info); +} + +type_init(rcmt_register_types) diff --git a/hw/timer/renesas_tmr.c b/hw/timer/renesas_tmr.c new file mode 100644 index 0000000000..446f2eacdd --- /dev/null +++ b/hw/timer/renesas_tmr.c @@ -0,0 +1,477 @@ +/* + * Renesas 8bit timer + * + * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware + * (Rev.1.40 R01UH0033EJ0140) + * + * Copyright (c) 2019 Yoshinori Sato + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/irq.h" +#include "hw/registerfields.h" +#include "hw/qdev-properties.h" +#include "hw/timer/renesas_tmr.h" +#include "migration/vmstate.h" + +REG8(TCR, 0) + FIELD(TCR, CCLR, 3, 2) + FIELD(TCR, OVIE, 5, 1) + FIELD(TCR, CMIEA, 6, 1) + FIELD(TCR, CMIEB, 7, 1) +REG8(TCSR, 2) + FIELD(TCSR, OSA, 0, 2) + FIELD(TCSR, OSB, 2, 2) + FIELD(TCSR, ADTE, 4, 2) +REG8(TCORA, 4) +REG8(TCORB, 6) +REG8(TCNT, 8) +REG8(TCCR, 10) + FIELD(TCCR, CKS, 0, 3) + FIELD(TCCR, CSS, 3, 2) + FIELD(TCCR, TMRIS, 7, 1) + +#define INTERNAL 0x01 +#define CASCADING 0x03 +#define CCLR_A 0x01 +#define CCLR_B 0x02 + +static const int clkdiv[] = {0, 1, 2, 8, 32, 64, 1024, 8192}; + +static uint8_t concat_reg(uint8_t *reg) +{ + return (reg[0] << 8) | reg[1]; +} + +static void update_events(RTMRState *tmr, int ch) +{ + uint16_t diff[TMR_NR_EVENTS], min; + int64_t next_time; + int i, event; + + if (tmr->tccr[ch] == 0) { + return ; + } + if (FIELD_EX8(tmr->tccr[ch], TCCR, CSS) == 0) { + /* external clock mode */ + /* event not happened */ + return ; + } + if (FIELD_EX8(tmr->tccr[0], TCCR, CSS) == CASCADING) { + /* cascading mode */ + if (ch == 1) { + tmr->next[ch] = none; + return ; + } + diff[cmia] = concat_reg(tmr->tcora) - concat_reg(tmr->tcnt); + diff[cmib] = concat_reg(tmr->tcorb) - concat_reg(tmr->tcnt); + diff[ovi] = 0x10000 - concat_reg(tmr->tcnt); + } else { + /* separate mode */ + diff[cmia] = tmr->tcora[ch] - tmr->tcnt[ch]; + diff[cmib] = tmr->tcorb[ch] - tmr->tcnt[ch]; + diff[ovi] = 0x100 - tmr->tcnt[ch]; + } + /* Search for the most recently occurring event. */ + for (event = 0, min = diff[0], i = 1; i < none; i++) { + if (min > diff[i]) { + event = i; + min = diff[i]; + } + } + tmr->next[ch] = event; + next_time = diff[event]; + next_time *= clkdiv[FIELD_EX8(tmr->tccr[ch], TCCR, CKS)]; + next_time *= NANOSECONDS_PER_SECOND; + next_time /= tmr->input_freq; + next_time += qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + timer_mod(&tmr->timer[ch], next_time); +} + +static int elapsed_time(RTMRState *tmr, int ch, int64_t delta) +{ + int divrate = clkdiv[FIELD_EX8(tmr->tccr[ch], TCCR, CKS)]; + int et; + + tmr->div_round[ch] += delta; + if (divrate > 0) { + et = tmr->div_round[ch] / divrate; + tmr->div_round[ch] %= divrate; + } else { + /* disble clock. so no update */ + et = 0; + } + return et; +} + +static uint16_t read_tcnt(RTMRState *tmr, unsigned size, int ch) +{ + int64_t delta, now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + int elapsed, ovf = 0; + uint16_t tcnt[2]; + uint32_t ret; + + delta = (now - tmr->tick) * NANOSECONDS_PER_SECOND / tmr->input_freq; + if (delta > 0) { + tmr->tick = now; + + if (FIELD_EX8(tmr->tccr[1], TCCR, CSS) == INTERNAL) { + /* timer1 count update */ + elapsed = elapsed_time(tmr, 1, delta); + if (elapsed >= 0x100) { + ovf = elapsed >> 8; + } + tcnt[1] = tmr->tcnt[1] + (elapsed & 0xff); + } + switch (FIELD_EX8(tmr->tccr[0], TCCR, CSS)) { + case INTERNAL: + elapsed = elapsed_time(tmr, 0, delta); + tcnt[0] = tmr->tcnt[0] + elapsed; + break; + case CASCADING: + if (ovf > 0) { + tcnt[0] = tmr->tcnt[0] + ovf; + } + break; + } + } else { + tcnt[0] = tmr->tcnt[0]; + tcnt[1] = tmr->tcnt[1]; + } + if (size == 1) { + return tcnt[ch]; + } else { + ret = 0; + ret = deposit32(ret, 0, 8, tcnt[1]); + ret = deposit32(ret, 8, 8, tcnt[0]); + return ret; + } +} + +static uint8_t read_tccr(uint8_t r) +{ + uint8_t tccr = 0; + tccr = FIELD_DP8(tccr, TCCR, TMRIS, + FIELD_EX8(r, TCCR, TMRIS)); + tccr = FIELD_DP8(tccr, TCCR, CSS, + FIELD_EX8(r, TCCR, CSS)); + tccr = FIELD_DP8(tccr, TCCR, CKS, + FIELD_EX8(r, TCCR, CKS)); + return tccr; +} + +static uint64_t tmr_read(void *opaque, hwaddr addr, unsigned size) +{ + RTMRState *tmr = opaque; + int ch = addr & 1; + uint64_t ret; + + if (size == 2 && (ch != 0 || addr == A_TCR || addr == A_TCSR)) { + qemu_log_mask(LOG_GUEST_ERROR, "renesas_tmr: Invalid read size 0x%" + HWADDR_PRIX "\n", + addr); + return UINT64_MAX; + } + switch (addr & 0x0e) { + case A_TCR: + ret = 0; + ret = FIELD_DP8(ret, TCR, CCLR, + FIELD_EX8(tmr->tcr[ch], TCR, CCLR)); + ret = FIELD_DP8(ret, TCR, OVIE, + FIELD_EX8(tmr->tcr[ch], TCR, OVIE)); + ret = FIELD_DP8(ret, TCR, CMIEA, + FIELD_EX8(tmr->tcr[ch], TCR, CMIEA)); + ret = FIELD_DP8(ret, TCR, CMIEB, + FIELD_EX8(tmr->tcr[ch], TCR, CMIEB)); + return ret; + case A_TCSR: + ret = 0; + ret = FIELD_DP8(ret, TCSR, OSA, + FIELD_EX8(tmr->tcsr[ch], TCSR, OSA)); + ret = FIELD_DP8(ret, TCSR, OSB, + FIELD_EX8(tmr->tcsr[ch], TCSR, OSB)); + switch (ch) { + case 0: + ret = FIELD_DP8(ret, TCSR, ADTE, + FIELD_EX8(tmr->tcsr[ch], TCSR, ADTE)); + break; + case 1: /* CH1 ADTE unimplement always 1 */ + ret = FIELD_DP8(ret, TCSR, ADTE, 1); + break; + } + return ret; + case A_TCORA: + if (size == 1) { + return tmr->tcora[ch]; + } else if (ch == 0) { + return concat_reg(tmr->tcora); + } + case A_TCORB: + if (size == 1) { + return tmr->tcorb[ch]; + } else { + return concat_reg(tmr->tcorb); + } + case A_TCNT: + return read_tcnt(tmr, size, ch); + case A_TCCR: + if (size == 1) { + return read_tccr(tmr->tccr[ch]); + } else { + return read_tccr(tmr->tccr[0]) << 8 | read_tccr(tmr->tccr[1]); + } + default: + qemu_log_mask(LOG_UNIMP, "renesas_tmr: Register 0x%" HWADDR_PRIX + " not implemented\n", + addr); + break; + } + return UINT64_MAX; +} + +static void tmr_write_count(RTMRState *tmr, int ch, unsigned size, + uint8_t *reg, uint64_t val) +{ + if (size == 1) { + reg[ch] = val; + update_events(tmr, ch); + } else { + reg[0] = extract32(val, 8, 8); + reg[1] = extract32(val, 0, 8); + update_events(tmr, 0); + update_events(tmr, 1); + } +} + +static void tmr_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) +{ + RTMRState *tmr = opaque; + int ch = addr & 1; + + if (size == 2 && (ch != 0 || addr == A_TCR || addr == A_TCSR)) { + qemu_log_mask(LOG_GUEST_ERROR, + "renesas_tmr: Invalid write size 0x%" HWADDR_PRIX "\n", + addr); + return; + } + switch (addr & 0x0e) { + case A_TCR: + tmr->tcr[ch] = val; + break; + case A_TCSR: + tmr->tcsr[ch] = val; + break; + case A_TCORA: + tmr_write_count(tmr, ch, size, tmr->tcora, val); + break; + case A_TCORB: + tmr_write_count(tmr, ch, size, tmr->tcorb, val); + break; + case A_TCNT: + tmr_write_count(tmr, ch, size, tmr->tcnt, val); + break; + case A_TCCR: + tmr_write_count(tmr, ch, size, tmr->tccr, val); + break; + default: + qemu_log_mask(LOG_UNIMP, "renesas_tmr: Register 0x%" HWADDR_PRIX + " not implemented\n", + addr); + break; + } +} + +static const MemoryRegionOps tmr_ops = { + .write = tmr_write, + .read = tmr_read, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 1, + .max_access_size = 2, + }, + .valid = { + .min_access_size = 1, + .max_access_size = 2, + }, +}; + +static void timer_events(RTMRState *tmr, int ch); + +static uint16_t issue_event(RTMRState *tmr, int ch, int sz, + uint16_t tcnt, uint16_t tcora, uint16_t tcorb) +{ + uint16_t ret = tcnt; + + switch (tmr->next[ch]) { + case none: + break; + case cmia: + if (tcnt >= tcora) { + if (FIELD_EX8(tmr->tcr[ch], TCR, CCLR) == CCLR_A) { + ret = tcnt - tcora; + } + if (FIELD_EX8(tmr->tcr[ch], TCR, CMIEA)) { + qemu_irq_pulse(tmr->cmia[ch]); + } + if (sz == 8 && ch == 0 && + FIELD_EX8(tmr->tccr[1], TCCR, CSS) == CASCADING) { + tmr->tcnt[1]++; + timer_events(tmr, 1); + } + } + break; + case cmib: + if (tcnt >= tcorb) { + if (FIELD_EX8(tmr->tcr[ch], TCR, CCLR) == CCLR_B) { + ret = tcnt - tcorb; + } + if (FIELD_EX8(tmr->tcr[ch], TCR, CMIEB)) { + qemu_irq_pulse(tmr->cmib[ch]); + } + } + break; + case ovi: + if ((tcnt >= (1 << sz)) && FIELD_EX8(tmr->tcr[ch], TCR, OVIE)) { + qemu_irq_pulse(tmr->ovi[ch]); + } + break; + default: + g_assert_not_reached(); + } + return ret; +} + +static void timer_events(RTMRState *tmr, int ch) +{ + uint16_t tcnt; + + tmr->tcnt[ch] = read_tcnt(tmr, 1, ch); + if (FIELD_EX8(tmr->tccr[0], TCCR, CSS) != CASCADING) { + tmr->tcnt[ch] = issue_event(tmr, ch, 8, + tmr->tcnt[ch], + tmr->tcora[ch], + tmr->tcorb[ch]) & 0xff; + } else { + if (ch == 1) { + return ; + } + tcnt = issue_event(tmr, ch, 16, + concat_reg(tmr->tcnt), + concat_reg(tmr->tcora), + concat_reg(tmr->tcorb)); + tmr->tcnt[0] = (tcnt >> 8) & 0xff; + tmr->tcnt[1] = tcnt & 0xff; + } + update_events(tmr, ch); +} + +static void timer_event0(void *opaque) +{ + RTMRState *tmr = opaque; + + timer_events(tmr, 0); +} + +static void timer_event1(void *opaque) +{ + RTMRState *tmr = opaque; + + timer_events(tmr, 1); +} + +static void rtmr_reset(DeviceState *dev) +{ + RTMRState *tmr = RTMR(dev); + tmr->tcr[0] = tmr->tcr[1] = 0x00; + tmr->tcsr[0] = 0x00; + tmr->tcsr[1] = 0x10; + tmr->tcnt[0] = tmr->tcnt[1] = 0x00; + tmr->tcora[0] = tmr->tcora[1] = 0xff; + tmr->tcorb[0] = tmr->tcorb[1] = 0xff; + tmr->tccr[0] = tmr->tccr[1] = 0x00; + tmr->next[0] = tmr->next[1] = none; + tmr->tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); +} + +static void rtmr_init(Object *obj) +{ + SysBusDevice *d = SYS_BUS_DEVICE(obj); + RTMRState *tmr = RTMR(obj); + int i; + + memory_region_init_io(&tmr->memory, OBJECT(tmr), &tmr_ops, + tmr, "renesas-tmr", 0x10); + sysbus_init_mmio(d, &tmr->memory); + + for (i = 0; i < ARRAY_SIZE(tmr->ovi); i++) { + sysbus_init_irq(d, &tmr->cmia[i]); + sysbus_init_irq(d, &tmr->cmib[i]); + sysbus_init_irq(d, &tmr->ovi[i]); + } + timer_init_ns(&tmr->timer[0], QEMU_CLOCK_VIRTUAL, timer_event0, tmr); + timer_init_ns(&tmr->timer[1], QEMU_CLOCK_VIRTUAL, timer_event1, tmr); +} + +static const VMStateDescription vmstate_rtmr = { + .name = "rx-tmr", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_INT64(tick, RTMRState), + VMSTATE_UINT8_ARRAY(tcnt, RTMRState, TMR_CH), + VMSTATE_UINT8_ARRAY(tcora, RTMRState, TMR_CH), + VMSTATE_UINT8_ARRAY(tcorb, RTMRState, TMR_CH), + VMSTATE_UINT8_ARRAY(tcr, RTMRState, TMR_CH), + VMSTATE_UINT8_ARRAY(tccr, RTMRState, TMR_CH), + VMSTATE_UINT8_ARRAY(tcor, RTMRState, TMR_CH), + VMSTATE_UINT8_ARRAY(tcsr, RTMRState, TMR_CH), + VMSTATE_INT64_ARRAY(div_round, RTMRState, TMR_CH), + VMSTATE_UINT8_ARRAY(next, RTMRState, TMR_CH), + VMSTATE_TIMER_ARRAY(timer, RTMRState, TMR_CH), + VMSTATE_END_OF_LIST() + } +}; + +static Property rtmr_properties[] = { + DEFINE_PROP_UINT64("input-freq", RTMRState, input_freq, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void rtmr_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_rtmr; + dc->reset = rtmr_reset; + device_class_set_props(dc, rtmr_properties); +} + +static const TypeInfo rtmr_info = { + .name = TYPE_RENESAS_TMR, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(RTMRState), + .instance_init = rtmr_init, + .class_init = rtmr_class_init, +}; + +static void rtmr_register_types(void) +{ + type_register_static(&rtmr_info); +} + +type_init(rtmr_register_types) diff --git a/hw/timer/sh_timer.c b/hw/timer/sh_timer.c index 13c4051808..bb0e1c8ee5 100644 --- a/hw/timer/sh_timer.c +++ b/hw/timer/sh_timer.c @@ -9,10 +9,11 @@ */ #include "qemu/osdep.h" +#include "exec/memory.h" #include "hw/hw.h" #include "hw/irq.h" #include "hw/sh4/sh.h" -#include "qemu/timer.h" +#include "hw/timer/tmu012.h" #include "hw/ptimer.h" //#define DEBUG_TIMER diff --git a/hw/tpm/Kconfig b/hw/tpm/Kconfig index 4794e7fe28..29e82f3c92 100644 --- a/hw/tpm/Kconfig +++ b/hw/tpm/Kconfig @@ -1,7 +1,3 @@ -config TPMDEV - bool - depends on TPM - config TPM_TIS_ISA bool depends on TPM && ISA_BUS @@ -15,26 +11,15 @@ config TPM_TIS_SYSBUS config TPM_TIS bool depends on TPM - select TPMDEV + select TPM_BACKEND config TPM_CRB bool depends on TPM && PC - select TPMDEV - -config TPM_PASSTHROUGH - bool - default y - # FIXME: should check for x86 host as well - depends on TPMDEV && LINUX - -config TPM_EMULATOR - bool - default y - depends on TPMDEV + select TPM_BACKEND config TPM_SPAPR bool default y depends on TPM && PSERIES - select TPMDEV + select TPM_BACKEND diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs index f1ec4beb95..6fc05be67c 100644 --- a/hw/tpm/Makefile.objs +++ b/hw/tpm/Makefile.objs @@ -1,9 +1,6 @@ -common-obj-$(CONFIG_TPM) += tpm_util.o obj-$(call lor,$(CONFIG_TPM_TIS),$(CONFIG_TPM_CRB)) += tpm_ppi.o common-obj-$(CONFIG_TPM_TIS_ISA) += tpm_tis_isa.o common-obj-$(CONFIG_TPM_TIS_SYSBUS) += tpm_tis_sysbus.o common-obj-$(CONFIG_TPM_TIS) += tpm_tis_common.o common-obj-$(CONFIG_TPM_CRB) += tpm_crb.o -common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o -common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o obj-$(CONFIG_TPM_SPAPR) += tpm_spapr.o diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c index cd004e7f8e..60247295d4 100644 --- a/hw/tpm/tpm_crb.c +++ b/hw/tpm/tpm_crb.c @@ -24,9 +24,9 @@ #include "hw/acpi/tpm.h" #include "migration/vmstate.h" #include "sysemu/tpm_backend.h" +#include "sysemu/tpm_util.h" #include "sysemu/reset.h" -#include "tpm_int.h" -#include "tpm_util.h" +#include "tpm_prop.h" #include "tpm_ppi.h" #include "trace.h" diff --git a/hw/tpm/tpm_emulator.c b/hw/tpm/tpm_emulator.c deleted file mode 100644 index 3a0fc442f3..0000000000 --- a/hw/tpm/tpm_emulator.c +++ /dev/null @@ -1,997 +0,0 @@ -/* - * Emulator TPM driver - * - * Copyright (c) 2017 Intel Corporation - * Author: Amarnath Valluri <amarnath.valluri@intel.com> - * - * Copyright (c) 2010 - 2013, 2018 IBM Corporation - * Authors: - * Stefan Berger <stefanb@us.ibm.com> - * - * Copyright (C) 2011 IAIK, Graz University of Technology - * Author: Andreas Niederl - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/> - * - */ - -#include "qemu/osdep.h" -#include "qemu/error-report.h" -#include "qemu/module.h" -#include "qemu/sockets.h" -#include "io/channel-socket.h" -#include "sysemu/tpm_backend.h" -#include "tpm_int.h" -#include "tpm_util.h" -#include "tpm_ioctl.h" -#include "migration/blocker.h" -#include "migration/vmstate.h" -#include "qapi/error.h" -#include "qapi/clone-visitor.h" -#include "qapi/qapi-visit-tpm.h" -#include "chardev/char-fe.h" -#include "trace.h" - -#define TYPE_TPM_EMULATOR "tpm-emulator" -#define TPM_EMULATOR(obj) \ - OBJECT_CHECK(TPMEmulator, (obj), TYPE_TPM_EMULATOR) - -#define TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(S, cap) (((S)->caps & (cap)) == (cap)) - -/* data structures */ - -/* blobs from the TPM; part of VM state when migrating */ -typedef struct TPMBlobBuffers { - uint32_t permanent_flags; - TPMSizedBuffer permanent; - - uint32_t volatil_flags; - TPMSizedBuffer volatil; - - uint32_t savestate_flags; - TPMSizedBuffer savestate; -} TPMBlobBuffers; - -typedef struct TPMEmulator { - TPMBackend parent; - - TPMEmulatorOptions *options; - CharBackend ctrl_chr; - QIOChannel *data_ioc; - TPMVersion tpm_version; - ptm_cap caps; /* capabilities of the TPM */ - uint8_t cur_locty_number; /* last set locality */ - Error *migration_blocker; - - QemuMutex mutex; - - unsigned int established_flag:1; - unsigned int established_flag_cached:1; - - TPMBlobBuffers state_blobs; -} TPMEmulator; - -struct tpm_error { - uint32_t tpm_result; - const char *string; -}; - -static const struct tpm_error tpm_errors[] = { - /* TPM 1.2 error codes */ - { TPM_BAD_PARAMETER , "a parameter is bad" }, - { TPM_FAIL , "operation failed" }, - { TPM_KEYNOTFOUND , "key could not be found" }, - { TPM_BAD_PARAM_SIZE , "bad parameter size"}, - { TPM_ENCRYPT_ERROR , "encryption error" }, - { TPM_DECRYPT_ERROR , "decryption error" }, - { TPM_BAD_KEY_PROPERTY, "bad key property" }, - { TPM_BAD_MODE , "bad (encryption) mode" }, - { TPM_BAD_VERSION , "bad version identifier" }, - { TPM_BAD_LOCALITY , "bad locality" }, - /* TPM 2 error codes */ - { TPM_RC_FAILURE , "operation failed" }, - { TPM_RC_LOCALITY , "bad locality" }, - { TPM_RC_INSUFFICIENT, "insufficient amount of data" }, -}; - -static const char *tpm_emulator_strerror(uint32_t tpm_result) -{ - size_t i; - - for (i = 0; i < ARRAY_SIZE(tpm_errors); i++) { - if (tpm_errors[i].tpm_result == tpm_result) { - return tpm_errors[i].string; - } - } - return ""; -} - -static int tpm_emulator_ctrlcmd(TPMEmulator *tpm, unsigned long cmd, void *msg, - size_t msg_len_in, size_t msg_len_out) -{ - CharBackend *dev = &tpm->ctrl_chr; - uint32_t cmd_no = cpu_to_be32(cmd); - ssize_t n = sizeof(uint32_t) + msg_len_in; - uint8_t *buf = NULL; - int ret = -1; - - qemu_mutex_lock(&tpm->mutex); - - buf = g_alloca(n); - memcpy(buf, &cmd_no, sizeof(cmd_no)); - memcpy(buf + sizeof(cmd_no), msg, msg_len_in); - - n = qemu_chr_fe_write_all(dev, buf, n); - if (n <= 0) { - goto end; - } - - if (msg_len_out != 0) { - n = qemu_chr_fe_read_all(dev, msg, msg_len_out); - if (n <= 0) { - goto end; - } - } - - ret = 0; - -end: - qemu_mutex_unlock(&tpm->mutex); - return ret; -} - -static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_emu, - const uint8_t *in, uint32_t in_len, - uint8_t *out, uint32_t out_len, - bool *selftest_done, - Error **errp) -{ - ssize_t ret; - bool is_selftest = false; - - if (selftest_done) { - *selftest_done = false; - is_selftest = tpm_util_is_selftest(in, in_len); - } - - ret = qio_channel_write_all(tpm_emu->data_ioc, (char *)in, in_len, errp); - if (ret != 0) { - return -1; - } - - ret = qio_channel_read_all(tpm_emu->data_ioc, (char *)out, - sizeof(struct tpm_resp_hdr), errp); - if (ret != 0) { - return -1; - } - - ret = qio_channel_read_all(tpm_emu->data_ioc, - (char *)out + sizeof(struct tpm_resp_hdr), - tpm_cmd_get_size(out) - sizeof(struct tpm_resp_hdr), errp); - if (ret != 0) { - return -1; - } - - if (is_selftest) { - *selftest_done = tpm_cmd_get_errcode(out) == 0; - } - - return 0; -} - -static int tpm_emulator_set_locality(TPMEmulator *tpm_emu, uint8_t locty_number, - Error **errp) -{ - ptm_loc loc; - - if (tpm_emu->cur_locty_number == locty_number) { - return 0; - } - - trace_tpm_emulator_set_locality(locty_number); - - memset(&loc, 0, sizeof(loc)); - loc.u.req.loc = locty_number; - if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_LOCALITY, &loc, - sizeof(loc), sizeof(loc)) < 0) { - error_setg(errp, "tpm-emulator: could not set locality : %s", - strerror(errno)); - return -1; - } - - loc.u.resp.tpm_result = be32_to_cpu(loc.u.resp.tpm_result); - if (loc.u.resp.tpm_result != 0) { - error_setg(errp, "tpm-emulator: TPM result for set locality : 0x%x", - loc.u.resp.tpm_result); - return -1; - } - - tpm_emu->cur_locty_number = locty_number; - - return 0; -} - -static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd *cmd, - Error **errp) -{ - TPMEmulator *tpm_emu = TPM_EMULATOR(tb); - - trace_tpm_emulator_handle_request(); - - if (tpm_emulator_set_locality(tpm_emu, cmd->locty, errp) < 0 || - tpm_emulator_unix_tx_bufs(tpm_emu, cmd->in, cmd->in_len, - cmd->out, cmd->out_len, - &cmd->selftest_done, errp) < 0) { - tpm_util_write_fatal_error_response(cmd->out, cmd->out_len); - } -} - -static int tpm_emulator_probe_caps(TPMEmulator *tpm_emu) -{ - if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_CAPABILITY, - &tpm_emu->caps, 0, sizeof(tpm_emu->caps)) < 0) { - error_report("tpm-emulator: probing failed : %s", strerror(errno)); - return -1; - } - - tpm_emu->caps = be64_to_cpu(tpm_emu->caps); - - trace_tpm_emulator_probe_caps(tpm_emu->caps); - - return 0; -} - -static int tpm_emulator_check_caps(TPMEmulator *tpm_emu) -{ - ptm_cap caps = 0; - const char *tpm = NULL; - - /* check for min. required capabilities */ - switch (tpm_emu->tpm_version) { - case TPM_VERSION_1_2: - caps = PTM_CAP_INIT | PTM_CAP_SHUTDOWN | PTM_CAP_GET_TPMESTABLISHED | - PTM_CAP_SET_LOCALITY | PTM_CAP_SET_DATAFD | PTM_CAP_STOP | - PTM_CAP_SET_BUFFERSIZE; - tpm = "1.2"; - break; - case TPM_VERSION_2_0: - caps = PTM_CAP_INIT | PTM_CAP_SHUTDOWN | PTM_CAP_GET_TPMESTABLISHED | - PTM_CAP_SET_LOCALITY | PTM_CAP_RESET_TPMESTABLISHED | - PTM_CAP_SET_DATAFD | PTM_CAP_STOP | PTM_CAP_SET_BUFFERSIZE; - tpm = "2"; - break; - case TPM_VERSION_UNSPEC: - error_report("tpm-emulator: TPM version has not been set"); - return -1; - } - - if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, caps)) { - error_report("tpm-emulator: TPM does not implement minimum set of " - "required capabilities for TPM %s (0x%x)", tpm, (int)caps); - return -1; - } - - return 0; -} - -static int tpm_emulator_stop_tpm(TPMBackend *tb) -{ - TPMEmulator *tpm_emu = TPM_EMULATOR(tb); - ptm_res res; - - if (tpm_emulator_ctrlcmd(tpm_emu, CMD_STOP, &res, 0, sizeof(res)) < 0) { - error_report("tpm-emulator: Could not stop TPM: %s", - strerror(errno)); - return -1; - } - - res = be32_to_cpu(res); - if (res) { - error_report("tpm-emulator: TPM result for CMD_STOP: 0x%x %s", res, - tpm_emulator_strerror(res)); - return -1; - } - - return 0; -} - -static int tpm_emulator_set_buffer_size(TPMBackend *tb, - size_t wanted_size, - size_t *actual_size) -{ - TPMEmulator *tpm_emu = TPM_EMULATOR(tb); - ptm_setbuffersize psbs; - - if (tpm_emulator_stop_tpm(tb) < 0) { - return -1; - } - - psbs.u.req.buffersize = cpu_to_be32(wanted_size); - - if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_BUFFERSIZE, &psbs, - sizeof(psbs.u.req), sizeof(psbs.u.resp)) < 0) { - error_report("tpm-emulator: Could not set buffer size: %s", - strerror(errno)); - return -1; - } - - psbs.u.resp.tpm_result = be32_to_cpu(psbs.u.resp.tpm_result); - if (psbs.u.resp.tpm_result != 0) { - error_report("tpm-emulator: TPM result for set buffer size : 0x%x %s", - psbs.u.resp.tpm_result, - tpm_emulator_strerror(psbs.u.resp.tpm_result)); - return -1; - } - - if (actual_size) { - *actual_size = be32_to_cpu(psbs.u.resp.buffersize); - } - - trace_tpm_emulator_set_buffer_size( - be32_to_cpu(psbs.u.resp.buffersize), - be32_to_cpu(psbs.u.resp.minsize), - be32_to_cpu(psbs.u.resp.maxsize)); - - return 0; -} - -static int tpm_emulator_startup_tpm_resume(TPMBackend *tb, size_t buffersize, - bool is_resume) -{ - TPMEmulator *tpm_emu = TPM_EMULATOR(tb); - ptm_init init = { - .u.req.init_flags = 0, - }; - ptm_res res; - - trace_tpm_emulator_startup_tpm_resume(is_resume, buffersize); - - if (buffersize != 0 && - tpm_emulator_set_buffer_size(tb, buffersize, NULL) < 0) { - goto err_exit; - } - - if (is_resume) { - init.u.req.init_flags |= cpu_to_be32(PTM_INIT_FLAG_DELETE_VOLATILE); - } - - if (tpm_emulator_ctrlcmd(tpm_emu, CMD_INIT, &init, sizeof(init), - sizeof(init)) < 0) { - error_report("tpm-emulator: could not send INIT: %s", - strerror(errno)); - goto err_exit; - } - - res = be32_to_cpu(init.u.resp.tpm_result); - if (res) { - error_report("tpm-emulator: TPM result for CMD_INIT: 0x%x %s", res, - tpm_emulator_strerror(res)); - goto err_exit; - } - return 0; - -err_exit: - return -1; -} - -static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize) -{ - return tpm_emulator_startup_tpm_resume(tb, buffersize, false); -} - -static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb) -{ - TPMEmulator *tpm_emu = TPM_EMULATOR(tb); - ptm_est est; - - if (tpm_emu->established_flag_cached) { - return tpm_emu->established_flag; - } - - if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_TPMESTABLISHED, &est, - 0, sizeof(est)) < 0) { - error_report("tpm-emulator: Could not get the TPM established flag: %s", - strerror(errno)); - return false; - } - trace_tpm_emulator_get_tpm_established_flag(est.u.resp.bit); - - tpm_emu->established_flag_cached = 1; - tpm_emu->established_flag = (est.u.resp.bit != 0); - - return tpm_emu->established_flag; -} - -static int tpm_emulator_reset_tpm_established_flag(TPMBackend *tb, - uint8_t locty) -{ - TPMEmulator *tpm_emu = TPM_EMULATOR(tb); - ptm_reset_est reset_est; - ptm_res res; - - /* only a TPM 2.0 will support this */ - if (tpm_emu->tpm_version != TPM_VERSION_2_0) { - return 0; - } - - reset_est.u.req.loc = tpm_emu->cur_locty_number; - if (tpm_emulator_ctrlcmd(tpm_emu, CMD_RESET_TPMESTABLISHED, - &reset_est, sizeof(reset_est), - sizeof(reset_est)) < 0) { - error_report("tpm-emulator: Could not reset the establishment bit: %s", - strerror(errno)); - return -1; - } - - res = be32_to_cpu(reset_est.u.resp.tpm_result); - if (res) { - error_report( - "tpm-emulator: TPM result for rest established flag: 0x%x %s", - res, tpm_emulator_strerror(res)); - return -1; - } - - tpm_emu->established_flag_cached = 0; - - return 0; -} - -static void tpm_emulator_cancel_cmd(TPMBackend *tb) -{ - TPMEmulator *tpm_emu = TPM_EMULATOR(tb); - ptm_res res; - - if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, PTM_CAP_CANCEL_TPM_CMD)) { - trace_tpm_emulator_cancel_cmd_not_supt(); - return; - } - - /* FIXME: make the function non-blocking, or it may block a VCPU */ - if (tpm_emulator_ctrlcmd(tpm_emu, CMD_CANCEL_TPM_CMD, &res, 0, - sizeof(res)) < 0) { - error_report("tpm-emulator: Could not cancel command: %s", - strerror(errno)); - } else if (res != 0) { - error_report("tpm-emulator: Failed to cancel TPM: 0x%x", - be32_to_cpu(res)); - } -} - -static TPMVersion tpm_emulator_get_tpm_version(TPMBackend *tb) -{ - TPMEmulator *tpm_emu = TPM_EMULATOR(tb); - - return tpm_emu->tpm_version; -} - -static size_t tpm_emulator_get_buffer_size(TPMBackend *tb) -{ - size_t actual_size; - - if (tpm_emulator_set_buffer_size(tb, 0, &actual_size) < 0) { - return 4096; - } - - return actual_size; -} - -static int tpm_emulator_block_migration(TPMEmulator *tpm_emu) -{ - Error *err = NULL; - ptm_cap caps = PTM_CAP_GET_STATEBLOB | PTM_CAP_SET_STATEBLOB | - PTM_CAP_STOP; - - if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, caps)) { - error_setg(&tpm_emu->migration_blocker, - "Migration disabled: TPM emulator does not support " - "migration"); - migrate_add_blocker(tpm_emu->migration_blocker, &err); - if (err) { - error_report_err(err); - error_free(tpm_emu->migration_blocker); - tpm_emu->migration_blocker = NULL; - - return -1; - } - } - - return 0; -} - -static int tpm_emulator_prepare_data_fd(TPMEmulator *tpm_emu) -{ - ptm_res res; - Error *err = NULL; - int fds[2] = { -1, -1 }; - - if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { - error_report("tpm-emulator: Failed to create socketpair"); - return -1; - } - - qemu_chr_fe_set_msgfds(&tpm_emu->ctrl_chr, fds + 1, 1); - - if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_DATAFD, &res, 0, - sizeof(res)) < 0 || res != 0) { - error_report("tpm-emulator: Failed to send CMD_SET_DATAFD: %s", - strerror(errno)); - goto err_exit; - } - - tpm_emu->data_ioc = QIO_CHANNEL(qio_channel_socket_new_fd(fds[0], &err)); - if (err) { - error_prepend(&err, "tpm-emulator: Failed to create io channel: "); - error_report_err(err); - goto err_exit; - } - - closesocket(fds[1]); - - return 0; - -err_exit: - closesocket(fds[0]); - closesocket(fds[1]); - return -1; -} - -static int tpm_emulator_handle_device_opts(TPMEmulator *tpm_emu, QemuOpts *opts) -{ - const char *value; - - value = qemu_opt_get(opts, "chardev"); - if (value) { - Error *err = NULL; - Chardev *dev = qemu_chr_find(value); - - if (!dev) { - error_report("tpm-emulator: tpm chardev '%s' not found.", value); - goto err; - } - - if (!qemu_chr_fe_init(&tpm_emu->ctrl_chr, dev, &err)) { - error_prepend(&err, "tpm-emulator: No valid chardev found at '%s':", - value); - error_report_err(err); - goto err; - } - - tpm_emu->options->chardev = g_strdup(value); - } - - if (tpm_emulator_prepare_data_fd(tpm_emu) < 0) { - goto err; - } - - /* FIXME: tpm_util_test_tpmdev() accepts only on socket fd, as it also used - * by passthrough driver, which not yet using GIOChannel. - */ - if (tpm_util_test_tpmdev(QIO_CHANNEL_SOCKET(tpm_emu->data_ioc)->fd, - &tpm_emu->tpm_version)) { - error_report("'%s' is not emulating TPM device. Error: %s", - tpm_emu->options->chardev, strerror(errno)); - goto err; - } - - switch (tpm_emu->tpm_version) { - case TPM_VERSION_1_2: - trace_tpm_emulator_handle_device_opts_tpm12(); - break; - case TPM_VERSION_2_0: - trace_tpm_emulator_handle_device_opts_tpm2(); - break; - default: - trace_tpm_emulator_handle_device_opts_unspec(); - } - - if (tpm_emulator_probe_caps(tpm_emu) || - tpm_emulator_check_caps(tpm_emu)) { - goto err; - } - - return tpm_emulator_block_migration(tpm_emu); - -err: - trace_tpm_emulator_handle_device_opts_startup_error(); - - return -1; -} - -static TPMBackend *tpm_emulator_create(QemuOpts *opts) -{ - TPMBackend *tb = TPM_BACKEND(object_new(TYPE_TPM_EMULATOR)); - - if (tpm_emulator_handle_device_opts(TPM_EMULATOR(tb), opts)) { - object_unref(OBJECT(tb)); - return NULL; - } - - return tb; -} - -static TpmTypeOptions *tpm_emulator_get_tpm_options(TPMBackend *tb) -{ - TPMEmulator *tpm_emu = TPM_EMULATOR(tb); - TpmTypeOptions *options = g_new0(TpmTypeOptions, 1); - - options->type = TPM_TYPE_OPTIONS_KIND_EMULATOR; - options->u.emulator.data = QAPI_CLONE(TPMEmulatorOptions, tpm_emu->options); - - return options; -} - -static const QemuOptDesc tpm_emulator_cmdline_opts[] = { - TPM_STANDARD_CMDLINE_OPTS, - { - .name = "chardev", - .type = QEMU_OPT_STRING, - .help = "Character device to use for out-of-band control messages", - }, - { /* end of list */ }, -}; - -/* - * Transfer a TPM state blob from the TPM into a provided buffer. - * - * @tpm_emu: TPMEmulator - * @type: the type of blob to transfer - * @tsb: the TPMSizeBuffer to fill with the blob - * @flags: the flags to return to the caller - */ -static int tpm_emulator_get_state_blob(TPMEmulator *tpm_emu, - uint8_t type, - TPMSizedBuffer *tsb, - uint32_t *flags) -{ - ptm_getstate pgs; - ptm_res res; - ssize_t n; - uint32_t totlength, length; - - tpm_sized_buffer_reset(tsb); - - pgs.u.req.state_flags = cpu_to_be32(PTM_STATE_FLAG_DECRYPTED); - pgs.u.req.type = cpu_to_be32(type); - pgs.u.req.offset = 0; - - if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_STATEBLOB, - &pgs, sizeof(pgs.u.req), - offsetof(ptm_getstate, u.resp.data)) < 0) { - error_report("tpm-emulator: could not get state blob type %d : %s", - type, strerror(errno)); - return -1; - } - - res = be32_to_cpu(pgs.u.resp.tpm_result); - if (res != 0 && (res & 0x800) == 0) { - error_report("tpm-emulator: Getting the stateblob (type %d) failed " - "with a TPM error 0x%x %s", type, res, - tpm_emulator_strerror(res)); - return -1; - } - - totlength = be32_to_cpu(pgs.u.resp.totlength); - length = be32_to_cpu(pgs.u.resp.length); - if (totlength != length) { - error_report("tpm-emulator: Expecting to read %u bytes " - "but would get %u", totlength, length); - return -1; - } - - *flags = be32_to_cpu(pgs.u.resp.state_flags); - - if (totlength > 0) { - tsb->buffer = g_try_malloc(totlength); - if (!tsb->buffer) { - error_report("tpm-emulator: Out of memory allocating %u bytes", - totlength); - return -1; - } - - n = qemu_chr_fe_read_all(&tpm_emu->ctrl_chr, tsb->buffer, totlength); - if (n != totlength) { - error_report("tpm-emulator: Could not read stateblob (type %d); " - "expected %u bytes, got %zd", - type, totlength, n); - return -1; - } - } - tsb->size = totlength; - - trace_tpm_emulator_get_state_blob(type, tsb->size, *flags); - - return 0; -} - -static int tpm_emulator_get_state_blobs(TPMEmulator *tpm_emu) -{ - TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs; - - if (tpm_emulator_get_state_blob(tpm_emu, PTM_BLOB_TYPE_PERMANENT, - &state_blobs->permanent, - &state_blobs->permanent_flags) < 0 || - tpm_emulator_get_state_blob(tpm_emu, PTM_BLOB_TYPE_VOLATILE, - &state_blobs->volatil, - &state_blobs->volatil_flags) < 0 || - tpm_emulator_get_state_blob(tpm_emu, PTM_BLOB_TYPE_SAVESTATE, - &state_blobs->savestate, - &state_blobs->savestate_flags) < 0) { - goto err_exit; - } - - return 0; - - err_exit: - tpm_sized_buffer_reset(&state_blobs->volatil); - tpm_sized_buffer_reset(&state_blobs->permanent); - tpm_sized_buffer_reset(&state_blobs->savestate); - - return -1; -} - -/* - * Transfer a TPM state blob to the TPM emulator. - * - * @tpm_emu: TPMEmulator - * @type: the type of TPM state blob to transfer - * @tsb: TPMSizedBuffer containing the TPM state blob - * @flags: Flags describing the (encryption) state of the TPM state blob - */ -static int tpm_emulator_set_state_blob(TPMEmulator *tpm_emu, - uint32_t type, - TPMSizedBuffer *tsb, - uint32_t flags) -{ - ssize_t n; - ptm_setstate pss; - ptm_res tpm_result; - - if (tsb->size == 0) { - return 0; - } - - pss = (ptm_setstate) { - .u.req.state_flags = cpu_to_be32(flags), - .u.req.type = cpu_to_be32(type), - .u.req.length = cpu_to_be32(tsb->size), - }; - - /* write the header only */ - if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_STATEBLOB, &pss, - offsetof(ptm_setstate, u.req.data), 0) < 0) { - error_report("tpm-emulator: could not set state blob type %d : %s", - type, strerror(errno)); - return -1; - } - - /* now the body */ - n = qemu_chr_fe_write_all(&tpm_emu->ctrl_chr, tsb->buffer, tsb->size); - if (n != tsb->size) { - error_report("tpm-emulator: Writing the stateblob (type %d) " - "failed; could not write %u bytes, but only %zd", - type, tsb->size, n); - return -1; - } - - /* now get the result */ - n = qemu_chr_fe_read_all(&tpm_emu->ctrl_chr, - (uint8_t *)&pss, sizeof(pss.u.resp)); - if (n != sizeof(pss.u.resp)) { - error_report("tpm-emulator: Reading response from writing stateblob " - "(type %d) failed; expected %zu bytes, got %zd", type, - sizeof(pss.u.resp), n); - return -1; - } - - tpm_result = be32_to_cpu(pss.u.resp.tpm_result); - if (tpm_result != 0) { - error_report("tpm-emulator: Setting the stateblob (type %d) failed " - "with a TPM error 0x%x %s", type, tpm_result, - tpm_emulator_strerror(tpm_result)); - return -1; - } - - trace_tpm_emulator_set_state_blob(type, tsb->size, flags); - - return 0; -} - -/* - * Set all the TPM state blobs. - * - * Returns a negative errno code in case of error. - */ -static int tpm_emulator_set_state_blobs(TPMBackend *tb) -{ - TPMEmulator *tpm_emu = TPM_EMULATOR(tb); - TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs; - - trace_tpm_emulator_set_state_blobs(); - - if (tpm_emulator_stop_tpm(tb) < 0) { - trace_tpm_emulator_set_state_blobs_error("Could not stop TPM"); - return -EIO; - } - - if (tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_PERMANENT, - &state_blobs->permanent, - state_blobs->permanent_flags) < 0 || - tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_VOLATILE, - &state_blobs->volatil, - state_blobs->volatil_flags) < 0 || - tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_SAVESTATE, - &state_blobs->savestate, - state_blobs->savestate_flags) < 0) { - return -EIO; - } - - trace_tpm_emulator_set_state_blobs_done(); - - return 0; -} - -static int tpm_emulator_pre_save(void *opaque) -{ - TPMBackend *tb = opaque; - TPMEmulator *tpm_emu = TPM_EMULATOR(tb); - - trace_tpm_emulator_pre_save(); - - tpm_backend_finish_sync(tb); - - /* get the state blobs from the TPM */ - return tpm_emulator_get_state_blobs(tpm_emu); -} - -/* - * Load the TPM state blobs into the TPM. - * - * Returns negative errno codes in case of error. - */ -static int tpm_emulator_post_load(void *opaque, int version_id) -{ - TPMBackend *tb = opaque; - int ret; - - ret = tpm_emulator_set_state_blobs(tb); - if (ret < 0) { - return ret; - } - - if (tpm_emulator_startup_tpm_resume(tb, 0, true) < 0) { - return -EIO; - } - - return 0; -} - -static const VMStateDescription vmstate_tpm_emulator = { - .name = "tpm-emulator", - .version_id = 0, - .pre_save = tpm_emulator_pre_save, - .post_load = tpm_emulator_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32(state_blobs.permanent_flags, TPMEmulator), - VMSTATE_UINT32(state_blobs.permanent.size, TPMEmulator), - VMSTATE_VBUFFER_ALLOC_UINT32(state_blobs.permanent.buffer, - TPMEmulator, 0, 0, - state_blobs.permanent.size), - - VMSTATE_UINT32(state_blobs.volatil_flags, TPMEmulator), - VMSTATE_UINT32(state_blobs.volatil.size, TPMEmulator), - VMSTATE_VBUFFER_ALLOC_UINT32(state_blobs.volatil.buffer, - TPMEmulator, 0, 0, - state_blobs.volatil.size), - - VMSTATE_UINT32(state_blobs.savestate_flags, TPMEmulator), - VMSTATE_UINT32(state_blobs.savestate.size, TPMEmulator), - VMSTATE_VBUFFER_ALLOC_UINT32(state_blobs.savestate.buffer, - TPMEmulator, 0, 0, - state_blobs.savestate.size), - - VMSTATE_END_OF_LIST() - } -}; - -static void tpm_emulator_inst_init(Object *obj) -{ - TPMEmulator *tpm_emu = TPM_EMULATOR(obj); - - trace_tpm_emulator_inst_init(); - - tpm_emu->options = g_new0(TPMEmulatorOptions, 1); - tpm_emu->cur_locty_number = ~0; - qemu_mutex_init(&tpm_emu->mutex); - - vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, - &vmstate_tpm_emulator, obj); -} - -/* - * Gracefully shut down the external TPM - */ -static void tpm_emulator_shutdown(TPMEmulator *tpm_emu) -{ - ptm_res res; - - if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SHUTDOWN, &res, 0, sizeof(res)) < 0) { - error_report("tpm-emulator: Could not cleanly shutdown the TPM: %s", - strerror(errno)); - } else if (res != 0) { - error_report("tpm-emulator: TPM result for shutdown: 0x%x %s", - be32_to_cpu(res), tpm_emulator_strerror(be32_to_cpu(res))); - } -} - -static void tpm_emulator_inst_finalize(Object *obj) -{ - TPMEmulator *tpm_emu = TPM_EMULATOR(obj); - TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs; - - tpm_emulator_shutdown(tpm_emu); - - object_unref(OBJECT(tpm_emu->data_ioc)); - - qemu_chr_fe_deinit(&tpm_emu->ctrl_chr, false); - - qapi_free_TPMEmulatorOptions(tpm_emu->options); - - if (tpm_emu->migration_blocker) { - migrate_del_blocker(tpm_emu->migration_blocker); - error_free(tpm_emu->migration_blocker); - } - - tpm_sized_buffer_reset(&state_blobs->volatil); - tpm_sized_buffer_reset(&state_blobs->permanent); - tpm_sized_buffer_reset(&state_blobs->savestate); - - qemu_mutex_destroy(&tpm_emu->mutex); - - vmstate_unregister(NULL, &vmstate_tpm_emulator, obj); -} - -static void tpm_emulator_class_init(ObjectClass *klass, void *data) -{ - TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass); - - tbc->type = TPM_TYPE_EMULATOR; - tbc->opts = tpm_emulator_cmdline_opts; - tbc->desc = "TPM emulator backend driver"; - tbc->create = tpm_emulator_create; - tbc->startup_tpm = tpm_emulator_startup_tpm; - tbc->cancel_cmd = tpm_emulator_cancel_cmd; - tbc->get_tpm_established_flag = tpm_emulator_get_tpm_established_flag; - tbc->reset_tpm_established_flag = tpm_emulator_reset_tpm_established_flag; - tbc->get_tpm_version = tpm_emulator_get_tpm_version; - tbc->get_buffer_size = tpm_emulator_get_buffer_size; - tbc->get_tpm_options = tpm_emulator_get_tpm_options; - - tbc->handle_request = tpm_emulator_handle_request; -} - -static const TypeInfo tpm_emulator_info = { - .name = TYPE_TPM_EMULATOR, - .parent = TYPE_TPM_BACKEND, - .instance_size = sizeof(TPMEmulator), - .class_init = tpm_emulator_class_init, - .instance_init = tpm_emulator_inst_init, - .instance_finalize = tpm_emulator_inst_finalize, -}; - -static void tpm_emulator_register(void) -{ - type_register_static(&tpm_emulator_info); -} - -type_init(tpm_emulator_register) diff --git a/hw/tpm/tpm_int.h b/hw/tpm/tpm_int.h deleted file mode 100644 index 3fb28a9d6c..0000000000 --- a/hw/tpm/tpm_int.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * TPM configuration - * - * Copyright (C) 2011-2013 IBM Corporation - * - * Authors: - * Stefan Berger <stefanb@us.ibm.com> - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ -#ifndef TPM_TPM_INT_H -#define TPM_TPM_INT_H - -#define TPM_STANDARD_CMDLINE_OPTS \ - { \ - .name = "type", \ - .type = QEMU_OPT_STRING, \ - .help = "Type of TPM backend", \ - } - -struct tpm_req_hdr { - uint16_t tag; - uint32_t len; - uint32_t ordinal; -} QEMU_PACKED; - -struct tpm_resp_hdr { - uint16_t tag; - uint32_t len; - uint32_t errcode; -} QEMU_PACKED; - -#define TPM_TAG_RQU_COMMAND 0xc1 -#define TPM_TAG_RQU_AUTH1_COMMAND 0xc2 -#define TPM_TAG_RQU_AUTH2_COMMAND 0xc3 - -#define TPM_TAG_RSP_COMMAND 0xc4 -#define TPM_TAG_RSP_AUTH1_COMMAND 0xc5 -#define TPM_TAG_RSP_AUTH2_COMMAND 0xc6 - -#define TPM_BAD_PARAMETER 3 -#define TPM_FAIL 9 -#define TPM_KEYNOTFOUND 13 -#define TPM_BAD_PARAM_SIZE 25 -#define TPM_ENCRYPT_ERROR 32 -#define TPM_DECRYPT_ERROR 33 -#define TPM_BAD_KEY_PROPERTY 40 -#define TPM_BAD_MODE 44 -#define TPM_BAD_VERSION 46 -#define TPM_BAD_LOCALITY 61 - -#define TPM_ORD_ContinueSelfTest 0x53 -#define TPM_ORD_GetTicks 0xf1 -#define TPM_ORD_GetCapability 0x65 - -#define TPM_CAP_PROPERTY 0x05 - -#define TPM_CAP_PROP_INPUT_BUFFER 0x124 - -/* TPM2 defines */ -#define TPM2_ST_NO_SESSIONS 0x8001 - -#define TPM2_CC_ReadClock 0x00000181 -#define TPM2_CC_GetCapability 0x0000017a - -#define TPM2_CAP_TPM_PROPERTIES 0x6 - -#define TPM2_PT_MAX_COMMAND_SIZE 0x11e - -#define TPM_RC_INSUFFICIENT 0x9a -#define TPM_RC_FAILURE 0x101 -#define TPM_RC_LOCALITY 0x907 - -#endif /* TPM_TPM_INT_H */ diff --git a/hw/tpm/tpm_ioctl.h b/hw/tpm/tpm_ioctl.h deleted file mode 100644 index f5f5c553a9..0000000000 --- a/hw/tpm/tpm_ioctl.h +++ /dev/null @@ -1,271 +0,0 @@ -/* - * tpm_ioctl.h - * - * (c) Copyright IBM Corporation 2014, 2015. - * - * This file is licensed under the terms of the 3-clause BSD license - */ - -#ifndef TPM_IOCTL_H -#define TPM_IOCTL_H - -#include <sys/uio.h> -#include <sys/ioctl.h> - -/* - * Every response from a command involving a TPM command execution must hold - * the ptm_res as the first element. - * ptm_res corresponds to the error code of a command executed by the TPM. - */ - -typedef uint32_t ptm_res; - -/* PTM_GET_TPMESTABLISHED: get the establishment bit */ -struct ptm_est { - union { - struct { - ptm_res tpm_result; - unsigned char bit; /* TPM established bit */ - } resp; /* response */ - } u; -}; - -/* PTM_RESET_TPMESTABLISHED: reset establishment bit */ -struct ptm_reset_est { - union { - struct { - uint8_t loc; /* locality to use */ - } req; /* request */ - struct { - ptm_res tpm_result; - } resp; /* response */ - } u; -}; - -/* PTM_INIT */ -struct ptm_init { - union { - struct { - uint32_t init_flags; /* see definitions below */ - } req; /* request */ - struct { - ptm_res tpm_result; - } resp; /* response */ - } u; -}; - -/* above init_flags */ -#define PTM_INIT_FLAG_DELETE_VOLATILE (1 << 0) - /* delete volatile state file after reading it */ - -/* PTM_SET_LOCALITY */ -struct ptm_loc { - union { - struct { - uint8_t loc; /* locality to set */ - } req; /* request */ - struct { - ptm_res tpm_result; - } resp; /* response */ - } u; -}; - -/* PTM_HASH_DATA: hash given data */ -struct ptm_hdata { - union { - struct { - uint32_t length; - uint8_t data[4096]; - } req; /* request */ - struct { - ptm_res tpm_result; - } resp; /* response */ - } u; -}; - -/* - * size of the TPM state blob to transfer; x86_64 can handle 8k, - * ppc64le only ~7k; keep the response below a 4k page size - */ -#define PTM_STATE_BLOB_SIZE (3 * 1024) - -/* - * The following is the data structure to get state blobs from the TPM. - * If the size of the state blob exceeds the PTM_STATE_BLOB_SIZE, multiple reads - * with this ioctl and with adjusted offset are necessary. All bytes - * must be transferred and the transfer is done once the last byte has been - * returned. - * It is possible to use the read() interface for reading the data; however, the - * first bytes of the state blob will be part of the response to the ioctl(); a - * subsequent read() is only necessary if the total length (totlength) exceeds - * the number of received bytes. seek() is not supported. - */ -struct ptm_getstate { - union { - struct { - uint32_t state_flags; /* may be: PTM_STATE_FLAG_DECRYPTED */ - uint32_t type; /* which blob to pull */ - uint32_t offset; /* offset from where to read */ - } req; /* request */ - struct { - ptm_res tpm_result; - uint32_t state_flags; /* may be: PTM_STATE_FLAG_ENCRYPTED */ - uint32_t totlength; /* total length that will be transferred */ - uint32_t length; /* number of bytes in following buffer */ - uint8_t data[PTM_STATE_BLOB_SIZE]; - } resp; /* response */ - } u; -}; - -/* TPM state blob types */ -#define PTM_BLOB_TYPE_PERMANENT 1 -#define PTM_BLOB_TYPE_VOLATILE 2 -#define PTM_BLOB_TYPE_SAVESTATE 3 - -/* state_flags above : */ -#define PTM_STATE_FLAG_DECRYPTED 1 /* on input: get decrypted state */ -#define PTM_STATE_FLAG_ENCRYPTED 2 /* on output: state is encrypted */ - -/* - * The following is the data structure to set state blobs in the TPM. - * If the size of the state blob exceeds the PTM_STATE_BLOB_SIZE, multiple - * 'writes' using this ioctl are necessary. The last packet is indicated - * by the length being smaller than the PTM_STATE_BLOB_SIZE. - * The very first packet may have a length indicator of '0' enabling - * a write() with all the bytes from a buffer. If the write() interface - * is used, a final ioctl with a non-full buffer must be made to indicate - * that all data were transferred (a write with 0 bytes would not work). - */ -struct ptm_setstate { - union { - struct { - uint32_t state_flags; /* may be PTM_STATE_FLAG_ENCRYPTED */ - uint32_t type; /* which blob to set */ - uint32_t length; /* length of the data; - use 0 on the first packet to - transfer using write() */ - uint8_t data[PTM_STATE_BLOB_SIZE]; - } req; /* request */ - struct { - ptm_res tpm_result; - } resp; /* response */ - } u; -}; - -/* - * PTM_GET_CONFIG: Data structure to get runtime configuration information - * such as which keys are applied. - */ -struct ptm_getconfig { - union { - struct { - ptm_res tpm_result; - uint32_t flags; - } resp; /* response */ - } u; -}; - -#define PTM_CONFIG_FLAG_FILE_KEY 0x1 -#define PTM_CONFIG_FLAG_MIGRATION_KEY 0x2 - -/* - * PTM_SET_BUFFERSIZE: Set the buffer size to be used by the TPM. - * A 0 on input queries for the current buffer size. Any other - * number will try to set the buffer size. The returned number is - * the buffer size that will be used, which can be larger than the - * requested one, if it was below the minimum, or smaller than the - * requested one, if it was above the maximum. - */ -struct ptm_setbuffersize { - union { - struct { - uint32_t buffersize; /* 0 to query for current buffer size */ - } req; /* request */ - struct { - ptm_res tpm_result; - uint32_t buffersize; /* buffer size in use */ - uint32_t minsize; /* min. supported buffer size */ - uint32_t maxsize; /* max. supported buffer size */ - } resp; /* response */ - } u; -}; - - -typedef uint64_t ptm_cap; -typedef struct ptm_est ptm_est; -typedef struct ptm_reset_est ptm_reset_est; -typedef struct ptm_loc ptm_loc; -typedef struct ptm_hdata ptm_hdata; -typedef struct ptm_init ptm_init; -typedef struct ptm_getstate ptm_getstate; -typedef struct ptm_setstate ptm_setstate; -typedef struct ptm_getconfig ptm_getconfig; -typedef struct ptm_setbuffersize ptm_setbuffersize; - -/* capability flags returned by PTM_GET_CAPABILITY */ -#define PTM_CAP_INIT (1) -#define PTM_CAP_SHUTDOWN (1 << 1) -#define PTM_CAP_GET_TPMESTABLISHED (1 << 2) -#define PTM_CAP_SET_LOCALITY (1 << 3) -#define PTM_CAP_HASHING (1 << 4) -#define PTM_CAP_CANCEL_TPM_CMD (1 << 5) -#define PTM_CAP_STORE_VOLATILE (1 << 6) -#define PTM_CAP_RESET_TPMESTABLISHED (1 << 7) -#define PTM_CAP_GET_STATEBLOB (1 << 8) -#define PTM_CAP_SET_STATEBLOB (1 << 9) -#define PTM_CAP_STOP (1 << 10) -#define PTM_CAP_GET_CONFIG (1 << 11) -#define PTM_CAP_SET_DATAFD (1 << 12) -#define PTM_CAP_SET_BUFFERSIZE (1 << 13) - -enum { - PTM_GET_CAPABILITY = _IOR('P', 0, ptm_cap), - PTM_INIT = _IOWR('P', 1, ptm_init), - PTM_SHUTDOWN = _IOR('P', 2, ptm_res), - PTM_GET_TPMESTABLISHED = _IOR('P', 3, ptm_est), - PTM_SET_LOCALITY = _IOWR('P', 4, ptm_loc), - PTM_HASH_START = _IOR('P', 5, ptm_res), - PTM_HASH_DATA = _IOWR('P', 6, ptm_hdata), - PTM_HASH_END = _IOR('P', 7, ptm_res), - PTM_CANCEL_TPM_CMD = _IOR('P', 8, ptm_res), - PTM_STORE_VOLATILE = _IOR('P', 9, ptm_res), - PTM_RESET_TPMESTABLISHED = _IOWR('P', 10, ptm_reset_est), - PTM_GET_STATEBLOB = _IOWR('P', 11, ptm_getstate), - PTM_SET_STATEBLOB = _IOWR('P', 12, ptm_setstate), - PTM_STOP = _IOR('P', 13, ptm_res), - PTM_GET_CONFIG = _IOR('P', 14, ptm_getconfig), - PTM_SET_DATAFD = _IOR('P', 15, ptm_res), - PTM_SET_BUFFERSIZE = _IOWR('P', 16, ptm_setbuffersize), -}; - -/* - * Commands used by the non-CUSE TPMs - * - * All messages container big-endian data. - * - * The return messages only contain the 'resp' part of the unions - * in the data structures above. Besides that the limits in the - * buffers above (ptm_hdata:u.req.data and ptm_get_state:u.resp.data - * and ptm_set_state:u.req.data) are 0xffffffff. - */ -enum { - CMD_GET_CAPABILITY = 1, - CMD_INIT, - CMD_SHUTDOWN, - CMD_GET_TPMESTABLISHED, - CMD_SET_LOCALITY, - CMD_HASH_START, - CMD_HASH_DATA, - CMD_HASH_END, - CMD_CANCEL_TPM_CMD, - CMD_STORE_VOLATILE, - CMD_RESET_TPMESTABLISHED, - CMD_GET_STATEBLOB, - CMD_SET_STATEBLOB, - CMD_STOP, - CMD_GET_CONFIG, - CMD_SET_DATAFD, - CMD_SET_BUFFERSIZE, -}; - -#endif /* TPM_IOCTL_H */ diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c deleted file mode 100644 index f67244b5d4..0000000000 --- a/hw/tpm/tpm_passthrough.c +++ /dev/null @@ -1,405 +0,0 @@ -/* - * passthrough TPM driver - * - * Copyright (c) 2010 - 2013 IBM Corporation - * Authors: - * Stefan Berger <stefanb@us.ibm.com> - * - * Copyright (C) 2011 IAIK, Graz University of Technology - * Author: Andreas Niederl - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/> - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "qemu/error-report.h" -#include "qemu/module.h" -#include "qemu/sockets.h" -#include "sysemu/tpm_backend.h" -#include "tpm_int.h" -#include "qapi/clone-visitor.h" -#include "qapi/qapi-visit-tpm.h" -#include "tpm_util.h" -#include "trace.h" - -#define TYPE_TPM_PASSTHROUGH "tpm-passthrough" -#define TPM_PASSTHROUGH(obj) \ - OBJECT_CHECK(TPMPassthruState, (obj), TYPE_TPM_PASSTHROUGH) - -/* data structures */ -struct TPMPassthruState { - TPMBackend parent; - - TPMPassthroughOptions *options; - const char *tpm_dev; - int tpm_fd; - bool tpm_executing; - bool tpm_op_canceled; - int cancel_fd; - - TPMVersion tpm_version; - size_t tpm_buffersize; -}; - -typedef struct TPMPassthruState TPMPassthruState; - -#define TPM_PASSTHROUGH_DEFAULT_DEVICE "/dev/tpm0" - -/* functions */ - -static void tpm_passthrough_cancel_cmd(TPMBackend *tb); - -static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len) -{ - int ret; - reread: - ret = read(fd, buf, len); - if (ret < 0) { - if (errno != EINTR && errno != EAGAIN) { - return -1; - } - goto reread; - } - return ret; -} - -static void tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt, - const uint8_t *in, uint32_t in_len, - uint8_t *out, uint32_t out_len, - bool *selftest_done, Error **errp) -{ - ssize_t ret; - bool is_selftest; - - /* FIXME: protect shared variables or use other sync mechanism */ - tpm_pt->tpm_op_canceled = false; - tpm_pt->tpm_executing = true; - *selftest_done = false; - - is_selftest = tpm_util_is_selftest(in, in_len); - - ret = qemu_write_full(tpm_pt->tpm_fd, in, in_len); - if (ret != in_len) { - if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) { - error_setg_errno(errp, errno, "tpm_passthrough: error while " - "transmitting data to TPM"); - } - goto err_exit; - } - - tpm_pt->tpm_executing = false; - - ret = tpm_passthrough_unix_read(tpm_pt->tpm_fd, out, out_len); - if (ret < 0) { - if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) { - error_setg_errno(errp, errno, "tpm_passthrough: error while " - "reading data from TPM"); - } - } else if (ret < sizeof(struct tpm_resp_hdr) || - tpm_cmd_get_size(out) != ret) { - ret = -1; - error_setg_errno(errp, errno, "tpm_passthrough: received invalid " - "response packet from TPM"); - } - - if (is_selftest && (ret >= sizeof(struct tpm_resp_hdr))) { - *selftest_done = tpm_cmd_get_errcode(out) == 0; - } - -err_exit: - if (ret < 0) { - tpm_util_write_fatal_error_response(out, out_len); - } - - tpm_pt->tpm_executing = false; -} - -static void tpm_passthrough_handle_request(TPMBackend *tb, TPMBackendCmd *cmd, - Error **errp) -{ - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); - - trace_tpm_passthrough_handle_request(cmd); - - tpm_passthrough_unix_tx_bufs(tpm_pt, cmd->in, cmd->in_len, - cmd->out, cmd->out_len, &cmd->selftest_done, - errp); -} - -static void tpm_passthrough_reset(TPMBackend *tb) -{ - trace_tpm_passthrough_reset(); - - tpm_passthrough_cancel_cmd(tb); -} - -static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb) -{ - return false; -} - -static int tpm_passthrough_reset_tpm_established_flag(TPMBackend *tb, - uint8_t locty) -{ - /* only a TPM 2.0 will support this */ - return 0; -} - -static void tpm_passthrough_cancel_cmd(TPMBackend *tb) -{ - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); - int n; - - /* - * As of Linux 3.7 the tpm_tis driver does not properly cancel - * commands on all TPM manufacturers' TPMs. - * Only cancel if we're busy so we don't cancel someone else's - * command, e.g., a command executed on the host. - */ - if (tpm_pt->tpm_executing) { - if (tpm_pt->cancel_fd >= 0) { - tpm_pt->tpm_op_canceled = true; - n = write(tpm_pt->cancel_fd, "-", 1); - if (n != 1) { - error_report("Canceling TPM command failed: %s", - strerror(errno)); - } - } else { - error_report("Cannot cancel TPM command due to missing " - "TPM sysfs cancel entry"); - } - } -} - -static TPMVersion tpm_passthrough_get_tpm_version(TPMBackend *tb) -{ - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); - - return tpm_pt->tpm_version; -} - -static size_t tpm_passthrough_get_buffer_size(TPMBackend *tb) -{ - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); - int ret; - - ret = tpm_util_get_buffer_size(tpm_pt->tpm_fd, tpm_pt->tpm_version, - &tpm_pt->tpm_buffersize); - if (ret < 0) { - tpm_pt->tpm_buffersize = 4096; - } - return tpm_pt->tpm_buffersize; -} - -/* - * Unless path or file descriptor set has been provided by user, - * determine the sysfs cancel file following kernel documentation - * in Documentation/ABI/stable/sysfs-class-tpm. - * From /dev/tpm0 create /sys/class/tpm/tpm0/device/cancel - * before 4.0: /sys/class/misc/tpm0/device/cancel - */ -static int tpm_passthrough_open_sysfs_cancel(TPMPassthruState *tpm_pt) -{ - int fd = -1; - char *dev; - char path[PATH_MAX]; - - if (tpm_pt->options->cancel_path) { - fd = qemu_open(tpm_pt->options->cancel_path, O_WRONLY); - if (fd < 0) { - error_report("tpm_passthrough: Could not open TPM cancel path: %s", - strerror(errno)); - } - return fd; - } - - dev = strrchr(tpm_pt->tpm_dev, '/'); - if (!dev) { - error_report("tpm_passthrough: Bad TPM device path %s", - tpm_pt->tpm_dev); - return -1; - } - - dev++; - if (snprintf(path, sizeof(path), "/sys/class/tpm/%s/device/cancel", - dev) < sizeof(path)) { - fd = qemu_open(path, O_WRONLY); - if (fd < 0) { - if (snprintf(path, sizeof(path), "/sys/class/misc/%s/device/cancel", - dev) < sizeof(path)) { - fd = qemu_open(path, O_WRONLY); - } - } - } - - if (fd < 0) { - error_report("tpm_passthrough: Could not guess TPM cancel path"); - } else { - tpm_pt->options->cancel_path = g_strdup(path); - } - - return fd; -} - -static int -tpm_passthrough_handle_device_opts(TPMPassthruState *tpm_pt, QemuOpts *opts) -{ - const char *value; - - value = qemu_opt_get(opts, "cancel-path"); - if (value) { - tpm_pt->options->cancel_path = g_strdup(value); - tpm_pt->options->has_cancel_path = true; - } - - value = qemu_opt_get(opts, "path"); - if (value) { - tpm_pt->options->has_path = true; - tpm_pt->options->path = g_strdup(value); - } - - tpm_pt->tpm_dev = value ? value : TPM_PASSTHROUGH_DEFAULT_DEVICE; - tpm_pt->tpm_fd = qemu_open(tpm_pt->tpm_dev, O_RDWR); - if (tpm_pt->tpm_fd < 0) { - error_report("Cannot access TPM device using '%s': %s", - tpm_pt->tpm_dev, strerror(errno)); - return -1; - } - - if (tpm_util_test_tpmdev(tpm_pt->tpm_fd, &tpm_pt->tpm_version)) { - error_report("'%s' is not a TPM device.", - tpm_pt->tpm_dev); - return -1; - } - - tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tpm_pt); - if (tpm_pt->cancel_fd < 0) { - return -1; - } - - return 0; -} - -static TPMBackend *tpm_passthrough_create(QemuOpts *opts) -{ - Object *obj = object_new(TYPE_TPM_PASSTHROUGH); - - if (tpm_passthrough_handle_device_opts(TPM_PASSTHROUGH(obj), opts)) { - object_unref(obj); - return NULL; - } - - return TPM_BACKEND(obj); -} - -static int tpm_passthrough_startup_tpm(TPMBackend *tb, size_t buffersize) -{ - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); - - if (buffersize && buffersize < tpm_pt->tpm_buffersize) { - error_report("Requested buffer size of %zu is smaller than host TPM's " - "fixed buffer size of %zu", - buffersize, tpm_pt->tpm_buffersize); - return -1; - } - - return 0; -} - -static TpmTypeOptions *tpm_passthrough_get_tpm_options(TPMBackend *tb) -{ - TpmTypeOptions *options = g_new0(TpmTypeOptions, 1); - - options->type = TPM_TYPE_OPTIONS_KIND_PASSTHROUGH; - options->u.passthrough.data = QAPI_CLONE(TPMPassthroughOptions, - TPM_PASSTHROUGH(tb)->options); - - return options; -} - -static const QemuOptDesc tpm_passthrough_cmdline_opts[] = { - TPM_STANDARD_CMDLINE_OPTS, - { - .name = "cancel-path", - .type = QEMU_OPT_STRING, - .help = "Sysfs file entry for canceling TPM commands", - }, - { - .name = "path", - .type = QEMU_OPT_STRING, - .help = "Path to TPM device on the host", - }, - { /* end of list */ }, -}; - -static void tpm_passthrough_inst_init(Object *obj) -{ - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(obj); - - tpm_pt->options = g_new0(TPMPassthroughOptions, 1); - tpm_pt->tpm_fd = -1; - tpm_pt->cancel_fd = -1; -} - -static void tpm_passthrough_inst_finalize(Object *obj) -{ - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(obj); - - tpm_passthrough_cancel_cmd(TPM_BACKEND(obj)); - - if (tpm_pt->tpm_fd >= 0) { - qemu_close(tpm_pt->tpm_fd); - } - if (tpm_pt->cancel_fd >= 0) { - qemu_close(tpm_pt->cancel_fd); - } - qapi_free_TPMPassthroughOptions(tpm_pt->options); -} - -static void tpm_passthrough_class_init(ObjectClass *klass, void *data) -{ - TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass); - - tbc->type = TPM_TYPE_PASSTHROUGH; - tbc->opts = tpm_passthrough_cmdline_opts; - tbc->desc = "Passthrough TPM backend driver"; - tbc->create = tpm_passthrough_create; - tbc->startup_tpm = tpm_passthrough_startup_tpm; - tbc->reset = tpm_passthrough_reset; - tbc->cancel_cmd = tpm_passthrough_cancel_cmd; - tbc->get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag; - tbc->reset_tpm_established_flag = - tpm_passthrough_reset_tpm_established_flag; - tbc->get_tpm_version = tpm_passthrough_get_tpm_version; - tbc->get_buffer_size = tpm_passthrough_get_buffer_size; - tbc->get_tpm_options = tpm_passthrough_get_tpm_options; - tbc->handle_request = tpm_passthrough_handle_request; -} - -static const TypeInfo tpm_passthrough_info = { - .name = TYPE_TPM_PASSTHROUGH, - .parent = TYPE_TPM_BACKEND, - .instance_size = sizeof(TPMPassthruState), - .class_init = tpm_passthrough_class_init, - .instance_init = tpm_passthrough_inst_init, - .instance_finalize = tpm_passthrough_inst_finalize, -}; - -static void tpm_passthrough_register(void) -{ - type_register_static(&tpm_passthrough_info); -} - -type_init(tpm_passthrough_register) diff --git a/hw/tpm/tpm_ppi.c b/hw/tpm/tpm_ppi.c index 6d9c1a3e40..72d7a3d926 100644 --- a/hw/tpm/tpm_ppi.c +++ b/hw/tpm/tpm_ppi.c @@ -17,6 +17,7 @@ #include "cpu.h" #include "sysemu/memory_mapping.h" #include "migration/vmstate.h" +#include "hw/acpi/tpm.h" #include "tpm_ppi.h" #include "trace.h" diff --git a/hw/tpm/tpm_ppi.h b/hw/tpm/tpm_ppi.h index d33ef27de6..6f773c25a0 100644 --- a/hw/tpm/tpm_ppi.h +++ b/hw/tpm/tpm_ppi.h @@ -12,7 +12,6 @@ #ifndef TPM_TPM_PPI_H #define TPM_TPM_PPI_H -#include "hw/acpi/tpm.h" #include "exec/address-spaces.h" typedef struct TPMPPI { diff --git a/hw/tpm/tpm_prop.h b/hw/tpm/tpm_prop.h new file mode 100644 index 0000000000..85e1ae5718 --- /dev/null +++ b/hw/tpm/tpm_prop.h @@ -0,0 +1,31 @@ +/* + * TPM utility functions + * + * Copyright (c) 2010 - 2015 IBM Corporation + * Authors: + * Stefan Berger <stefanb@us.ibm.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/> + */ + +#ifndef HW_TPM_PROP_H +#define HW_TPM_PROP_H + +#include "sysemu/tpm_backend.h" +#include "hw/qdev-properties.h" + +#define DEFINE_PROP_TPMBE(_n, _s, _f) \ + DEFINE_PROP(_n, _s, _f, qdev_prop_tpm, TPMBackend *) + +#endif /* HW_TPM_PROP_H */ diff --git a/hw/tpm/tpm_spapr.c b/hw/tpm/tpm_spapr.c index ce65eb2e45..cb4dfd1e6a 100644 --- a/hw/tpm/tpm_spapr.c +++ b/hw/tpm/tpm_spapr.c @@ -20,8 +20,8 @@ #include "migration/vmstate.h" #include "sysemu/tpm_backend.h" -#include "tpm_int.h" -#include "tpm_util.h" +#include "sysemu/tpm_util.h" +#include "tpm_prop.h" #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_vio.h" diff --git a/hw/tpm/tpm_tis.h b/hw/tpm/tpm_tis.h index 5554989395..f6b5872ba6 100644 --- a/hw/tpm/tpm_tis.h +++ b/hw/tpm/tpm_tis.h @@ -24,7 +24,6 @@ #ifndef TPM_TPM_TIS_H #define TPM_TPM_TIS_H -#include "qemu/osdep.h" #include "sysemu/tpm_backend.h" #include "tpm_ppi.h" diff --git a/hw/tpm/tpm_tis_common.c b/hw/tpm/tpm_tis_common.c index 1af4bce139..e700d82181 100644 --- a/hw/tpm/tpm_tis_common.c +++ b/hw/tpm/tpm_tis_common.c @@ -33,8 +33,7 @@ #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "sysemu/tpm_backend.h" -#include "tpm_int.h" -#include "tpm_util.h" +#include "sysemu/tpm_util.h" #include "tpm_ppi.h" #include "trace.h" @@ -79,9 +78,7 @@ static void tpm_tis_sts_set(TPMLocality *l, uint32_t flags) */ static void tpm_tis_tpm_send(TPMState *s, uint8_t locty) { - if (trace_event_get_state_backends(TRACE_TPM_UTIL_SHOW_BUFFER)) { - tpm_util_show_buffer(s->buffer, s->be_buffer_size, "To TPM"); - } + tpm_util_show_buffer(s->buffer, s->be_buffer_size, "To TPM"); /* * rw_offset serves as length indicator for length of data; @@ -247,9 +244,7 @@ void tpm_tis_request_completed(TPMState *s, int ret) s->loc[locty].state = TPM_TIS_STATE_COMPLETION; s->rw_offset = 0; - if (trace_event_get_state_backends(TRACE_TPM_UTIL_SHOW_BUFFER)) { - tpm_util_show_buffer(s->buffer, s->be_buffer_size, "From TPM"); - } + tpm_util_show_buffer(s->buffer, s->be_buffer_size, "From TPM"); if (TPM_TIS_IS_VALID_LOCTY(s->next_locty)) { tpm_tis_abort(s); diff --git a/hw/tpm/tpm_tis_isa.c b/hw/tpm/tpm_tis_isa.c index 30ba37079d..5faf6231c0 100644 --- a/hw/tpm/tpm_tis_isa.c +++ b/hw/tpm/tpm_tis_isa.c @@ -26,7 +26,8 @@ #include "hw/isa/isa.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" -#include "tpm_util.h" +#include "hw/acpi/tpm.h" +#include "tpm_prop.h" #include "tpm_tis.h" typedef struct TPMStateISA { diff --git a/hw/tpm/tpm_tis_sysbus.c b/hw/tpm/tpm_tis_sysbus.c index eced1fc843..4a3bc70625 100644 --- a/hw/tpm/tpm_tis_sysbus.c +++ b/hw/tpm/tpm_tis_sysbus.c @@ -25,7 +25,8 @@ #include "qemu/osdep.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" -#include "tpm_util.h" +#include "hw/acpi/tpm.h" +#include "tpm_prop.h" #include "hw/sysbus.h" #include "tpm_tis.h" diff --git a/hw/tpm/tpm_util.c b/hw/tpm/tpm_util.c deleted file mode 100644 index c0a0f3d71f..0000000000 --- a/hw/tpm/tpm_util.c +++ /dev/null @@ -1,377 +0,0 @@ -/* - * TPM utility functions - * - * Copyright (c) 2010 - 2015 IBM Corporation - * Authors: - * Stefan Berger <stefanb@us.ibm.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/> - */ - -#include "qemu/osdep.h" -#include "qemu/error-report.h" -#include "qapi/error.h" -#include "qapi/visitor.h" -#include "tpm_util.h" -#include "tpm_int.h" -#include "exec/memory.h" -#include "hw/qdev-properties.h" -#include "sysemu/tpm_backend.h" -#include "trace.h" - -/* tpm backend property */ - -static void get_tpm(Object *obj, Visitor *v, const char *name, void *opaque, - Error **errp) -{ - DeviceState *dev = DEVICE(obj); - TPMBackend **be = qdev_get_prop_ptr(dev, opaque); - char *p; - - p = g_strdup(*be ? (*be)->id : ""); - visit_type_str(v, name, &p, errp); - g_free(p); -} - -static void set_tpm(Object *obj, Visitor *v, const char *name, void *opaque, - Error **errp) -{ - DeviceState *dev = DEVICE(obj); - Error *local_err = NULL; - Property *prop = opaque; - TPMBackend *s, **be = qdev_get_prop_ptr(dev, prop); - char *str; - - if (dev->realized) { - qdev_prop_set_after_realize(dev, name, errp); - return; - } - - visit_type_str(v, name, &str, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - s = qemu_find_tpm_be(str); - if (s == NULL) { - error_setg(errp, "Property '%s.%s' can't find value '%s'", - object_get_typename(obj), prop->name, str); - } else if (tpm_backend_init(s, TPM_IF(obj), errp) == 0) { - *be = s; /* weak reference, avoid cyclic ref */ - } - g_free(str); -} - -static void release_tpm(Object *obj, const char *name, void *opaque) -{ - DeviceState *dev = DEVICE(obj); - Property *prop = opaque; - TPMBackend **be = qdev_get_prop_ptr(dev, prop); - - if (*be) { - tpm_backend_reset(*be); - } -} - -const PropertyInfo qdev_prop_tpm = { - .name = "str", - .description = "ID of a tpm to use as a backend", - .get = get_tpm, - .set = set_tpm, - .release = release_tpm, -}; - -/* - * Write an error message in the given output buffer. - */ -void tpm_util_write_fatal_error_response(uint8_t *out, uint32_t out_len) -{ - if (out_len >= sizeof(struct tpm_resp_hdr)) { - tpm_cmd_set_tag(out, TPM_TAG_RSP_COMMAND); - tpm_cmd_set_size(out, sizeof(struct tpm_resp_hdr)); - tpm_cmd_set_error(out, TPM_FAIL); - } -} - -bool tpm_util_is_selftest(const uint8_t *in, uint32_t in_len) -{ - if (in_len >= sizeof(struct tpm_req_hdr)) { - return tpm_cmd_get_ordinal(in) == TPM_ORD_ContinueSelfTest; - } - - return false; -} - -/* - * Send request to a TPM device. We expect a response within one second. - */ -static int tpm_util_request(int fd, - const void *request, - size_t requestlen, - void *response, - size_t responselen) -{ - fd_set readfds; - int n; - struct timeval tv = { - .tv_sec = 1, - .tv_usec = 0, - }; - - n = write(fd, request, requestlen); - if (n < 0) { - return -errno; - } - if (n != requestlen) { - return -EFAULT; - } - - FD_ZERO(&readfds); - FD_SET(fd, &readfds); - - /* wait for a second */ - n = select(fd + 1, &readfds, NULL, NULL, &tv); - if (n != 1) { - return -errno; - } - - n = read(fd, response, responselen); - if (n < sizeof(struct tpm_resp_hdr)) { - return -EFAULT; - } - - /* check the header */ - if (tpm_cmd_get_size(response) != n) { - return -EMSGSIZE; - } - - return 0; -} - -/* - * A basic test of a TPM device. We expect a well formatted response header - * (error response is fine). - */ -static int tpm_util_test(int fd, - const void *request, - size_t requestlen, - uint16_t *return_tag) -{ - char buf[1024]; - ssize_t ret; - - ret = tpm_util_request(fd, request, requestlen, - buf, sizeof(buf)); - if (ret < 0) { - return ret; - } - - *return_tag = tpm_cmd_get_tag(buf); - - return 0; -} - -/* - * Probe for the TPM device in the back - * Returns 0 on success with the version of the probed TPM set, 1 on failure. - */ -int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version) -{ - /* - * Sending a TPM1.2 command to a TPM2 should return a TPM1.2 - * header (tag = 0xc4) and error code (TPM_BADTAG = 0x1e) - * - * Sending a TPM2 command to a TPM 2 will give a TPM 2 tag in the - * header. - * Sending a TPM2 command to a TPM 1.2 will give a TPM 1.2 tag - * in the header and an error code. - */ - const struct tpm_req_hdr test_req = { - .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND), - .len = cpu_to_be32(sizeof(test_req)), - .ordinal = cpu_to_be32(TPM_ORD_GetTicks), - }; - - const struct tpm_req_hdr test_req_tpm2 = { - .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), - .len = cpu_to_be32(sizeof(test_req_tpm2)), - .ordinal = cpu_to_be32(TPM2_CC_ReadClock), - }; - uint16_t return_tag; - int ret; - - /* Send TPM 2 command */ - ret = tpm_util_test(tpm_fd, &test_req_tpm2, - sizeof(test_req_tpm2), &return_tag); - /* TPM 2 would respond with a tag of TPM2_ST_NO_SESSIONS */ - if (!ret && return_tag == TPM2_ST_NO_SESSIONS) { - *tpm_version = TPM_VERSION_2_0; - return 0; - } - - /* Send TPM 1.2 command */ - ret = tpm_util_test(tpm_fd, &test_req, - sizeof(test_req), &return_tag); - if (!ret && return_tag == TPM_TAG_RSP_COMMAND) { - *tpm_version = TPM_VERSION_1_2; - /* this is a TPM 1.2 */ - return 0; - } - - *tpm_version = TPM_VERSION_UNSPEC; - - return 1; -} - -int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version, - size_t *buffersize) -{ - int ret; - - switch (tpm_version) { - case TPM_VERSION_1_2: { - const struct tpm_req_get_buffer_size { - struct tpm_req_hdr hdr; - uint32_t capability; - uint32_t len; - uint32_t subcap; - } QEMU_PACKED tpm_get_buffer_size = { - .hdr = { - .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND), - .len = cpu_to_be32(sizeof(tpm_get_buffer_size)), - .ordinal = cpu_to_be32(TPM_ORD_GetCapability), - }, - .capability = cpu_to_be32(TPM_CAP_PROPERTY), - .len = cpu_to_be32(sizeof(uint32_t)), - .subcap = cpu_to_be32(TPM_CAP_PROP_INPUT_BUFFER), - }; - struct tpm_resp_get_buffer_size { - struct tpm_resp_hdr hdr; - uint32_t len; - uint32_t buffersize; - } QEMU_PACKED tpm_resp; - - ret = tpm_util_request(tpm_fd, &tpm_get_buffer_size, - sizeof(tpm_get_buffer_size), - &tpm_resp, sizeof(tpm_resp)); - if (ret < 0) { - return ret; - } - - if (be32_to_cpu(tpm_resp.hdr.len) != sizeof(tpm_resp) || - be32_to_cpu(tpm_resp.len) != sizeof(uint32_t)) { - trace_tpm_util_get_buffer_size_hdr_len( - be32_to_cpu(tpm_resp.hdr.len), - sizeof(tpm_resp)); - trace_tpm_util_get_buffer_size_len(be32_to_cpu(tpm_resp.len), - sizeof(uint32_t)); - error_report("tpm_util: Got unexpected response to " - "TPM_GetCapability; errcode: 0x%x", - be32_to_cpu(tpm_resp.hdr.errcode)); - return -EFAULT; - } - *buffersize = be32_to_cpu(tpm_resp.buffersize); - break; - } - case TPM_VERSION_2_0: { - const struct tpm2_req_get_buffer_size { - struct tpm_req_hdr hdr; - uint32_t capability; - uint32_t property; - uint32_t count; - } QEMU_PACKED tpm2_get_buffer_size = { - .hdr = { - .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), - .len = cpu_to_be32(sizeof(tpm2_get_buffer_size)), - .ordinal = cpu_to_be32(TPM2_CC_GetCapability), - }, - .capability = cpu_to_be32(TPM2_CAP_TPM_PROPERTIES), - .property = cpu_to_be32(TPM2_PT_MAX_COMMAND_SIZE), - .count = cpu_to_be32(2), /* also get TPM2_PT_MAX_RESPONSE_SIZE */ - }; - struct tpm2_resp_get_buffer_size { - struct tpm_resp_hdr hdr; - uint8_t more; - uint32_t capability; - uint32_t count; - uint32_t property1; - uint32_t value1; - uint32_t property2; - uint32_t value2; - } QEMU_PACKED tpm2_resp; - - ret = tpm_util_request(tpm_fd, &tpm2_get_buffer_size, - sizeof(tpm2_get_buffer_size), - &tpm2_resp, sizeof(tpm2_resp)); - if (ret < 0) { - return ret; - } - - if (be32_to_cpu(tpm2_resp.hdr.len) != sizeof(tpm2_resp) || - be32_to_cpu(tpm2_resp.count) != 2) { - trace_tpm_util_get_buffer_size_hdr_len2( - be32_to_cpu(tpm2_resp.hdr.len), - sizeof(tpm2_resp)); - trace_tpm_util_get_buffer_size_len2( - be32_to_cpu(tpm2_resp.count), 2); - error_report("tpm_util: Got unexpected response to " - "TPM2_GetCapability; errcode: 0x%x", - be32_to_cpu(tpm2_resp.hdr.errcode)); - return -EFAULT; - } - *buffersize = MAX(be32_to_cpu(tpm2_resp.value1), - be32_to_cpu(tpm2_resp.value2)); - break; - } - case TPM_VERSION_UNSPEC: - return -EFAULT; - } - - trace_tpm_util_get_buffer_size(*buffersize); - - return 0; -} - -void tpm_sized_buffer_reset(TPMSizedBuffer *tsb) -{ - g_free(tsb->buffer); - tsb->buffer = NULL; - tsb->size = 0; -} - -void tpm_util_show_buffer(const unsigned char *buffer, - size_t buffer_size, const char *string) -{ - size_t len, i; - char *line_buffer, *p; - - len = MIN(tpm_cmd_get_size(buffer), buffer_size); - - /* - * allocate enough room for 3 chars per buffer entry plus a - * newline after every 16 chars and a final null terminator. - */ - line_buffer = g_malloc(len * 3 + (len / 16) + 1); - - for (i = 0, p = line_buffer; i < len; i++) { - if (i && !(i % 16)) { - p += sprintf(p, "\n"); - } - p += sprintf(p, "%.2X ", buffer[i]); - } - trace_tpm_util_show_buffer(string, len, line_buffer); - - g_free(line_buffer); -} diff --git a/hw/tpm/tpm_util.h b/hw/tpm/tpm_util.h deleted file mode 100644 index 7889081fba..0000000000 --- a/hw/tpm/tpm_util.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * TPM utility functions - * - * Copyright (c) 2010 - 2015 IBM Corporation - * Authors: - * Stefan Berger <stefanb@us.ibm.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/> - */ - -#ifndef TPM_TPM_UTIL_H -#define TPM_TPM_UTIL_H - -#include "sysemu/tpm.h" -#include "qemu/bswap.h" - -void tpm_util_write_fatal_error_response(uint8_t *out, uint32_t out_len); - -bool tpm_util_is_selftest(const uint8_t *in, uint32_t in_len); - -int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version); - -static inline uint16_t tpm_cmd_get_tag(const void *b) -{ - return lduw_be_p(b); -} - -static inline void tpm_cmd_set_tag(void *b, uint16_t tag) -{ - stw_be_p(b, tag); -} - -static inline uint32_t tpm_cmd_get_size(const void *b) -{ - return ldl_be_p(b + 2); -} - -static inline void tpm_cmd_set_size(void *b, uint32_t size) -{ - stl_be_p(b + 2, size); -} - -static inline uint32_t tpm_cmd_get_ordinal(const void *b) -{ - return ldl_be_p(b + 6); -} - -static inline uint32_t tpm_cmd_get_errcode(const void *b) -{ - return ldl_be_p(b + 6); -} - -static inline void tpm_cmd_set_error(void *b, uint32_t error) -{ - stl_be_p(b + 6, error); -} - -int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version, - size_t *buffersize); - -#define DEFINE_PROP_TPMBE(_n, _s, _f) \ - DEFINE_PROP(_n, _s, _f, qdev_prop_tpm, TPMBackend *) - -typedef struct TPMSizedBuffer { - uint32_t size; - uint8_t *buffer; -} TPMSizedBuffer; - -void tpm_sized_buffer_reset(TPMSizedBuffer *tsb); - -void tpm_util_show_buffer(const unsigned char *buffer, - size_t buffer_size, const char *string); - -#endif /* TPM_TPM_UTIL_H */ diff --git a/hw/tpm/trace-events b/hw/tpm/trace-events index 439e514787..de9bf1e01b 100644 --- a/hw/tpm/trace-events +++ b/hw/tpm/trace-events @@ -4,38 +4,6 @@ tpm_crb_mmio_read(uint64_t addr, unsigned size, uint32_t val) "CRB read 0x%016" PRIx64 " len:%u val: 0x%" PRIx32 tpm_crb_mmio_write(uint64_t addr, unsigned size, uint32_t val) "CRB write 0x%016" PRIx64 " len:%u val: 0x%" PRIx32 -# tpm_passthrough.c -tpm_passthrough_handle_request(void *cmd) "processing command %p" -tpm_passthrough_reset(void) "reset" - -# tpm_util.c -tpm_util_get_buffer_size_hdr_len(uint32_t len, size_t expected) "tpm_resp->hdr.len = %u, expected = %zu" -tpm_util_get_buffer_size_len(uint32_t len, size_t expected) "tpm_resp->len = %u, expected = %zu" -tpm_util_get_buffer_size_hdr_len2(uint32_t len, size_t expected) "tpm2_resp->hdr.len = %u, expected = %zu" -tpm_util_get_buffer_size_len2(uint32_t len, size_t expected) "tpm2_resp->len = %u, expected = %zu" -tpm_util_get_buffer_size(size_t len) "buffersize of device: %zu" -tpm_util_show_buffer(const char *direction, size_t len, const char *buf) "direction: %s len: %zu\n%s" - -# tpm_emulator.c -tpm_emulator_set_locality(uint8_t locty) "setting locality to %d" -tpm_emulator_handle_request(void) "processing TPM command" -tpm_emulator_probe_caps(uint64_t caps) "capabilities: 0x%"PRIx64 -tpm_emulator_set_buffer_size(uint32_t buffersize, uint32_t minsize, uint32_t maxsize) "buffer size: %u, min: %u, max: %u" -tpm_emulator_startup_tpm_resume(bool is_resume, size_t buffersize) "is_resume: %d, buffer size: %zu" -tpm_emulator_get_tpm_established_flag(uint8_t flag) "got established flag: %d" -tpm_emulator_cancel_cmd_not_supt(void) "Backend does not support CANCEL_TPM_CMD" -tpm_emulator_handle_device_opts_tpm12(void) "TPM Version 1.2" -tpm_emulator_handle_device_opts_tpm2(void) "TPM Version 2" -tpm_emulator_handle_device_opts_unspec(void) "TPM Version Unspecified" -tpm_emulator_handle_device_opts_startup_error(void) "Startup error" -tpm_emulator_get_state_blob(uint8_t type, uint32_t size, uint32_t flags) "got state blob type %d, %u bytes, flags 0x%08x" -tpm_emulator_set_state_blob(uint8_t type, uint32_t size, uint32_t flags) "set state blob type %d, %u bytes, flags 0x%08x" -tpm_emulator_set_state_blobs(void) "setting state blobs" -tpm_emulator_set_state_blobs_error(const char *msg) "error while setting state blobs: %s" -tpm_emulator_set_state_blobs_done(void) "Done setting state blobs" -tpm_emulator_pre_save(void) "" -tpm_emulator_inst_init(void) "" - # tpm_tis.c tpm_tis_raise_irq(uint32_t irqmask) "Raising IRQ for flag 0x%08x" tpm_tis_new_active_locality(uint8_t locty) "Active locality is now %d" @@ -56,7 +24,7 @@ tpm_tis_pre_save(uint8_t locty, uint32_t rw_offset) "locty: %d, rw_offset = %u" # tpm_ppi.c tpm_ppi_memset(uint8_t *ptr, size_t size) "memset: %p %zu" -# hw/tpm/tpm_spapr.c +# tpm_spapr.c tpm_spapr_show_buffer(const char *direction, size_t len, const char *buf) "direction: %s len: %zu\n%s" tpm_spapr_do_crq(uint8_t raw1, uint8_t raw2) "1st 2 bytes in CRQ: 0x%02x 0x%02x" tpm_spapr_do_crq_crq_result(void) "SPAPR_VTPM_INIT_CRQ_RESULT" diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 4d6cd4e58a..31231218dc 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -460,12 +460,14 @@ static MemoryRegion *vhost_user_get_mr_data(uint64_t addr, ram_addr_t *offset, } static void vhost_user_fill_msg_region(VhostUserMemoryRegion *dst, - struct vhost_memory_region *src) + struct vhost_memory_region *src, + uint64_t mmap_offset) { assert(src != NULL && dst != NULL); dst->userspace_addr = src->userspace_addr; dst->memory_size = src->memory_size; dst->guest_phys_addr = src->guest_phys_addr; + dst->mmap_offset = mmap_offset; } static int vhost_user_fill_set_mem_table_msg(struct vhost_user *u, @@ -500,9 +502,8 @@ static int vhost_user_fill_set_mem_table_msg(struct vhost_user *u, error_report("Failed preparing vhost-user memory table msg"); return -1; } - vhost_user_fill_msg_region(®ion_buffer, reg); + vhost_user_fill_msg_region(®ion_buffer, reg, offset); msg->payload.memory.regions[*fd_num] = region_buffer; - msg->payload.memory.regions[*fd_num].mmap_offset = offset; fds[(*fd_num)++] = fd; } else if (track_ramblocks) { u->region_rb_offset[i] = 0; @@ -649,7 +650,7 @@ static int send_remove_regions(struct vhost_dev *dev, if (fd > 0) { msg->hdr.request = VHOST_USER_REM_MEM_REG; - vhost_user_fill_msg_region(®ion_buffer, shadow_reg); + vhost_user_fill_msg_region(®ion_buffer, shadow_reg, 0); msg->payload.mem_reg.region = region_buffer; if (vhost_user_write(dev, msg, &fd, 1) < 0) { @@ -709,9 +710,8 @@ static int send_add_regions(struct vhost_dev *dev, u->region_rb[reg_idx] = mr->ram_block; } msg->hdr.request = VHOST_USER_ADD_MEM_REG; - vhost_user_fill_msg_region(®ion_buffer, reg); + vhost_user_fill_msg_region(®ion_buffer, reg, offset); msg->payload.mem_reg.region = region_buffer; - msg->payload.mem_reg.region.mmap_offset = offset; if (vhost_user_write(dev, msg, &fd, 1) < 0) { return -1; diff --git a/hw/watchdog/cmsdk-apb-watchdog.c b/hw/watchdog/cmsdk-apb-watchdog.c index 1541365914..5bbadadfa6 100644 --- a/hw/watchdog/cmsdk-apb-watchdog.c +++ b/hw/watchdog/cmsdk-apb-watchdog.c @@ -225,6 +225,7 @@ static void cmsdk_apb_watchdog_write(void *opaque, hwaddr offset, break; case A_WDOGLOCK: s->lock = (value != WDOG_UNLOCK_VALUE); + trace_cmsdk_apb_watchdog_lock(s->lock); break; case A_WDOGITCR: if (s->is_luminary) { diff --git a/hw/watchdog/trace-events b/hw/watchdog/trace-events index ab94d7df50..3124ca1f1b 100644 --- a/hw/watchdog/trace-events +++ b/hw/watchdog/trace-events @@ -4,3 +4,4 @@ cmsdk_apb_watchdog_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB watchdog read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" cmsdk_apb_watchdog_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB watchdog write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" cmsdk_apb_watchdog_reset(void) "CMSDK APB watchdog: reset" +cmsdk_apb_watchdog_lock(uint32_t lock) "CMSDK APB watchdog: lock %" PRIu32 diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c index 5d0834c1d9..10de15855a 100644 --- a/hw/xtensa/xtfpga.c +++ b/hw/xtensa/xtfpga.c @@ -173,8 +173,7 @@ static PFlashCFI01 *xtfpga_flash_init(MemoryRegion *address_space, SysBusDevice *s; DeviceState *dev = qdev_new(TYPE_PFLASH_CFI01); - qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(dinfo), - &error_abort); + qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(dinfo)); qdev_prop_set_uint32(dev, "num-blocks", board->flash->size / board->flash->sector_size); qdev_prop_set_uint64(dev, "sector-length", board->flash->sector_size); |