diff options
Diffstat (limited to 'hw')
44 files changed, 3474 insertions, 867 deletions
diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 40c13838fb..a92c2f1c36 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -20,6 +20,8 @@ #include "qemu/log.h" #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" +#include "hw/loader.h" +#include "qemu/error-report.h" static struct arm_boot_info aspeed_board_binfo = { .board_id = -1, /* device-tree-only board */ @@ -104,6 +106,28 @@ static const AspeedBoardConfig aspeed_boards[] = { }, }; +#define FIRMWARE_ADDR 0x0 + +static void write_boot_rom(DriveInfo *dinfo, hwaddr addr, size_t rom_size, + Error **errp) +{ + BlockBackend *blk = blk_by_legacy_dinfo(dinfo); + uint8_t *storage; + + if (rom_size > blk_getlength(blk)) { + rom_size = blk_getlength(blk); + } + + storage = g_new0(uint8_t, rom_size); + if (blk_pread(blk, 0, storage, rom_size) < 0) { + error_setg(errp, "failed to read the initial flash content"); + return; + } + + rom_add_blob_fixed("aspeed.boot_rom", storage, rom_size, addr); + g_free(storage); +} + static void aspeed_board_init_flashes(AspeedSMCState *s, const char *flashtype, Error **errp) { @@ -135,6 +159,7 @@ static void aspeed_board_init(MachineState *machine, { AspeedBoardState *bmc; AspeedSoCClass *sc; + DriveInfo *drive0 = drive_get(IF_MTD, 0, 0); bmc = g_new0(AspeedBoardState, 1); object_initialize(&bmc->soc, (sizeof(bmc->soc)), cfg->soc_name); @@ -168,6 +193,22 @@ static void aspeed_board_init(MachineState *machine, aspeed_board_init_flashes(&bmc->soc.fmc, cfg->fmc_model, &error_abort); aspeed_board_init_flashes(&bmc->soc.spi[0], cfg->spi_model, &error_abort); + /* Install first FMC flash content as a boot rom. */ + if (drive0) { + AspeedSMCFlash *fl = &bmc->soc.fmc.flashes[0]; + MemoryRegion *boot_rom = g_new(MemoryRegion, 1); + + /* + * create a ROM region using the default mapping window size of + * the flash module. + */ + memory_region_init_rom(boot_rom, OBJECT(bmc), "aspeed.boot_rom", + fl->size, &error_abort); + memory_region_add_subregion(get_system_memory(), FIRMWARE_ADDR, + boot_rom); + write_boot_rom(drive0, FIRMWARE_ADDR, fl->size, &error_abort); + } + aspeed_board_binfo.kernel_filename = machine->kernel_filename; aspeed_board_binfo.initrd_filename = machine->initrd_filename; aspeed_board_binfo.kernel_cmdline = machine->kernel_cmdline; diff --git a/hw/arm/imx25_pdk.c b/hw/arm/imx25_pdk.c index 025b60843e..44e741fde3 100644 --- a/hw/arm/imx25_pdk.c +++ b/hw/arm/imx25_pdk.c @@ -139,7 +139,7 @@ static void imx25_pdk_init(MachineState *machine) * of simple qtest. See "make check" for details. */ i2c_create_slave((I2CBus *)qdev_get_child_bus(DEVICE(&s->soc.i2c[0]), - "i2c"), + "i2c-bus.0"), "ds1338", 0x68); } } diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 085a611173..07a10aca40 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -310,6 +310,13 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, Aml *dev_rp0 = aml_device("%s", "RP0"); aml_append(dev_rp0, aml_name_decl("_ADR", aml_int(0))); aml_append(dev, dev_rp0); + + Aml *dev_res0 = aml_device("%s", "RES0"); + aml_append(dev_res0, aml_name_decl("_HID", aml_string("PNP0C02"))); + crs = aml_resource_template(); + aml_append(crs, aml_memory32_fixed(base_ecam, size_ecam, AML_READ_WRITE)); + aml_append(dev_res0, aml_name_decl("_CRS", crs)); + aml_append(dev, dev_res0); aml_append(scope, dev); } @@ -607,6 +614,9 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) if (arm_feature(&armcpu->env, ARM_FEATURE_PMU)) { gicc->performance_interrupt = cpu_to_le32(PPI(VIRTUAL_PMU_IRQ)); } + if (vms->virt && vms->gic_version == 3) { + gicc->vgic_interrupt = cpu_to_le32(PPI(ARCH_GICV3_MAINT_IRQ)); + } } if (vms->gic_version == 3) { @@ -643,16 +653,30 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) } /* FADT */ -static void -build_fadt(GArray *table_data, BIOSLinker *linker, unsigned dsdt_tbl_offset) +static void build_fadt(GArray *table_data, BIOSLinker *linker, + VirtMachineState *vms, unsigned dsdt_tbl_offset) { AcpiFadtDescriptorRev5_1 *fadt = acpi_data_push(table_data, sizeof(*fadt)); unsigned dsdt_entry_offset = (char *)&fadt->dsdt - table_data->data; + uint16_t bootflags; + + switch (vms->psci_conduit) { + case QEMU_PSCI_CONDUIT_DISABLED: + bootflags = 0; + break; + case QEMU_PSCI_CONDUIT_HVC: + bootflags = ACPI_FADT_ARM_PSCI_COMPLIANT | ACPI_FADT_ARM_PSCI_USE_HVC; + break; + case QEMU_PSCI_CONDUIT_SMC: + bootflags = ACPI_FADT_ARM_PSCI_COMPLIANT; + break; + default: + g_assert_not_reached(); + } - /* Hardware Reduced = 1 and use PSCI 0.2+ and with HVC */ + /* Hardware Reduced = 1 and use PSCI 0.2+ */ fadt->flags = cpu_to_le32(1 << ACPI_FADT_F_HW_REDUCED_ACPI); - fadt->arm_boot_flags = cpu_to_le16(ACPI_FADT_ARM_PSCI_COMPLIANT | - ACPI_FADT_ARM_PSCI_USE_HVC); + fadt->arm_boot_flags = cpu_to_le16(bootflags); /* ACPI v5.1 (fadt->revision.fadt->minor_revision) */ fadt->minor_revision = 0x1; @@ -738,7 +762,7 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables) /* FADT MADT GTDT MCFG SPCR pointed to by RSDT */ acpi_add_table(table_offsets, tables_blob); - build_fadt(tables_blob, tables->linker, dsdt); + build_fadt(tables_blob, tables->linker, vms, dsdt); acpi_add_table(table_offsets, tables_blob); build_madt(tables_blob, tables->linker, vms); @@ -818,7 +842,7 @@ static MemoryRegion *acpi_add_rom_blob(AcpiBuildState *build_state, uint64_t max_size) { return rom_add_blob(name, blob->data, acpi_data_len(blob), max_size, -1, - name, virt_acpi_build_update, build_state, NULL); + name, virt_acpi_build_update, build_state, NULL, true); } static const VMStateDescription vmstate_virt_acpi_build = { diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 7a03f84051..6c9e8985bf 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -167,7 +167,6 @@ static const char *valid_cpus[] = { "cortex-a53", "cortex-a57", "host", - NULL }; static bool cpuname_valid(const char *cpu) @@ -230,9 +229,19 @@ static void fdt_add_psci_node(const VirtMachineState *vms) uint32_t migrate_fn; void *fdt = vms->fdt; ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(0)); + const char *psci_method; - if (!vms->using_psci) { + switch (vms->psci_conduit) { + case QEMU_PSCI_CONDUIT_DISABLED: return; + case QEMU_PSCI_CONDUIT_HVC: + psci_method = "hvc"; + break; + case QEMU_PSCI_CONDUIT_SMC: + psci_method = "smc"; + break; + default: + g_assert_not_reached(); } qemu_fdt_add_subnode(fdt, "/psci"); @@ -264,7 +273,7 @@ static void fdt_add_psci_node(const VirtMachineState *vms) * However, the device tree binding uses 'method' instead, so that is * what we should use here. */ - qemu_fdt_setprop_string(fdt, "/psci", "method", "hvc"); + qemu_fdt_setprop_string(fdt, "/psci", "method", psci_method); qemu_fdt_setprop_cell(fdt, "/psci", "cpu_suspend", cpu_suspend_fn); qemu_fdt_setprop_cell(fdt, "/psci", "cpu_off", cpu_off_fn); @@ -366,7 +375,8 @@ static void fdt_add_cpu_nodes(const VirtMachineState *vms) qemu_fdt_setprop_string(vms->fdt, nodename, "compatible", armcpu->dtb_compatible); - if (vms->using_psci && vms->smp_cpus > 1) { + if (vms->psci_conduit != QEMU_PSCI_CONDUIT_DISABLED + && vms->smp_cpus > 1) { qemu_fdt_setprop_string(vms->fdt, nodename, "enable-method", "psci"); } @@ -433,6 +443,11 @@ static void fdt_add_gic_node(VirtMachineState *vms) 2, vms->memmap[VIRT_GIC_DIST].size, 2, vms->memmap[VIRT_GIC_REDIST].base, 2, vms->memmap[VIRT_GIC_REDIST].size); + if (vms->virt) { + qemu_fdt_setprop_cells(vms->fdt, "/intc", "interrupts", + GIC_FDT_IRQ_TYPE_PPI, ARCH_GICV3_MAINT_IRQ, + GIC_FDT_IRQ_FLAGS_LEVEL_HI); + } } else { /* 'cortex-a15-gic' means 'GIC v2' */ qemu_fdt_setprop_string(vms->fdt, "/intc", "compatible", @@ -547,9 +562,9 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic) sysbus_mmio_map(gicbusdev, 1, vms->memmap[VIRT_GIC_CPU].base); } - /* Wire the outputs from each CPU's generic timer to the - * appropriate GIC PPI inputs, and the GIC's IRQ output to - * the CPU's IRQ input. + /* Wire the outputs from each CPU's generic timer and the GICv3 + * maintenance interrupt signal to the appropriate GIC PPI inputs, + * and the GIC's IRQ/FIQ/VIRQ/VFIQ interrupt outputs to the CPU's inputs. */ for (i = 0; i < smp_cpus; i++) { DeviceState *cpudev = DEVICE(qemu_get_cpu(i)); @@ -571,9 +586,17 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic) ppibase + timer_irq[irq])); } + qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt", 0, + qdev_get_gpio_in(gicdev, ppibase + + ARCH_GICV3_MAINT_IRQ)); + sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); sysbus_connect_irq(gicbusdev, i + smp_cpus, qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); + sysbus_connect_irq(gicbusdev, i + 2 * smp_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ)); + sysbus_connect_irq(gicbusdev, i + 3 * smp_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); } for (i = 0; i < NUM_IRQS; i++) { @@ -1221,9 +1244,18 @@ static void machvirt_init(MachineState *machine) * so it doesn't get in the way. Instead of starting secondary * CPUs in PSCI powerdown state we will start them all running and * let the boot ROM sort them out. - * The usual case is that we do use QEMU's PSCI implementation. + * The usual case is that we do use QEMU's PSCI implementation; + * if the guest has EL2 then we will use SMC as the conduit, + * and otherwise we will use HVC (for backwards compatibility and + * because if we're using KVM then we must use HVC). */ - vms->using_psci = !(vms->secure && firmware_loaded); + if (vms->secure && firmware_loaded) { + vms->psci_conduit = QEMU_PSCI_CONDUIT_DISABLED; + } else if (vms->virt) { + vms->psci_conduit = QEMU_PSCI_CONDUIT_SMC; + } else { + vms->psci_conduit = QEMU_PSCI_CONDUIT_HVC; + } /* The maximum number of CPUs depends on the GIC version, or on how * many redistributors we can fit into the memory map. @@ -1250,6 +1282,12 @@ static void machvirt_init(MachineState *machine) exit(1); } + if (vms->virt && kvm_enabled()) { + error_report("mach-virt: KVM does not support providing " + "Virtualization extensions to the guest CPU"); + exit(1); + } + if (vms->secure) { if (kvm_enabled()) { error_report("mach-virt: KVM does not support Security extensions"); @@ -1306,8 +1344,12 @@ static void machvirt_init(MachineState *machine) object_property_set_bool(cpuobj, false, "has_el3", NULL); } - if (vms->using_psci) { - object_property_set_int(cpuobj, QEMU_PSCI_CONDUIT_HVC, + if (!vms->virt && object_property_find(cpuobj, "has_el2", NULL)) { + object_property_set_bool(cpuobj, false, "has_el2", NULL); + } + + if (vms->psci_conduit != QEMU_PSCI_CONDUIT_DISABLED) { + object_property_set_int(cpuobj, vms->psci_conduit, "psci-conduit", NULL); /* Secondary CPUs start in PSCI powered-down state */ @@ -1408,6 +1450,20 @@ static void virt_set_secure(Object *obj, bool value, Error **errp) vms->secure = value; } +static bool virt_get_virt(Object *obj, Error **errp) +{ + VirtMachineState *vms = VIRT_MACHINE(obj); + + return vms->virt; +} + +static void virt_set_virt(Object *obj, bool value, Error **errp) +{ + VirtMachineState *vms = VIRT_MACHINE(obj); + + vms->virt = value; +} + static bool virt_get_highmem(Object *obj, Error **errp) { VirtMachineState *vms = VIRT_MACHINE(obj); @@ -1495,6 +1551,16 @@ static void virt_2_9_instance_init(Object *obj) "Security Extensions (TrustZone)", NULL); + /* EL2 is also disabled by default, for similar reasons */ + vms->virt = false; + object_property_add_bool(obj, "virtualization", virt_get_virt, + virt_set_virt, NULL); + object_property_set_description(obj, "virtualization", + "Set on/off to enable/disable emulating a " + "guest CPU which implements the ARM " + "Virtualization Extensions", + NULL); + /* High memory is enabled by default */ vms->highmem = true; object_property_add_bool(obj, "highmem", virt_get_highmem, diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index 0d86ba35ae..bc4e66b862 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -258,6 +258,8 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) object_property_set_bool(OBJECT(&s->apu_cpu[i]), s->secure, "has_el3", NULL); + object_property_set_bool(OBJECT(&s->apu_cpu[i]), + false, "has_el2", NULL); object_property_set_int(OBJECT(&s->apu_cpu[i]), GIC_BASE_ADDR, "reset-cbar", &error_abort); object_property_set_bool(OBJECT(&s->apu_cpu[i]), true, "realized", diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index 4c5f8c3590..e90451496e 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -74,6 +74,12 @@ typedef struct FlashPartInfo { uint32_t n_sectors; uint32_t page_size; uint16_t flags; + /* + * Big sized spi nor are often stacked devices, thus sometime + * replace chip erase with die erase. + * This field inform how many die is in the chip. + */ + uint8_t die_cnt; } FlashPartInfo; /* adapted from linux */ @@ -91,7 +97,8 @@ typedef struct FlashPartInfo { .sector_size = (_sector_size),\ .n_sectors = (_n_sectors),\ .page_size = 256,\ - .flags = (_flags), + .flags = (_flags),\ + .die_cnt = 0 #define INFO6(_part_name, _jedec_id, _ext_id, _sector_size, _n_sectors, _flags)\ .part_name = _part_name,\ @@ -108,6 +115,24 @@ typedef struct FlashPartInfo { .n_sectors = (_n_sectors),\ .page_size = 256,\ .flags = (_flags),\ + .die_cnt = 0 + +#define INFO_STACKED(_part_name, _jedec_id, _ext_id, _sector_size, _n_sectors,\ + _flags, _die_cnt)\ + .part_name = _part_name,\ + .id = {\ + ((_jedec_id) >> 16) & 0xff,\ + ((_jedec_id) >> 8) & 0xff,\ + (_jedec_id) & 0xff,\ + ((_ext_id) >> 8) & 0xff,\ + (_ext_id) & 0xff,\ + },\ + .id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))),\ + .sector_size = (_sector_size),\ + .n_sectors = (_n_sectors),\ + .page_size = 256,\ + .flags = (_flags),\ + .die_cnt = _die_cnt #define JEDEC_NUMONYX 0x20 #define JEDEC_WINBOND 0xEF @@ -218,8 +243,10 @@ static const FlashPartInfo known_devices[] = { { INFO("n25q128", 0x20ba18, 0, 64 << 10, 256, 0) }, { INFO("n25q256a", 0x20ba19, 0, 64 << 10, 512, ER_4K) }, { INFO("n25q512a", 0x20ba20, 0, 64 << 10, 1024, ER_4K) }, - { INFO("mt25ql01g", 0x20ba21, 0, 64 << 10, 2048, ER_4K) }, - { INFO("mt25qu01g", 0x20bb21, 0, 64 << 10, 2048, ER_4K) }, + { INFO_STACKED("n25q00", 0x20ba21, 0x1000, 64 << 10, 2048, ER_4K, 4) }, + { INFO_STACKED("n25q00a", 0x20bb21, 0x1000, 64 << 10, 2048, ER_4K, 4) }, + { INFO_STACKED("mt25ql01g", 0x20ba21, 0x1040, 64 << 10, 2048, ER_4K, 2) }, + { INFO_STACKED("mt25qu01g", 0x20bb21, 0x1040, 64 << 10, 2048, ER_4K, 2) }, /* Spansion -- single (large) sector size only, at least * for the chips listed here (without boot sectors). @@ -327,6 +354,7 @@ typedef enum { PP4_4 = 0x3e, DPP = 0xa2, QPP = 0x32, + QPP_4 = 0x34, ERASE_4K = 0x20, ERASE4_4K = 0x21, @@ -359,6 +387,8 @@ typedef enum { REVCR = 0x65, WEVCR = 0x61, + + DIE_ERASE = 0xC4, } FlashCMD; typedef enum { @@ -516,6 +546,16 @@ static void flash_erase(Flash *s, int offset, FlashCMD cmd) case BULK_ERASE: len = s->size; break; + case DIE_ERASE: + if (s->pi->die_cnt) { + len = s->size / s->pi->die_cnt; + offset = offset & (~(len - 1)); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "M25P80: die erase is not supported" + " by device\n"); + return; + } + break; default: abort(); } @@ -577,6 +617,7 @@ static inline int get_addr_length(Flash *s) switch (s->cmd_in_progress) { case PP4: case PP4_4: + case QPP_4: case READ4: case QIOR4: case ERASE4_4K: @@ -610,6 +651,7 @@ static void complete_collecting_data(Flash *s) switch (s->cmd_in_progress) { case DPP: case QPP: + case QPP_4: case PP: case PP4: case PP4_4: @@ -635,6 +677,7 @@ static void complete_collecting_data(Flash *s) case ERASE4_32K: case ERASE_SECTOR: case ERASE4_SECTOR: + case DIE_ERASE: flash_erase(s, s->cur_addr, s->cmd_in_progress); break; case WRSR: @@ -877,9 +920,11 @@ static void decode_new_cmd(Flash *s, uint32_t value) case READ4: case DPP: case QPP: + case QPP_4: case PP: case PP4: case PP4_4: + case DIE_ERASE: s->needed_bytes = get_addr_length(s); s->pos = 0; s->len = 0; diff --git a/hw/core/loader.c b/hw/core/loader.c index 45742494e6..ee5abd6eb7 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -853,7 +853,7 @@ static void fw_cfg_resized(const char *id, uint64_t length, void *host) } } -static void *rom_set_mr(Rom *rom, Object *owner, const char *name) +static void *rom_set_mr(Rom *rom, Object *owner, const char *name, bool ro) { void *data; @@ -862,7 +862,7 @@ static void *rom_set_mr(Rom *rom, Object *owner, const char *name) rom->datasize, rom->romsize, fw_cfg_resized, &error_fatal); - memory_region_set_readonly(rom->mr, true); + memory_region_set_readonly(rom->mr, ro); vmstate_register_ram_global(rom->mr); data = memory_region_get_ram_ptr(rom->mr); @@ -942,7 +942,7 @@ int rom_add_file(const char *file, const char *fw_dir, snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name); if ((!option_rom || mc->option_rom_has_mr) && mc->rom_file_has_mr) { - data = rom_set_mr(rom, OBJECT(fw_cfg), devpath); + data = rom_set_mr(rom, OBJECT(fw_cfg), devpath, true); } else { data = rom->data; } @@ -979,7 +979,7 @@ err: MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len, size_t max_len, hwaddr addr, const char *fw_file_name, FWCfgReadCallback fw_callback, void *callback_opaque, - AddressSpace *as) + AddressSpace *as, bool read_only) { MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); Rom *rom; @@ -998,10 +998,14 @@ MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len, char devpath[100]; void *data; - snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name); + if (read_only) { + snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name); + } else { + snprintf(devpath, sizeof(devpath), "/ram@%s", fw_file_name); + } if (mc->rom_file_has_mr) { - data = rom_set_mr(rom, OBJECT(fw_cfg), devpath); + data = rom_set_mr(rom, OBJECT(fw_cfg), devpath, read_only); mr = rom->mr; } else { data = rom->data; @@ -1009,7 +1013,7 @@ MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len, fw_cfg_add_file_callback(fw_cfg, fw_file_name, fw_callback, callback_opaque, - data, rom->datasize); + data, rom->datasize, read_only); } return mr; } diff --git a/hw/i2c/imx_i2c.c b/hw/i2c/imx_i2c.c index 37e5a62ce7..6c81b98ebd 100644 --- a/hw/i2c/imx_i2c.c +++ b/hw/i2c/imx_i2c.c @@ -310,7 +310,7 @@ static void imx_i2c_realize(DeviceState *dev, Error **errp) IMX_I2C_MEM_SIZE); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); - s->bus = i2c_init_bus(DEVICE(dev), "i2c"); + s->bus = i2c_init_bus(DEVICE(dev), NULL); } static void imx_i2c_class_init(ObjectClass *klass, void *data) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 0c8912fd86..a1d781ae42 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -2806,7 +2806,7 @@ static MemoryRegion *acpi_add_rom_blob(AcpiBuildState *build_state, uint64_t max_size) { return rom_add_blob(name, blob->data, acpi_data_len(blob), max_size, -1, - name, acpi_build_update, build_state, NULL); + name, acpi_build_update, build_state, NULL, true); } static const VMStateDescription vmstate_acpi_build = { @@ -2872,7 +2872,7 @@ void acpi_setup(void) build_state->rsdp = g_memdup(tables.rsdp->data, rsdp_size); fw_cfg_add_file_callback(pcms->fw_cfg, ACPI_BUILD_RSDP_FILE, acpi_build_update, build_state, - build_state->rsdp, rsdp_size); + build_state->rsdp, rsdp_size, true); build_state->rsdp_mr = NULL; } else { build_state->rsdp = NULL; diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 5e1adbe53c..9f102aa388 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -437,13 +437,24 @@ static void pc_i440fx_machine_options(MachineClass *m) m->default_display = "std"; } -static void pc_i440fx_2_8_machine_options(MachineClass *m) +static void pc_i440fx_2_9_machine_options(MachineClass *m) { pc_i440fx_machine_options(m); m->alias = "pc"; m->is_default = 1; } +DEFINE_I440FX_MACHINE(v2_9, "pc-i440fx-2.9", NULL, + pc_i440fx_2_9_machine_options); + +static void pc_i440fx_2_8_machine_options(MachineClass *m) +{ + pc_i440fx_2_9_machine_options(m); + m->is_default = 0; + m->alias = NULL; + SET_MACHINE_COMPAT(m, PC_COMPAT_2_8); +} + DEFINE_I440FX_MACHINE(v2_8, "pc-i440fx-2.8", NULL, pc_i440fx_2_8_machine_options); @@ -451,8 +462,6 @@ DEFINE_I440FX_MACHINE(v2_8, "pc-i440fx-2.8", NULL, static void pc_i440fx_2_7_machine_options(MachineClass *m) { pc_i440fx_2_8_machine_options(m); - m->is_default = 0; - m->alias = NULL; SET_MACHINE_COMPAT(m, PC_COMPAT_2_7); } diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index d042fe0843..dd792a8547 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -301,19 +301,28 @@ static void pc_q35_machine_options(MachineClass *m) m->max_cpus = 288; } -static void pc_q35_2_8_machine_options(MachineClass *m) +static void pc_q35_2_9_machine_options(MachineClass *m) { pc_q35_machine_options(m); m->alias = "q35"; } +DEFINE_Q35_MACHINE(v2_9, "pc-q35-2.9", NULL, + pc_q35_2_9_machine_options); + +static void pc_q35_2_8_machine_options(MachineClass *m) +{ + pc_q35_2_9_machine_options(m); + m->alias = NULL; + SET_MACHINE_COMPAT(m, PC_COMPAT_2_8); +} + DEFINE_Q35_MACHINE(v2_8, "pc-q35-2.8", NULL, pc_q35_2_8_machine_options); static void pc_q35_2_7_machine_options(MachineClass *m) { pc_q35_2_8_machine_options(m); - m->alias = NULL; m->max_cpus = 255; SET_MACHINE_COMPAT(m, PC_COMPAT_2_7); } diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c index 0a1f56af19..4a8df44fb1 100644 --- a/hw/intc/arm_gic_common.c +++ b/hw/intc/arm_gic_common.c @@ -110,6 +110,12 @@ void gic_init_irqs_and_mmio(GICState *s, qemu_irq_handler handler, for (i = 0; i < s->num_cpu; i++) { sysbus_init_irq(sbd, &s->parent_fiq[i]); } + for (i = 0; i < s->num_cpu; i++) { + sysbus_init_irq(sbd, &s->parent_virq[i]); + } + for (i = 0; i < s->num_cpu; i++) { + sysbus_init_irq(sbd, &s->parent_vfiq[i]); + } /* Distributor */ memory_region_init_io(&s->iomem, OBJECT(s), ops, s, "gic_dist", 0x1000); diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index 0aa9b9ca66..16b9b0f7eb 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -49,6 +49,27 @@ static int gicv3_post_load(void *opaque, int version_id) return 0; } +static bool virt_state_needed(void *opaque) +{ + GICv3CPUState *cs = opaque; + + return cs->num_list_regs != 0; +} + +static const VMStateDescription vmstate_gicv3_cpu_virt = { + .name = "arm_gicv3_cpu/virt", + .version_id = 1, + .minimum_version_id = 1, + .needed = virt_state_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT64_2DARRAY(ich_apr, GICv3CPUState, 3, 4), + VMSTATE_UINT64(ich_hcr_el2, GICv3CPUState), + VMSTATE_UINT64_ARRAY(ich_lr_el2, GICv3CPUState, GICV3_LR_MAX), + VMSTATE_UINT64(ich_vmcr_el2, GICv3CPUState), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_gicv3_cpu = { .name = "arm_gicv3_cpu", .version_id = 1, @@ -75,6 +96,10 @@ static const VMStateDescription vmstate_gicv3_cpu = { VMSTATE_UINT64_ARRAY(icc_igrpen, GICv3CPUState, 3), VMSTATE_UINT64(icc_ctlr_el3, GICv3CPUState), VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * []) { + &vmstate_gicv3_cpu_virt, + NULL } }; @@ -126,6 +151,12 @@ void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler, for (i = 0; i < s->num_cpu; i++) { sysbus_init_irq(sbd, &s->cpu[i].parent_fiq); } + for (i = 0; i < s->num_cpu; i++) { + sysbus_init_irq(sbd, &s->cpu[i].parent_virq); + } + for (i = 0; i < s->num_cpu; i++) { + sysbus_init_irq(sbd, &s->cpu[i].parent_vfiq); + } memory_region_init_io(&s->iomem_dist, OBJECT(s), ops, s, "gicv3_dist", 0x10000); diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index 35e8eb30fc..a9ee7fddf9 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -13,6 +13,7 @@ */ #include "qemu/osdep.h" +#include "qemu/bitops.h" #include "trace.h" #include "gicv3_internal.h" #include "cpu.h" @@ -36,6 +37,610 @@ static bool gicv3_use_ns_bank(CPUARMState *env) return !arm_is_secure_below_el3(env); } +/* The minimum BPR for the virtual interface is a configurable property */ +static inline int icv_min_vbpr(GICv3CPUState *cs) +{ + return 7 - cs->vprebits; +} + +/* Simple accessor functions for LR fields */ +static uint32_t ich_lr_vintid(uint64_t lr) +{ + return extract64(lr, ICH_LR_EL2_VINTID_SHIFT, ICH_LR_EL2_VINTID_LENGTH); +} + +static uint32_t ich_lr_pintid(uint64_t lr) +{ + return extract64(lr, ICH_LR_EL2_PINTID_SHIFT, ICH_LR_EL2_PINTID_LENGTH); +} + +static uint32_t ich_lr_prio(uint64_t lr) +{ + return extract64(lr, ICH_LR_EL2_PRIORITY_SHIFT, ICH_LR_EL2_PRIORITY_LENGTH); +} + +static int ich_lr_state(uint64_t lr) +{ + return extract64(lr, ICH_LR_EL2_STATE_SHIFT, ICH_LR_EL2_STATE_LENGTH); +} + +static bool icv_access(CPUARMState *env, int hcr_flags) +{ + /* Return true if this ICC_ register access should really be + * directed to an ICV_ access. hcr_flags is a mask of + * HCR_EL2 bits to check: we treat this as an ICV_ access + * if we are in NS EL1 and at least one of the specified + * HCR_EL2 bits is set. + * + * ICV registers fall into four categories: + * * access if NS EL1 and HCR_EL2.FMO == 1: + * all ICV regs with '0' in their name + * * access if NS EL1 and HCR_EL2.IMO == 1: + * all ICV regs with '1' in their name + * * access if NS EL1 and either IMO or FMO == 1: + * CTLR, DIR, PMR, RPR + */ + return (env->cp15.hcr_el2 & hcr_flags) && arm_current_el(env) == 1 + && !arm_is_secure_below_el3(env); +} + +static int read_vbpr(GICv3CPUState *cs, int grp) +{ + /* Read VBPR value out of the VMCR field (caller must handle + * VCBPR effects if required) + */ + if (grp == GICV3_G0) { + return extract64(cs->ich_vmcr_el2, ICH_VMCR_EL2_VBPR0_SHIFT, + ICH_VMCR_EL2_VBPR0_LENGTH); + } else { + return extract64(cs->ich_vmcr_el2, ICH_VMCR_EL2_VBPR1_SHIFT, + ICH_VMCR_EL2_VBPR1_LENGTH); + } +} + +static void write_vbpr(GICv3CPUState *cs, int grp, int value) +{ + /* Write new VBPR1 value, handling the "writing a value less than + * the minimum sets it to the minimum" semantics. + */ + int min = icv_min_vbpr(cs); + + if (grp != GICV3_G0) { + min++; + } + + value = MAX(value, min); + + if (grp == GICV3_G0) { + cs->ich_vmcr_el2 = deposit64(cs->ich_vmcr_el2, ICH_VMCR_EL2_VBPR0_SHIFT, + ICH_VMCR_EL2_VBPR0_LENGTH, value); + } else { + cs->ich_vmcr_el2 = deposit64(cs->ich_vmcr_el2, ICH_VMCR_EL2_VBPR1_SHIFT, + ICH_VMCR_EL2_VBPR1_LENGTH, value); + } +} + +static uint32_t icv_fullprio_mask(GICv3CPUState *cs) +{ + /* Return a mask word which clears the unimplemented priority bits + * from a priority value for a virtual interrupt. (Not to be confused + * with the group priority, whose mask depends on the value of VBPR + * for the interrupt group.) + */ + return ~0U << (8 - cs->vpribits); +} + +static int ich_highest_active_virt_prio(GICv3CPUState *cs) +{ + /* Calculate the current running priority based on the set bits + * in the ICH Active Priority Registers. + */ + int i; + int aprmax = 1 << (cs->vprebits - 5); + + assert(aprmax <= ARRAY_SIZE(cs->ich_apr[0])); + + for (i = 0; i < aprmax; i++) { + uint32_t apr = cs->ich_apr[GICV3_G0][i] | + cs->ich_apr[GICV3_G1NS][i]; + + if (!apr) { + continue; + } + return (i * 32 + ctz32(apr)) << (icv_min_vbpr(cs) + 1); + } + /* No current active interrupts: return idle priority */ + return 0xff; +} + +static int hppvi_index(GICv3CPUState *cs) +{ + /* Return the list register index of the highest priority pending + * virtual interrupt, as per the HighestPriorityVirtualInterrupt + * pseudocode. If no pending virtual interrupts, return -1. + */ + int idx = -1; + int i; + /* Note that a list register entry with a priority of 0xff will + * never be reported by this function; this is the architecturally + * correct behaviour. + */ + int prio = 0xff; + + if (!(cs->ich_vmcr_el2 & (ICH_VMCR_EL2_VENG0 | ICH_VMCR_EL2_VENG1))) { + /* Both groups disabled, definitely nothing to do */ + return idx; + } + + for (i = 0; i < cs->num_list_regs; i++) { + uint64_t lr = cs->ich_lr_el2[i]; + int thisprio; + + if (ich_lr_state(lr) != ICH_LR_EL2_STATE_PENDING) { + /* Not Pending */ + continue; + } + + /* Ignore interrupts if relevant group enable not set */ + if (lr & ICH_LR_EL2_GROUP) { + if (!(cs->ich_vmcr_el2 & ICH_VMCR_EL2_VENG1)) { + continue; + } + } else { + if (!(cs->ich_vmcr_el2 & ICH_VMCR_EL2_VENG0)) { + continue; + } + } + + thisprio = ich_lr_prio(lr); + + if (thisprio < prio) { + prio = thisprio; + idx = i; + } + } + + return idx; +} + +static uint32_t icv_gprio_mask(GICv3CPUState *cs, int group) +{ + /* Return a mask word which clears the subpriority bits from + * a priority value for a virtual interrupt in the specified group. + * This depends on the VBPR value: + * a BPR of 0 means the group priority bits are [7:1]; + * a BPR of 1 means they are [7:2], and so on down to + * a BPR of 7 meaning no group priority bits at all. + * Which BPR to use depends on the group of the interrupt and + * the current ICH_VMCR_EL2.VCBPR settings. + */ + if (group == GICV3_G1NS && cs->ich_vmcr_el2 & ICH_VMCR_EL2_VCBPR) { + group = GICV3_G0; + } + + return ~0U << (read_vbpr(cs, group) + 1); +} + +static bool icv_hppi_can_preempt(GICv3CPUState *cs, uint64_t lr) +{ + /* Return true if we can signal this virtual interrupt defined by + * the given list register value; see the pseudocode functions + * CanSignalVirtualInterrupt and CanSignalVirtualInt. + * Compare also icc_hppi_can_preempt() which is the non-virtual + * equivalent of these checks. + */ + int grp; + uint32_t mask, prio, rprio, vpmr; + + if (!(cs->ich_hcr_el2 & ICH_HCR_EL2_EN)) { + /* Virtual interface disabled */ + return false; + } + + /* We don't need to check that this LR is in Pending state because + * that has already been done in hppvi_index(). + */ + + prio = ich_lr_prio(lr); + vpmr = extract64(cs->ich_vmcr_el2, ICH_VMCR_EL2_VPMR_SHIFT, + ICH_VMCR_EL2_VPMR_LENGTH); + + if (prio >= vpmr) { + /* Priority mask masks this interrupt */ + return false; + } + + rprio = ich_highest_active_virt_prio(cs); + if (rprio == 0xff) { + /* No running interrupt so we can preempt */ + return true; + } + + grp = (lr & ICH_LR_EL2_GROUP) ? GICV3_G1NS : GICV3_G0; + + mask = icv_gprio_mask(cs, grp); + + /* We only preempt a running interrupt if the pending interrupt's + * group priority is sufficient (the subpriorities are not considered). + */ + if ((prio & mask) < (rprio & mask)) { + return true; + } + + return false; +} + +static uint32_t eoi_maintenance_interrupt_state(GICv3CPUState *cs, + uint32_t *misr) +{ + /* Return a set of bits indicating the EOI maintenance interrupt status + * for each list register. The EOI maintenance interrupt status is + * 1 if LR.State == 0 && LR.HW == 0 && LR.EOI == 1 + * (see the GICv3 spec for the ICH_EISR_EL2 register). + * If misr is not NULL then we should also collect the information + * about the MISR.EOI, MISR.NP and MISR.U bits. + */ + uint32_t value = 0; + int validcount = 0; + bool seenpending = false; + int i; + + for (i = 0; i < cs->num_list_regs; i++) { + uint64_t lr = cs->ich_lr_el2[i]; + + if ((lr & (ICH_LR_EL2_STATE_MASK | ICH_LR_EL2_HW | ICH_LR_EL2_EOI)) + == ICH_LR_EL2_EOI) { + value |= (1 << i); + } + if ((lr & ICH_LR_EL2_STATE_MASK)) { + validcount++; + } + if (ich_lr_state(lr) == ICH_LR_EL2_STATE_PENDING) { + seenpending = true; + } + } + + if (misr) { + if (validcount < 2 && (cs->ich_hcr_el2 & ICH_HCR_EL2_UIE)) { + *misr |= ICH_MISR_EL2_U; + } + if (!seenpending && (cs->ich_hcr_el2 & ICH_HCR_EL2_NPIE)) { + *misr |= ICH_MISR_EL2_NP; + } + if (value) { + *misr |= ICH_MISR_EL2_EOI; + } + } + return value; +} + +static uint32_t maintenance_interrupt_state(GICv3CPUState *cs) +{ + /* Return a set of bits indicating the maintenance interrupt status + * (as seen in the ICH_MISR_EL2 register). + */ + uint32_t value = 0; + + /* Scan list registers and fill in the U, NP and EOI bits */ + eoi_maintenance_interrupt_state(cs, &value); + + if (cs->ich_hcr_el2 & (ICH_HCR_EL2_LRENPIE | ICH_HCR_EL2_EOICOUNT_MASK)) { + value |= ICH_MISR_EL2_LRENP; + } + + if ((cs->ich_hcr_el2 & ICH_HCR_EL2_VGRP0EIE) && + (cs->ich_vmcr_el2 & ICH_VMCR_EL2_VENG0)) { + value |= ICH_MISR_EL2_VGRP0E; + } + + if ((cs->ich_hcr_el2 & ICH_HCR_EL2_VGRP0DIE) && + !(cs->ich_vmcr_el2 & ICH_VMCR_EL2_VENG1)) { + value |= ICH_MISR_EL2_VGRP0D; + } + if ((cs->ich_hcr_el2 & ICH_HCR_EL2_VGRP1EIE) && + (cs->ich_vmcr_el2 & ICH_VMCR_EL2_VENG1)) { + value |= ICH_MISR_EL2_VGRP1E; + } + + if ((cs->ich_hcr_el2 & ICH_HCR_EL2_VGRP1DIE) && + !(cs->ich_vmcr_el2 & ICH_VMCR_EL2_VENG1)) { + value |= ICH_MISR_EL2_VGRP1D; + } + + return value; +} + +static void gicv3_cpuif_virt_update(GICv3CPUState *cs) +{ + /* Tell the CPU about any pending virtual interrupts or + * maintenance interrupts, following a change to the state + * of the CPU interface relevant to virtual interrupts. + * + * CAUTION: this function will call qemu_set_irq() on the + * CPU maintenance IRQ line, which is typically wired up + * to the GIC as a per-CPU interrupt. This means that it + * will recursively call back into the GIC code via + * gicv3_redist_set_irq() and thus into the CPU interface code's + * gicv3_cpuif_update(). It is therefore important that this + * function is only called as the final action of a CPU interface + * register write implementation, after all the GIC state + * fields have been updated. gicv3_cpuif_update() also must + * not cause this function to be called, but that happens + * naturally as a result of there being no architectural + * linkage between the physical and virtual GIC logic. + */ + int idx; + int irqlevel = 0; + int fiqlevel = 0; + int maintlevel = 0; + + idx = hppvi_index(cs); + trace_gicv3_cpuif_virt_update(gicv3_redist_affid(cs), idx); + if (idx >= 0) { + uint64_t lr = cs->ich_lr_el2[idx]; + + if (icv_hppi_can_preempt(cs, lr)) { + /* Virtual interrupts are simple: G0 are always FIQ, and G1 IRQ */ + if (lr & ICH_LR_EL2_GROUP) { + irqlevel = 1; + } else { + fiqlevel = 1; + } + } + } + + if (cs->ich_hcr_el2 & ICH_HCR_EL2_EN) { + maintlevel = maintenance_interrupt_state(cs); + } + + trace_gicv3_cpuif_virt_set_irqs(gicv3_redist_affid(cs), fiqlevel, + irqlevel, maintlevel); + + qemu_set_irq(cs->parent_vfiq, fiqlevel); + qemu_set_irq(cs->parent_virq, irqlevel); + qemu_set_irq(cs->maintenance_irq, maintlevel); +} + +static uint64_t icv_ap_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + int regno = ri->opc2 & 3; + int grp = ri->crm & 1 ? GICV3_G0 : GICV3_G1NS; + uint64_t value = cs->ich_apr[grp][regno]; + + trace_gicv3_icv_ap_read(ri->crm & 1, regno, gicv3_redist_affid(cs), value); + return value; +} + +static void icv_ap_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + int regno = ri->opc2 & 3; + int grp = ri->crm & 1 ? GICV3_G0 : GICV3_G1NS; + + trace_gicv3_icv_ap_write(ri->crm & 1, regno, gicv3_redist_affid(cs), value); + + cs->ich_apr[grp][regno] = value & 0xFFFFFFFFU; + + gicv3_cpuif_virt_update(cs); + return; +} + +static uint64_t icv_bpr_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + int grp = (ri->crm == 8) ? GICV3_G0 : GICV3_G1NS; + uint64_t bpr; + bool satinc = false; + + if (grp == GICV3_G1NS && (cs->ich_vmcr_el2 & ICH_VMCR_EL2_VCBPR)) { + /* reads return bpr0 + 1 saturated to 7, writes ignored */ + grp = GICV3_G0; + satinc = true; + } + + bpr = read_vbpr(cs, grp); + + if (satinc) { + bpr++; + bpr = MIN(bpr, 7); + } + + trace_gicv3_icv_bpr_read(ri->crm == 8 ? 0 : 1, gicv3_redist_affid(cs), bpr); + + return bpr; +} + +static void icv_bpr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + int grp = (ri->crm == 8) ? GICV3_G0 : GICV3_G1NS; + + trace_gicv3_icv_bpr_write(ri->crm == 8 ? 0 : 1, + gicv3_redist_affid(cs), value); + + if (grp == GICV3_G1NS && (cs->ich_vmcr_el2 & ICH_VMCR_EL2_VCBPR)) { + /* reads return bpr0 + 1 saturated to 7, writes ignored */ + return; + } + + write_vbpr(cs, grp, value); + + gicv3_cpuif_virt_update(cs); +} + +static uint64_t icv_pmr_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + uint64_t value; + + value = extract64(cs->ich_vmcr_el2, ICH_VMCR_EL2_VPMR_SHIFT, + ICH_VMCR_EL2_VPMR_LENGTH); + + trace_gicv3_icv_pmr_read(gicv3_redist_affid(cs), value); + return value; +} + +static void icv_pmr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + + trace_gicv3_icv_pmr_write(gicv3_redist_affid(cs), value); + + value &= icv_fullprio_mask(cs); + + cs->ich_vmcr_el2 = deposit64(cs->ich_vmcr_el2, ICH_VMCR_EL2_VPMR_SHIFT, + ICH_VMCR_EL2_VPMR_LENGTH, value); + + gicv3_cpuif_virt_update(cs); +} + +static uint64_t icv_igrpen_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + int enbit; + uint64_t value; + + enbit = ri->opc2 & 1 ? ICH_VMCR_EL2_VENG1_SHIFT : ICH_VMCR_EL2_VENG0_SHIFT; + value = extract64(cs->ich_vmcr_el2, enbit, 1); + + trace_gicv3_icv_igrpen_read(ri->opc2 & 1 ? 1 : 0, + gicv3_redist_affid(cs), value); + return value; +} + +static void icv_igrpen_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + int enbit; + + trace_gicv3_icv_igrpen_write(ri->opc2 & 1 ? 1 : 0, + gicv3_redist_affid(cs), value); + + enbit = ri->opc2 & 1 ? ICH_VMCR_EL2_VENG1_SHIFT : ICH_VMCR_EL2_VENG0_SHIFT; + + cs->ich_vmcr_el2 = deposit64(cs->ich_vmcr_el2, enbit, 1, value); + gicv3_cpuif_virt_update(cs); +} + +static uint64_t icv_ctlr_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + uint64_t value; + + /* Note that the fixed fields here (A3V, SEIS, IDbits, PRIbits) + * should match the ones reported in ich_vtr_read(). + */ + value = ICC_CTLR_EL1_A3V | (1 << ICC_CTLR_EL1_IDBITS_SHIFT) | + (7 << ICC_CTLR_EL1_PRIBITS_SHIFT); + + if (cs->ich_vmcr_el2 & ICH_VMCR_EL2_VEOIM) { + value |= ICC_CTLR_EL1_EOIMODE; + } + + if (cs->ich_vmcr_el2 & ICH_VMCR_EL2_VCBPR) { + value |= ICC_CTLR_EL1_CBPR; + } + + trace_gicv3_icv_ctlr_read(gicv3_redist_affid(cs), value); + return value; +} + +static void icv_ctlr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + + trace_gicv3_icv_ctlr_write(gicv3_redist_affid(cs), value); + + cs->ich_vmcr_el2 = deposit64(cs->ich_vmcr_el2, ICH_VMCR_EL2_VCBPR_SHIFT, + 1, value & ICC_CTLR_EL1_CBPR ? 1 : 0); + cs->ich_vmcr_el2 = deposit64(cs->ich_vmcr_el2, ICH_VMCR_EL2_VEOIM_SHIFT, + 1, value & ICC_CTLR_EL1_EOIMODE ? 1 : 0); + + gicv3_cpuif_virt_update(cs); +} + +static uint64_t icv_rpr_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + int prio = ich_highest_active_virt_prio(cs); + + trace_gicv3_icv_rpr_read(gicv3_redist_affid(cs), prio); + return prio; +} + +static uint64_t icv_hppir_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + int grp = ri->crm == 8 ? GICV3_G0 : GICV3_G1NS; + int idx = hppvi_index(cs); + uint64_t value = INTID_SPURIOUS; + + if (idx >= 0) { + uint64_t lr = cs->ich_lr_el2[idx]; + int thisgrp = (lr & ICH_LR_EL2_GROUP) ? GICV3_G1NS : GICV3_G0; + + if (grp == thisgrp) { + value = ich_lr_vintid(lr); + } + } + + trace_gicv3_icv_hppir_read(grp, gicv3_redist_affid(cs), value); + return value; +} + +static void icv_activate_irq(GICv3CPUState *cs, int idx, int grp) +{ + /* Activate the interrupt in the specified list register + * by moving it from Pending to Active state, and update the + * Active Priority Registers. + */ + uint32_t mask = icv_gprio_mask(cs, grp); + int prio = ich_lr_prio(cs->ich_lr_el2[idx]) & mask; + int aprbit = prio >> (8 - cs->vprebits); + int regno = aprbit / 32; + int regbit = aprbit % 32; + + cs->ich_lr_el2[idx] &= ~ICH_LR_EL2_STATE_PENDING_BIT; + cs->ich_lr_el2[idx] |= ICH_LR_EL2_STATE_ACTIVE_BIT; + cs->ich_apr[grp][regno] |= (1 << regbit); +} + +static uint64_t icv_iar_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + int grp = ri->crm == 8 ? GICV3_G0 : GICV3_G1NS; + int idx = hppvi_index(cs); + uint64_t intid = INTID_SPURIOUS; + + if (idx >= 0) { + uint64_t lr = cs->ich_lr_el2[idx]; + int thisgrp = (lr & ICH_LR_EL2_GROUP) ? GICV3_G1NS : GICV3_G0; + + if (thisgrp == grp && icv_hppi_can_preempt(cs, lr)) { + intid = ich_lr_vintid(lr); + if (intid < INTID_SECURE) { + icv_activate_irq(cs, idx, grp); + } else { + /* Interrupt goes from Pending to Invalid */ + cs->ich_lr_el2[idx] &= ~ICH_LR_EL2_STATE_PENDING_BIT; + /* We will now return the (bogus) ID from the list register, + * as per the pseudocode. + */ + } + } + } + + trace_gicv3_icv_iar_read(ri->crm == 8 ? 0 : 1, + gicv3_redist_affid(cs), intid); + return intid; +} + static int icc_highest_active_prio(GICv3CPUState *cs) { /* Calculate the current running priority based on the set bits @@ -177,6 +782,10 @@ static uint64_t icc_pmr_read(CPUARMState *env, const ARMCPRegInfo *ri) GICv3CPUState *cs = icc_cs_from_env(env); uint32_t value = cs->icc_pmr_el1; + if (icv_access(env, HCR_FMO | HCR_IMO)) { + return icv_pmr_read(env, ri); + } + if (arm_feature(env, ARM_FEATURE_EL3) && !arm_is_secure(env) && (env->cp15.scr_el3 & SCR_FIQ)) { /* NS access and Group 0 is inaccessible to NS: return the @@ -200,6 +809,10 @@ static void icc_pmr_write(CPUARMState *env, const ARMCPRegInfo *ri, { GICv3CPUState *cs = icc_cs_from_env(env); + if (icv_access(env, HCR_FMO | HCR_IMO)) { + return icv_pmr_write(env, ri, value); + } + trace_gicv3_icc_pmr_write(gicv3_redist_affid(cs), value); value &= 0xff; @@ -321,6 +934,10 @@ static uint64_t icc_iar0_read(CPUARMState *env, const ARMCPRegInfo *ri) GICv3CPUState *cs = icc_cs_from_env(env); uint64_t intid; + if (icv_access(env, HCR_FMO)) { + return icv_iar_read(env, ri); + } + if (!icc_hppi_can_preempt(cs)) { intid = INTID_SPURIOUS; } else { @@ -340,6 +957,10 @@ static uint64_t icc_iar1_read(CPUARMState *env, const ARMCPRegInfo *ri) GICv3CPUState *cs = icc_cs_from_env(env); uint64_t intid; + if (icv_access(env, HCR_IMO)) { + return icv_iar_read(env, ri); + } + if (!icc_hppi_can_preempt(cs)) { intid = INTID_SPURIOUS; } else { @@ -446,6 +1067,190 @@ static void icc_deactivate_irq(GICv3CPUState *cs, int irq) } } +static bool icv_eoi_split(CPUARMState *env, GICv3CPUState *cs) +{ + /* Return true if we should split priority drop and interrupt + * deactivation, ie whether the virtual EOIMode bit is set. + */ + return cs->ich_vmcr_el2 & ICH_VMCR_EL2_VEOIM; +} + +static int icv_find_active(GICv3CPUState *cs, int irq) +{ + /* Given an interrupt number for an active interrupt, return the index + * of the corresponding list register, or -1 if there is no match. + * Corresponds to FindActiveVirtualInterrupt pseudocode. + */ + int i; + + for (i = 0; i < cs->num_list_regs; i++) { + uint64_t lr = cs->ich_lr_el2[i]; + + if ((lr & ICH_LR_EL2_STATE_ACTIVE_BIT) && ich_lr_vintid(lr) == irq) { + return i; + } + } + + return -1; +} + +static void icv_deactivate_irq(GICv3CPUState *cs, int idx) +{ + /* Deactivate the interrupt in the specified list register index */ + uint64_t lr = cs->ich_lr_el2[idx]; + + if (lr & ICH_LR_EL2_HW) { + /* Deactivate the associated physical interrupt */ + int pirq = ich_lr_pintid(lr); + + if (pirq < INTID_SECURE) { + icc_deactivate_irq(cs, pirq); + } + } + + /* Clear the 'active' part of the state, so ActivePending->Pending + * and Active->Invalid. + */ + lr &= ~ICH_LR_EL2_STATE_ACTIVE_BIT; + cs->ich_lr_el2[idx] = lr; +} + +static void icv_increment_eoicount(GICv3CPUState *cs) +{ + /* Increment the EOICOUNT field in ICH_HCR_EL2 */ + int eoicount = extract64(cs->ich_hcr_el2, ICH_HCR_EL2_EOICOUNT_SHIFT, + ICH_HCR_EL2_EOICOUNT_LENGTH); + + cs->ich_hcr_el2 = deposit64(cs->ich_hcr_el2, ICH_HCR_EL2_EOICOUNT_SHIFT, + ICH_HCR_EL2_EOICOUNT_LENGTH, eoicount + 1); +} + +static int icv_drop_prio(GICv3CPUState *cs) +{ + /* Drop the priority of the currently active virtual interrupt + * (favouring group 0 if there is a set active bit at + * the same priority for both group 0 and group 1). + * Return the priority value for the bit we just cleared, + * or 0xff if no bits were set in the AP registers at all. + * Note that though the ich_apr[] are uint64_t only the low + * 32 bits are actually relevant. + */ + int i; + int aprmax = 1 << (cs->vprebits - 5); + + assert(aprmax <= ARRAY_SIZE(cs->ich_apr[0])); + + for (i = 0; i < aprmax; i++) { + uint64_t *papr0 = &cs->ich_apr[GICV3_G0][i]; + uint64_t *papr1 = &cs->ich_apr[GICV3_G1NS][i]; + int apr0count, apr1count; + + if (!*papr0 && !*papr1) { + continue; + } + + /* We can't just use the bit-twiddling hack icc_drop_prio() does + * because we need to return the bit number we cleared so + * it can be compared against the list register's priority field. + */ + apr0count = ctz32(*papr0); + apr1count = ctz32(*papr1); + + if (apr0count <= apr1count) { + *papr0 &= *papr0 - 1; + return (apr0count + i * 32) << (icv_min_vbpr(cs) + 1); + } else { + *papr1 &= *papr1 - 1; + return (apr1count + i * 32) << (icv_min_vbpr(cs) + 1); + } + } + return 0xff; +} + +static void icv_dir_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Deactivate interrupt */ + GICv3CPUState *cs = icc_cs_from_env(env); + int idx; + int irq = value & 0xffffff; + + trace_gicv3_icv_dir_write(gicv3_redist_affid(cs), value); + + if (irq >= cs->gic->num_irq) { + /* Also catches special interrupt numbers and LPIs */ + return; + } + + if (!icv_eoi_split(env, cs)) { + return; + } + + idx = icv_find_active(cs, irq); + + if (idx < 0) { + /* No list register matching this, so increment the EOI count + * (might trigger a maintenance interrupt) + */ + icv_increment_eoicount(cs); + } else { + icv_deactivate_irq(cs, idx); + } + + gicv3_cpuif_virt_update(cs); +} + +static void icv_eoir_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* End of Interrupt */ + GICv3CPUState *cs = icc_cs_from_env(env); + int irq = value & 0xffffff; + int grp = ri->crm == 8 ? GICV3_G0 : GICV3_G1NS; + int idx, dropprio; + + trace_gicv3_icv_eoir_write(ri->crm == 8 ? 0 : 1, + gicv3_redist_affid(cs), value); + + if (irq >= cs->gic->num_irq) { + /* Also catches special interrupt numbers and LPIs */ + return; + } + + /* We implement the IMPDEF choice of "drop priority before doing + * error checks" (because that lets us avoid scanning the AP + * registers twice). + */ + dropprio = icv_drop_prio(cs); + if (dropprio == 0xff) { + /* No active interrupt. It is CONSTRAINED UNPREDICTABLE + * whether the list registers are checked in this + * situation; we choose not to. + */ + return; + } + + idx = icv_find_active(cs, irq); + + if (idx < 0) { + /* No valid list register corresponding to EOI ID */ + icv_increment_eoicount(cs); + } else { + uint64_t lr = cs->ich_lr_el2[idx]; + int thisgrp = (lr & ICH_LR_EL2_GROUP) ? GICV3_G1NS : GICV3_G0; + int lr_gprio = ich_lr_prio(lr) & icv_gprio_mask(cs, grp); + + if (thisgrp == grp && lr_gprio == dropprio) { + if (!icv_eoi_split(env, cs)) { + /* Priority drop and deactivate not split: deactivate irq now */ + icv_deactivate_irq(cs, idx); + } + } + } + + gicv3_cpuif_virt_update(cs); +} + static void icc_eoir_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -454,6 +1259,11 @@ static void icc_eoir_write(CPUARMState *env, const ARMCPRegInfo *ri, int irq = value & 0xffffff; int grp; + if (icv_access(env, ri->crm == 8 ? HCR_FMO : HCR_IMO)) { + icv_eoir_write(env, ri, value); + return; + } + trace_gicv3_icc_eoir_write(ri->crm == 8 ? 0 : 1, gicv3_redist_affid(cs), value); @@ -496,8 +1306,13 @@ static void icc_eoir_write(CPUARMState *env, const ARMCPRegInfo *ri, static uint64_t icc_hppir0_read(CPUARMState *env, const ARMCPRegInfo *ri) { GICv3CPUState *cs = icc_cs_from_env(env); - uint64_t value = icc_hppir0_value(cs, env); + uint64_t value; + if (icv_access(env, HCR_FMO)) { + return icv_hppir_read(env, ri); + } + + value = icc_hppir0_value(cs, env); trace_gicv3_icc_hppir0_read(gicv3_redist_affid(cs), value); return value; } @@ -505,8 +1320,13 @@ static uint64_t icc_hppir0_read(CPUARMState *env, const ARMCPRegInfo *ri) static uint64_t icc_hppir1_read(CPUARMState *env, const ARMCPRegInfo *ri) { GICv3CPUState *cs = icc_cs_from_env(env); - uint64_t value = icc_hppir1_value(cs, env); + uint64_t value; + if (icv_access(env, HCR_IMO)) { + return icv_hppir_read(env, ri); + } + + value = icc_hppir1_value(cs, env); trace_gicv3_icc_hppir1_read(gicv3_redist_affid(cs), value); return value; } @@ -518,6 +1338,10 @@ static uint64_t icc_bpr_read(CPUARMState *env, const ARMCPRegInfo *ri) bool satinc = false; uint64_t bpr; + if (icv_access(env, grp == GICV3_G0 ? HCR_FMO : HCR_IMO)) { + return icv_bpr_read(env, ri); + } + if (grp == GICV3_G1 && gicv3_use_ns_bank(env)) { grp = GICV3_G1NS; } @@ -554,6 +1378,11 @@ static void icc_bpr_write(CPUARMState *env, const ARMCPRegInfo *ri, GICv3CPUState *cs = icc_cs_from_env(env); int grp = (ri->crm == 8) ? GICV3_G0 : GICV3_G1; + if (icv_access(env, grp == GICV3_G0 ? HCR_FMO : HCR_IMO)) { + icv_bpr_write(env, ri, value); + return; + } + trace_gicv3_icc_bpr_write(ri->crm == 8 ? 0 : 1, gicv3_redist_affid(cs), value); @@ -587,6 +1416,10 @@ static uint64_t icc_ap_read(CPUARMState *env, const ARMCPRegInfo *ri) int regno = ri->opc2 & 3; int grp = ri->crm & 1 ? GICV3_G0 : GICV3_G1; + if (icv_access(env, grp == GICV3_G0 ? HCR_FMO : HCR_IMO)) { + return icv_ap_read(env, ri); + } + if (grp == GICV3_G1 && gicv3_use_ns_bank(env)) { grp = GICV3_G1NS; } @@ -605,6 +1438,11 @@ static void icc_ap_write(CPUARMState *env, const ARMCPRegInfo *ri, int regno = ri->opc2 & 3; int grp = ri->crm & 1 ? GICV3_G0 : GICV3_G1; + if (icv_access(env, grp == GICV3_G0 ? HCR_FMO : HCR_IMO)) { + icv_ap_write(env, ri, value); + return; + } + trace_gicv3_icc_ap_write(ri->crm & 1, regno, gicv3_redist_affid(cs), value); if (grp == GICV3_G1 && gicv3_use_ns_bank(env)) { @@ -633,6 +1471,11 @@ static void icc_dir_write(CPUARMState *env, const ARMCPRegInfo *ri, bool irq_is_secure, single_sec_state, irq_is_grp0; bool route_fiq_to_el3, route_irq_to_el3, route_fiq_to_el2, route_irq_to_el2; + if (icv_access(env, HCR_FMO | HCR_IMO)) { + icv_dir_write(env, ri, value); + return; + } + trace_gicv3_icc_dir_write(gicv3_redist_affid(cs), value); if (irq >= cs->gic->num_irq) { @@ -704,7 +1547,13 @@ static void icc_dir_write(CPUARMState *env, const ARMCPRegInfo *ri, static uint64_t icc_rpr_read(CPUARMState *env, const ARMCPRegInfo *ri) { GICv3CPUState *cs = icc_cs_from_env(env); - int prio = icc_highest_active_prio(cs); + int prio; + + if (icv_access(env, HCR_FMO | HCR_IMO)) { + return icv_rpr_read(env, ri); + } + + prio = icc_highest_active_prio(cs); if (arm_feature(env, ARM_FEATURE_EL3) && !arm_is_secure(env) && (env->cp15.scr_el3 & SCR_FIQ)) { @@ -817,6 +1666,10 @@ static uint64_t icc_igrpen_read(CPUARMState *env, const ARMCPRegInfo *ri) int grp = ri->opc2 & 1 ? GICV3_G1 : GICV3_G0; uint64_t value; + if (icv_access(env, grp == GICV3_G0 ? HCR_FMO : HCR_IMO)) { + return icv_igrpen_read(env, ri); + } + if (grp == GICV3_G1 && gicv3_use_ns_bank(env)) { grp = GICV3_G1NS; } @@ -833,6 +1686,11 @@ static void icc_igrpen_write(CPUARMState *env, const ARMCPRegInfo *ri, GICv3CPUState *cs = icc_cs_from_env(env); int grp = ri->opc2 & 1 ? GICV3_G1 : GICV3_G0; + if (icv_access(env, grp == GICV3_G0 ? HCR_FMO : HCR_IMO)) { + icv_igrpen_write(env, ri, value); + return; + } + trace_gicv3_icc_igrpen_write(ri->opc2 & 1 ? 1 : 0, gicv3_redist_affid(cs), value); @@ -874,6 +1732,10 @@ static uint64_t icc_ctlr_el1_read(CPUARMState *env, const ARMCPRegInfo *ri) int bank = gicv3_use_ns_bank(env) ? GICV3_NS : GICV3_S; uint64_t value; + if (icv_access(env, HCR_FMO | HCR_IMO)) { + return icv_ctlr_read(env, ri); + } + value = cs->icc_ctlr_el1[bank]; trace_gicv3_icc_ctlr_read(gicv3_redist_affid(cs), value); return value; @@ -886,6 +1748,11 @@ static void icc_ctlr_el1_write(CPUARMState *env, const ARMCPRegInfo *ri, int bank = gicv3_use_ns_bank(env) ? GICV3_NS : GICV3_S; uint64_t mask; + if (icv_access(env, HCR_FMO | HCR_IMO)) { + icv_ctlr_write(env, ri, value); + return; + } + trace_gicv3_icc_ctlr_write(gicv3_redist_affid(cs), value); /* Only CBPR and EOIMODE can be RW; @@ -966,9 +1833,17 @@ static CPAccessResult gicv3_irqfiq_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { CPAccessResult r = CP_ACCESS_OK; + GICv3CPUState *cs = icc_cs_from_env(env); + int el = arm_current_el(env); + + if ((cs->ich_hcr_el2 & ICH_HCR_EL2_TC) && + el == 1 && !arm_is_secure_below_el3(env)) { + /* Takes priority over a possible EL3 trap */ + return CP_ACCESS_TRAP_EL2; + } if ((env->cp15.scr_el3 & (SCR_FIQ | SCR_IRQ)) == (SCR_FIQ | SCR_IRQ)) { - switch (arm_current_el(env)) { + switch (el) { case 1: if (arm_is_secure_below_el3(env) || ((env->cp15.hcr_el2 & (HCR_IMO | HCR_FMO)) == 0)) { @@ -994,13 +1869,47 @@ static CPAccessResult gicv3_irqfiq_access(CPUARMState *env, return r; } +static CPAccessResult gicv3_dir_access(CPUARMState *env, + const ARMCPRegInfo *ri, bool isread) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + + if ((cs->ich_hcr_el2 & ICH_HCR_EL2_TDIR) && + arm_current_el(env) == 1 && !arm_is_secure_below_el3(env)) { + /* Takes priority over a possible EL3 trap */ + return CP_ACCESS_TRAP_EL2; + } + + return gicv3_irqfiq_access(env, ri, isread); +} + +static CPAccessResult gicv3_sgi_access(CPUARMState *env, + const ARMCPRegInfo *ri, bool isread) +{ + if ((env->cp15.hcr_el2 & (HCR_IMO | HCR_FMO)) && + arm_current_el(env) == 1 && !arm_is_secure_below_el3(env)) { + /* Takes priority over a possible EL3 trap */ + return CP_ACCESS_TRAP_EL2; + } + + return gicv3_irqfiq_access(env, ri, isread); +} + static CPAccessResult gicv3_fiq_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { CPAccessResult r = CP_ACCESS_OK; + GICv3CPUState *cs = icc_cs_from_env(env); + int el = arm_current_el(env); + + if ((cs->ich_hcr_el2 & ICH_HCR_EL2_TALL0) && + el == 1 && !arm_is_secure_below_el3(env)) { + /* Takes priority over a possible EL3 trap */ + return CP_ACCESS_TRAP_EL2; + } if (env->cp15.scr_el3 & SCR_FIQ) { - switch (arm_current_el(env)) { + switch (el) { case 1: if (arm_is_secure_below_el3(env) || ((env->cp15.hcr_el2 & HCR_FMO) == 0)) { @@ -1030,9 +1939,17 @@ static CPAccessResult gicv3_irq_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { CPAccessResult r = CP_ACCESS_OK; + GICv3CPUState *cs = icc_cs_from_env(env); + int el = arm_current_el(env); + + if ((cs->ich_hcr_el2 & ICH_HCR_EL2_TALL1) && + el == 1 && !arm_is_secure_below_el3(env)) { + /* Takes priority over a possible EL3 trap */ + return CP_ACCESS_TRAP_EL2; + } if (env->cp15.scr_el3 & SCR_IRQ) { - switch (arm_current_el(env)) { + switch (el) { case 1: if (arm_is_secure_below_el3(env) || ((env->cp15.hcr_el2 & HCR_IMO) == 0)) { @@ -1081,6 +1998,13 @@ static void icc_reset(CPUARMState *env, const ARMCPRegInfo *ri) cs->icc_ctlr_el3 = ICC_CTLR_EL3_NDS | ICC_CTLR_EL3_A3V | (1 << ICC_CTLR_EL3_IDBITS_SHIFT) | (7 << ICC_CTLR_EL3_PRIBITS_SHIFT); + + memset(cs->ich_apr, 0, sizeof(cs->ich_apr)); + cs->ich_hcr_el2 = 0; + memset(cs->ich_lr_el2, 0, sizeof(cs->ich_lr_el2)); + cs->ich_vmcr_el2 = ICH_VMCR_EL2_VFIQEN | + (icv_min_vbpr(cs) << ICH_VMCR_EL2_VBPR1_SHIFT) | + (icv_min_vbpr(cs) << ICH_VMCR_EL2_VBPR0_SHIFT); } static const ARMCPRegInfo gicv3_cpuif_reginfo[] = { @@ -1181,7 +2105,7 @@ static const ARMCPRegInfo gicv3_cpuif_reginfo[] = { { .name = "ICC_DIR_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 11, .opc2 = 1, .type = ARM_CP_IO | ARM_CP_NO_RAW, - .access = PL1_W, .accessfn = gicv3_irqfiq_access, + .access = PL1_W, .accessfn = gicv3_dir_access, .writefn = icc_dir_write, }, { .name = "ICC_RPR_EL1", .state = ARM_CP_STATE_BOTH, @@ -1193,37 +2117,37 @@ static const ARMCPRegInfo gicv3_cpuif_reginfo[] = { { .name = "ICC_SGI1R_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 11, .opc2 = 5, .type = ARM_CP_IO | ARM_CP_NO_RAW, - .access = PL1_W, .accessfn = gicv3_irqfiq_access, + .access = PL1_W, .accessfn = gicv3_sgi_access, .writefn = icc_sgi1r_write, }, { .name = "ICC_SGI1R", .cp = 15, .opc1 = 0, .crm = 12, .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_NO_RAW, - .access = PL1_W, .accessfn = gicv3_irqfiq_access, + .access = PL1_W, .accessfn = gicv3_sgi_access, .writefn = icc_sgi1r_write, }, { .name = "ICC_ASGI1R_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 11, .opc2 = 6, .type = ARM_CP_IO | ARM_CP_NO_RAW, - .access = PL1_W, .accessfn = gicv3_irqfiq_access, + .access = PL1_W, .accessfn = gicv3_sgi_access, .writefn = icc_asgi1r_write, }, { .name = "ICC_ASGI1R", .cp = 15, .opc1 = 1, .crm = 12, .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_NO_RAW, - .access = PL1_W, .accessfn = gicv3_irqfiq_access, + .access = PL1_W, .accessfn = gicv3_sgi_access, .writefn = icc_asgi1r_write, }, { .name = "ICC_SGI0R_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 11, .opc2 = 7, .type = ARM_CP_IO | ARM_CP_NO_RAW, - .access = PL1_W, .accessfn = gicv3_irqfiq_access, + .access = PL1_W, .accessfn = gicv3_sgi_access, .writefn = icc_sgi0r_write, }, { .name = "ICC_SGI0R", .cp = 15, .opc1 = 2, .crm = 12, .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_NO_RAW, - .access = PL1_W, .accessfn = gicv3_irqfiq_access, + .access = PL1_W, .accessfn = gicv3_sgi_access, .writefn = icc_sgi0r_write, }, { .name = "ICC_IAR1_EL1", .state = ARM_CP_STATE_BOTH, @@ -1321,6 +2245,306 @@ static const ARMCPRegInfo gicv3_cpuif_reginfo[] = { REGINFO_SENTINEL }; +static uint64_t ich_ap_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + int regno = ri->opc2 & 3; + int grp = ri->crm & 1 ? GICV3_G0 : GICV3_G1NS; + uint64_t value; + + value = cs->ich_apr[grp][regno]; + trace_gicv3_ich_ap_read(ri->crm & 1, regno, gicv3_redist_affid(cs), value); + return value; +} + +static void ich_ap_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + int regno = ri->opc2 & 3; + int grp = ri->crm & 1 ? GICV3_G0 : GICV3_G1NS; + + trace_gicv3_ich_ap_write(ri->crm & 1, regno, gicv3_redist_affid(cs), value); + + cs->ich_apr[grp][regno] = value & 0xFFFFFFFFU; + gicv3_cpuif_virt_update(cs); +} + +static uint64_t ich_hcr_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + uint64_t value = cs->ich_hcr_el2; + + trace_gicv3_ich_hcr_read(gicv3_redist_affid(cs), value); + return value; +} + +static void ich_hcr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + + trace_gicv3_ich_hcr_write(gicv3_redist_affid(cs), value); + + value &= ICH_HCR_EL2_EN | ICH_HCR_EL2_UIE | ICH_HCR_EL2_LRENPIE | + ICH_HCR_EL2_NPIE | ICH_HCR_EL2_VGRP0EIE | ICH_HCR_EL2_VGRP0DIE | + ICH_HCR_EL2_VGRP1EIE | ICH_HCR_EL2_VGRP1DIE | ICH_HCR_EL2_TC | + ICH_HCR_EL2_TALL0 | ICH_HCR_EL2_TALL1 | ICH_HCR_EL2_TSEI | + ICH_HCR_EL2_TDIR | ICH_HCR_EL2_EOICOUNT_MASK; + + cs->ich_hcr_el2 = value; + gicv3_cpuif_virt_update(cs); +} + +static uint64_t ich_vmcr_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + uint64_t value = cs->ich_vmcr_el2; + + trace_gicv3_ich_vmcr_read(gicv3_redist_affid(cs), value); + return value; +} + +static void ich_vmcr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + + trace_gicv3_ich_vmcr_write(gicv3_redist_affid(cs), value); + + value &= ICH_VMCR_EL2_VENG0 | ICH_VMCR_EL2_VENG1 | ICH_VMCR_EL2_VCBPR | + ICH_VMCR_EL2_VEOIM | ICH_VMCR_EL2_VBPR1_MASK | + ICH_VMCR_EL2_VBPR0_MASK | ICH_VMCR_EL2_VPMR_MASK; + value |= ICH_VMCR_EL2_VFIQEN; + + cs->ich_vmcr_el2 = value; + /* Enforce "writing BPRs to less than minimum sets them to the minimum" + * by reading and writing back the fields. + */ + write_vbpr(cs, GICV3_G1, read_vbpr(cs, GICV3_G0)); + write_vbpr(cs, GICV3_G1, read_vbpr(cs, GICV3_G1)); + + gicv3_cpuif_virt_update(cs); +} + +static uint64_t ich_lr_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + int regno = ri->opc2 | ((ri->crm & 1) << 3); + uint64_t value; + + /* This read function handles all of: + * 64-bit reads of the whole LR + * 32-bit reads of the low half of the LR + * 32-bit reads of the high half of the LR + */ + if (ri->state == ARM_CP_STATE_AA32) { + if (ri->crm >= 14) { + value = extract64(cs->ich_lr_el2[regno], 32, 32); + trace_gicv3_ich_lrc_read(regno, gicv3_redist_affid(cs), value); + } else { + value = extract64(cs->ich_lr_el2[regno], 0, 32); + trace_gicv3_ich_lr32_read(regno, gicv3_redist_affid(cs), value); + } + } else { + value = cs->ich_lr_el2[regno]; + trace_gicv3_ich_lr_read(regno, gicv3_redist_affid(cs), value); + } + + return value; +} + +static void ich_lr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + int regno = ri->opc2 | ((ri->crm & 1) << 3); + + /* This write function handles all of: + * 64-bit writes to the whole LR + * 32-bit writes to the low half of the LR + * 32-bit writes to the high half of the LR + */ + if (ri->state == ARM_CP_STATE_AA32) { + if (ri->crm >= 14) { + trace_gicv3_ich_lrc_write(regno, gicv3_redist_affid(cs), value); + value = deposit64(cs->ich_lr_el2[regno], 32, 32, value); + } else { + trace_gicv3_ich_lr32_write(regno, gicv3_redist_affid(cs), value); + value = deposit64(cs->ich_lr_el2[regno], 0, 32, value); + } + } else { + trace_gicv3_ich_lr_write(regno, gicv3_redist_affid(cs), value); + } + + /* Enforce RES0 bits in priority field */ + if (cs->vpribits < 8) { + value = deposit64(value, ICH_LR_EL2_PRIORITY_SHIFT, + 8 - cs->vpribits, 0); + } + + cs->ich_lr_el2[regno] = value; + gicv3_cpuif_virt_update(cs); +} + +static uint64_t ich_vtr_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + uint64_t value; + + value = ((cs->num_list_regs - 1) << ICH_VTR_EL2_LISTREGS_SHIFT) + | ICH_VTR_EL2_TDS | ICH_VTR_EL2_NV4 | ICH_VTR_EL2_A3V + | (1 << ICH_VTR_EL2_IDBITS_SHIFT) + | ((cs->vprebits - 1) << ICH_VTR_EL2_PREBITS_SHIFT) + | ((cs->vpribits - 1) << ICH_VTR_EL2_PRIBITS_SHIFT); + + trace_gicv3_ich_vtr_read(gicv3_redist_affid(cs), value); + return value; +} + +static uint64_t ich_misr_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + uint64_t value = maintenance_interrupt_state(cs); + + trace_gicv3_ich_misr_read(gicv3_redist_affid(cs), value); + return value; +} + +static uint64_t ich_eisr_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + uint64_t value = eoi_maintenance_interrupt_state(cs, NULL); + + trace_gicv3_ich_eisr_read(gicv3_redist_affid(cs), value); + return value; +} + +static uint64_t ich_elrsr_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + uint64_t value = 0; + int i; + + for (i = 0; i < cs->num_list_regs; i++) { + uint64_t lr = cs->ich_lr_el2[i]; + + if ((lr & ICH_LR_EL2_STATE_MASK) == 0 && + ((lr & ICH_LR_EL2_HW) == 1 || (lr & ICH_LR_EL2_EOI) == 0)) { + value |= (1 << i); + } + } + + trace_gicv3_ich_elrsr_read(gicv3_redist_affid(cs), value); + return value; +} + +static const ARMCPRegInfo gicv3_cpuif_hcr_reginfo[] = { + { .name = "ICH_AP0R0_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 8, .opc2 = 0, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL2_RW, + .readfn = ich_ap_read, + .writefn = ich_ap_write, + }, + { .name = "ICH_AP1R0_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 9, .opc2 = 0, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL2_RW, + .readfn = ich_ap_read, + .writefn = ich_ap_write, + }, + { .name = "ICH_HCR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 11, .opc2 = 0, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL2_RW, + .readfn = ich_hcr_read, + .writefn = ich_hcr_write, + }, + { .name = "ICH_VTR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 11, .opc2 = 1, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL2_R, + .readfn = ich_vtr_read, + }, + { .name = "ICH_MISR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 11, .opc2 = 2, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL2_R, + .readfn = ich_misr_read, + }, + { .name = "ICH_EISR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 11, .opc2 = 3, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL2_R, + .readfn = ich_eisr_read, + }, + { .name = "ICH_ELRSR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 11, .opc2 = 5, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL2_R, + .readfn = ich_elrsr_read, + }, + { .name = "ICH_VMCR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 11, .opc2 = 7, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL2_RW, + .readfn = ich_vmcr_read, + .writefn = ich_vmcr_write, + }, + REGINFO_SENTINEL +}; + +static const ARMCPRegInfo gicv3_cpuif_ich_apxr1_reginfo[] = { + { .name = "ICH_AP0R1_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 8, .opc2 = 1, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL2_RW, + .readfn = ich_ap_read, + .writefn = ich_ap_write, + }, + { .name = "ICH_AP1R1_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 9, .opc2 = 1, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL2_RW, + .readfn = ich_ap_read, + .writefn = ich_ap_write, + }, + REGINFO_SENTINEL +}; + +static const ARMCPRegInfo gicv3_cpuif_ich_apxr23_reginfo[] = { + { .name = "ICH_AP0R2_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 8, .opc2 = 2, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL2_RW, + .readfn = ich_ap_read, + .writefn = ich_ap_write, + }, + { .name = "ICH_AP0R3_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 8, .opc2 = 3, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL2_RW, + .readfn = ich_ap_read, + .writefn = ich_ap_write, + }, + { .name = "ICH_AP1R2_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 9, .opc2 = 2, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL2_RW, + .readfn = ich_ap_read, + .writefn = ich_ap_write, + }, + { .name = "ICH_AP1R3_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 9, .opc2 = 3, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL2_RW, + .readfn = ich_ap_read, + .writefn = ich_ap_write, + }, + REGINFO_SENTINEL +}; + static void gicv3_cpuif_el_change_hook(ARMCPU *cpu, void *opaque) { GICv3CPUState *cs = opaque; @@ -1349,6 +2573,59 @@ void gicv3_init_cpuif(GICv3State *s) * to need to register anyway. */ define_arm_cp_regs(cpu, gicv3_cpuif_reginfo); + if (arm_feature(&cpu->env, ARM_FEATURE_EL2) + && cpu->gic_num_lrs) { + int j; + + cs->maintenance_irq = cpu->gicv3_maintenance_interrupt; + + cs->num_list_regs = cpu->gic_num_lrs; + cs->vpribits = cpu->gic_vpribits; + cs->vprebits = cpu->gic_vprebits; + + /* Check against architectural constraints: getting these + * wrong would be a bug in the CPU code defining these, + * and the implementation relies on them holding. + */ + g_assert(cs->vprebits <= cs->vpribits); + g_assert(cs->vprebits >= 5 && cs->vprebits <= 7); + g_assert(cs->vpribits >= 5 && cs->vpribits <= 8); + + define_arm_cp_regs(cpu, gicv3_cpuif_hcr_reginfo); + + for (j = 0; j < cs->num_list_regs; j++) { + /* Note that the AArch64 LRs are 64-bit; the AArch32 LRs + * are split into two cp15 regs, LR (the low part, with the + * same encoding as the AArch64 LR) and LRC (the high part). + */ + ARMCPRegInfo lr_regset[] = { + { .name = "ICH_LRn_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 12, + .crm = 12 + (j >> 3), .opc2 = j & 7, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL2_RW, + .readfn = ich_lr_read, + .writefn = ich_lr_write, + }, + { .name = "ICH_LRCn_EL2", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 4, .crn = 12, + .crm = 14 + (j >> 3), .opc2 = j & 7, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL2_RW, + .readfn = ich_lr_read, + .writefn = ich_lr_write, + }, + REGINFO_SENTINEL + }; + define_arm_cp_regs(cpu, lr_regset); + } + if (cs->vprebits >= 6) { + define_arm_cp_regs(cpu, gicv3_cpuif_ich_apxr1_reginfo); + } + if (cs->vprebits == 7) { + define_arm_cp_regs(cpu, gicv3_cpuif_ich_apxr23_reginfo); + } + } arm_register_el_change_hook(cpu, gicv3_cpuif_el_change_hook, cs); } } diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h index 8f3567edaa..aeb801d133 100644 --- a/hw/intc/gicv3_internal.h +++ b/hw/intc/gicv3_internal.h @@ -159,6 +159,85 @@ #define ICC_CTLR_EL3_A3V (1U << 15) #define ICC_CTLR_EL3_NDS (1U << 17) +#define ICH_VMCR_EL2_VENG0_SHIFT 0 +#define ICH_VMCR_EL2_VENG0 (1U << ICH_VMCR_EL2_VENG0_SHIFT) +#define ICH_VMCR_EL2_VENG1_SHIFT 1 +#define ICH_VMCR_EL2_VENG1 (1U << ICH_VMCR_EL2_VENG1_SHIFT) +#define ICH_VMCR_EL2_VACKCTL (1U << 2) +#define ICH_VMCR_EL2_VFIQEN (1U << 3) +#define ICH_VMCR_EL2_VCBPR_SHIFT 4 +#define ICH_VMCR_EL2_VCBPR (1U << ICH_VMCR_EL2_VCBPR_SHIFT) +#define ICH_VMCR_EL2_VEOIM_SHIFT 9 +#define ICH_VMCR_EL2_VEOIM (1U << ICH_VMCR_EL2_VEOIM_SHIFT) +#define ICH_VMCR_EL2_VBPR1_SHIFT 18 +#define ICH_VMCR_EL2_VBPR1_LENGTH 3 +#define ICH_VMCR_EL2_VBPR1_MASK (0x7U << ICH_VMCR_EL2_VBPR1_SHIFT) +#define ICH_VMCR_EL2_VBPR0_SHIFT 21 +#define ICH_VMCR_EL2_VBPR0_LENGTH 3 +#define ICH_VMCR_EL2_VBPR0_MASK (0x7U << ICH_VMCR_EL2_VBPR0_SHIFT) +#define ICH_VMCR_EL2_VPMR_SHIFT 24 +#define ICH_VMCR_EL2_VPMR_LENGTH 8 +#define ICH_VMCR_EL2_VPMR_MASK (0xffU << ICH_VMCR_EL2_VPMR_SHIFT) + +#define ICH_HCR_EL2_EN (1U << 0) +#define ICH_HCR_EL2_UIE (1U << 1) +#define ICH_HCR_EL2_LRENPIE (1U << 2) +#define ICH_HCR_EL2_NPIE (1U << 3) +#define ICH_HCR_EL2_VGRP0EIE (1U << 4) +#define ICH_HCR_EL2_VGRP0DIE (1U << 5) +#define ICH_HCR_EL2_VGRP1EIE (1U << 6) +#define ICH_HCR_EL2_VGRP1DIE (1U << 7) +#define ICH_HCR_EL2_TC (1U << 10) +#define ICH_HCR_EL2_TALL0 (1U << 11) +#define ICH_HCR_EL2_TALL1 (1U << 12) +#define ICH_HCR_EL2_TSEI (1U << 13) +#define ICH_HCR_EL2_TDIR (1U << 14) +#define ICH_HCR_EL2_EOICOUNT_SHIFT 27 +#define ICH_HCR_EL2_EOICOUNT_LENGTH 5 +#define ICH_HCR_EL2_EOICOUNT_MASK (0x1fU << ICH_HCR_EL2_EOICOUNT_SHIFT) + +#define ICH_LR_EL2_VINTID_SHIFT 0 +#define ICH_LR_EL2_VINTID_LENGTH 32 +#define ICH_LR_EL2_VINTID_MASK (0xffffffffULL << ICH_LR_EL2_VINTID_SHIFT) +#define ICH_LR_EL2_PINTID_SHIFT 32 +#define ICH_LR_EL2_PINTID_LENGTH 10 +#define ICH_LR_EL2_PINTID_MASK (0x3ffULL << ICH_LR_EL2_PINTID_SHIFT) +/* Note that EOI shares with the top bit of the pINTID field */ +#define ICH_LR_EL2_EOI (1ULL << 41) +#define ICH_LR_EL2_PRIORITY_SHIFT 48 +#define ICH_LR_EL2_PRIORITY_LENGTH 8 +#define ICH_LR_EL2_PRIORITY_MASK (0xffULL << ICH_LR_EL2_PRIORITY_SHIFT) +#define ICH_LR_EL2_GROUP (1ULL << 60) +#define ICH_LR_EL2_HW (1ULL << 61) +#define ICH_LR_EL2_STATE_SHIFT 62 +#define ICH_LR_EL2_STATE_LENGTH 2 +#define ICH_LR_EL2_STATE_MASK (3ULL << ICH_LR_EL2_STATE_SHIFT) +/* values for the state field: */ +#define ICH_LR_EL2_STATE_INVALID 0 +#define ICH_LR_EL2_STATE_PENDING 1 +#define ICH_LR_EL2_STATE_ACTIVE 2 +#define ICH_LR_EL2_STATE_ACTIVE_PENDING 3 +#define ICH_LR_EL2_STATE_PENDING_BIT (1ULL << ICH_LR_EL2_STATE_SHIFT) +#define ICH_LR_EL2_STATE_ACTIVE_BIT (2ULL << ICH_LR_EL2_STATE_SHIFT) + +#define ICH_MISR_EL2_EOI (1U << 0) +#define ICH_MISR_EL2_U (1U << 1) +#define ICH_MISR_EL2_LRENP (1U << 2) +#define ICH_MISR_EL2_NP (1U << 3) +#define ICH_MISR_EL2_VGRP0E (1U << 4) +#define ICH_MISR_EL2_VGRP0D (1U << 5) +#define ICH_MISR_EL2_VGRP1E (1U << 6) +#define ICH_MISR_EL2_VGRP1D (1U << 7) + +#define ICH_VTR_EL2_LISTREGS_SHIFT 0 +#define ICH_VTR_EL2_TDS (1U << 19) +#define ICH_VTR_EL2_NV4 (1U << 20) +#define ICH_VTR_EL2_A3V (1U << 21) +#define ICH_VTR_EL2_SEIS (1U << 22) +#define ICH_VTR_EL2_IDBITS_SHIFT 23 +#define ICH_VTR_EL2_PREBITS_SHIFT 26 +#define ICH_VTR_EL2_PRIBITS_SHIFT 29 + /* Special interrupt IDs */ #define INTID_SECURE 1020 #define INTID_NONSECURE 1021 diff --git a/hw/intc/s390_flic_kvm.c b/hw/intc/s390_flic_kvm.c index 21ac2e2dcd..c313166fbe 100644 --- a/hw/intc/s390_flic_kvm.c +++ b/hw/intc/s390_flic_kvm.c @@ -201,7 +201,7 @@ static int kvm_s390_register_io_adapter(S390FLICState *fs, uint32_t id, .addr = (uint64_t)&adapter, }; - if (!kvm_check_extension(kvm_state, KVM_CAP_IRQ_ROUTING)) { + if (!kvm_gsi_routing_enabled()) { /* nothing to do */ return 0; } @@ -226,7 +226,7 @@ static int kvm_s390_io_adapter_map(S390FLICState *fs, uint32_t id, KVMS390FLICState *flic = KVM_S390_FLIC(fs); int r; - if (!kvm_check_extension(kvm_state, KVM_CAP_IRQ_ROUTING)) { + if (!kvm_gsi_routing_enabled()) { /* nothing to do */ return 0; } diff --git a/hw/intc/trace-events b/hw/intc/trace-events index 180b893b15..92a6171692 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -114,6 +114,39 @@ gicv3_icc_hppir0_read(uint32_t cpu, uint64_t val) "GICv3 ICC_HPPIR0 read cpu %x gicv3_icc_hppir1_read(uint32_t cpu, uint64_t val) "GICv3 ICC_HPPIR1 read cpu %x value 0x%" PRIx64 gicv3_icc_dir_write(uint32_t cpu, uint64_t val) "GICv3 ICC_DIR write cpu %x value 0x%" PRIx64 gicv3_icc_rpr_read(uint32_t cpu, uint64_t val) "GICv3 ICC_RPR read cpu %x value 0x%" PRIx64 +gicv3_ich_ap_read(int grp, int regno, uint32_t cpu, uint64_t val) "GICv3 ICH_AP%dR%d read cpu %x value 0x%" PRIx64 +gicv3_ich_ap_write(int grp, int regno, uint32_t cpu, uint64_t val) "GICv3 ICH_AP%dR%d write cpu %x value 0x%" PRIx64 +gicv3_ich_hcr_read(uint32_t cpu, uint64_t val) "GICv3 ICH_HCR_EL2 read cpu %x value 0x%" PRIx64 +gicv3_ich_hcr_write(uint32_t cpu, uint64_t val) "GICv3 ICH_HCR_EL2 write cpu %x value 0x%" PRIx64 +gicv3_ich_vmcr_read(uint32_t cpu, uint64_t val) "GICv3 ICH_VMCR_EL2 read cpu %x value 0x%" PRIx64 +gicv3_ich_vmcr_write(uint32_t cpu, uint64_t val) "GICv3 ICH_VMCR_EL2 write cpu %x value 0x%" PRIx64 +gicv3_ich_lr_read(int regno, uint32_t cpu, uint64_t val) "GICv3 ICH_LR%d_EL2 read cpu %x value 0x%" PRIx64 +gicv3_ich_lr32_read(int regno, uint32_t cpu, uint32_t val) "GICv3 ICH_LR%d read cpu %x value 0x%" PRIx32 +gicv3_ich_lrc_read(int regno, uint32_t cpu, uint32_t val) "GICv3 ICH_LRC%d read cpu %x value 0x%" PRIx32 +gicv3_ich_lr_write(int regno, uint32_t cpu, uint64_t val) "GICv3 ICH_LR%d_EL2 write cpu %x value 0x%" PRIx64 +gicv3_ich_lr32_write(int regno, uint32_t cpu, uint32_t val) "GICv3 ICH_LR%d write cpu %x value 0x%" PRIx32 +gicv3_ich_lrc_write(int regno, uint32_t cpu, uint32_t val) "GICv3 ICH_LRC%d write cpu %x value 0x%" PRIx32 +gicv3_ich_vtr_read(uint32_t cpu, uint64_t val) "GICv3 ICH_VTR read cpu %x value 0x%" PRIx64 +gicv3_ich_misr_read(uint32_t cpu, uint64_t val) "GICv3 ICH_MISR read cpu %x value 0x%" PRIx64 +gicv3_ich_eisr_read(uint32_t cpu, uint64_t val) "GICv3 ICH_EISR read cpu %x value 0x%" PRIx64 +gicv3_ich_elrsr_read(uint32_t cpu, uint64_t val) "GICv3 ICH_ELRSR read cpu %x value 0x%" PRIx64 +gicv3_icv_ap_read(int grp, int regno, uint32_t cpu, uint64_t val) "GICv3 ICV_AP%dR%d read cpu %x value 0x%" PRIx64 +gicv3_icv_ap_write(int grp, int regno, uint32_t cpu, uint64_t val) "GICv3 ICV_AP%dR%d write cpu %x value 0x%" PRIx64 +gicv3_icv_bpr_read(int grp, uint32_t cpu, uint64_t val) "GICv3 ICV_BPR%d read cpu %x value 0x%" PRIx64 +gicv3_icv_bpr_write(int grp, uint32_t cpu, uint64_t val) "GICv3 ICV_BPR%d write cpu %x value 0x%" PRIx64 +gicv3_icv_pmr_read(uint32_t cpu, uint64_t val) "GICv3 ICV_PMR read cpu %x value 0x%" PRIx64 +gicv3_icv_pmr_write(uint32_t cpu, uint64_t val) "GICv3 ICV_PMR write cpu %x value 0x%" PRIx64 +gicv3_icv_igrpen_read(int grp, uint32_t cpu, uint64_t val) "GICv3 ICV_IGRPEN%d read cpu %x value 0x%" PRIx64 +gicv3_icv_igrpen_write(int grp, uint32_t cpu, uint64_t val) "GICv3 ICV_IGRPEN%d write cpu %x value 0x%" PRIx64 +gicv3_icv_ctlr_read(uint32_t cpu, uint64_t val) "GICv3 ICV_CTLR read cpu %x value 0x%" PRIx64 +gicv3_icv_ctlr_write(uint32_t cpu, uint64_t val) "GICv3 ICV_CTLR write cpu %x value 0x%" PRIx64 +gicv3_icv_rpr_read(uint32_t cpu, uint64_t val) "GICv3 ICV_RPR read cpu %x value 0x%" PRIx64 +gicv3_icv_hppir_read(int grp, uint32_t cpu, uint64_t val) "GICv3 ICV_HPPIR%d read cpu %x value 0x%" PRIx64 +gicv3_icv_dir_write(uint32_t cpu, uint64_t val) "GICv3 ICV_DIR write cpu %x value 0x%" PRIx64 +gicv3_icv_iar_read(int grp, uint32_t cpu, uint64_t val) "GICv3 ICV_IAR%d read cpu %x value 0x%" PRIx64 +gicv3_icv_eoir_write(int grp, uint32_t cpu, uint64_t val) "GICv3 ICV_EOIR%d write cpu %x value 0x%" PRIx64 +gicv3_cpuif_virt_update(uint32_t cpuid, int idx) "GICv3 CPU i/f %x virt HPPI update LR index %d" +gicv3_cpuif_virt_set_irqs(uint32_t cpuid, int fiqlevel, int irqlevel, int maintlevel) "GICv3 CPU i/f %x virt HPPI update: setting FIQ %d IRQ %d maintenance-irq %d" # hw/intc/arm_gicv3_dist.c gicv3_dist_read(uint64_t offset, uint64_t data, unsigned size, bool secure) "GICv3 distributor read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u secure %d" diff --git a/hw/lm32/lm32_hwsetup.h b/hw/lm32/lm32_hwsetup.h index 23e18784df..a01f6bc5df 100644 --- a/hw/lm32/lm32_hwsetup.h +++ b/hw/lm32/lm32_hwsetup.h @@ -75,7 +75,7 @@ static inline void hwsetup_create_rom(HWSetup *hw, hwaddr base) { rom_add_blob("hwsetup", hw->data, TARGET_PAGE_SIZE, - TARGET_PAGE_SIZE, base, NULL, NULL, NULL, NULL); + TARGET_PAGE_SIZE, base, NULL, NULL, NULL, NULL, true); } static inline void hwsetup_add_u8(HWSetup *hw, uint8_t u) diff --git a/hw/m68k/mcf5208.c b/hw/m68k/mcf5208.c index 3438314c35..bad1d332ed 100644 --- a/hw/m68k/mcf5208.c +++ b/hw/m68k/mcf5208.c @@ -11,6 +11,7 @@ #include "cpu.h" #include "hw/hw.h" #include "hw/m68k/mcf.h" +#include "hw/m68k/mcf_fec.h" #include "qemu/timer.h" #include "hw/ptimer.h" #include "sysemu/sysemu.h" @@ -18,6 +19,7 @@ #include "net/net.h" #include "hw/boards.h" #include "hw/loader.h" +#include "hw/sysbus.h" #include "elf.h" #include "exec/address-spaces.h" @@ -192,6 +194,26 @@ static void mcf5208_sys_init(MemoryRegion *address_space, qemu_irq *pic) } } +static void mcf_fec_init(MemoryRegion *sysmem, NICInfo *nd, hwaddr base, + qemu_irq *irqs) +{ + DeviceState *dev; + SysBusDevice *s; + int i; + + qemu_check_nic_model(nd, TYPE_MCF_FEC_NET); + dev = qdev_create(NULL, TYPE_MCF_FEC_NET); + qdev_set_nic_properties(dev, nd); + qdev_init_nofail(dev); + + s = SYS_BUS_DEVICE(dev); + for (i = 0; i < FEC_NUM_IRQ; i++) { + sysbus_connect_irq(s, i, irqs[i]); + } + + memory_region_add_subregion(sysmem, base, sysbus_mmio_get_region(s, 0)); +} + static void mcf5208evb_init(MachineState *machine) { ram_addr_t ram_size = machine->ram_size; @@ -243,9 +265,10 @@ static void mcf5208evb_init(MachineState *machine) fprintf(stderr, "Too many NICs\n"); exit(1); } - if (nd_table[0].used) + if (nd_table[0].used) { mcf_fec_init(address_space_mem, &nd_table[0], 0xfc030000, pic + 36); + } /* 0xfc000000 SCM. */ /* 0xfc004000 XBS. */ diff --git a/hw/net/dp8393x.c b/hw/net/dp8393x.c index 17f0338d1c..efa33ad40a 100644 --- a/hw/net/dp8393x.c +++ b/hw/net/dp8393x.c @@ -174,6 +174,52 @@ typedef struct dp8393xState { AddressSpace as; } dp8393xState; +/* Accessor functions for values which are formed by + * concatenating two 16 bit device registers. By putting these + * in their own functions with a uint32_t return type we avoid the + * pitfall of implicit sign extension where ((x << 16) | y) is a + * signed 32 bit integer that might get sign-extended to a 64 bit integer. + */ +static uint32_t dp8393x_cdp(dp8393xState *s) +{ + return (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP]; +} + +static uint32_t dp8393x_crba(dp8393xState *s) +{ + return (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0]; +} + +static uint32_t dp8393x_crda(dp8393xState *s) +{ + return (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]; +} + +static uint32_t dp8393x_rbwc(dp8393xState *s) +{ + return (s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0]; +} + +static uint32_t dp8393x_rrp(dp8393xState *s) +{ + return (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_RRP]; +} + +static uint32_t dp8393x_tsa(dp8393xState *s) +{ + return (s->regs[SONIC_TSA1] << 16) | s->regs[SONIC_TSA0]; +} + +static uint32_t dp8393x_ttda(dp8393xState *s) +{ + return (s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]; +} + +static uint32_t dp8393x_wt(dp8393xState *s) +{ + return s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0]; +} + static void dp8393x_update_irq(dp8393xState *s) { int level = (s->regs[SONIC_IMR] & s->regs[SONIC_ISR]) ? 1 : 0; @@ -203,8 +249,7 @@ static void dp8393x_do_load_cam(dp8393xState *s) while (s->regs[SONIC_CDC] & 0x1f) { /* Fill current entry */ - address_space_rw(&s->as, - (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP], + address_space_rw(&s->as, dp8393x_cdp(s), MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0); s->cam[index][0] = data[1 * width] & 0xff; s->cam[index][1] = data[1 * width] >> 8; @@ -222,8 +267,7 @@ static void dp8393x_do_load_cam(dp8393xState *s) } /* Read CAM enable */ - address_space_rw(&s->as, - (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP], + address_space_rw(&s->as, dp8393x_cdp(s), MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0); s->regs[SONIC_CE] = data[0 * width]; DPRINTF("load cam done. cam enable mask 0x%04x\n", s->regs[SONIC_CE]); @@ -242,8 +286,7 @@ static void dp8393x_do_read_rra(dp8393xState *s) /* Read memory */ width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; size = sizeof(uint16_t) * 4 * width; - address_space_rw(&s->as, - (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_RRP], + address_space_rw(&s->as, dp8393x_rrp(s), MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0); /* Update SONIC registers */ @@ -292,7 +335,7 @@ static void dp8393x_set_next_tick(dp8393xState *s) return; } - ticks = s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0]; + ticks = dp8393x_wt(s); s->wt_last_update = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); delay = NANOSECONDS_PER_SECOND * ticks / 5000000; timer_mod(s->watchdog, s->wt_last_update + delay); @@ -309,7 +352,7 @@ static void dp8393x_update_wt_regs(dp8393xState *s) } elapsed = s->wt_last_update - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - val = s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0]; + val = dp8393x_wt(s); val -= elapsed / 5000000; s->regs[SONIC_WT1] = (val >> 16) & 0xffff; s->regs[SONIC_WT0] = (val >> 0) & 0xffff; @@ -356,12 +399,11 @@ static void dp8393x_do_transmit_packets(dp8393xState *s) while (1) { /* Read memory */ - DPRINTF("Transmit packet at %08x\n", - (s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_CTDA]); size = sizeof(uint16_t) * 6 * width; s->regs[SONIC_TTDA] = s->regs[SONIC_CTDA]; + DPRINTF("Transmit packet at %08x\n", dp8393x_ttda(s)); address_space_rw(&s->as, - ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * width, + dp8393x_ttda(s) + sizeof(uint16_t) * width, MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0); tx_len = 0; @@ -386,8 +428,7 @@ static void dp8393x_do_transmit_packets(dp8393xState *s) if (tx_len + len > sizeof(s->tx_buffer)) { len = sizeof(s->tx_buffer) - tx_len; } - address_space_rw(&s->as, - (s->regs[SONIC_TSA1] << 16) | s->regs[SONIC_TSA0], + address_space_rw(&s->as, dp8393x_tsa(s), MEMTXATTRS_UNSPECIFIED, &s->tx_buffer[tx_len], len, 0); tx_len += len; @@ -396,7 +437,7 @@ static void dp8393x_do_transmit_packets(dp8393xState *s) /* Read next fragment details */ size = sizeof(uint16_t) * 3 * width; address_space_rw(&s->as, - ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * (4 + 3 * i) * width, + dp8393x_ttda(s) + sizeof(uint16_t) * (4 + 3 * i) * width, MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0); s->regs[SONIC_TSA0] = data[0 * width]; s->regs[SONIC_TSA1] = data[1 * width]; @@ -430,14 +471,16 @@ static void dp8393x_do_transmit_packets(dp8393xState *s) data[0 * width] = s->regs[SONIC_TCR] & 0x0fff; /* status */ size = sizeof(uint16_t) * width; address_space_rw(&s->as, - (s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA], + dp8393x_ttda(s), MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 1); if (!(s->regs[SONIC_CR] & SONIC_CR_HTX)) { /* Read footer of packet */ size = sizeof(uint16_t) * width; address_space_rw(&s->as, - ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * (4 + 3 * s->regs[SONIC_TFC]) * width, + dp8393x_ttda(s) + + sizeof(uint16_t) * + (4 + 3 * s->regs[SONIC_TFC]) * width, MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0); s->regs[SONIC_CTDA] = data[0 * width] & ~0x1; if (data[0 * width] & 0x1) { @@ -700,7 +743,7 @@ static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf, if (s->regs[SONIC_LLFA] & 0x1) { /* Are we still in resource exhaustion? */ size = sizeof(uint16_t) * 1 * width; - address = ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 5 * width; + address = dp8393x_crda(s) + sizeof(uint16_t) * 5 * width; address_space_rw(&s->as, address, MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0); if (data[0 * width] & 0x1) { @@ -719,8 +762,8 @@ static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf, checksum = cpu_to_le32(crc32(0, buf, rx_len)); /* Put packet into RBA */ - DPRINTF("Receive packet at %08x\n", (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0]); - address = (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0]; + DPRINTF("Receive packet at %08x\n", dp8393x_crba(s)); + address = dp8393x_crba(s); address_space_rw(&s->as, address, MEMTXATTRS_UNSPECIFIED, (uint8_t *)buf, rx_len, 1); address += rx_len; @@ -729,13 +772,13 @@ static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf, rx_len += 4; s->regs[SONIC_CRBA1] = address >> 16; s->regs[SONIC_CRBA0] = address & 0xffff; - available = (s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0]; + available = dp8393x_rbwc(s); available -= rx_len / 2; s->regs[SONIC_RBWC1] = available >> 16; s->regs[SONIC_RBWC0] = available & 0xffff; /* Update status */ - if (((s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0]) < s->regs[SONIC_EOBC]) { + if (dp8393x_rbwc(s) < s->regs[SONIC_EOBC]) { s->regs[SONIC_RCR] |= SONIC_RCR_LPKT; } s->regs[SONIC_RCR] |= packet_type; @@ -746,20 +789,19 @@ static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf, } /* Write status to memory */ - DPRINTF("Write status at %08x\n", (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]); + DPRINTF("Write status at %08x\n", dp8393x_crda(s)); data[0 * width] = s->regs[SONIC_RCR]; /* status */ data[1 * width] = rx_len; /* byte count */ data[2 * width] = s->regs[SONIC_TRBA0]; /* pkt_ptr0 */ data[3 * width] = s->regs[SONIC_TRBA1]; /* pkt_ptr1 */ data[4 * width] = s->regs[SONIC_RSC]; /* seq_no */ size = sizeof(uint16_t) * 5 * width; - address_space_rw(&s->as, (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA], + address_space_rw(&s->as, dp8393x_crda(s), MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 1); /* Move to next descriptor */ size = sizeof(uint16_t) * width; - address_space_rw(&s->as, - ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 5 * width, + address_space_rw(&s->as, dp8393x_crda(s) + sizeof(uint16_t) * 5 * width, MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0); s->regs[SONIC_LLFA] = data[0 * width]; if (s->regs[SONIC_LLFA] & 0x1) { @@ -767,8 +809,7 @@ static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf, s->regs[SONIC_ISR] |= SONIC_ISR_RDE; } else { data[0 * width] = 0; /* in_use */ - address_space_rw(&s->as, - ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 6 * width, + address_space_rw(&s->as, dp8393x_crda(s) + sizeof(uint16_t) * 6 * width, MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, sizeof(uint16_t), 1); s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA]; s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX; diff --git a/hw/net/mcf_fec.c b/hw/net/mcf_fec.c index 4025eb3b33..a3eca7e0f5 100644 --- a/hw/net/mcf_fec.c +++ b/hw/net/mcf_fec.c @@ -9,7 +9,9 @@ #include "hw/hw.h" #include "net/net.h" #include "hw/m68k/mcf.h" +#include "hw/m68k/mcf_fec.h" #include "hw/net/mii.h" +#include "hw/sysbus.h" /* For crc32 */ #include <zlib.h> #include "exec/address-spaces.h" @@ -27,9 +29,10 @@ do { printf("mcf_fec: " fmt , ## __VA_ARGS__); } while (0) #define FEC_MAX_FRAME_SIZE 2032 typedef struct { - MemoryRegion *sysmem; + SysBusDevice parent_obj; + MemoryRegion iomem; - qemu_irq *irq; + qemu_irq irq[FEC_NUM_IRQ]; NICState *nic; NICConf conf; uint32_t irq_state; @@ -68,7 +71,6 @@ typedef struct { #define FEC_RESET 1 /* Map interrupt flags onto IRQ lines. */ -#define FEC_NUM_IRQ 13 static const uint32_t mcf_fec_irq_map[FEC_NUM_IRQ] = { FEC_INT_TXF, FEC_INT_TXB, @@ -208,8 +210,10 @@ static void mcf_fec_enable_rx(mcf_fec_state *s) } } -static void mcf_fec_reset(mcf_fec_state *s) +static void mcf_fec_reset(DeviceState *dev) { + mcf_fec_state *s = MCF_FEC_NET(dev); + s->eir = 0; s->eimr = 0; s->rx_enabled = 0; @@ -330,7 +334,7 @@ static void mcf_fec_write(void *opaque, hwaddr addr, s->ecr = value; if (value & FEC_RESET) { DPRINTF("Reset\n"); - mcf_fec_reset(s); + mcf_fec_reset(opaque); } if ((s->ecr & FEC_EN) == 0) { s->rx_enabled = 0; @@ -513,24 +517,55 @@ static NetClientInfo net_mcf_fec_info = { .receive = mcf_fec_receive, }; -void mcf_fec_init(MemoryRegion *sysmem, NICInfo *nd, - hwaddr base, qemu_irq *irq) +static void mcf_fec_realize(DeviceState *dev, Error **errp) { - mcf_fec_state *s; + mcf_fec_state *s = MCF_FEC_NET(dev); + + s->nic = qemu_new_nic(&net_mcf_fec_info, &s->conf, + object_get_typename(OBJECT(dev)), dev->id, s); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); +} - qemu_check_nic_model(nd, "mcf_fec"); +static void mcf_fec_instance_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + mcf_fec_state *s = MCF_FEC_NET(obj); + int i; + + memory_region_init_io(&s->iomem, obj, &mcf_fec_ops, s, "fec", 0x400); + sysbus_init_mmio(sbd, &s->iomem); + for (i = 0; i < FEC_NUM_IRQ; i++) { + sysbus_init_irq(sbd, &s->irq[i]); + } +} - s = (mcf_fec_state *)g_malloc0(sizeof(mcf_fec_state)); - s->sysmem = sysmem; - s->irq = irq; +static Property mcf_fec_properties[] = { + DEFINE_NIC_PROPERTIES(mcf_fec_state, conf), + DEFINE_PROP_END_OF_LIST(), +}; - memory_region_init_io(&s->iomem, NULL, &mcf_fec_ops, s, "fec", 0x400); - memory_region_add_subregion(sysmem, base, &s->iomem); +static void mcf_fec_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); - s->conf.macaddr = nd->macaddr; - s->conf.peers.ncs[0] = nd->netdev; + set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); + dc->realize = mcf_fec_realize; + dc->desc = "MCF Fast Ethernet Controller network device"; + dc->reset = mcf_fec_reset; + dc->props = mcf_fec_properties; +} - s->nic = qemu_new_nic(&net_mcf_fec_info, &s->conf, nd->model, nd->name, s); +static const TypeInfo mcf_fec_info = { + .name = TYPE_MCF_FEC_NET, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(mcf_fec_state), + .instance_init = mcf_fec_instance_init, + .class_init = mcf_fec_class_init, +}; - qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); +static void mcf_fec_register_types(void) +{ + type_register_static(&mcf_fec_info); } + +type_init(mcf_fec_register_types) diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index 6280422d02..22874a9777 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -52,6 +52,7 @@ static const int kernel_feature_bits[] = { VIRTIO_NET_F_MRG_RXBUF, VIRTIO_F_VERSION_1, VIRTIO_NET_F_MTU, + VIRTIO_F_IOMMU_PLATFORM, VHOST_INVALID_FEATURE_BIT }; diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index 3ebecb2260..523d585dcf 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -33,6 +33,9 @@ #include "qemu/error-report.h" #include "qemu/config-file.h" #include "qemu/cutils.h" +#include "qapi/error.h" + +#define FW_CFG_FILE_SLOTS_DFLT 0x20 #define FW_CFG_NAME "fw_cfg" #define FW_CFG_PATH "/machine/" FW_CFG_NAME @@ -54,11 +57,13 @@ #define FW_CFG_DMA_CTL_READ 0x02 #define FW_CFG_DMA_CTL_SKIP 0x04 #define FW_CFG_DMA_CTL_SELECT 0x08 +#define FW_CFG_DMA_CTL_WRITE 0x10 #define FW_CFG_DMA_SIGNATURE 0x51454d5520434647ULL /* "QEMU CFG" */ typedef struct FWCfgEntry { uint32_t len; + bool allow_write; uint8_t *data; void *callback_opaque; FWCfgReadCallback read_callback; @@ -69,8 +74,9 @@ struct FWCfgState { SysBusDevice parent_obj; /*< public >*/ - FWCfgEntry entries[2][FW_CFG_MAX_ENTRY]; - int entry_order[FW_CFG_MAX_ENTRY]; + uint16_t file_slots; + FWCfgEntry *entries[2]; + int *entry_order; FWCfgFiles *files; uint16_t cur_entry; uint32_t cur_offset; @@ -255,13 +261,24 @@ static void fw_cfg_write(FWCfgState *s, uint8_t value) /* nothing, write support removed in QEMU v2.4+ */ } +static inline uint16_t fw_cfg_file_slots(const FWCfgState *s) +{ + return s->file_slots; +} + +/* Note: this function returns an exclusive limit. */ +static inline uint32_t fw_cfg_max_entry(const FWCfgState *s) +{ + return FW_CFG_FILE_FIRST + fw_cfg_file_slots(s); +} + static int fw_cfg_select(FWCfgState *s, uint16_t key) { int arch, ret; FWCfgEntry *e; s->cur_offset = 0; - if ((key & FW_CFG_ENTRY_MASK) >= FW_CFG_MAX_ENTRY) { + if ((key & FW_CFG_ENTRY_MASK) >= fw_cfg_max_entry(s)) { s->cur_entry = FW_CFG_INVALID; ret = 0; } else { @@ -326,7 +343,7 @@ static void fw_cfg_dma_transfer(FWCfgState *s) FWCfgDmaAccess dma; int arch; FWCfgEntry *e; - int read; + int read = 0, write = 0; dma_addr_t dma_addr; /* Reset the address before the next access */ @@ -353,8 +370,13 @@ static void fw_cfg_dma_transfer(FWCfgState *s) if (dma.control & FW_CFG_DMA_CTL_READ) { read = 1; + write = 0; + } else if (dma.control & FW_CFG_DMA_CTL_WRITE) { + read = 0; + write = 1; } else if (dma.control & FW_CFG_DMA_CTL_SKIP) { read = 0; + write = 0; } else { dma.length = 0; } @@ -374,7 +396,9 @@ static void fw_cfg_dma_transfer(FWCfgState *s) dma.control |= FW_CFG_DMA_CTL_ERROR; } } - + if (write) { + dma.control |= FW_CFG_DMA_CTL_ERROR; + } } else { if (dma.length <= (e->len - s->cur_offset)) { len = dma.length; @@ -391,6 +415,14 @@ static void fw_cfg_dma_transfer(FWCfgState *s) dma.control |= FW_CFG_DMA_CTL_ERROR; } } + if (write) { + if (!e->allow_write || + len != dma.length || + dma_memory_read(s->dma_as, dma.address, + &e->data[s->cur_offset], len)) { + dma.control |= FW_CFG_DMA_CTL_ERROR; + } + } s->cur_offset += len; } @@ -586,19 +618,21 @@ static const VMStateDescription vmstate_fw_cfg = { static void fw_cfg_add_bytes_read_callback(FWCfgState *s, uint16_t key, FWCfgReadCallback callback, void *callback_opaque, - void *data, size_t len) + void *data, size_t len, + bool read_only) { int arch = !!(key & FW_CFG_ARCH_LOCAL); key &= FW_CFG_ENTRY_MASK; - assert(key < FW_CFG_MAX_ENTRY && len < UINT32_MAX); + assert(key < fw_cfg_max_entry(s) && len < UINT32_MAX); assert(s->entries[arch][key].data == NULL); /* avoid key conflict */ s->entries[arch][key].data = data; s->entries[arch][key].len = (uint32_t)len; s->entries[arch][key].read_callback = callback; s->entries[arch][key].callback_opaque = callback_opaque; + s->entries[arch][key].allow_write = !read_only; } static void *fw_cfg_modify_bytes_read(FWCfgState *s, uint16_t key, @@ -609,20 +643,21 @@ static void *fw_cfg_modify_bytes_read(FWCfgState *s, uint16_t key, key &= FW_CFG_ENTRY_MASK; - assert(key < FW_CFG_MAX_ENTRY && len < UINT32_MAX); + assert(key < fw_cfg_max_entry(s) && len < UINT32_MAX); /* return the old data to the function caller, avoid memory leak */ ptr = s->entries[arch][key].data; s->entries[arch][key].data = data; s->entries[arch][key].len = len; s->entries[arch][key].callback_opaque = NULL; + s->entries[arch][key].allow_write = false; return ptr; } void fw_cfg_add_bytes(FWCfgState *s, uint16_t key, void *data, size_t len) { - fw_cfg_add_bytes_read_callback(s, key, NULL, NULL, data, len); + fw_cfg_add_bytes_read_callback(s, key, NULL, NULL, data, len, true); } void fw_cfg_add_string(FWCfgState *s, uint16_t key, const char *value) @@ -749,7 +784,7 @@ static int get_fw_cfg_order(FWCfgState *s, const char *name) void fw_cfg_add_file_callback(FWCfgState *s, const char *filename, FWCfgReadCallback callback, void *callback_opaque, - void *data, size_t len) + void *data, size_t len, bool read_only) { int i, index, count; size_t dsize; @@ -757,13 +792,13 @@ void fw_cfg_add_file_callback(FWCfgState *s, const char *filename, int order = 0; if (!s->files) { - dsize = sizeof(uint32_t) + sizeof(FWCfgFile) * FW_CFG_FILE_SLOTS; + dsize = sizeof(uint32_t) + sizeof(FWCfgFile) * fw_cfg_file_slots(s); s->files = g_malloc0(dsize); fw_cfg_add_bytes(s, FW_CFG_FILE_DIR, s->files, dsize); } count = be32_to_cpu(s->files->count); - assert(count < FW_CFG_FILE_SLOTS); + assert(count < fw_cfg_file_slots(s)); /* Find the insertion point. */ if (mc->legacy_fw_cfg_order) { @@ -811,7 +846,8 @@ void fw_cfg_add_file_callback(FWCfgState *s, const char *filename, } fw_cfg_add_bytes_read_callback(s, FW_CFG_FILE_FIRST + index, - callback, callback_opaque, data, len); + callback, callback_opaque, data, len, + read_only); s->files->f[index].size = cpu_to_be32(len); s->files->f[index].select = cpu_to_be16(FW_CFG_FILE_FIRST + index); @@ -824,7 +860,7 @@ void fw_cfg_add_file_callback(FWCfgState *s, const char *filename, void fw_cfg_add_file(FWCfgState *s, const char *filename, void *data, size_t len) { - fw_cfg_add_file_callback(s, filename, NULL, NULL, data, len); + fw_cfg_add_file_callback(s, filename, NULL, NULL, data, len, true); } void *fw_cfg_modify_file(FWCfgState *s, const char *filename, @@ -836,7 +872,7 @@ void *fw_cfg_modify_file(FWCfgState *s, const char *filename, assert(s->files); index = be32_to_cpu(s->files->count); - assert(index < FW_CFG_FILE_SLOTS); + assert(index < fw_cfg_file_slots(s)); for (i = 0; i < index; i++) { if (strcmp(filename, s->files->f[i].name) == 0) { @@ -847,7 +883,7 @@ void *fw_cfg_modify_file(FWCfgState *s, const char *filename, } } /* add new one */ - fw_cfg_add_file_callback(s, filename, NULL, NULL, data, len); + fw_cfg_add_file_callback(s, filename, NULL, NULL, data, len, true); return NULL; } @@ -993,12 +1029,38 @@ static const TypeInfo fw_cfg_info = { .class_init = fw_cfg_class_init, }; +static void fw_cfg_file_slots_allocate(FWCfgState *s, Error **errp) +{ + uint16_t file_slots_max; + + if (fw_cfg_file_slots(s) < FW_CFG_FILE_SLOTS_MIN) { + error_setg(errp, "\"file_slots\" must be at least 0x%x", + FW_CFG_FILE_SLOTS_MIN); + return; + } + + /* (UINT16_MAX & FW_CFG_ENTRY_MASK) is the highest inclusive selector value + * that we permit. The actual (exclusive) value coming from the + * configuration is (FW_CFG_FILE_FIRST + fw_cfg_file_slots(s)). */ + file_slots_max = (UINT16_MAX & FW_CFG_ENTRY_MASK) - FW_CFG_FILE_FIRST + 1; + if (fw_cfg_file_slots(s) > file_slots_max) { + error_setg(errp, "\"file_slots\" must not exceed 0x%" PRIx16, + file_slots_max); + return; + } + + s->entries[0] = g_new0(FWCfgEntry, fw_cfg_max_entry(s)); + s->entries[1] = g_new0(FWCfgEntry, fw_cfg_max_entry(s)); + s->entry_order = g_new0(int, fw_cfg_max_entry(s)); +} static Property fw_cfg_io_properties[] = { DEFINE_PROP_UINT32("iobase", FWCfgIoState, iobase, -1), DEFINE_PROP_UINT32("dma_iobase", FWCfgIoState, dma_iobase, -1), DEFINE_PROP_BOOL("dma_enabled", FWCfgIoState, parent_obj.dma_enabled, true), + DEFINE_PROP_UINT16("x-file-slots", FWCfgIoState, parent_obj.file_slots, + FW_CFG_FILE_SLOTS_DFLT), DEFINE_PROP_END_OF_LIST(), }; @@ -1006,6 +1068,13 @@ static void fw_cfg_io_realize(DeviceState *dev, Error **errp) { FWCfgIoState *s = FW_CFG_IO(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Error *local_err = NULL; + + fw_cfg_file_slots_allocate(FW_CFG(s), &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } /* when using port i/o, the 8-bit data register ALWAYS overlaps * with half of the 16-bit control register. Hence, the total size @@ -1042,6 +1111,8 @@ static Property fw_cfg_mem_properties[] = { DEFINE_PROP_UINT32("data_width", FWCfgMemState, data_width, -1), DEFINE_PROP_BOOL("dma_enabled", FWCfgMemState, parent_obj.dma_enabled, true), + DEFINE_PROP_UINT16("x-file-slots", FWCfgMemState, parent_obj.file_slots, + FW_CFG_FILE_SLOTS_DFLT), DEFINE_PROP_END_OF_LIST(), }; @@ -1050,6 +1121,13 @@ static void fw_cfg_mem_realize(DeviceState *dev, Error **errp) FWCfgMemState *s = FW_CFG_MEM(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); const MemoryRegionOps *data_ops = &fw_cfg_data_mem_ops; + Error *local_err = NULL; + + fw_cfg_file_slots_allocate(FW_CFG(s), &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } memory_region_init_io(&s->ctl_iomem, OBJECT(s), &fw_cfg_ctl_mem_ops, FW_CFG(s), "fwcfg.ctl", FW_CFG_CTL_SIZE); diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 637d54549e..fe9acecbbf 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -1779,7 +1779,6 @@ PCIDevice *pci_nic_init_nofail(NICInfo *nd, PCIBus *rootbus, const char *default_devaddr) { const char *devaddr = nd->devaddr ? nd->devaddr : default_devaddr; - Error *err = NULL; PCIBus *bus; PCIDevice *pci_dev; DeviceState *dev; @@ -1805,13 +1804,7 @@ PCIDevice *pci_nic_init_nofail(NICInfo *nd, PCIBus *rootbus, pci_dev = pci_create(bus, devfn, pci_nic_names[i]); dev = &pci_dev->qdev; qdev_set_nic_properties(dev, nd); - - object_property_set_bool(OBJECT(dev), true, "realized", &err); - if (err) { - error_report_err(err); - object_unparent(OBJECT(dev)); - exit(1); - } + qdev_init_nofail(dev); return pci_dev; } diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 63f6248f1d..69b0291e8a 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -19,6 +19,7 @@ #include "s390-pci-bus.h" #include "s390-pci-inst.h" #include "hw/pci/pci_bus.h" +#include "hw/pci/pci_bridge.h" #include "hw/pci/msi.h" #include "qemu/error-report.h" @@ -31,7 +32,7 @@ do { } while (0) #endif -static S390pciState *s390_get_phb(void) +S390pciState *s390_get_phb(void) { static S390pciState *phb; @@ -91,35 +92,25 @@ int chsc_sei_nt2_have_event(void) return !QTAILQ_EMPTY(&s->pending_sei); } -S390PCIBusDevice *s390_pci_find_next_avail_dev(S390PCIBusDevice *pbdev) +S390PCIBusDevice *s390_pci_find_next_avail_dev(S390pciState *s, + S390PCIBusDevice *pbdev) { - int idx = 0; - S390PCIBusDevice *dev = NULL; - S390pciState *s = s390_get_phb(); - - if (pbdev) { - idx = (pbdev->fh & FH_MASK_INDEX) + 1; - } + S390PCIBusDevice *ret = pbdev ? QTAILQ_NEXT(pbdev, link) : + QTAILQ_FIRST(&s->zpci_devs); - for (; idx < PCI_SLOT_MAX; idx++) { - dev = s->pbdev[idx]; - if (dev && dev->state != ZPCI_FS_RESERVED) { - return dev; - } + while (ret && ret->state == ZPCI_FS_RESERVED) { + ret = QTAILQ_NEXT(ret, link); } - return NULL; + return ret; } -S390PCIBusDevice *s390_pci_find_dev_by_fid(uint32_t fid) +S390PCIBusDevice *s390_pci_find_dev_by_fid(S390pciState *s, uint32_t fid) { S390PCIBusDevice *pbdev; - int i; - S390pciState *s = s390_get_phb(); - for (i = 0; i < PCI_SLOT_MAX; i++) { - pbdev = s->pbdev[i]; - if (pbdev && pbdev->fid == fid) { + QTAILQ_FOREACH(pbdev, &s->zpci_devs, link) { + if (pbdev->fid == fid) { return pbdev; } } @@ -130,7 +121,8 @@ S390PCIBusDevice *s390_pci_find_dev_by_fid(uint32_t fid) void s390_pci_sclp_configure(SCCB *sccb) { PciCfgSccb *psccb = (PciCfgSccb *)sccb; - S390PCIBusDevice *pbdev = s390_pci_find_dev_by_fid(be32_to_cpu(psccb->aid)); + S390PCIBusDevice *pbdev = s390_pci_find_dev_by_fid(s390_get_phb(), + be32_to_cpu(psccb->aid)); uint16_t rc; if (be16_to_cpu(sccb->h.length) < 16) { @@ -162,7 +154,8 @@ out: void s390_pci_sclp_deconfigure(SCCB *sccb) { PciCfgSccb *psccb = (PciCfgSccb *)sccb; - S390PCIBusDevice *pbdev = s390_pci_find_dev_by_fid(be32_to_cpu(psccb->aid)); + S390PCIBusDevice *pbdev = s390_pci_find_dev_by_fid(s390_get_phb(), + be32_to_cpu(psccb->aid)); uint16_t rc; if (be16_to_cpu(sccb->h.length) < 16) { @@ -187,8 +180,8 @@ void s390_pci_sclp_deconfigure(SCCB *sccb) if (pbdev->summary_ind) { pci_dereg_irqs(pbdev); } - if (pbdev->iommu_enabled) { - pci_dereg_ioat(pbdev); + if (pbdev->iommu->enabled) { + pci_dereg_ioat(pbdev->iommu); } pbdev->state = ZPCI_FS_STANDBY; rc = SCLP_RC_NORMAL_COMPLETION; @@ -201,18 +194,11 @@ out: psccb->header.response_code = cpu_to_be16(rc); } -static S390PCIBusDevice *s390_pci_find_dev_by_uid(uint16_t uid) +static S390PCIBusDevice *s390_pci_find_dev_by_uid(S390pciState *s, uint16_t uid) { - int i; S390PCIBusDevice *pbdev; - S390pciState *s = s390_get_phb(); - - for (i = 0; i < PCI_SLOT_MAX; i++) { - pbdev = s->pbdev[i]; - if (!pbdev) { - continue; - } + QTAILQ_FOREACH(pbdev, &s->zpci_devs, link) { if (pbdev->uid == uid) { return pbdev; } @@ -221,22 +207,16 @@ static S390PCIBusDevice *s390_pci_find_dev_by_uid(uint16_t uid) return NULL; } -static S390PCIBusDevice *s390_pci_find_dev_by_target(const char *target) +static S390PCIBusDevice *s390_pci_find_dev_by_target(S390pciState *s, + const char *target) { - int i; S390PCIBusDevice *pbdev; - S390pciState *s = s390_get_phb(); if (!target) { return NULL; } - for (i = 0; i < PCI_SLOT_MAX; i++) { - pbdev = s->pbdev[i]; - if (!pbdev) { - continue; - } - + QTAILQ_FOREACH(pbdev, &s->zpci_devs, link) { if (!strcmp(pbdev->target, target)) { return pbdev; } @@ -245,19 +225,16 @@ static S390PCIBusDevice *s390_pci_find_dev_by_target(const char *target) return NULL; } -S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx) +S390PCIBusDevice *s390_pci_find_dev_by_idx(S390pciState *s, uint32_t idx) { - S390pciState *s = s390_get_phb(); - - return s->pbdev[idx & FH_MASK_INDEX]; + return g_hash_table_lookup(s->zpci_table, &idx); } -S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh) +S390PCIBusDevice *s390_pci_find_dev_by_fh(S390pciState *s, uint32_t fh) { - S390pciState *s = s390_get_phb(); - S390PCIBusDevice *pbdev; + uint32_t idx = FH_MASK_INDEX & fh; + S390PCIBusDevice *pbdev = s390_pci_find_dev_by_idx(s, idx); - pbdev = s->pbdev[fh & FH_MASK_INDEX]; if (pbdev && pbdev->fh == fh) { return pbdev; } @@ -377,12 +354,12 @@ out: return pte; } -static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *iommu, hwaddr addr, +static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *mr, hwaddr addr, bool is_write) { uint64_t pte; uint32_t flags; - S390PCIBusDevice *pbdev = container_of(iommu, S390PCIBusDevice, iommu_mr); + S390PCIIOMMU *iommu = container_of(mr, S390PCIIOMMU, iommu_mr); IOMMUTLBEntry ret = { .target_as = &address_space_memory, .iova = 0, @@ -391,10 +368,10 @@ static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *iommu, hwaddr addr, .perm = IOMMU_NONE, }; - switch (pbdev->state) { + switch (iommu->pbdev->state) { case ZPCI_FS_ENABLED: case ZPCI_FS_BLOCKED: - if (!pbdev->iommu_enabled) { + if (!iommu->enabled) { return ret; } break; @@ -404,11 +381,11 @@ static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *iommu, hwaddr addr, DPRINTF("iommu trans addr 0x%" PRIx64 "\n", addr); - if (addr < pbdev->pba || addr > pbdev->pal) { + if (addr < iommu->pba || addr > iommu->pal) { return ret; } - pte = s390_guest_io_table_walk(s390_pci_get_table_origin(pbdev->g_iota), + pte = s390_guest_io_table_walk(s390_pci_get_table_origin(iommu->g_iota), addr); if (!pte) { return ret; @@ -432,11 +409,48 @@ static const MemoryRegionIOMMUOps s390_iommu_ops = { .translate = s390_translate_iommu, }; +static S390PCIIOMMU *s390_pci_get_iommu(S390pciState *s, PCIBus *bus, + int devfn) +{ + uint64_t key = (uintptr_t)bus; + S390PCIIOMMUTable *table = g_hash_table_lookup(s->iommu_table, &key); + S390PCIIOMMU *iommu; + + if (!table) { + table = g_malloc0(sizeof(S390PCIIOMMUTable)); + table->key = key; + g_hash_table_insert(s->iommu_table, &table->key, table); + } + + iommu = table->iommu[PCI_SLOT(devfn)]; + if (!iommu) { + iommu = S390_PCI_IOMMU(object_new(TYPE_S390_PCI_IOMMU)); + + char *mr_name = g_strdup_printf("iommu-root-%02x:%02x.%01x", + pci_bus_num(bus), + PCI_SLOT(devfn), + PCI_FUNC(devfn)); + char *as_name = g_strdup_printf("iommu-pci-%02x:%02x.%01x", + pci_bus_num(bus), + PCI_SLOT(devfn), + PCI_FUNC(devfn)); + memory_region_init(&iommu->mr, OBJECT(iommu), mr_name, UINT64_MAX); + address_space_init(&iommu->as, &iommu->mr, as_name); + table->iommu[PCI_SLOT(devfn)] = iommu; + + g_free(mr_name); + g_free(as_name); + } + + return iommu; +} + static AddressSpace *s390_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn) { S390pciState *s = opaque; + S390PCIIOMMU *iommu = s390_pci_get_iommu(s, bus, devfn); - return &s->iommu[PCI_SLOT(devfn)]->as; + return &iommu->as; } static uint8_t set_ind_atomic(uint64_t ind_loc, uint8_t to_be_set) @@ -503,34 +517,38 @@ static const MemoryRegionOps s390_msi_ctrl_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -void s390_pci_iommu_enable(S390PCIBusDevice *pbdev) +void s390_pci_iommu_enable(S390PCIIOMMU *iommu) { - memory_region_init_iommu(&pbdev->iommu_mr, OBJECT(&pbdev->iommu->mr), - &s390_iommu_ops, "iommu-s390", pbdev->pal + 1); - memory_region_add_subregion(&pbdev->iommu->mr, 0, &pbdev->iommu_mr); - pbdev->iommu_enabled = true; + char *name = g_strdup_printf("iommu-s390-%04x", iommu->pbdev->uid); + memory_region_init_iommu(&iommu->iommu_mr, OBJECT(&iommu->mr), + &s390_iommu_ops, name, iommu->pal + 1); + iommu->enabled = true; + memory_region_add_subregion(&iommu->mr, 0, &iommu->iommu_mr); + g_free(name); } -void s390_pci_iommu_disable(S390PCIBusDevice *pbdev) +void s390_pci_iommu_disable(S390PCIIOMMU *iommu) { - memory_region_del_subregion(&pbdev->iommu->mr, &pbdev->iommu_mr); - object_unparent(OBJECT(&pbdev->iommu_mr)); - pbdev->iommu_enabled = false; + iommu->enabled = false; + memory_region_del_subregion(&iommu->mr, &iommu->iommu_mr); + object_unparent(OBJECT(&iommu->iommu_mr)); } -static void s390_pcihost_init_as(S390pciState *s) +static void s390_pci_iommu_free(S390pciState *s, PCIBus *bus, int32_t devfn) { - int i; - S390PCIIOMMU *iommu; + uint64_t key = (uintptr_t)bus; + S390PCIIOMMUTable *table = g_hash_table_lookup(s->iommu_table, &key); + S390PCIIOMMU *iommu = table ? table->iommu[PCI_SLOT(devfn)] : NULL; - for (i = 0; i < PCI_SLOT_MAX; i++) { - iommu = g_malloc0(sizeof(S390PCIIOMMU)); - memory_region_init(&iommu->mr, OBJECT(s), - "iommu-root-s390", UINT64_MAX); - address_space_init(&iommu->as, &iommu->mr, "iommu-pci"); - - s->iommu[i] = iommu; + if (!table || !iommu) { + return; } + + table->iommu[PCI_SLOT(devfn)] = NULL; + address_space_destroy(&iommu->as); + object_unparent(OBJECT(&iommu->mr)); + object_unparent(OBJECT(iommu)); + object_unref(OBJECT(iommu)); } static int s390_pcihost_init(SysBusDevice *dev) @@ -546,7 +564,6 @@ static int s390_pcihost_init(SysBusDevice *dev) s390_pci_set_irq, s390_pci_map_irq, NULL, get_system_memory(), get_system_io(), 0, 64, TYPE_PCI_BUS); - s390_pcihost_init_as(s); pci_setup_iommu(b, s390_pci_dma_iommu, s); bus = BUS(b); @@ -556,12 +573,18 @@ static int s390_pcihost_init(SysBusDevice *dev) s->bus = S390_PCI_BUS(qbus_create(TYPE_S390_PCI_BUS, DEVICE(s), NULL)); qbus_set_hotplug_handler(BUS(s->bus), DEVICE(s), NULL); + s->iommu_table = g_hash_table_new_full(g_int64_hash, g_int64_equal, + NULL, g_free); + s->zpci_table = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, NULL); + s->bus_no = 0; QTAILQ_INIT(&s->pending_sei); + QTAILQ_INIT(&s->zpci_devs); return 0; } -static int s390_pci_setup_msix(S390PCIBusDevice *pbdev) +static int s390_pci_msix_init(S390PCIBusDevice *pbdev) { + char *name; uint8_t pos; uint16_t ctrl; uint32_t table, pba; @@ -569,7 +592,7 @@ static int s390_pci_setup_msix(S390PCIBusDevice *pbdev) pos = pci_find_capability(pbdev->pdev, PCI_CAP_ID_MSIX); if (!pos) { pbdev->msix.available = false; - return 0; + return -1; } ctrl = pci_host_config_read_common(pbdev->pdev, pos + PCI_MSIX_FLAGS, @@ -585,21 +608,15 @@ static int s390_pci_setup_msix(S390PCIBusDevice *pbdev) pbdev->msix.pba_offset = pba & ~PCI_MSIX_FLAGS_BIRMASK; pbdev->msix.entries = (ctrl & PCI_MSIX_FLAGS_QSIZE) + 1; pbdev->msix.available = true; - return 0; -} - -static void s390_pci_msix_init(S390PCIBusDevice *pbdev) -{ - char *name; name = g_strdup_printf("msix-s390-%04x", pbdev->uid); - memory_region_init_io(&pbdev->msix_notify_mr, OBJECT(pbdev), &s390_msi_ctrl_ops, pbdev, name, PAGE_SIZE); memory_region_add_subregion(&pbdev->iommu->mr, ZPCI_MSI_ADDR, &pbdev->msix_notify_mr); - g_free(name); + + return 0; } static void s390_pci_msix_free(S390PCIBusDevice *pbdev) @@ -608,10 +625,10 @@ static void s390_pci_msix_free(S390PCIBusDevice *pbdev) object_unparent(OBJECT(&pbdev->msix_notify_mr)); } -static S390PCIBusDevice *s390_pci_device_new(const char *target) +static S390PCIBusDevice *s390_pci_device_new(S390pciState *s, + const char *target) { DeviceState *dev = NULL; - S390pciState *s = s390_get_phb(); dev = qdev_try_create(BUS(s->bus), TYPE_S390_PCI_DEVICE); if (!dev) { @@ -624,6 +641,24 @@ static S390PCIBusDevice *s390_pci_device_new(const char *target) return S390_PCI_DEVICE(dev); } +static bool s390_pci_alloc_idx(S390pciState *s, S390PCIBusDevice *pbdev) +{ + uint32_t idx; + + idx = s->next_idx; + while (s390_pci_find_dev_by_idx(s, idx)) { + idx = (idx + 1) & FH_MASK_INDEX; + if (idx == s->next_idx) { + return false; + } + } + + pbdev->idx = idx; + s->next_idx = (idx + 1) & FH_MASK_INDEX; + + return true; +} + static void s390_pcihost_hot_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { @@ -631,7 +666,28 @@ static void s390_pcihost_hot_plug(HotplugHandler *hotplug_dev, S390PCIBusDevice *pbdev = NULL; S390pciState *s = s390_get_phb(); - if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { + if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_BRIDGE)) { + BusState *bus; + PCIBridge *pb = PCI_BRIDGE(dev); + PCIDevice *pdev = PCI_DEVICE(dev); + + pci_bridge_map_irq(pb, dev->id, s390_pci_map_irq); + pci_setup_iommu(&pb->sec_bus, s390_pci_dma_iommu, s); + + bus = BUS(&pb->sec_bus); + qbus_set_hotplug_handler(bus, DEVICE(s), errp); + + if (dev->hotplugged) { + pci_default_write_config(pdev, PCI_PRIMARY_BUS, s->bus_no, 1); + s->bus_no += 1; + pci_default_write_config(pdev, PCI_SECONDARY_BUS, s->bus_no, 1); + do { + pdev = pdev->bus->parent_dev; + pci_default_write_config(pdev, PCI_SUBORDINATE_BUS, + s->bus_no, 1); + } while (pdev->bus && pci_bus_num(pdev->bus)); + } + } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { pdev = PCI_DEVICE(dev); if (!dev->id) { @@ -643,9 +699,9 @@ static void s390_pcihost_hot_plug(HotplugHandler *hotplug_dev, PCI_FUNC(pdev->devfn)); } - pbdev = s390_pci_find_dev_by_target(dev->id); + pbdev = s390_pci_find_dev_by_target(s, dev->id); if (!pbdev) { - pbdev = s390_pci_device_new(dev->id); + pbdev = s390_pci_device_new(s, dev->id); if (!pbdev) { error_setg(errp, "create zpci device failed"); return; @@ -659,29 +715,30 @@ static void s390_pcihost_hot_plug(HotplugHandler *hotplug_dev, } pbdev->pdev = pdev; - pbdev->iommu = s->iommu[PCI_SLOT(pdev->devfn)]; + pbdev->iommu = s390_pci_get_iommu(s, pdev->bus, pdev->devfn); + pbdev->iommu->pbdev = pbdev; pbdev->state = ZPCI_FS_STANDBY; - s390_pci_msix_init(pbdev); - s390_pci_setup_msix(pbdev); + if (s390_pci_msix_init(pbdev)) { + error_setg(errp, "MSI-X support is mandatory " + "in the S390 architecture"); + return; + } if (dev->hotplugged) { s390_pci_generate_plug_event(HP_EVENT_RESERVED_TO_STANDBY, pbdev->fh, pbdev->fid); } } else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) { - int idx; - pbdev = S390_PCI_DEVICE(dev); - for (idx = 0; idx < PCI_SLOT_MAX; idx++) { - if (!s->pbdev[idx]) { - s->pbdev[idx] = pbdev; - pbdev->fh = idx; - return; - } - } - error_setg(errp, "no slot for plugging zpci device"); + if (!s390_pci_alloc_idx(s, pbdev)) { + error_setg(errp, "no slot for plugging zpci device"); + return; + } + pbdev->fh = pbdev->idx; + QTAILQ_INSERT_TAIL(&s->zpci_devs, pbdev, link); + g_hash_table_insert(s->zpci_table, &pbdev->idx, pbdev); } } @@ -692,8 +749,8 @@ static void s390_pcihost_timer_cb(void *opaque) if (pbdev->summary_ind) { pci_dereg_irqs(pbdev); } - if (pbdev->iommu_enabled) { - pci_dereg_ioat(pbdev); + if (pbdev->iommu->enabled) { + pci_dereg_ioat(pbdev->iommu); } pbdev->state = ZPCI_FS_STANDBY; @@ -705,17 +762,20 @@ static void s390_pcihost_timer_cb(void *opaque) static void s390_pcihost_hot_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - int i; PCIDevice *pci_dev = NULL; + PCIBus *bus; + int32_t devfn; S390PCIBusDevice *pbdev = NULL; S390pciState *s = s390_get_phb(); - if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { + if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_BRIDGE)) { + error_setg(errp, "PCI bridge hot unplug currently not supported"); + return; + } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { pci_dev = PCI_DEVICE(dev); - for (i = 0 ; i < PCI_SLOT_MAX; i++) { - if (s->pbdev[i] && s->pbdev[i]->pdev == pci_dev) { - pbdev = s->pbdev[i]; + QTAILQ_FOREACH(pbdev, &s->zpci_devs, link) { + if (pbdev->pdev == pci_dev) { break; } } @@ -749,16 +809,58 @@ static void s390_pcihost_hot_unplug(HotplugHandler *hotplug_dev, s390_pci_generate_plug_event(HP_EVENT_STANDBY_TO_RESERVED, pbdev->fh, pbdev->fid); + bus = pci_dev->bus; + devfn = pci_dev->devfn; object_unparent(OBJECT(pci_dev)); s390_pci_msix_free(pbdev); + s390_pci_iommu_free(s, bus, devfn); pbdev->pdev = NULL; pbdev->state = ZPCI_FS_RESERVED; out: pbdev->fid = 0; - s->pbdev[pbdev->fh & FH_MASK_INDEX] = NULL; + QTAILQ_REMOVE(&s->zpci_devs, pbdev, link); + g_hash_table_remove(s->zpci_table, &pbdev->idx); object_unparent(OBJECT(pbdev)); } +static void s390_pci_enumerate_bridge(PCIBus *bus, PCIDevice *pdev, + void *opaque) +{ + S390pciState *s = opaque; + unsigned int primary = s->bus_no; + unsigned int subordinate = 0xff; + PCIBus *sec_bus = NULL; + + if ((pci_default_read_config(pdev, PCI_HEADER_TYPE, 1) != + PCI_HEADER_TYPE_BRIDGE)) { + return; + } + + (s->bus_no)++; + pci_default_write_config(pdev, PCI_PRIMARY_BUS, primary, 1); + pci_default_write_config(pdev, PCI_SECONDARY_BUS, s->bus_no, 1); + pci_default_write_config(pdev, PCI_SUBORDINATE_BUS, s->bus_no, 1); + + sec_bus = pci_bridge_get_sec_bus(PCI_BRIDGE(pdev)); + if (!sec_bus) { + return; + } + + pci_default_write_config(pdev, PCI_SUBORDINATE_BUS, subordinate, 1); + pci_for_each_device(sec_bus, pci_bus_num(sec_bus), + s390_pci_enumerate_bridge, s); + pci_default_write_config(pdev, PCI_SUBORDINATE_BUS, s->bus_no, 1); +} + +static void s390_pcihost_reset(DeviceState *dev) +{ + S390pciState *s = S390_PCI_HOST_BRIDGE(dev); + PCIBus *bus = s->parent_obj.bus; + + s->bus_no = 0; + pci_for_each_device(bus, pci_bus_num(bus), s390_pci_enumerate_bridge, s); +} + static void s390_pcihost_class_init(ObjectClass *klass, void *data) { SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); @@ -766,6 +868,7 @@ static void s390_pcihost_class_init(ObjectClass *klass, void *data) HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); dc->cannot_instantiate_with_device_add_yet = true; + dc->reset = s390_pcihost_reset; k->init = s390_pcihost_init; hc->plug = s390_pcihost_hot_plug; hc->unplug = s390_pcihost_hot_unplug; @@ -789,13 +892,13 @@ static const TypeInfo s390_pcibus_info = { .instance_size = sizeof(S390PCIBus), }; -static uint16_t s390_pci_generate_uid(void) +static uint16_t s390_pci_generate_uid(S390pciState *s) { uint16_t uid = 0; do { uid++; - if (!s390_pci_find_dev_by_uid(uid)) { + if (!s390_pci_find_dev_by_uid(s, uid)) { return uid; } } while (uid < ZPCI_MAX_UID); @@ -803,12 +906,12 @@ static uint16_t s390_pci_generate_uid(void) return UID_UNDEFINED; } -static uint32_t s390_pci_generate_fid(Error **errp) +static uint32_t s390_pci_generate_fid(S390pciState *s, Error **errp) { uint32_t fid = 0; do { - if (!s390_pci_find_dev_by_fid(fid)) { + if (!s390_pci_find_dev_by_fid(s, fid)) { return fid; } } while (fid++ != ZPCI_MAX_FID); @@ -820,25 +923,26 @@ static uint32_t s390_pci_generate_fid(Error **errp) static void s390_pci_device_realize(DeviceState *dev, Error **errp) { S390PCIBusDevice *zpci = S390_PCI_DEVICE(dev); + S390pciState *s = s390_get_phb(); if (!zpci->target) { error_setg(errp, "target must be defined"); return; } - if (s390_pci_find_dev_by_target(zpci->target)) { + if (s390_pci_find_dev_by_target(s, zpci->target)) { error_setg(errp, "target %s already has an associated zpci device", zpci->target); return; } if (zpci->uid == UID_UNDEFINED) { - zpci->uid = s390_pci_generate_uid(); + zpci->uid = s390_pci_generate_uid(s); if (!zpci->uid) { error_setg(errp, "no free uid could be found"); return; } - } else if (s390_pci_find_dev_by_uid(zpci->uid)) { + } else if (s390_pci_find_dev_by_uid(s, zpci->uid)) { error_setg(errp, "uid %u already in use", zpci->uid); return; } @@ -846,12 +950,12 @@ static void s390_pci_device_realize(DeviceState *dev, Error **errp) if (!zpci->fid_defined) { Error *local_error = NULL; - zpci->fid = s390_pci_generate_fid(&local_error); + zpci->fid = s390_pci_generate_fid(s, &local_error); if (local_error) { error_propagate(errp, local_error); return; } - } else if (s390_pci_find_dev_by_fid(zpci->fid)) { + } else if (s390_pci_find_dev_by_fid(s, zpci->fid)) { error_setg(errp, "fid %u already in use", zpci->fid); return; } @@ -877,8 +981,8 @@ static void s390_pci_device_reset(DeviceState *dev) if (pbdev->summary_ind) { pci_dereg_irqs(pbdev); } - if (pbdev->iommu_enabled) { - pci_dereg_ioat(pbdev); + if (pbdev->iommu->enabled) { + pci_dereg_ioat(pbdev->iommu); } pbdev->fmb_addr = 0; @@ -944,11 +1048,18 @@ static const TypeInfo s390_pci_device_info = { .class_init = s390_pci_device_class_init, }; +static TypeInfo s390_pci_iommu_info = { + .name = TYPE_S390_PCI_IOMMU, + .parent = TYPE_OBJECT, + .instance_size = sizeof(S390PCIIOMMU), +}; + static void s390_pci_register_types(void) { type_register_static(&s390_pcihost_info); type_register_static(&s390_pcibus_info); type_register_static(&s390_pci_device_info); + type_register_static(&s390_pci_iommu_info); } type_init(s390_pci_register_types) diff --git a/hw/s390x/s390-pci-bus.h b/hw/s390x/s390-pci-bus.h index 7f2701301e..b0adefa788 100644 --- a/hw/s390x/s390-pci-bus.h +++ b/hw/s390x/s390-pci-bus.h @@ -23,10 +23,11 @@ #define TYPE_S390_PCI_HOST_BRIDGE "s390-pcihost" #define TYPE_S390_PCI_BUS "s390-pcibus" #define TYPE_S390_PCI_DEVICE "zpci" +#define TYPE_S390_PCI_IOMMU "s390-pci-iommu" #define FH_MASK_ENABLE 0x80000000 #define FH_MASK_INSTANCE 0x7f000000 #define FH_MASK_SHM 0x00ff0000 -#define FH_MASK_INDEX 0x0000001f +#define FH_MASK_INDEX 0x0000ffff #define FH_SHM_VFIO 0x00010000 #define FH_SHM_EMUL 0x00020000 #define S390_PCIPT_ADAPTER 2 @@ -42,6 +43,8 @@ OBJECT_CHECK(S390PCIBus, (obj), TYPE_S390_PCI_BUS) #define S390_PCI_DEVICE(obj) \ OBJECT_CHECK(S390PCIBusDevice, (obj), TYPE_S390_PCI_DEVICE) +#define S390_PCI_IOMMU(obj) \ + OBJECT_CHECK(S390PCIIOMMU, (obj), TYPE_S390_PCI_IOMMU) #define HP_EVENT_TO_CONFIGURED 0x0301 #define HP_EVENT_RESERVED_TO_STANDBY 0x0302 @@ -258,24 +261,34 @@ typedef struct S390MsixInfo { uint32_t pba_offset; } S390MsixInfo; +typedef struct S390PCIBusDevice S390PCIBusDevice; typedef struct S390PCIIOMMU { + Object parent_obj; + S390PCIBusDevice *pbdev; AddressSpace as; MemoryRegion mr; + MemoryRegion iommu_mr; + bool enabled; + uint64_t g_iota; + uint64_t pba; + uint64_t pal; } S390PCIIOMMU; +typedef struct S390PCIIOMMUTable { + uint64_t key; + S390PCIIOMMU *iommu[PCI_SLOT_MAX]; +} S390PCIIOMMUTable; + typedef struct S390PCIBusDevice { DeviceState qdev; PCIDevice *pdev; ZpciState state; - bool iommu_enabled; char *target; uint16_t uid; + uint32_t idx; uint32_t fh; uint32_t fid; bool fid_defined; - uint64_t g_iota; - uint64_t pba; - uint64_t pal; uint64_t fmb_addr; uint8_t isc; uint16_t noi; @@ -283,11 +296,11 @@ typedef struct S390PCIBusDevice { S390MsixInfo msix; AdapterRoutes routes; S390PCIIOMMU *iommu; - MemoryRegion iommu_mr; MemoryRegion msix_notify_mr; IndAddr *summary_ind; IndAddr *indicator; QEMUTimer *release_timer; + QTAILQ_ENTRY(S390PCIBusDevice) link; } S390PCIBusDevice; typedef struct S390PCIBus { @@ -296,23 +309,28 @@ typedef struct S390PCIBus { typedef struct S390pciState { PCIHostState parent_obj; + uint32_t next_idx; + int bus_no; S390PCIBus *bus; - S390PCIBusDevice *pbdev[PCI_SLOT_MAX]; - S390PCIIOMMU *iommu[PCI_SLOT_MAX]; + GHashTable *iommu_table; + GHashTable *zpci_table; QTAILQ_HEAD(, SeiContainer) pending_sei; + QTAILQ_HEAD(, S390PCIBusDevice) zpci_devs; } S390pciState; +S390pciState *s390_get_phb(void); int chsc_sei_nt2_get_event(void *res); int chsc_sei_nt2_have_event(void); void s390_pci_sclp_configure(SCCB *sccb); void s390_pci_sclp_deconfigure(SCCB *sccb); -void s390_pci_iommu_enable(S390PCIBusDevice *pbdev); -void s390_pci_iommu_disable(S390PCIBusDevice *pbdev); +void s390_pci_iommu_enable(S390PCIIOMMU *iommu); +void s390_pci_iommu_disable(S390PCIIOMMU *iommu); void s390_pci_generate_error_event(uint16_t pec, uint32_t fh, uint32_t fid, uint64_t faddr, uint32_t e); -S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx); -S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh); -S390PCIBusDevice *s390_pci_find_dev_by_fid(uint32_t fid); -S390PCIBusDevice *s390_pci_find_next_avail_dev(S390PCIBusDevice *pbdev); +S390PCIBusDevice *s390_pci_find_dev_by_idx(S390pciState *s, uint32_t idx); +S390PCIBusDevice *s390_pci_find_dev_by_fh(S390pciState *s, uint32_t fh); +S390PCIBusDevice *s390_pci_find_dev_by_fid(S390pciState *s, uint32_t fid); +S390PCIBusDevice *s390_pci_find_next_avail_dev(S390pciState *s, + S390PCIBusDevice *pbdev); #endif diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index 4d0775c83f..d2a8c0a083 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -39,6 +39,7 @@ static void s390_set_status_code(CPUS390XState *env, static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc) { S390PCIBusDevice *pbdev = NULL; + S390pciState *s = s390_get_phb(); uint32_t res_code, initial_l2, g_l2; int rc, i; uint64_t resume_token; @@ -66,14 +67,14 @@ static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc) resume_token = ldq_p(&rrb->request.resume_token); if (resume_token) { - pbdev = s390_pci_find_dev_by_idx(resume_token); + pbdev = s390_pci_find_dev_by_idx(s, resume_token); if (!pbdev) { res_code = CLP_RC_LISTPCI_BADRT; rc = -EINVAL; goto out; } } else { - pbdev = s390_pci_find_next_avail_dev(NULL); + pbdev = s390_pci_find_next_avail_dev(s, NULL); } if (lduw_p(&rrb->response.hdr.len) < 48) { @@ -119,7 +120,7 @@ static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc) lduw_p(&rrb->response.fh_list[i].device_id), ldl_p(&rrb->response.fh_list[i].fid), ldl_p(&rrb->response.fh_list[i].fh)); - pbdev = s390_pci_find_next_avail_dev(pbdev); + pbdev = s390_pci_find_next_avail_dev(s, pbdev); i++; } @@ -149,6 +150,7 @@ int clp_service_call(S390CPU *cpu, uint8_t r2) uint8_t buffer[4096 * 2]; uint8_t cc = 0; CPUS390XState *env = &cpu->env; + S390pciState *s = s390_get_phb(); int i; cpu_synchronize_state(CPU(cpu)); @@ -203,7 +205,7 @@ int clp_service_call(S390CPU *cpu, uint8_t r2) ClpReqSetPci *reqsetpci = (ClpReqSetPci *)reqh; ClpRspSetPci *ressetpci = (ClpRspSetPci *)resh; - pbdev = s390_pci_find_dev_by_fh(ldl_p(&reqsetpci->fh)); + pbdev = s390_pci_find_dev_by_fh(s, ldl_p(&reqsetpci->fh)); if (!pbdev) { stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_FH); goto out; @@ -254,7 +256,7 @@ int clp_service_call(S390CPU *cpu, uint8_t r2) ClpReqQueryPci *reqquery = (ClpReqQueryPci *)reqh; ClpRspQueryPci *resquery = (ClpRspQueryPci *)resh; - pbdev = s390_pci_find_dev_by_fh(ldl_p(&reqquery->fh)); + pbdev = s390_pci_find_dev_by_fh(s, ldl_p(&reqquery->fh)); if (!pbdev) { DPRINTF("query pci no pci dev\n"); stw_p(&resquery->hdr.rsp, CLP_RC_SETPCIFN_FH); @@ -339,7 +341,7 @@ int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) len = env->regs[r2] & 0xf; offset = env->regs[r2 + 1]; - pbdev = s390_pci_find_dev_by_fh(fh); + pbdev = s390_pci_find_dev_by_fh(s390_get_phb(), fh); if (!pbdev) { DPRINTF("pcilg no pci dev\n"); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); @@ -472,7 +474,7 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) len = env->regs[r2] & 0xf; offset = env->regs[r2 + 1]; - pbdev = s390_pci_find_dev_by_fh(fh); + pbdev = s390_pci_find_dev_by_fh(s390_get_phb(), fh); if (!pbdev) { DPRINTF("pcistg no pci dev\n"); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); @@ -556,6 +558,7 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) CPUS390XState *env = &cpu->env; uint32_t fh; S390PCIBusDevice *pbdev; + S390PCIIOMMU *iommu; hwaddr start, end; IOMMUTLBEntry entry; MemoryRegion *mr; @@ -576,7 +579,7 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) start = env->regs[r2]; end = start + env->regs[r2 + 1]; - pbdev = s390_pci_find_dev_by_fh(fh); + pbdev = s390_pci_find_dev_by_fh(s390_get_phb(), fh); if (!pbdev) { DPRINTF("rpcit no pci dev\n"); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); @@ -598,7 +601,8 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) break; } - if (!pbdev->g_iota) { + iommu = pbdev->iommu; + if (!iommu->g_iota) { pbdev->state = ZPCI_FS_ERROR; setcc(cpu, ZPCI_PCI_LS_ERR); s390_set_status_code(env, r1, ZPCI_PCI_ST_INSUF_RES); @@ -607,7 +611,7 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) goto out; } - if (end < pbdev->pba || start > pbdev->pal) { + if (end < iommu->pba || start > iommu->pal) { pbdev->state = ZPCI_FS_ERROR; setcc(cpu, ZPCI_PCI_LS_ERR); s390_set_status_code(env, r1, ZPCI_PCI_ST_INSUF_RES); @@ -616,7 +620,7 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) goto out; } - mr = &pbdev->iommu_mr; + mr = &iommu->iommu_mr; while (start < end) { entry = mr->iommu_ops->translate(mr, start, 0); @@ -678,7 +682,7 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr, return 0; } - pbdev = s390_pci_find_dev_by_fh(fh); + pbdev = s390_pci_find_dev_by_fh(s390_get_phb(), fh); if (!pbdev) { DPRINTF("pcistb no pci dev fh 0x%x\n", fh); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); @@ -784,7 +788,7 @@ int pci_dereg_irqs(S390PCIBusDevice *pbdev) return 0; } -static int reg_ioat(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib) +static int reg_ioat(CPUS390XState *env, S390PCIIOMMU *iommu, ZpciFib fib) { uint64_t pba = ldq_p(&fib.pba); uint64_t pal = ldq_p(&fib.pal); @@ -804,21 +808,21 @@ static int reg_ioat(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib) return -EINVAL; } - pbdev->pba = pba; - pbdev->pal = pal; - pbdev->g_iota = g_iota; + iommu->pba = pba; + iommu->pal = pal; + iommu->g_iota = g_iota; - s390_pci_iommu_enable(pbdev); + s390_pci_iommu_enable(iommu); return 0; } -void pci_dereg_ioat(S390PCIBusDevice *pbdev) +void pci_dereg_ioat(S390PCIIOMMU *iommu) { - s390_pci_iommu_disable(pbdev); - pbdev->pba = 0; - pbdev->pal = 0; - pbdev->g_iota = 0; + s390_pci_iommu_disable(iommu); + iommu->pba = 0; + iommu->pal = 0; + iommu->g_iota = 0; } int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) @@ -844,7 +848,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) return 0; } - pbdev = s390_pci_find_dev_by_fh(fh); + pbdev = s390_pci_find_dev_by_fh(s390_get_phb(), fh); if (!pbdev) { DPRINTF("mpcifc no pci dev fh 0x%x\n", fh); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); @@ -893,10 +897,10 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) if (dmaas != 0) { cc = ZPCI_PCI_LS_ERR; s390_set_status_code(env, r1, ZPCI_MOD_ST_DMAAS_INVAL); - } else if (pbdev->iommu_enabled) { + } else if (pbdev->iommu->enabled) { cc = ZPCI_PCI_LS_ERR; s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE); - } else if (reg_ioat(env, pbdev, fib)) { + } else if (reg_ioat(env, pbdev->iommu, fib)) { cc = ZPCI_PCI_LS_ERR; s390_set_status_code(env, r1, ZPCI_MOD_ST_INSUF_RES); } @@ -905,23 +909,23 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) if (dmaas != 0) { cc = ZPCI_PCI_LS_ERR; s390_set_status_code(env, r1, ZPCI_MOD_ST_DMAAS_INVAL); - } else if (!pbdev->iommu_enabled) { + } else if (!pbdev->iommu->enabled) { cc = ZPCI_PCI_LS_ERR; s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE); } else { - pci_dereg_ioat(pbdev); + pci_dereg_ioat(pbdev->iommu); } break; case ZPCI_MOD_FC_REREG_IOAT: if (dmaas != 0) { cc = ZPCI_PCI_LS_ERR; s390_set_status_code(env, r1, ZPCI_MOD_ST_DMAAS_INVAL); - } else if (!pbdev->iommu_enabled) { + } else if (!pbdev->iommu->enabled) { cc = ZPCI_PCI_LS_ERR; s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE); } else { - pci_dereg_ioat(pbdev); - if (reg_ioat(env, pbdev, fib)) { + pci_dereg_ioat(pbdev->iommu); + if (reg_ioat(env, pbdev->iommu, fib)) { cc = ZPCI_PCI_LS_ERR; s390_set_status_code(env, r1, ZPCI_MOD_ST_INSUF_RES); } @@ -989,7 +993,7 @@ int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) return 0; } - pbdev = s390_pci_find_dev_by_idx(fh & FH_MASK_INDEX); + pbdev = s390_pci_find_dev_by_idx(s390_get_phb(), fh & FH_MASK_INDEX); if (!pbdev) { setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); return 0; @@ -1016,7 +1020,7 @@ int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) fib.fc |= 0x40; case ZPCI_FS_ENABLED: fib.fc |= 0x80; - if (pbdev->iommu_enabled) { + if (pbdev->iommu->enabled) { fib.fc |= 0x10; } if (!(fh & FH_MASK_ENABLE)) { @@ -1029,9 +1033,9 @@ int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) return 0; } - stq_p(&fib.pba, pbdev->pba); - stq_p(&fib.pal, pbdev->pal); - stq_p(&fib.iota, pbdev->g_iota); + stq_p(&fib.pba, pbdev->iommu->pba); + stq_p(&fib.pal, pbdev->iommu->pal); + stq_p(&fib.iota, pbdev->iommu->g_iota); stq_p(&fib.aibv, pbdev->routes.adapter.ind_addr); stq_p(&fib.aisb, pbdev->routes.adapter.summary_addr); stq_p(&fib.fmb_addr, pbdev->fmb_addr); diff --git a/hw/s390x/s390-pci-inst.h b/hw/s390x/s390-pci-inst.h index 23f4bfa0ed..94a959f91c 100644 --- a/hw/s390x/s390-pci-inst.h +++ b/hw/s390x/s390-pci-inst.h @@ -292,7 +292,7 @@ typedef struct ZpciFib { } QEMU_PACKED ZpciFib; int pci_dereg_irqs(S390PCIBusDevice *pbdev); -void pci_dereg_ioat(S390PCIBusDevice *pbdev); +void pci_dereg_ioat(S390PCIIOMMU *iommu); int clp_service_call(S390CPU *cpu, uint8_t r2); int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2); int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2); diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index e340eab36b..e9a676797a 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -335,11 +335,13 @@ static const TypeInfo ccw_machine_info = { } \ type_init(ccw_machine_register_##suffix) +#define CCW_COMPAT_2_8 \ + HW_COMPAT_2_8 + #define CCW_COMPAT_2_7 \ HW_COMPAT_2_7 #define CCW_COMPAT_2_6 \ - CCW_COMPAT_2_7 \ HW_COMPAT_2_6 \ {\ .driver = TYPE_S390_IPL,\ @@ -352,7 +354,6 @@ static const TypeInfo ccw_machine_info = { }, #define CCW_COMPAT_2_5 \ - CCW_COMPAT_2_6 \ HW_COMPAT_2_5 #define CCW_COMPAT_2_4 \ @@ -395,14 +396,26 @@ static const TypeInfo ccw_machine_info = { .value = "0",\ }, +static void ccw_machine_2_9_instance_options(MachineState *machine) +{ +} + +static void ccw_machine_2_9_class_options(MachineClass *mc) +{ +} +DEFINE_CCW_MACHINE(2_9, "2.9", true); + static void ccw_machine_2_8_instance_options(MachineState *machine) { + ccw_machine_2_9_instance_options(machine); } static void ccw_machine_2_8_class_options(MachineClass *mc) { + ccw_machine_2_9_class_options(mc); + SET_MACHINE_COMPAT(mc, CCW_COMPAT_2_8); } -DEFINE_CCW_MACHINE(2_8, "2.8", true); +DEFINE_CCW_MACHINE(2_8, "2.8", false); static void ccw_machine_2_7_instance_options(MachineState *machine) { diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 07650683f7..63c46373fb 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -149,7 +149,7 @@ static int virtio_ccw_set_vqs(SubchDev *sch, VqInfoBlock *info, } else { if (info) { /* virtio-1 allows changing the ring size. */ - if (virtio_queue_get_num(vdev, index) < num) { + if (virtio_queue_get_max_num(vdev, index) < num) { /* Fail if we exceed the maximum number. */ return -EINVAL; } diff --git a/hw/sh4/sh7750.c b/hw/sh4/sh7750.c index 3132d559d7..166e4bd947 100644 --- a/hw/sh4/sh7750.c +++ b/hw/sh4/sh7750.c @@ -417,7 +417,7 @@ static void sh7750_mem_writel(void *opaque, hwaddr addr, case SH7750_PTEH_A7: /* If asid changes, clear all registered tlb entries. */ if ((s->cpu->env.pteh & 0xff) != (mem_value & 0xff)) { - tlb_flush(CPU(s->cpu), 1); + tlb_flush(CPU(s->cpu)); } s->cpu->env.pteh = mem_value; return; diff --git a/hw/sparc64/Makefile.objs b/hw/sparc64/Makefile.objs index a84cfe3ec7..cf9de21133 100644 --- a/hw/sparc64/Makefile.objs +++ b/hw/sparc64/Makefile.objs @@ -1 +1,3 @@ +obj-y += sparc64.o obj-y += sun4u.o +obj-y += niagara.o
\ No newline at end of file diff --git a/hw/sparc64/niagara.c b/hw/sparc64/niagara.c new file mode 100644 index 0000000000..b55d4bb8d3 --- /dev/null +++ b/hw/sparc64/niagara.c @@ -0,0 +1,177 @@ +/* + * QEMU Sun4v/Niagara System Emulator + * + * Copyright (c) 2016 Artyom Tarasenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" +#include "hw/hw.h" +#include "hw/boards.h" +#include "hw/char/serial.h" +#include "hw/empty_slot.h" +#include "hw/loader.h" +#include "hw/sparc/sparc64.h" +#include "hw/timer/sun4v-rtc.h" +#include "exec/address-spaces.h" +#include "sysemu/block-backend.h" + + +typedef struct NiagaraBoardState { + MemoryRegion hv_ram; + MemoryRegion partition_ram; + MemoryRegion nvram; + MemoryRegion md_rom; + MemoryRegion hv_rom; + MemoryRegion vdisk_ram; + MemoryRegion prom; +} NiagaraBoardState; + +#define NIAGARA_HV_RAM_BASE 0x100000ULL +#define NIAGARA_HV_RAM_SIZE 0x3f00000ULL /* 63 MiB */ + +#define NIAGARA_PARTITION_RAM_BASE 0x80000000ULL + +#define NIAGARA_UART_BASE 0x1f10000000ULL + +#define NIAGARA_NVRAM_BASE 0x1f11000000ULL +#define NIAGARA_NVRAM_SIZE 0x2000 + +#define NIAGARA_MD_ROM_BASE 0x1f12000000ULL +#define NIAGARA_MD_ROM_SIZE 0x2000 + +#define NIAGARA_HV_ROM_BASE 0x1f12080000ULL +#define NIAGARA_HV_ROM_SIZE 0x2000 + +#define NIAGARA_IOBBASE 0x9800000000ULL +#define NIAGARA_IOBSIZE 0x0100000000ULL + +#define NIAGARA_VDISK_BASE 0x1f40000000ULL +#define NIAGARA_RTC_BASE 0xfff0c1fff8ULL +#define NIAGARA_UART_BASE 0x1f10000000ULL + +/* Firmware layout + * + * |------------------| + * | openboot.bin | + * |------------------| PROM_ADDR + OBP_OFFSET + * | q.bin | + * |------------------| PROM_ADDR + Q_OFFSET + * | reset.bin | + * |------------------| PROM_ADDR + */ +#define NIAGARA_PROM_BASE 0xfff0000000ULL +#define NIAGARA_Q_OFFSET 0x10000ULL +#define NIAGARA_OBP_OFFSET 0x80000ULL +#define PROM_SIZE_MAX (4 * 1024 * 1024) + +/* Niagara hardware initialisation */ +static void niagara_init(MachineState *machine) +{ + NiagaraBoardState *s = g_new(NiagaraBoardState, 1); + DriveInfo *dinfo = drive_get_next(IF_PFLASH); + MemoryRegion *sysmem = get_system_memory(); + + /* init CPUs */ + sparc64_cpu_devinit(machine->cpu_model, "Sun UltraSparc T1", + NIAGARA_PROM_BASE); + /* set up devices */ + memory_region_allocate_system_memory(&s->hv_ram, NULL, "sun4v-hv.ram", + NIAGARA_HV_RAM_SIZE); + memory_region_add_subregion(sysmem, NIAGARA_HV_RAM_BASE, &s->hv_ram); + + memory_region_allocate_system_memory(&s->partition_ram, NULL, + "sun4v-partition.ram", + machine->ram_size); + memory_region_add_subregion(sysmem, NIAGARA_PARTITION_RAM_BASE, + &s->partition_ram); + + memory_region_allocate_system_memory(&s->nvram, NULL, + "sun4v.nvram", NIAGARA_NVRAM_SIZE); + memory_region_add_subregion(sysmem, NIAGARA_NVRAM_BASE, &s->nvram); + memory_region_allocate_system_memory(&s->md_rom, NULL, + "sun4v-md.rom", NIAGARA_MD_ROM_SIZE); + memory_region_add_subregion(sysmem, NIAGARA_MD_ROM_BASE, &s->md_rom); + memory_region_allocate_system_memory(&s->hv_rom, NULL, + "sun4v-hv.rom", NIAGARA_HV_ROM_SIZE); + memory_region_add_subregion(sysmem, NIAGARA_HV_ROM_BASE, &s->hv_rom); + memory_region_allocate_system_memory(&s->prom, NULL, + "sun4v.prom", PROM_SIZE_MAX); + memory_region_add_subregion(sysmem, NIAGARA_PROM_BASE, &s->prom); + + rom_add_file_fixed("nvram1", NIAGARA_NVRAM_BASE, -1); + rom_add_file_fixed("1up-md.bin", NIAGARA_MD_ROM_BASE, -1); + rom_add_file_fixed("1up-hv.bin", NIAGARA_HV_ROM_BASE, -1); + + rom_add_file_fixed("reset.bin", NIAGARA_PROM_BASE, -1); + rom_add_file_fixed("q.bin", NIAGARA_PROM_BASE + NIAGARA_Q_OFFSET, -1); + rom_add_file_fixed("openboot.bin", NIAGARA_PROM_BASE + NIAGARA_OBP_OFFSET, + -1); + + /* the virtual ramdisk is kind of initrd, but it resides + outside of the partition RAM */ + if (dinfo) { + BlockBackend *blk = blk_by_legacy_dinfo(dinfo); + int size = blk_getlength(blk); + if (size > 0) { + memory_region_allocate_system_memory(&s->vdisk_ram, NULL, + "sun4v_vdisk.ram", size); + memory_region_add_subregion(get_system_memory(), + NIAGARA_VDISK_BASE, &s->vdisk_ram); + dinfo->is_default = 1; + rom_add_file_fixed(blk_bs(blk)->filename, NIAGARA_VDISK_BASE, -1); + } else { + fprintf(stderr, "qemu: could not load ram disk '%s'\n", + blk_bs(blk)->filename); + exit(1); + } + } + serial_mm_init(sysmem, NIAGARA_UART_BASE, 0, NULL, 115200, + serial_hds[0], DEVICE_BIG_ENDIAN); + + empty_slot_init(NIAGARA_IOBBASE, NIAGARA_IOBSIZE); + sun4v_rtc_init(NIAGARA_RTC_BASE); +} + +static void niagara_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Sun4v platform, Niagara"; + mc->init = niagara_init; + mc->max_cpus = 1; /* XXX for now */ + mc->default_boot_order = "c"; +} + +static const TypeInfo niagara_type = { + .name = MACHINE_TYPE_NAME("niagara"), + .parent = TYPE_MACHINE, + .class_init = niagara_class_init, +}; + +static void niagara_register_types(void) +{ + type_register_static(&niagara_type); +} + +type_init(niagara_register_types) diff --git a/hw/sparc64/sparc64.c b/hw/sparc64/sparc64.c new file mode 100644 index 0000000000..b3d219c769 --- /dev/null +++ b/hw/sparc64/sparc64.c @@ -0,0 +1,378 @@ +/* + * QEMU Sun4u/Sun4v System Emulator common routines + * + * Copyright (c) 2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#include "qemu/osdep.h" +#include "cpu.h" +#include "hw/char/serial.h" +#include "hw/sparc/sparc64.h" +#include "qemu/timer.h" + + +//#define DEBUG_IRQ +//#define DEBUG_TIMER + +#ifdef DEBUG_IRQ +#define CPUIRQ_DPRINTF(fmt, ...) \ + do { printf("CPUIRQ: " fmt , ## __VA_ARGS__); } while (0) +#else +#define CPUIRQ_DPRINTF(fmt, ...) +#endif + +#ifdef DEBUG_TIMER +#define TIMER_DPRINTF(fmt, ...) \ + do { printf("TIMER: " fmt , ## __VA_ARGS__); } while (0) +#else +#define TIMER_DPRINTF(fmt, ...) +#endif + +#define TICK_MAX 0x7fffffffffffffffULL + +void cpu_check_irqs(CPUSPARCState *env) +{ + CPUState *cs; + uint32_t pil = env->pil_in | + (env->softint & ~(SOFTINT_TIMER | SOFTINT_STIMER)); + + /* TT_IVEC has a higher priority (16) than TT_EXTINT (31..17) */ + if (env->ivec_status & 0x20) { + return; + } + cs = CPU(sparc_env_get_cpu(env)); + /* check if TM or SM in SOFTINT are set + setting these also causes interrupt 14 */ + if (env->softint & (SOFTINT_TIMER | SOFTINT_STIMER)) { + pil |= 1 << 14; + } + + /* The bit corresponding to psrpil is (1<< psrpil), the next bit + is (2 << psrpil). */ + if (pil < (2 << env->psrpil)) { + if (cs->interrupt_request & CPU_INTERRUPT_HARD) { + CPUIRQ_DPRINTF("Reset CPU IRQ (current interrupt %x)\n", + env->interrupt_index); + env->interrupt_index = 0; + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); + } + return; + } + + if (cpu_interrupts_enabled(env)) { + + unsigned int i; + + for (i = 15; i > env->psrpil; i--) { + if (pil & (1 << i)) { + int old_interrupt = env->interrupt_index; + int new_interrupt = TT_EXTINT | i; + + if (unlikely(env->tl > 0 && cpu_tsptr(env)->tt > new_interrupt + && ((cpu_tsptr(env)->tt & 0x1f0) == TT_EXTINT))) { + CPUIRQ_DPRINTF("Not setting CPU IRQ: TL=%d " + "current %x >= pending %x\n", + env->tl, cpu_tsptr(env)->tt, new_interrupt); + } else if (old_interrupt != new_interrupt) { + env->interrupt_index = new_interrupt; + CPUIRQ_DPRINTF("Set CPU IRQ %d old=%x new=%x\n", i, + old_interrupt, new_interrupt); + cpu_interrupt(cs, CPU_INTERRUPT_HARD); + } + break; + } + } + } else if (cs->interrupt_request & CPU_INTERRUPT_HARD) { + CPUIRQ_DPRINTF("Interrupts disabled, pil=%08x pil_in=%08x softint=%08x " + "current interrupt %x\n", + pil, env->pil_in, env->softint, env->interrupt_index); + env->interrupt_index = 0; + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); + } +} + +static void cpu_kick_irq(SPARCCPU *cpu) +{ + CPUState *cs = CPU(cpu); + CPUSPARCState *env = &cpu->env; + + cs->halted = 0; + cpu_check_irqs(env); + qemu_cpu_kick(cs); +} + +void sparc64_cpu_set_ivec_irq(void *opaque, int irq, int level) +{ + SPARCCPU *cpu = opaque; + CPUSPARCState *env = &cpu->env; + CPUState *cs; + + if (level) { + if (!(env->ivec_status & 0x20)) { + CPUIRQ_DPRINTF("Raise IVEC IRQ %d\n", irq); + cs = CPU(cpu); + cs->halted = 0; + env->interrupt_index = TT_IVEC; + env->ivec_status |= 0x20; + env->ivec_data[0] = (0x1f << 6) | irq; + env->ivec_data[1] = 0; + env->ivec_data[2] = 0; + cpu_interrupt(cs, CPU_INTERRUPT_HARD); + } + } else { + if (env->ivec_status & 0x20) { + CPUIRQ_DPRINTF("Lower IVEC IRQ %d\n", irq); + cs = CPU(cpu); + env->ivec_status &= ~0x20; + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); + } + } +} + +typedef struct ResetData { + SPARCCPU *cpu; + uint64_t prom_addr; +} ResetData; + +static CPUTimer *cpu_timer_create(const char *name, SPARCCPU *cpu, + QEMUBHFunc *cb, uint32_t frequency, + uint64_t disabled_mask, uint64_t npt_mask) +{ + CPUTimer *timer = g_malloc0(sizeof(CPUTimer)); + + timer->name = name; + timer->frequency = frequency; + timer->disabled_mask = disabled_mask; + timer->npt_mask = npt_mask; + + timer->disabled = 1; + timer->npt = 1; + timer->clock_offset = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + + timer->qtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cb, cpu); + + return timer; +} + +static void cpu_timer_reset(CPUTimer *timer) +{ + timer->disabled = 1; + timer->clock_offset = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + + timer_del(timer->qtimer); +} + +static void main_cpu_reset(void *opaque) +{ + ResetData *s = (ResetData *)opaque; + CPUSPARCState *env = &s->cpu->env; + static unsigned int nr_resets; + + cpu_reset(CPU(s->cpu)); + + cpu_timer_reset(env->tick); + cpu_timer_reset(env->stick); + cpu_timer_reset(env->hstick); + + env->gregs[1] = 0; /* Memory start */ + env->gregs[2] = ram_size; /* Memory size */ + env->gregs[3] = 0; /* Machine description XXX */ + if (nr_resets++ == 0) { + /* Power on reset */ + env->pc = s->prom_addr + 0x20ULL; + } else { + env->pc = s->prom_addr + 0x40ULL; + } + env->npc = env->pc + 4; +} + +static void tick_irq(void *opaque) +{ + SPARCCPU *cpu = opaque; + CPUSPARCState *env = &cpu->env; + + CPUTimer *timer = env->tick; + + if (timer->disabled) { + CPUIRQ_DPRINTF("tick_irq: softint disabled\n"); + return; + } else { + CPUIRQ_DPRINTF("tick: fire\n"); + } + + env->softint |= SOFTINT_TIMER; + cpu_kick_irq(cpu); +} + +static void stick_irq(void *opaque) +{ + SPARCCPU *cpu = opaque; + CPUSPARCState *env = &cpu->env; + + CPUTimer *timer = env->stick; + + if (timer->disabled) { + CPUIRQ_DPRINTF("stick_irq: softint disabled\n"); + return; + } else { + CPUIRQ_DPRINTF("stick: fire\n"); + } + + env->softint |= SOFTINT_STIMER; + cpu_kick_irq(cpu); +} + +static void hstick_irq(void *opaque) +{ + SPARCCPU *cpu = opaque; + CPUSPARCState *env = &cpu->env; + + CPUTimer *timer = env->hstick; + + if (timer->disabled) { + CPUIRQ_DPRINTF("hstick_irq: softint disabled\n"); + return; + } else { + CPUIRQ_DPRINTF("hstick: fire\n"); + } + + env->softint |= SOFTINT_STIMER; + cpu_kick_irq(cpu); +} + +static int64_t cpu_to_timer_ticks(int64_t cpu_ticks, uint32_t frequency) +{ + return muldiv64(cpu_ticks, NANOSECONDS_PER_SECOND, frequency); +} + +static uint64_t timer_to_cpu_ticks(int64_t timer_ticks, uint32_t frequency) +{ + return muldiv64(timer_ticks, frequency, NANOSECONDS_PER_SECOND); +} + +void cpu_tick_set_count(CPUTimer *timer, uint64_t count) +{ + uint64_t real_count = count & ~timer->npt_mask; + uint64_t npt_bit = count & timer->npt_mask; + + int64_t vm_clock_offset = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - + cpu_to_timer_ticks(real_count, timer->frequency); + + TIMER_DPRINTF("%s set_count count=0x%016lx (npt %s) p=%p\n", + timer->name, real_count, + timer->npt ? "disabled" : "enabled", timer); + + timer->npt = npt_bit ? 1 : 0; + timer->clock_offset = vm_clock_offset; +} + +uint64_t cpu_tick_get_count(CPUTimer *timer) +{ + uint64_t real_count = timer_to_cpu_ticks( + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - timer->clock_offset, + timer->frequency); + + TIMER_DPRINTF("%s get_count count=0x%016lx (npt %s) p=%p\n", + timer->name, real_count, + timer->npt ? "disabled" : "enabled", timer); + + if (timer->npt) { + real_count |= timer->npt_mask; + } + + return real_count; +} + +void cpu_tick_set_limit(CPUTimer *timer, uint64_t limit) +{ + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + + uint64_t real_limit = limit & ~timer->disabled_mask; + timer->disabled = (limit & timer->disabled_mask) ? 1 : 0; + + int64_t expires = cpu_to_timer_ticks(real_limit, timer->frequency) + + timer->clock_offset; + + if (expires < now) { + expires = now + 1; + } + + TIMER_DPRINTF("%s set_limit limit=0x%016lx (%s) p=%p " + "called with limit=0x%016lx at 0x%016lx (delta=0x%016lx)\n", + timer->name, real_limit, + timer->disabled ? "disabled" : "enabled", + timer, limit, + timer_to_cpu_ticks(now - timer->clock_offset, + timer->frequency), + timer_to_cpu_ticks(expires - now, timer->frequency)); + + if (!real_limit) { + TIMER_DPRINTF("%s set_limit limit=ZERO - not starting timer\n", + timer->name); + timer_del(timer->qtimer); + } else if (timer->disabled) { + timer_del(timer->qtimer); + } else { + timer_mod(timer->qtimer, expires); + } +} + +SPARCCPU *sparc64_cpu_devinit(const char *cpu_model, + const char *default_cpu_model, uint64_t prom_addr) +{ + SPARCCPU *cpu; + CPUSPARCState *env; + ResetData *reset_info; + + uint32_t tick_frequency = 100 * 1000000; + uint32_t stick_frequency = 100 * 1000000; + uint32_t hstick_frequency = 100 * 1000000; + + if (cpu_model == NULL) { + cpu_model = default_cpu_model; + } + cpu = cpu_sparc_init(cpu_model); + if (cpu == NULL) { + fprintf(stderr, "Unable to find Sparc CPU definition\n"); + exit(1); + } + env = &cpu->env; + + env->tick = cpu_timer_create("tick", cpu, tick_irq, + tick_frequency, TICK_INT_DIS, + TICK_NPT_MASK); + + env->stick = cpu_timer_create("stick", cpu, stick_irq, + stick_frequency, TICK_INT_DIS, + TICK_NPT_MASK); + + env->hstick = cpu_timer_create("hstick", cpu, hstick_irq, + hstick_frequency, TICK_INT_DIS, + TICK_NPT_MASK); + + reset_info = g_malloc0(sizeof(ResetData)); + reset_info->cpu = cpu; + reset_info->prom_addr = prom_addr; + qemu_register_reset(main_cpu_reset, reset_info); + + return cpu; +} diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index 466331535b..d1a6bca873 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -38,25 +38,15 @@ #include "hw/boards.h" #include "hw/nvram/sun_nvram.h" #include "hw/nvram/chrp_nvram.h" +#include "hw/sparc/sparc64.h" #include "hw/nvram/fw_cfg.h" #include "hw/sysbus.h" #include "hw/ide.h" #include "hw/loader.h" #include "elf.h" -#include "sysemu/block-backend.h" -#include "exec/address-spaces.h" #include "qemu/cutils.h" -//#define DEBUG_IRQ //#define DEBUG_EBUS -//#define DEBUG_TIMER - -#ifdef DEBUG_IRQ -#define CPUIRQ_DPRINTF(fmt, ...) \ - do { printf("CPUIRQ: " fmt , ## __VA_ARGS__); } while (0) -#else -#define CPUIRQ_DPRINTF(fmt, ...) -#endif #ifdef DEBUG_EBUS #define EBUS_DPRINTF(fmt, ...) \ @@ -65,13 +55,6 @@ #define EBUS_DPRINTF(fmt, ...) #endif -#ifdef DEBUG_TIMER -#define TIMER_DPRINTF(fmt, ...) \ - do { printf("TIMER: " fmt , ## __VA_ARGS__); } while (0) -#else -#define TIMER_DPRINTF(fmt, ...) -#endif - #define KERNEL_LOAD_ADDR 0x00404000 #define CMDLINE_ADDR 0x003ff000 #define PROM_SIZE_MAX (4 * 1024 * 1024) @@ -89,8 +72,6 @@ #define IVEC_MAX 0x40 -#define TICK_MAX 0x7fffffffffffffffULL - struct hwdef { const char * const default_cpu_model; uint16_t machine_id; @@ -216,293 +197,11 @@ static uint64_t sun4u_load_kernel(const char *kernel_filename, return kernel_size; } -void cpu_check_irqs(CPUSPARCState *env) -{ - CPUState *cs; - uint32_t pil = env->pil_in | - (env->softint & ~(SOFTINT_TIMER | SOFTINT_STIMER)); - - /* TT_IVEC has a higher priority (16) than TT_EXTINT (31..17) */ - if (env->ivec_status & 0x20) { - return; - } - cs = CPU(sparc_env_get_cpu(env)); - /* check if TM or SM in SOFTINT are set - setting these also causes interrupt 14 */ - if (env->softint & (SOFTINT_TIMER | SOFTINT_STIMER)) { - pil |= 1 << 14; - } - - /* The bit corresponding to psrpil is (1<< psrpil), the next bit - is (2 << psrpil). */ - if (pil < (2 << env->psrpil)){ - if (cs->interrupt_request & CPU_INTERRUPT_HARD) { - CPUIRQ_DPRINTF("Reset CPU IRQ (current interrupt %x)\n", - env->interrupt_index); - env->interrupt_index = 0; - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); - } - return; - } - - if (cpu_interrupts_enabled(env)) { - - unsigned int i; - - for (i = 15; i > env->psrpil; i--) { - if (pil & (1 << i)) { - int old_interrupt = env->interrupt_index; - int new_interrupt = TT_EXTINT | i; - - if (unlikely(env->tl > 0 && cpu_tsptr(env)->tt > new_interrupt - && ((cpu_tsptr(env)->tt & 0x1f0) == TT_EXTINT))) { - CPUIRQ_DPRINTF("Not setting CPU IRQ: TL=%d " - "current %x >= pending %x\n", - env->tl, cpu_tsptr(env)->tt, new_interrupt); - } else if (old_interrupt != new_interrupt) { - env->interrupt_index = new_interrupt; - CPUIRQ_DPRINTF("Set CPU IRQ %d old=%x new=%x\n", i, - old_interrupt, new_interrupt); - cpu_interrupt(cs, CPU_INTERRUPT_HARD); - } - break; - } - } - } else if (cs->interrupt_request & CPU_INTERRUPT_HARD) { - CPUIRQ_DPRINTF("Interrupts disabled, pil=%08x pil_in=%08x softint=%08x " - "current interrupt %x\n", - pil, env->pil_in, env->softint, env->interrupt_index); - env->interrupt_index = 0; - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); - } -} - -static void cpu_kick_irq(SPARCCPU *cpu) -{ - CPUState *cs = CPU(cpu); - CPUSPARCState *env = &cpu->env; - - cs->halted = 0; - cpu_check_irqs(env); - qemu_cpu_kick(cs); -} - -static void cpu_set_ivec_irq(void *opaque, int irq, int level) -{ - SPARCCPU *cpu = opaque; - CPUSPARCState *env = &cpu->env; - CPUState *cs; - - if (level) { - if (!(env->ivec_status & 0x20)) { - CPUIRQ_DPRINTF("Raise IVEC IRQ %d\n", irq); - cs = CPU(cpu); - cs->halted = 0; - env->interrupt_index = TT_IVEC; - env->ivec_status |= 0x20; - env->ivec_data[0] = (0x1f << 6) | irq; - env->ivec_data[1] = 0; - env->ivec_data[2] = 0; - cpu_interrupt(cs, CPU_INTERRUPT_HARD); - } - } else { - if (env->ivec_status & 0x20) { - CPUIRQ_DPRINTF("Lower IVEC IRQ %d\n", irq); - cs = CPU(cpu); - env->ivec_status &= ~0x20; - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); - } - } -} - typedef struct ResetData { SPARCCPU *cpu; uint64_t prom_addr; } ResetData; -static CPUTimer *cpu_timer_create(const char *name, SPARCCPU *cpu, - QEMUBHFunc *cb, uint32_t frequency, - uint64_t disabled_mask, uint64_t npt_mask) -{ - CPUTimer *timer = g_malloc0(sizeof (CPUTimer)); - - timer->name = name; - timer->frequency = frequency; - timer->disabled_mask = disabled_mask; - timer->npt_mask = npt_mask; - - timer->disabled = 1; - timer->npt = 1; - timer->clock_offset = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - - timer->qtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cb, cpu); - - return timer; -} - -static void cpu_timer_reset(CPUTimer *timer) -{ - timer->disabled = 1; - timer->clock_offset = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - - timer_del(timer->qtimer); -} - -static void main_cpu_reset(void *opaque) -{ - ResetData *s = (ResetData *)opaque; - CPUSPARCState *env = &s->cpu->env; - static unsigned int nr_resets; - - cpu_reset(CPU(s->cpu)); - - cpu_timer_reset(env->tick); - cpu_timer_reset(env->stick); - cpu_timer_reset(env->hstick); - - env->gregs[1] = 0; // Memory start - env->gregs[2] = ram_size; // Memory size - env->gregs[3] = 0; // Machine description XXX - if (nr_resets++ == 0) { - /* Power on reset */ - env->pc = s->prom_addr + 0x20ULL; - } else { - env->pc = s->prom_addr + 0x40ULL; - } - env->npc = env->pc + 4; -} - -static void tick_irq(void *opaque) -{ - SPARCCPU *cpu = opaque; - CPUSPARCState *env = &cpu->env; - - CPUTimer* timer = env->tick; - - if (timer->disabled) { - CPUIRQ_DPRINTF("tick_irq: softint disabled\n"); - return; - } else { - CPUIRQ_DPRINTF("tick: fire\n"); - } - - env->softint |= SOFTINT_TIMER; - cpu_kick_irq(cpu); -} - -static void stick_irq(void *opaque) -{ - SPARCCPU *cpu = opaque; - CPUSPARCState *env = &cpu->env; - - CPUTimer* timer = env->stick; - - if (timer->disabled) { - CPUIRQ_DPRINTF("stick_irq: softint disabled\n"); - return; - } else { - CPUIRQ_DPRINTF("stick: fire\n"); - } - - env->softint |= SOFTINT_STIMER; - cpu_kick_irq(cpu); -} - -static void hstick_irq(void *opaque) -{ - SPARCCPU *cpu = opaque; - CPUSPARCState *env = &cpu->env; - - CPUTimer* timer = env->hstick; - - if (timer->disabled) { - CPUIRQ_DPRINTF("hstick_irq: softint disabled\n"); - return; - } else { - CPUIRQ_DPRINTF("hstick: fire\n"); - } - - env->softint |= SOFTINT_STIMER; - cpu_kick_irq(cpu); -} - -static int64_t cpu_to_timer_ticks(int64_t cpu_ticks, uint32_t frequency) -{ - return muldiv64(cpu_ticks, NANOSECONDS_PER_SECOND, frequency); -} - -static uint64_t timer_to_cpu_ticks(int64_t timer_ticks, uint32_t frequency) -{ - return muldiv64(timer_ticks, frequency, NANOSECONDS_PER_SECOND); -} - -void cpu_tick_set_count(CPUTimer *timer, uint64_t count) -{ - uint64_t real_count = count & ~timer->npt_mask; - uint64_t npt_bit = count & timer->npt_mask; - - int64_t vm_clock_offset = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - - cpu_to_timer_ticks(real_count, timer->frequency); - - TIMER_DPRINTF("%s set_count count=0x%016lx (npt %s) p=%p\n", - timer->name, real_count, - timer->npt ? "disabled" : "enabled", timer); - - timer->npt = npt_bit ? 1 : 0; - timer->clock_offset = vm_clock_offset; -} - -uint64_t cpu_tick_get_count(CPUTimer *timer) -{ - uint64_t real_count = timer_to_cpu_ticks( - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - timer->clock_offset, - timer->frequency); - - TIMER_DPRINTF("%s get_count count=0x%016lx (npt %s) p=%p\n", - timer->name, real_count, - timer->npt ? "disabled" : "enabled", timer); - - if (timer->npt) { - real_count |= timer->npt_mask; - } - - return real_count; -} - -void cpu_tick_set_limit(CPUTimer *timer, uint64_t limit) -{ - int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - - uint64_t real_limit = limit & ~timer->disabled_mask; - timer->disabled = (limit & timer->disabled_mask) ? 1 : 0; - - int64_t expires = cpu_to_timer_ticks(real_limit, timer->frequency) + - timer->clock_offset; - - if (expires < now) { - expires = now + 1; - } - - TIMER_DPRINTF("%s set_limit limit=0x%016lx (%s) p=%p " - "called with limit=0x%016lx at 0x%016lx (delta=0x%016lx)\n", - timer->name, real_limit, - timer->disabled?"disabled":"enabled", - timer, limit, - timer_to_cpu_ticks(now - timer->clock_offset, - timer->frequency), - timer_to_cpu_ticks(expires - now, timer->frequency)); - - if (!real_limit) { - TIMER_DPRINTF("%s set_limit limit=ZERO - not starting timer\n", - timer->name); - timer_del(timer->qtimer); - } else if (timer->disabled) { - timer_del(timer->qtimer); - } else { - timer_mod(timer->qtimer, expires); - } -} - static void isa_irq_handler(void *opaque, int n, int level) { static const int isa_irq_to_ivec[16] = { @@ -723,46 +422,6 @@ static const TypeInfo ram_info = { .class_init = ram_class_init, }; -static SPARCCPU *cpu_devinit(const char *cpu_model, const struct hwdef *hwdef) -{ - SPARCCPU *cpu; - CPUSPARCState *env; - ResetData *reset_info; - - uint32_t tick_frequency = 100*1000000; - uint32_t stick_frequency = 100*1000000; - uint32_t hstick_frequency = 100*1000000; - - if (cpu_model == NULL) { - cpu_model = hwdef->default_cpu_model; - } - cpu = cpu_sparc_init(cpu_model); - if (cpu == NULL) { - fprintf(stderr, "Unable to find Sparc CPU definition\n"); - exit(1); - } - env = &cpu->env; - - env->tick = cpu_timer_create("tick", cpu, tick_irq, - tick_frequency, TICK_INT_DIS, - TICK_NPT_MASK); - - env->stick = cpu_timer_create("stick", cpu, stick_irq, - stick_frequency, TICK_INT_DIS, - TICK_NPT_MASK); - - env->hstick = cpu_timer_create("hstick", cpu, hstick_irq, - hstick_frequency, TICK_INT_DIS, - TICK_NPT_MASK); - - reset_info = g_malloc0(sizeof(ResetData)); - reset_info->cpu = cpu; - reset_info->prom_addr = hwdef->prom_addr; - qemu_register_reset(main_cpu_reset, reset_info); - - return cpu; -} - static void sun4uv_init(MemoryRegion *address_space_mem, MachineState *machine, const struct hwdef *hwdef) @@ -781,14 +440,15 @@ static void sun4uv_init(MemoryRegion *address_space_mem, FWCfgState *fw_cfg; /* init CPUs */ - cpu = cpu_devinit(machine->cpu_model, hwdef); + cpu = sparc64_cpu_devinit(machine->cpu_model, hwdef->default_cpu_model, + hwdef->prom_addr); /* set up devices */ ram_init(0, machine->ram_size); prom_init(hwdef->prom_addr, bios_name); - ivec_irqs = qemu_allocate_irqs(cpu_set_ivec_irq, cpu, IVEC_MAX); + ivec_irqs = qemu_allocate_irqs(sparc64_cpu_set_ivec_irq, cpu, IVEC_MAX); pci_bus = pci_apb_init(APB_SPECIAL_BASE, APB_MEM_BASE, ivec_irqs, &pci_bus2, &pci_bus3, &pbm_irqs); pci_vga_init(pci_bus); @@ -882,7 +542,6 @@ static void sun4uv_init(MemoryRegion *address_space_mem, enum { sun4u_id = 0, sun4v_id = 64, - niagara_id, }; static const struct hwdef hwdefs[] = { @@ -900,13 +559,6 @@ static const struct hwdef hwdefs[] = { .prom_addr = 0x1fff0000000ULL, .console_serial_base = 0, }, - /* Sun4v generic Niagara machine */ - { - .default_cpu_model = "Sun UltraSparc T1", - .machine_id = niagara_id, - .prom_addr = 0xfff0000000ULL, - .console_serial_base = 0xfff0c2c000ULL, - }, }; /* Sun4u hardware initialisation */ @@ -921,12 +573,6 @@ static void sun4v_init(MachineState *machine) sun4uv_init(get_system_memory(), machine, &hwdefs[1]); } -/* Niagara hardware initialisation */ -static void niagara_init(MachineState *machine) -{ - sun4uv_init(get_system_memory(), machine, &hwdefs[2]); -} - static void sun4u_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -960,22 +606,6 @@ static const TypeInfo sun4v_type = { .class_init = sun4v_class_init, }; -static void niagara_class_init(ObjectClass *oc, void *data) -{ - MachineClass *mc = MACHINE_CLASS(oc); - - mc->desc = "Sun4v platform, Niagara"; - mc->init = niagara_init; - mc->max_cpus = 1; /* XXX for now */ - mc->default_boot_order = "c"; -} - -static const TypeInfo niagara_type = { - .name = MACHINE_TYPE_NAME("Niagara"), - .parent = TYPE_MACHINE, - .class_init = niagara_class_init, -}; - static void sun4u_register_types(void) { type_register_static(&ebus_info); @@ -984,7 +614,6 @@ static void sun4u_register_types(void) type_register_static(&sun4u_type); type_register_static(&sun4v_type); - type_register_static(&niagara_type); } type_init(sun4u_register_types) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index 78f5aed532..ae1ad2dba6 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -39,11 +39,14 @@ #define CONF_ENABLE_W2 18 #define CONF_ENABLE_W1 17 #define CONF_ENABLE_W0 16 -#define CONF_FLASH_TYPE4 9 -#define CONF_FLASH_TYPE3 7 -#define CONF_FLASH_TYPE2 5 -#define CONF_FLASH_TYPE1 3 -#define CONF_FLASH_TYPE0 1 +#define CONF_FLASH_TYPE4 8 +#define CONF_FLASH_TYPE3 6 +#define CONF_FLASH_TYPE2 4 +#define CONF_FLASH_TYPE1 2 +#define CONF_FLASH_TYPE0 0 +#define CONF_FLASH_TYPE_NOR 0x0 +#define CONF_FLASH_TYPE_NAND 0x1 +#define CONF_FLASH_TYPE_SPI 0x2 /* CE Control Register */ #define R_CE_CTRL (0x04 / 4) @@ -66,6 +69,7 @@ #define R_CTRL0 (0x10 / 4) #define CTRL_CMD_SHIFT 16 #define CTRL_CMD_MASK 0xff +#define CTRL_AST2400_SPI_4BYTE (1 << 13) #define CTRL_CE_STOP_ACTIVE (1 << 2) #define CTRL_CMD_MODE_MASK 0x3 #define CTRL_READMODE 0x0 @@ -127,11 +131,17 @@ #define R_SPI_MISC_CTRL (0x10 / 4) #define R_SPI_TIMINGS (0x14 / 4) +#define ASPEED_SMC_R_SPI_MAX (0x20 / 4) +#define ASPEED_SMC_R_SMC_MAX (0x20 / 4) + #define ASPEED_SOC_SMC_FLASH_BASE 0x10000000 #define ASPEED_SOC_FMC_FLASH_BASE 0x20000000 #define ASPEED_SOC_SPI_FLASH_BASE 0x30000000 #define ASPEED_SOC_SPI2_FLASH_BASE 0x38000000 +/* Flash opcodes. */ +#define SPI_OP_READ 0x03 /* Read data bytes (low frequency) */ + /* * Default segments mapping addresses and size for each slave per * controller. These can be changed when board is initialized with the @@ -170,24 +180,85 @@ static const AspeedSegments aspeed_segments_ast2500_spi2[] = { }; static const AspeedSMCController controllers[] = { - { "aspeed.smc.smc", R_CONF, R_CE_CTRL, R_CTRL0, R_TIMINGS, - CONF_ENABLE_W0, 5, aspeed_segments_legacy, - ASPEED_SOC_SMC_FLASH_BASE, 0x6000000 }, - { "aspeed.smc.fmc", R_CONF, R_CE_CTRL, R_CTRL0, R_TIMINGS, - CONF_ENABLE_W0, 5, aspeed_segments_fmc, - ASPEED_SOC_FMC_FLASH_BASE, 0x10000000 }, - { "aspeed.smc.spi", R_SPI_CONF, 0xff, R_SPI_CTRL0, R_SPI_TIMINGS, - SPI_CONF_ENABLE_W0, 1, aspeed_segments_spi, - ASPEED_SOC_SPI_FLASH_BASE, 0x10000000 }, - { "aspeed.smc.ast2500-fmc", R_CONF, R_CE_CTRL, R_CTRL0, R_TIMINGS, - CONF_ENABLE_W0, 3, aspeed_segments_ast2500_fmc, - ASPEED_SOC_FMC_FLASH_BASE, 0x10000000 }, - { "aspeed.smc.ast2500-spi1", R_CONF, R_CE_CTRL, R_CTRL0, R_TIMINGS, - CONF_ENABLE_W0, 2, aspeed_segments_ast2500_spi1, - ASPEED_SOC_SPI_FLASH_BASE, 0x8000000 }, - { "aspeed.smc.ast2500-spi2", R_CONF, R_CE_CTRL, R_CTRL0, R_TIMINGS, - CONF_ENABLE_W0, 2, aspeed_segments_ast2500_spi2, - ASPEED_SOC_SPI2_FLASH_BASE, 0x8000000 }, + { + .name = "aspeed.smc.smc", + .r_conf = R_CONF, + .r_ce_ctrl = R_CE_CTRL, + .r_ctrl0 = R_CTRL0, + .r_timings = R_TIMINGS, + .conf_enable_w0 = CONF_ENABLE_W0, + .max_slaves = 5, + .segments = aspeed_segments_legacy, + .flash_window_base = ASPEED_SOC_SMC_FLASH_BASE, + .flash_window_size = 0x6000000, + .has_dma = false, + .nregs = ASPEED_SMC_R_SMC_MAX, + }, { + .name = "aspeed.smc.fmc", + .r_conf = R_CONF, + .r_ce_ctrl = R_CE_CTRL, + .r_ctrl0 = R_CTRL0, + .r_timings = R_TIMINGS, + .conf_enable_w0 = CONF_ENABLE_W0, + .max_slaves = 5, + .segments = aspeed_segments_fmc, + .flash_window_base = ASPEED_SOC_FMC_FLASH_BASE, + .flash_window_size = 0x10000000, + .has_dma = true, + .nregs = ASPEED_SMC_R_MAX, + }, { + .name = "aspeed.smc.spi", + .r_conf = R_SPI_CONF, + .r_ce_ctrl = 0xff, + .r_ctrl0 = R_SPI_CTRL0, + .r_timings = R_SPI_TIMINGS, + .conf_enable_w0 = SPI_CONF_ENABLE_W0, + .max_slaves = 1, + .segments = aspeed_segments_spi, + .flash_window_base = ASPEED_SOC_SPI_FLASH_BASE, + .flash_window_size = 0x10000000, + .has_dma = false, + .nregs = ASPEED_SMC_R_SPI_MAX, + }, { + .name = "aspeed.smc.ast2500-fmc", + .r_conf = R_CONF, + .r_ce_ctrl = R_CE_CTRL, + .r_ctrl0 = R_CTRL0, + .r_timings = R_TIMINGS, + .conf_enable_w0 = CONF_ENABLE_W0, + .max_slaves = 3, + .segments = aspeed_segments_ast2500_fmc, + .flash_window_base = ASPEED_SOC_FMC_FLASH_BASE, + .flash_window_size = 0x10000000, + .has_dma = true, + .nregs = ASPEED_SMC_R_MAX, + }, { + .name = "aspeed.smc.ast2500-spi1", + .r_conf = R_CONF, + .r_ce_ctrl = R_CE_CTRL, + .r_ctrl0 = R_CTRL0, + .r_timings = R_TIMINGS, + .conf_enable_w0 = CONF_ENABLE_W0, + .max_slaves = 2, + .segments = aspeed_segments_ast2500_spi1, + .flash_window_base = ASPEED_SOC_SPI_FLASH_BASE, + .flash_window_size = 0x8000000, + .has_dma = false, + .nregs = ASPEED_SMC_R_MAX, + }, { + .name = "aspeed.smc.ast2500-spi2", + .r_conf = R_CONF, + .r_ce_ctrl = R_CE_CTRL, + .r_ctrl0 = R_CTRL0, + .r_timings = R_TIMINGS, + .conf_enable_w0 = CONF_ENABLE_W0, + .max_slaves = 2, + .segments = aspeed_segments_ast2500_spi2, + .flash_window_base = ASPEED_SOC_SPI2_FLASH_BASE, + .flash_window_size = 0x8000000, + .has_dma = false, + .nregs = ASPEED_SMC_R_MAX, + }, }; /* @@ -328,36 +399,137 @@ static const MemoryRegionOps aspeed_smc_flash_default_ops = { }, }; -static inline int aspeed_smc_flash_mode(const AspeedSMCState *s, int cs) +static inline int aspeed_smc_flash_mode(const AspeedSMCFlash *fl) { - return s->regs[s->r_ctrl0 + cs] & CTRL_CMD_MODE_MASK; + const AspeedSMCState *s = fl->controller; + + return s->regs[s->r_ctrl0 + fl->id] & CTRL_CMD_MODE_MASK; } -static inline bool aspeed_smc_is_usermode(const AspeedSMCState *s, int cs) +static inline bool aspeed_smc_is_writable(const AspeedSMCFlash *fl) { - return aspeed_smc_flash_mode(s, cs) == CTRL_USERMODE; + const AspeedSMCState *s = fl->controller; + + return s->regs[s->r_conf] & (1 << (s->conf_enable_w0 + fl->id)); } -static inline bool aspeed_smc_is_writable(const AspeedSMCState *s, int cs) +static inline int aspeed_smc_flash_cmd(const AspeedSMCFlash *fl) { - return s->regs[s->r_conf] & (1 << (s->conf_enable_w0 + cs)); + const AspeedSMCState *s = fl->controller; + int cmd = (s->regs[s->r_ctrl0 + fl->id] >> CTRL_CMD_SHIFT) & CTRL_CMD_MASK; + + /* In read mode, the default SPI command is READ (0x3). In other + * modes, the command should necessarily be defined */ + if (aspeed_smc_flash_mode(fl) == CTRL_READMODE) { + cmd = SPI_OP_READ; + } + + if (!cmd) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: no command defined for mode %d\n", + __func__, aspeed_smc_flash_mode(fl)); + } + + return cmd; +} + +static inline int aspeed_smc_flash_is_4byte(const AspeedSMCFlash *fl) +{ + const AspeedSMCState *s = fl->controller; + + if (s->ctrl->segments == aspeed_segments_spi) { + return s->regs[s->r_ctrl0] & CTRL_AST2400_SPI_4BYTE; + } else { + return s->regs[s->r_ce_ctrl] & (1 << (CTRL_EXTENDED0 + fl->id)); + } +} + +static inline bool aspeed_smc_is_ce_stop_active(const AspeedSMCFlash *fl) +{ + const AspeedSMCState *s = fl->controller; + + return s->regs[s->r_ctrl0 + fl->id] & CTRL_CE_STOP_ACTIVE; +} + +static void aspeed_smc_flash_select(AspeedSMCFlash *fl) +{ + AspeedSMCState *s = fl->controller; + + s->regs[s->r_ctrl0 + fl->id] &= ~CTRL_CE_STOP_ACTIVE; + qemu_set_irq(s->cs_lines[fl->id], aspeed_smc_is_ce_stop_active(fl)); +} + +static void aspeed_smc_flash_unselect(AspeedSMCFlash *fl) +{ + AspeedSMCState *s = fl->controller; + + s->regs[s->r_ctrl0 + fl->id] |= CTRL_CE_STOP_ACTIVE; + qemu_set_irq(s->cs_lines[fl->id], aspeed_smc_is_ce_stop_active(fl)); +} + +static uint32_t aspeed_smc_check_segment_addr(const AspeedSMCFlash *fl, + uint32_t addr) +{ + const AspeedSMCState *s = fl->controller; + AspeedSegments seg; + + aspeed_smc_reg_to_segment(s->regs[R_SEG_ADDR0 + fl->id], &seg); + if ((addr & (seg.size - 1)) != addr) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid address 0x%08x for CS%d segment : " + "[ 0x%"HWADDR_PRIx" - 0x%"HWADDR_PRIx" ]\n", + s->ctrl->name, addr, fl->id, seg.addr, + seg.addr + seg.size); + } + + addr &= seg.size - 1; + return addr; +} + +static void aspeed_smc_flash_send_addr(AspeedSMCFlash *fl, uint32_t addr) +{ + const AspeedSMCState *s = fl->controller; + uint8_t cmd = aspeed_smc_flash_cmd(fl); + + /* Flash access can not exceed CS segment */ + addr = aspeed_smc_check_segment_addr(fl, addr); + + ssi_transfer(s->spi, cmd); + + if (aspeed_smc_flash_is_4byte(fl)) { + ssi_transfer(s->spi, (addr >> 24) & 0xff); + } + ssi_transfer(s->spi, (addr >> 16) & 0xff); + ssi_transfer(s->spi, (addr >> 8) & 0xff); + ssi_transfer(s->spi, (addr & 0xff)); } static uint64_t aspeed_smc_flash_read(void *opaque, hwaddr addr, unsigned size) { AspeedSMCFlash *fl = opaque; - const AspeedSMCState *s = fl->controller; + AspeedSMCState *s = fl->controller; uint64_t ret = 0; int i; - if (aspeed_smc_is_usermode(s, fl->id)) { + switch (aspeed_smc_flash_mode(fl)) { + case CTRL_USERMODE: for (i = 0; i < size; i++) { ret |= ssi_transfer(s->spi, 0x0) << (8 * i); } - } else { - qemu_log_mask(LOG_UNIMP, "%s: usermode not implemented\n", - __func__); - ret = -1; + break; + case CTRL_READMODE: + case CTRL_FREADMODE: + aspeed_smc_flash_select(fl); + aspeed_smc_flash_send_addr(fl, addr); + + for (i = 0; i < size; i++) { + ret |= ssi_transfer(s->spi, 0x0) << (8 * i); + } + + aspeed_smc_flash_unselect(fl); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid flash mode %d\n", + __func__, aspeed_smc_flash_mode(fl)); } return ret; @@ -367,23 +539,34 @@ static void aspeed_smc_flash_write(void *opaque, hwaddr addr, uint64_t data, unsigned size) { AspeedSMCFlash *fl = opaque; - const AspeedSMCState *s = fl->controller; + AspeedSMCState *s = fl->controller; int i; - if (!aspeed_smc_is_writable(s, fl->id)) { + if (!aspeed_smc_is_writable(fl)) { qemu_log_mask(LOG_GUEST_ERROR, "%s: flash is not writable at 0x%" HWADDR_PRIx "\n", __func__, addr); return; } - if (!aspeed_smc_is_usermode(s, fl->id)) { - qemu_log_mask(LOG_UNIMP, "%s: usermode not implemented\n", - __func__); - return; - } + switch (aspeed_smc_flash_mode(fl)) { + case CTRL_USERMODE: + for (i = 0; i < size; i++) { + ssi_transfer(s->spi, (data >> (8 * i)) & 0xff); + } + break; + case CTRL_WRITEMODE: + aspeed_smc_flash_select(fl); + aspeed_smc_flash_send_addr(fl, addr); - for (i = 0; i < size; i++) { - ssi_transfer(s->spi, (data >> (8 * i)) & 0xff); + for (i = 0; i < size; i++) { + ssi_transfer(s->spi, (data >> (8 * i)) & 0xff); + } + + aspeed_smc_flash_unselect(fl); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid flash mode %d\n", + __func__, aspeed_smc_flash_mode(fl)); } } @@ -397,18 +580,11 @@ static const MemoryRegionOps aspeed_smc_flash_ops = { }, }; -static bool aspeed_smc_is_ce_stop_active(const AspeedSMCState *s, int cs) -{ - return s->regs[s->r_ctrl0 + cs] & CTRL_CE_STOP_ACTIVE; -} - -static void aspeed_smc_update_cs(const AspeedSMCState *s) +static void aspeed_smc_flash_update_cs(AspeedSMCFlash *fl) { - int i; + const AspeedSMCState *s = fl->controller; - for (i = 0; i < s->num_cs; ++i) { - qemu_set_irq(s->cs_lines[i], aspeed_smc_is_ce_stop_active(s, i)); - } + qemu_set_irq(s->cs_lines[fl->id], aspeed_smc_is_ce_stop_active(fl)); } static void aspeed_smc_reset(DeviceState *d) @@ -424,6 +600,7 @@ static void aspeed_smc_reset(DeviceState *d) /* Unselect all slaves */ for (i = 0; i < s->num_cs; ++i) { s->regs[s->r_ctrl0 + i] |= CTRL_CE_STOP_ACTIVE; + qemu_set_irq(s->cs_lines[i], true); } /* setup default segment register values for all */ @@ -432,7 +609,24 @@ static void aspeed_smc_reset(DeviceState *d) aspeed_smc_segment_to_reg(&s->ctrl->segments[i]); } - aspeed_smc_update_cs(s); + /* HW strapping for AST2500 FMC controllers */ + if (s->ctrl->segments == aspeed_segments_ast2500_fmc) { + /* flash type is fixed to SPI for CE0 and CE1 */ + s->regs[s->r_conf] |= (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE0); + s->regs[s->r_conf] |= (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE1); + + /* 4BYTE mode is autodetected for CE0. Let's force it to 1 for + * now */ + s->regs[s->r_ce_ctrl] |= (1 << (CTRL_EXTENDED0)); + } + + /* HW strapping for AST2400 FMC controllers (SCU70). Let's use the + * configuration of the palmetto-bmc machine */ + if (s->ctrl->segments == aspeed_segments_fmc) { + s->regs[s->r_conf] |= (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE0); + + s->regs[s->r_ce_ctrl] |= (1 << (CTRL_EXTENDED0)); + } } static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size) @@ -441,13 +635,6 @@ static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size) addr >>= 2; - if (addr >= ARRAY_SIZE(s->regs)) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Out-of-bounds read at 0x%" HWADDR_PRIx "\n", - __func__, addr); - return 0; - } - if (addr == s->r_conf || addr == s->r_timings || addr == s->r_ce_ctrl || @@ -470,20 +657,14 @@ static void aspeed_smc_write(void *opaque, hwaddr addr, uint64_t data, addr >>= 2; - if (addr >= ARRAY_SIZE(s->regs)) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Out-of-bounds write at 0x%" HWADDR_PRIx "\n", - __func__, addr); - return; - } - if (addr == s->r_conf || addr == s->r_timings || addr == s->r_ce_ctrl) { s->regs[addr] = value; } else if (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + s->num_cs) { + int cs = addr - s->r_ctrl0; s->regs[addr] = value; - aspeed_smc_update_cs(s); + aspeed_smc_flash_update_cs(&s->flashes[cs]); } else if (addr >= R_SEG_ADDR0 && addr < R_SEG_ADDR0 + s->ctrl->max_slaves) { int cs = addr - R_SEG_ADDR0; @@ -541,11 +722,9 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp) sysbus_init_irq(sbd, &s->cs_lines[i]); } - aspeed_smc_reset(dev); - /* The memory region for the controller registers */ memory_region_init_io(&s->mmio, OBJECT(s), &aspeed_smc_ops, s, - s->ctrl->name, ASPEED_SMC_R_MAX * 4); + s->ctrl->name, s->ctrl->nregs * 4); sysbus_init_mmio(sbd, &s->mmio); /* diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs index 7ba8c23c75..c1e93a3924 100644 --- a/hw/timer/Makefile.objs +++ b/hw/timer/Makefile.objs @@ -34,3 +34,5 @@ obj-$(CONFIG_ALLWINNER_A10_PIT) += allwinner-a10-pit.o common-obj-$(CONFIG_STM32F2XX_TIMER) += stm32f2xx_timer.o common-obj-$(CONFIG_ASPEED_SOC) += aspeed_timer.o + +common-obj-$(CONFIG_SUN4V_RTC) += sun4v-rtc.o diff --git a/hw/timer/sun4v-rtc.c b/hw/timer/sun4v-rtc.c new file mode 100644 index 0000000000..310523225f --- /dev/null +++ b/hw/timer/sun4v-rtc.c @@ -0,0 +1,102 @@ +/* + * QEMU sun4v Real Time Clock device + * + * The sun4v_rtc device (sun4v tod clock) + * + * Copyright (c) 2016 Artyom Tarasenko + * + * This code is licensed under the GNU GPL v3 or (at your option) any later + * version. + */ + +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "qemu/timer.h" +#include "hw/timer/sun4v-rtc.h" + +//#define DEBUG_SUN4V_RTC + +#ifdef DEBUG_SUN4V_RTC +#define DPRINTF(fmt, ...) \ + do { printf("sun4v_rtc: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) do {} while (0) +#endif + +#define TYPE_SUN4V_RTC "sun4v_rtc" +#define SUN4V_RTC(obj) OBJECT_CHECK(Sun4vRtc, (obj), TYPE_SUN4V_RTC) + +typedef struct Sun4vRtc { + SysBusDevice parent_obj; + + MemoryRegion iomem; +} Sun4vRtc; + +static uint64_t sun4v_rtc_read(void *opaque, hwaddr addr, + unsigned size) +{ + uint64_t val = get_clock_realtime() / NANOSECONDS_PER_SECOND; + if (!(addr & 4ULL)) { + /* accessing the high 32 bits */ + val >>= 32; + } + DPRINTF("read from " TARGET_FMT_plx " val %lx\n", addr, val); + return val; +} + +static void sun4v_rtc_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + DPRINTF("write 0x%x to " TARGET_FMT_plx "\n", (unsigned)val, addr); +} + +static const MemoryRegionOps sun4v_rtc_ops = { + .read = sun4v_rtc_read, + .write = sun4v_rtc_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +void sun4v_rtc_init(hwaddr addr) +{ + DeviceState *dev; + SysBusDevice *s; + + dev = qdev_create(NULL, TYPE_SUN4V_RTC); + s = SYS_BUS_DEVICE(dev); + + qdev_init_nofail(dev); + + sysbus_mmio_map(s, 0, addr); +} + +static int sun4v_rtc_init1(SysBusDevice *dev) +{ + Sun4vRtc *s = SUN4V_RTC(dev); + + memory_region_init_io(&s->iomem, OBJECT(s), &sun4v_rtc_ops, s, + "sun4v-rtc", 0x08ULL); + sysbus_init_mmio(dev, &s->iomem); + return 0; +} + +static void sun4v_rtc_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = sun4v_rtc_init1; +} + +static const TypeInfo sun4v_rtc_info = { + .name = TYPE_SUN4V_RTC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Sun4vRtc), + .class_init = sun4v_rtc_class_init, +}; + +static void sun4v_rtc_register_types(void) +{ + type_register_static(&sun4v_rtc_info); +} + +type_init(sun4v_rtc_register_types) diff --git a/hw/virtio/vhost-backend.c b/hw/virtio/vhost-backend.c index 272a5ec584..be927b891e 100644 --- a/hw/virtio/vhost-backend.c +++ b/hw/virtio/vhost-backend.c @@ -185,6 +185,102 @@ static int vhost_kernel_vsock_set_running(struct vhost_dev *dev, int start) } #endif /* CONFIG_VHOST_VSOCK */ +static void vhost_kernel_iotlb_read(void *opaque) +{ + struct vhost_dev *dev = opaque; + struct vhost_msg msg; + ssize_t len; + + while ((len = read((uintptr_t)dev->opaque, &msg, sizeof msg)) > 0) { + struct vhost_iotlb_msg *imsg = &msg.iotlb; + if (len < sizeof msg) { + error_report("Wrong vhost message len: %d", (int)len); + break; + } + if (msg.type != VHOST_IOTLB_MSG) { + error_report("Unknown vhost iotlb message type"); + break; + } + switch (imsg->type) { + case VHOST_IOTLB_MISS: + vhost_device_iotlb_miss(dev, imsg->iova, + imsg->perm != VHOST_ACCESS_RO); + break; + case VHOST_IOTLB_UPDATE: + case VHOST_IOTLB_INVALIDATE: + error_report("Unexpected IOTLB message type"); + break; + case VHOST_IOTLB_ACCESS_FAIL: + /* FIXME: report device iotlb error */ + break; + default: + break; + } + } +} + +static int vhost_kernel_update_device_iotlb(struct vhost_dev *dev, + uint64_t iova, uint64_t uaddr, + uint64_t len, + IOMMUAccessFlags perm) +{ + struct vhost_msg msg; + msg.type = VHOST_IOTLB_MSG; + msg.iotlb.iova = iova; + msg.iotlb.uaddr = uaddr; + msg.iotlb.size = len; + msg.iotlb.type = VHOST_IOTLB_UPDATE; + + switch (perm) { + case IOMMU_RO: + msg.iotlb.perm = VHOST_ACCESS_RO; + break; + case IOMMU_WO: + msg.iotlb.perm = VHOST_ACCESS_WO; + break; + case IOMMU_RW: + msg.iotlb.perm = VHOST_ACCESS_RW; + break; + default: + g_assert_not_reached(); + } + + if (write((uintptr_t)dev->opaque, &msg, sizeof msg) != sizeof msg) { + error_report("Fail to update device iotlb"); + return -EFAULT; + } + + return 0; +} + +static int vhost_kernel_invalidate_device_iotlb(struct vhost_dev *dev, + uint64_t iova, uint64_t len) +{ + struct vhost_msg msg; + + msg.type = VHOST_IOTLB_MSG; + msg.iotlb.iova = iova; + msg.iotlb.size = len; + msg.iotlb.type = VHOST_IOTLB_INVALIDATE; + + if (write((uintptr_t)dev->opaque, &msg, sizeof msg) != sizeof msg) { + error_report("Fail to invalidate device iotlb"); + return -EFAULT; + } + + return 0; +} + +static void vhost_kernel_set_iotlb_callback(struct vhost_dev *dev, + int enabled) +{ + if (enabled) + qemu_set_fd_handler((uintptr_t)dev->opaque, + vhost_kernel_iotlb_read, NULL, dev); + else + qemu_set_fd_handler((uintptr_t)dev->opaque, NULL, NULL, NULL); +} + static const VhostOps kernel_ops = { .backend_type = VHOST_BACKEND_TYPE_KERNEL, .vhost_backend_init = vhost_kernel_init, @@ -214,6 +310,9 @@ static const VhostOps kernel_ops = { .vhost_vsock_set_guest_cid = vhost_kernel_vsock_set_guest_cid, .vhost_vsock_set_running = vhost_kernel_vsock_set_running, #endif /* CONFIG_VHOST_VSOCK */ + .vhost_set_iotlb_callback = vhost_kernel_set_iotlb_callback, + .vhost_update_device_iotlb = vhost_kernel_update_device_iotlb, + .vhost_invalidate_device_iotlb = vhost_kernel_invalidate_device_iotlb, }; int vhost_set_backend_type(struct vhost_dev *dev, VhostBackendType backend_type) diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index d396b22531..9cacf557f2 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -26,6 +26,7 @@ #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio-access.h" #include "migration/migration.h" +#include "sysemu/dma.h" /* enabled until disconnected backend stabilizes */ #define _VHOST_DEBUG 1 @@ -421,8 +422,36 @@ static inline void vhost_dev_log_resize(struct vhost_dev *dev, uint64_t size) dev->log_size = size; } +static int vhost_dev_has_iommu(struct vhost_dev *dev) +{ + VirtIODevice *vdev = dev->vdev; + AddressSpace *dma_as = vdev->dma_as; + + return memory_region_is_iommu(dma_as->root) && + virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM); +} + +static void *vhost_memory_map(struct vhost_dev *dev, hwaddr addr, + hwaddr *plen, int is_write) +{ + if (!vhost_dev_has_iommu(dev)) { + return cpu_physical_memory_map(addr, plen, is_write); + } else { + return (void *)(uintptr_t)addr; + } +} + +static void vhost_memory_unmap(struct vhost_dev *dev, void *buffer, + hwaddr len, int is_write, + hwaddr access_len) +{ + if (!vhost_dev_has_iommu(dev)) { + cpu_physical_memory_unmap(buffer, len, is_write, access_len); + } +} -static int vhost_verify_ring_part_mapping(void *part, +static int vhost_verify_ring_part_mapping(struct vhost_dev *dev, + void *part, uint64_t part_addr, uint64_t part_size, uint64_t start_addr, @@ -436,14 +465,14 @@ static int vhost_verify_ring_part_mapping(void *part, return 0; } l = part_size; - p = cpu_physical_memory_map(part_addr, &l, 1); + p = vhost_memory_map(dev, part_addr, &l, 1); if (!p || l != part_size) { r = -ENOMEM; } if (p != part) { r = -EBUSY; } - cpu_physical_memory_unmap(p, l, 0, 0); + vhost_memory_unmap(dev, p, l, 0, 0); return r; } @@ -463,21 +492,21 @@ static int vhost_verify_ring_mappings(struct vhost_dev *dev, struct vhost_virtqueue *vq = dev->vqs + i; j = 0; - r = vhost_verify_ring_part_mapping(vq->desc, vq->desc_phys, + r = vhost_verify_ring_part_mapping(dev, vq->desc, vq->desc_phys, vq->desc_size, start_addr, size); if (!r) { break; } j++; - r = vhost_verify_ring_part_mapping(vq->avail, vq->avail_phys, + r = vhost_verify_ring_part_mapping(dev, vq->avail, vq->avail_phys, vq->avail_size, start_addr, size); if (!r) { break; } j++; - r = vhost_verify_ring_part_mapping(vq->used, vq->used_phys, + r = vhost_verify_ring_part_mapping(dev, vq->used, vq->used_phys, vq->used_size, start_addr, size); if (!r) { break; @@ -715,7 +744,8 @@ static int vhost_virtqueue_set_addr(struct vhost_dev *dev, return 0; } -static int vhost_dev_set_features(struct vhost_dev *dev, bool enable_log) +static int vhost_dev_set_features(struct vhost_dev *dev, + bool enable_log) { uint64_t features = dev->acked_features; int r; @@ -858,6 +888,56 @@ static int vhost_virtqueue_set_vring_endian_legacy(struct vhost_dev *dev, return -errno; } +static int vhost_memory_region_lookup(struct vhost_dev *hdev, + uint64_t gpa, uint64_t *uaddr, + uint64_t *len) +{ + int i; + + for (i = 0; i < hdev->mem->nregions; i++) { + struct vhost_memory_region *reg = hdev->mem->regions + i; + + if (gpa >= reg->guest_phys_addr && + reg->guest_phys_addr + reg->memory_size > gpa) { + *uaddr = reg->userspace_addr + gpa - reg->guest_phys_addr; + *len = reg->guest_phys_addr + reg->memory_size - gpa; + return 0; + } + } + + return -EFAULT; +} + +void vhost_device_iotlb_miss(struct vhost_dev *dev, uint64_t iova, int write) +{ + IOMMUTLBEntry iotlb; + uint64_t uaddr, len; + + rcu_read_lock(); + + iotlb = address_space_get_iotlb_entry(dev->vdev->dma_as, + iova, write); + if (iotlb.target_as != NULL) { + if (vhost_memory_region_lookup(dev, iotlb.translated_addr, + &uaddr, &len)) { + error_report("Fail to lookup the translated address " + "%"PRIx64, iotlb.translated_addr); + goto out; + } + + len = MIN(iotlb.addr_mask + 1, len); + iova = iova & ~iotlb.addr_mask; + + if (dev->vhost_ops->vhost_update_device_iotlb(dev, iova, uaddr, + len, iotlb.perm)) { + error_report("Fail to update device iotlb"); + goto out; + } + } +out: + rcu_read_unlock(); +} + static int vhost_virtqueue_start(struct vhost_dev *dev, struct VirtIODevice *vdev, struct vhost_virtqueue *vq, @@ -903,21 +983,21 @@ static int vhost_virtqueue_start(struct vhost_dev *dev, vq->desc_size = s = l = virtio_queue_get_desc_size(vdev, idx); vq->desc_phys = a = virtio_queue_get_desc_addr(vdev, idx); - vq->desc = cpu_physical_memory_map(a, &l, 0); + vq->desc = vhost_memory_map(dev, a, &l, 0); if (!vq->desc || l != s) { r = -ENOMEM; goto fail_alloc_desc; } vq->avail_size = s = l = virtio_queue_get_avail_size(vdev, idx); vq->avail_phys = a = virtio_queue_get_avail_addr(vdev, idx); - vq->avail = cpu_physical_memory_map(a, &l, 0); + vq->avail = vhost_memory_map(dev, a, &l, 0); if (!vq->avail || l != s) { r = -ENOMEM; goto fail_alloc_avail; } vq->used_size = s = l = virtio_queue_get_used_size(vdev, idx); vq->used_phys = a = virtio_queue_get_used_addr(vdev, idx); - vq->used = cpu_physical_memory_map(a, &l, 1); + vq->used = vhost_memory_map(dev, a, &l, 1); if (!vq->used || l != s) { r = -ENOMEM; goto fail_alloc_used; @@ -963,14 +1043,14 @@ static int vhost_virtqueue_start(struct vhost_dev *dev, fail_vector: fail_kick: fail_alloc: - cpu_physical_memory_unmap(vq->used, virtio_queue_get_used_size(vdev, idx), - 0, 0); + vhost_memory_unmap(dev, vq->used, virtio_queue_get_used_size(vdev, idx), + 0, 0); fail_alloc_used: - cpu_physical_memory_unmap(vq->avail, virtio_queue_get_avail_size(vdev, idx), - 0, 0); + vhost_memory_unmap(dev, vq->avail, virtio_queue_get_avail_size(vdev, idx), + 0, 0); fail_alloc_avail: - cpu_physical_memory_unmap(vq->desc, virtio_queue_get_desc_size(vdev, idx), - 0, 0); + vhost_memory_unmap(dev, vq->desc, virtio_queue_get_desc_size(vdev, idx), + 0, 0); fail_alloc_desc: return r; } @@ -1004,12 +1084,12 @@ static void vhost_virtqueue_stop(struct vhost_dev *dev, vhost_vq_index); } - cpu_physical_memory_unmap(vq->used, virtio_queue_get_used_size(vdev, idx), - 1, virtio_queue_get_used_size(vdev, idx)); - cpu_physical_memory_unmap(vq->avail, virtio_queue_get_avail_size(vdev, idx), - 0, virtio_queue_get_avail_size(vdev, idx)); - cpu_physical_memory_unmap(vq->desc, virtio_queue_get_desc_size(vdev, idx), - 0, virtio_queue_get_desc_size(vdev, idx)); + vhost_memory_unmap(dev, vq->used, virtio_queue_get_used_size(vdev, idx), + 1, virtio_queue_get_used_size(vdev, idx)); + vhost_memory_unmap(dev, vq->avail, virtio_queue_get_avail_size(vdev, idx), + 0, virtio_queue_get_avail_size(vdev, idx)); + vhost_memory_unmap(dev, vq->desc, virtio_queue_get_desc_size(vdev, idx), + 0, virtio_queue_get_desc_size(vdev, idx)); } static void vhost_eventfd_add(MemoryListener *listener, @@ -1066,6 +1146,9 @@ static int vhost_virtqueue_init(struct vhost_dev *dev, r = -errno; goto fail_call; } + + vq->dev = dev; + return 0; fail_call: event_notifier_cleanup(&vq->masked_notifier); @@ -1077,12 +1160,24 @@ static void vhost_virtqueue_cleanup(struct vhost_virtqueue *vq) event_notifier_cleanup(&vq->masked_notifier); } +static void vhost_iommu_unmap_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) +{ + struct vhost_dev *hdev = container_of(n, struct vhost_dev, n); + + if (hdev->vhost_ops->vhost_invalidate_device_iotlb(hdev, + iotlb->iova, + iotlb->addr_mask + 1)) { + error_report("Fail to invalidate device iotlb"); + } +} + int vhost_dev_init(struct vhost_dev *hdev, void *opaque, VhostBackendType backend_type, uint32_t busyloop_timeout) { uint64_t features; int i, r, n_initialized_vqs = 0; + hdev->vdev = NULL; hdev->migration_blocker = NULL; r = vhost_set_backend_type(hdev, backend_type); @@ -1147,6 +1242,9 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, .priority = 10 }; + hdev->n.notify = vhost_iommu_unmap_notify; + hdev->n.notifier_flags = IOMMU_NOTIFIER_UNMAP; + if (hdev->migration_blocker == NULL) { if (!(hdev->features & (0x1ULL << VHOST_F_LOG_ALL))) { error_setg(&hdev->migration_blocker, @@ -1342,11 +1440,18 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) assert(hdev->vhost_ops); hdev->started = true; + hdev->vdev = vdev; r = vhost_dev_set_features(hdev, hdev->log_enabled); if (r < 0) { goto fail_features; } + + if (vhost_dev_has_iommu(hdev)) { + memory_region_register_iommu_notifier(vdev->dma_as->root, + &hdev->n); + } + r = hdev->vhost_ops->vhost_set_mem_table(hdev, hdev->mem); if (r < 0) { VHOST_OPS_DEBUG("vhost_set_mem_table failed"); @@ -1380,6 +1485,16 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) } } + if (vhost_dev_has_iommu(hdev)) { + hdev->vhost_ops->vhost_set_iotlb_callback(hdev, true); + + /* Update used ring information for IOTLB to work correctly, + * vhost-kernel code requires for this.*/ + for (i = 0; i < hdev->nvqs; ++i) { + struct vhost_virtqueue *vq = hdev->vqs + i; + vhost_device_iotlb_miss(hdev, vq->used_phys, true); + } + } return 0; fail_log: vhost_log_put(hdev, false); @@ -1391,6 +1506,7 @@ fail_vq: hdev->vq_index + i); } i = hdev->nvqs; + fail_mem: fail_features: @@ -1413,8 +1529,14 @@ void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev) hdev->vq_index + i); } + if (vhost_dev_has_iommu(hdev)) { + hdev->vhost_ops->vhost_set_iotlb_callback(hdev, false); + memory_region_unregister_iommu_notifier(vdev->dma_as->root, + &hdev->n); + } vhost_log_put(hdev, true); hdev->started = false; + hdev->vdev = NULL; } int vhost_net_set_backend(struct vhost_dev *hdev, diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c index d31cc00e83..a886011e75 100644 --- a/hw/virtio/virtio-bus.c +++ b/hw/virtio/virtio-bus.c @@ -47,6 +47,7 @@ void virtio_bus_device_plugged(VirtIODevice *vdev, Error **errp) VirtioBusState *bus = VIRTIO_BUS(qbus); VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus); VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); + bool has_iommu = virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM); DPRINTF("%s: plug device.\n", qbus->name); @@ -63,8 +64,8 @@ void virtio_bus_device_plugged(VirtIODevice *vdev, Error **errp) klass->device_plugged(qbus->parent, errp); } - if (klass->get_dma_as != NULL && - virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM)) { + if (klass->get_dma_as != NULL && has_iommu) { + virtio_add_feature(&vdev->host_features, VIRTIO_F_IOMMU_PLATFORM); vdev->dma_as = klass->get_dma_as(qbus->parent); } else { vdev->dma_as = &address_space_memory; diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c index 60654dc19d..5807aa87fe 100644 --- a/hw/virtio/virtio-mmio.c +++ b/hw/virtio/virtio-mmio.c @@ -20,6 +20,7 @@ */ #include "qemu/osdep.h" +#include "standard-headers/linux/virtio_mmio.h" #include "hw/sysbus.h" #include "hw/virtio/virtio.h" #include "qemu/host-utils.h" @@ -52,28 +53,6 @@ do { printf("virtio_mmio: " fmt , ## __VA_ARGS__); } while (0) #define VIRTIO_MMIO(obj) \ OBJECT_CHECK(VirtIOMMIOProxy, (obj), TYPE_VIRTIO_MMIO) -/* Memory mapped register offsets */ -#define VIRTIO_MMIO_MAGIC 0x0 -#define VIRTIO_MMIO_VERSION 0x4 -#define VIRTIO_MMIO_DEVICEID 0x8 -#define VIRTIO_MMIO_VENDORID 0xc -#define VIRTIO_MMIO_HOSTFEATURES 0x10 -#define VIRTIO_MMIO_HOSTFEATURESSEL 0x14 -#define VIRTIO_MMIO_GUESTFEATURES 0x20 -#define VIRTIO_MMIO_GUESTFEATURESSEL 0x24 -#define VIRTIO_MMIO_GUESTPAGESIZE 0x28 -#define VIRTIO_MMIO_QUEUESEL 0x30 -#define VIRTIO_MMIO_QUEUENUMMAX 0x34 -#define VIRTIO_MMIO_QUEUENUM 0x38 -#define VIRTIO_MMIO_QUEUEALIGN 0x3c -#define VIRTIO_MMIO_QUEUEPFN 0x40 -#define VIRTIO_MMIO_QUEUENOTIFY 0x50 -#define VIRTIO_MMIO_INTERRUPTSTATUS 0x60 -#define VIRTIO_MMIO_INTERRUPTACK 0x64 -#define VIRTIO_MMIO_STATUS 0x70 -/* Device specific config space starts here */ -#define VIRTIO_MMIO_CONFIG 0x100 - #define VIRT_MAGIC 0x74726976 /* 'virt' */ #define VIRT_VERSION 1 #define VIRT_VENDOR 0x554D4551 /* 'QEMU' */ @@ -104,10 +83,10 @@ static int virtio_mmio_ioeventfd_assign(DeviceState *d, VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d); if (assign) { - memory_region_add_eventfd(&proxy->iomem, VIRTIO_MMIO_QUEUENOTIFY, 4, + memory_region_add_eventfd(&proxy->iomem, VIRTIO_MMIO_QUEUE_NOTIFY, 4, true, n, notifier); } else { - memory_region_del_eventfd(&proxy->iomem, VIRTIO_MMIO_QUEUENOTIFY, 4, + memory_region_del_eventfd(&proxy->iomem, VIRTIO_MMIO_QUEUE_NOTIFY, 4, true, n, notifier); } return 0; @@ -140,11 +119,11 @@ static uint64_t virtio_mmio_read(void *opaque, hwaddr offset, unsigned size) * device ID of zero means no backend will claim it. */ switch (offset) { - case VIRTIO_MMIO_MAGIC: + case VIRTIO_MMIO_MAGIC_VALUE: return VIRT_MAGIC; case VIRTIO_MMIO_VERSION: return VIRT_VERSION; - case VIRTIO_MMIO_VENDORID: + case VIRTIO_MMIO_VENDOR_ID: return VIRT_VENDOR; default: return 0; @@ -169,40 +148,40 @@ static uint64_t virtio_mmio_read(void *opaque, hwaddr offset, unsigned size) return 0; } switch (offset) { - case VIRTIO_MMIO_MAGIC: + case VIRTIO_MMIO_MAGIC_VALUE: return VIRT_MAGIC; case VIRTIO_MMIO_VERSION: return VIRT_VERSION; - case VIRTIO_MMIO_DEVICEID: + case VIRTIO_MMIO_DEVICE_ID: return vdev->device_id; - case VIRTIO_MMIO_VENDORID: + case VIRTIO_MMIO_VENDOR_ID: return VIRT_VENDOR; - case VIRTIO_MMIO_HOSTFEATURES: + case VIRTIO_MMIO_DEVICE_FEATURES: if (proxy->host_features_sel) { return 0; } return vdev->host_features; - case VIRTIO_MMIO_QUEUENUMMAX: + case VIRTIO_MMIO_QUEUE_NUM_MAX: if (!virtio_queue_get_num(vdev, vdev->queue_sel)) { return 0; } return VIRTQUEUE_MAX_SIZE; - case VIRTIO_MMIO_QUEUEPFN: + case VIRTIO_MMIO_QUEUE_PFN: return virtio_queue_get_addr(vdev, vdev->queue_sel) >> proxy->guest_page_shift; - case VIRTIO_MMIO_INTERRUPTSTATUS: + case VIRTIO_MMIO_INTERRUPT_STATUS: return atomic_read(&vdev->isr); case VIRTIO_MMIO_STATUS: return vdev->status; - case VIRTIO_MMIO_HOSTFEATURESSEL: - case VIRTIO_MMIO_GUESTFEATURES: - case VIRTIO_MMIO_GUESTFEATURESSEL: - case VIRTIO_MMIO_GUESTPAGESIZE: - case VIRTIO_MMIO_QUEUESEL: - case VIRTIO_MMIO_QUEUENUM: - case VIRTIO_MMIO_QUEUEALIGN: - case VIRTIO_MMIO_QUEUENOTIFY: - case VIRTIO_MMIO_INTERRUPTACK: + case VIRTIO_MMIO_DEVICE_FEATURES_SEL: + case VIRTIO_MMIO_DRIVER_FEATURES: + case VIRTIO_MMIO_DRIVER_FEATURES_SEL: + case VIRTIO_MMIO_GUEST_PAGE_SIZE: + case VIRTIO_MMIO_QUEUE_SEL: + case VIRTIO_MMIO_QUEUE_NUM: + case VIRTIO_MMIO_QUEUE_ALIGN: + case VIRTIO_MMIO_QUEUE_NOTIFY: + case VIRTIO_MMIO_INTERRUPT_ACK: DPRINTF("read of write-only register\n"); return 0; default: @@ -251,18 +230,18 @@ static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value, return; } switch (offset) { - case VIRTIO_MMIO_HOSTFEATURESSEL: + case VIRTIO_MMIO_DEVICE_FEATURES_SEL: proxy->host_features_sel = value; break; - case VIRTIO_MMIO_GUESTFEATURES: + case VIRTIO_MMIO_DRIVER_FEATURES: if (!proxy->guest_features_sel) { virtio_set_features(vdev, value); } break; - case VIRTIO_MMIO_GUESTFEATURESSEL: + case VIRTIO_MMIO_DRIVER_FEATURES_SEL: proxy->guest_features_sel = value; break; - case VIRTIO_MMIO_GUESTPAGESIZE: + case VIRTIO_MMIO_GUEST_PAGE_SIZE: proxy->guest_page_shift = ctz32(value); if (proxy->guest_page_shift > 31) { proxy->guest_page_shift = 0; @@ -270,22 +249,22 @@ static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value, DPRINTF("guest page size %" PRIx64 " shift %d\n", value, proxy->guest_page_shift); break; - case VIRTIO_MMIO_QUEUESEL: + case VIRTIO_MMIO_QUEUE_SEL: if (value < VIRTIO_QUEUE_MAX) { vdev->queue_sel = value; } break; - case VIRTIO_MMIO_QUEUENUM: + case VIRTIO_MMIO_QUEUE_NUM: DPRINTF("mmio_queue write %d max %d\n", (int)value, VIRTQUEUE_MAX_SIZE); virtio_queue_set_num(vdev, vdev->queue_sel, value); /* Note: only call this function for legacy devices */ virtio_queue_update_rings(vdev, vdev->queue_sel); break; - case VIRTIO_MMIO_QUEUEALIGN: + case VIRTIO_MMIO_QUEUE_ALIGN: /* Note: this is only valid for legacy devices */ virtio_queue_set_align(vdev, vdev->queue_sel, value); break; - case VIRTIO_MMIO_QUEUEPFN: + case VIRTIO_MMIO_QUEUE_PFN: if (value == 0) { virtio_reset(vdev); } else { @@ -293,12 +272,12 @@ static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value, value << proxy->guest_page_shift); } break; - case VIRTIO_MMIO_QUEUENOTIFY: + case VIRTIO_MMIO_QUEUE_NOTIFY: if (value < VIRTIO_QUEUE_MAX) { virtio_queue_notify(vdev, value); } break; - case VIRTIO_MMIO_INTERRUPTACK: + case VIRTIO_MMIO_INTERRUPT_ACK: atomic_and(&vdev->isr, ~value); virtio_update_irq(vdev); break; @@ -317,13 +296,13 @@ static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value, virtio_reset(vdev); } break; - case VIRTIO_MMIO_MAGIC: + case VIRTIO_MMIO_MAGIC_VALUE: case VIRTIO_MMIO_VERSION: - case VIRTIO_MMIO_DEVICEID: - case VIRTIO_MMIO_VENDORID: - case VIRTIO_MMIO_HOSTFEATURES: - case VIRTIO_MMIO_QUEUENUMMAX: - case VIRTIO_MMIO_INTERRUPTSTATUS: + case VIRTIO_MMIO_DEVICE_ID: + case VIRTIO_MMIO_VENDOR_ID: + case VIRTIO_MMIO_DEVICE_FEATURES: + case VIRTIO_MMIO_QUEUE_NUM_MAX: + case VIRTIO_MMIO_INTERRUPT_STATUS: DPRINTF("write to readonly register\n"); break; diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 854b8f22bf..09230c05df 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -1316,7 +1316,6 @@ static void virtio_pci_common_write(void *opaque, hwaddr addr, virtio_queue_set_vector(vdev, vdev->queue_sel, val); break; case VIRTIO_PCI_COMMON_Q_ENABLE: - /* TODO: need a way to put num back on reset. */ virtio_queue_set_num(vdev, vdev->queue_sel, proxy->vqs[vdev->queue_sel].num); virtio_queue_set_rings(vdev, vdev->queue_sel, @@ -2278,7 +2277,7 @@ static const TypeInfo virtio_serial_pci_info = { static Property virtio_net_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, - VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, false), + VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 3), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index a181514b49..cc17b97899 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -88,8 +88,8 @@ struct VirtQueue /* Last used index value we have signalled on */ bool signalled_used_valid; - /* Nested host->guest notification disabled counter */ - unsigned int notification_disabled; + /* Notification enabled? */ + bool notification; uint16_t queue_index; @@ -202,7 +202,7 @@ static inline void vring_used_flags_unset_bit(VirtQueue *vq, int mask) static inline void vring_set_avail_event(VirtQueue *vq, uint16_t val) { hwaddr pa; - if (vq->notification_disabled) { + if (!vq->notification) { return; } pa = vq->vring.used + offsetof(VRingUsed, ring[vq->vring.num]); @@ -211,13 +211,7 @@ static inline void vring_set_avail_event(VirtQueue *vq, uint16_t val) void virtio_queue_set_notification(VirtQueue *vq, int enable) { - if (enable) { - assert(vq->notification_disabled > 0); - vq->notification_disabled--; - } else { - vq->notification_disabled++; - } - + vq->notification = enable; if (virtio_vdev_has_feature(vq->vdev, VIRTIO_RING_F_EVENT_IDX)) { vring_set_avail_event(vq, vring_avail_idx(vq)); } else if (enable) { @@ -605,23 +599,11 @@ static void virtqueue_undo_map_desc(unsigned int out_num, unsigned int in_num, static void virtqueue_map_iovec(VirtIODevice *vdev, struct iovec *sg, hwaddr *addr, unsigned int *num_sg, - unsigned int max_size, int is_write) + int is_write) { unsigned int i; hwaddr len; - /* Note: this function MUST validate input, some callers - * are passing in num_sg values received over the network. - */ - /* TODO: teach all callers that this can fail, and return failure instead - * of asserting here. - * When we do, we might be able to re-enable NDEBUG below. - */ -#ifdef NDEBUG -#error building with NDEBUG is not supported -#endif - assert(*num_sg <= max_size); - for (i = 0; i < *num_sg; i++) { len = sg[i].iov_len; sg[i].iov_base = dma_memory_map(vdev->dma_as, @@ -641,13 +623,8 @@ static void virtqueue_map_iovec(VirtIODevice *vdev, struct iovec *sg, void virtqueue_map(VirtIODevice *vdev, VirtQueueElement *elem) { - virtqueue_map_iovec(vdev, elem->in_sg, elem->in_addr, &elem->in_num, - MIN(ARRAY_SIZE(elem->in_sg), ARRAY_SIZE(elem->in_addr)), - 1); - virtqueue_map_iovec(vdev, elem->out_sg, elem->out_addr, &elem->out_num, - MIN(ARRAY_SIZE(elem->out_sg), - ARRAY_SIZE(elem->out_addr)), - 0); + virtqueue_map_iovec(vdev, elem->in_sg, elem->in_addr, &elem->in_num, 1); + virtqueue_map_iovec(vdev, elem->out_sg, elem->out_addr, &elem->out_num, 0); } static void *virtqueue_alloc_element(size_t sz, unsigned out_num, unsigned in_num) @@ -846,6 +823,16 @@ void *qemu_get_virtqueue_element(VirtIODevice *vdev, QEMUFile *f, size_t sz) qemu_get_buffer(f, (uint8_t *)&data, sizeof(VirtQueueElementOld)); + /* TODO: teach all callers that this can fail, and return failure instead + * of asserting here. + * When we do, we might be able to re-enable NDEBUG below. + */ +#ifdef NDEBUG +#error building with NDEBUG is not supported +#endif + assert(ARRAY_SIZE(data.in_addr) >= data.in_num); + assert(ARRAY_SIZE(data.out_addr) >= data.out_num); + elem = virtqueue_alloc_element(sz, data.out_num, data.in_num); elem->index = data.index; @@ -1020,7 +1007,7 @@ void virtio_reset(void *opaque) virtio_queue_set_vector(vdev, i, VIRTIO_NO_VECTOR); vdev->vq[i].signalled_used = 0; vdev->vq[i].signalled_used_valid = false; - vdev->vq[i].notification_disabled = 0; + vdev->vq[i].notification = true; vdev->vq[i].vring.num = vdev->vq[i].vring.num_default; vdev->vq[i].inuse = 0; } @@ -1262,6 +1249,11 @@ int virtio_queue_get_num(VirtIODevice *vdev, int n) return vdev->vq[n].vring.num; } +int virtio_queue_get_max_num(VirtIODevice *vdev, int n) +{ + return vdev->vq[n].vring.num_default; +} + int virtio_get_num_queues(VirtIODevice *vdev) { int i; @@ -1831,7 +1823,7 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) vdev->vq[i].vring.desc = qemu_get_be64(f); qemu_get_be16s(f, &vdev->vq[i].last_avail_idx); vdev->vq[i].signalled_used_valid = false; - vdev->vq[i].notification_disabled = 0; + vdev->vq[i].notification = true; if (vdev->vq[i].vring.desc) { /* XXX virtio-1 devices */ @@ -2132,6 +2124,9 @@ static bool virtio_queue_host_notifier_aio_poll(void *opaque) } virtio_queue_notify_aio_vq(vq); + + /* In case the handler function re-enabled notifications */ + virtio_queue_set_notification(vq, 0); return true; } |