aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/arm/aspeed.c41
-rw-r--r--hw/arm/imx25_pdk.c2
-rw-r--r--hw/arm/virt-acpi-build.c38
-rw-r--r--hw/arm/virt.c88
-rw-r--r--hw/arm/xlnx-zynqmp.c2
-rw-r--r--hw/block/m25p80.c51
-rw-r--r--hw/core/loader.c18
-rw-r--r--hw/i2c/imx_i2c.c2
-rw-r--r--hw/i386/acpi-build.c4
-rw-r--r--hw/i386/pc_piix.c15
-rw-r--r--hw/i386/pc_q35.c13
-rw-r--r--hw/intc/arm_gic_common.c6
-rw-r--r--hw/intc/arm_gicv3_common.c31
-rw-r--r--hw/intc/arm_gicv3_cpuif.c1303
-rw-r--r--hw/intc/gicv3_internal.h79
-rw-r--r--hw/intc/s390_flic_kvm.c4
-rw-r--r--hw/intc/trace-events33
-rw-r--r--hw/lm32/lm32_hwsetup.h2
-rw-r--r--hw/m68k/mcf5208.c25
-rw-r--r--hw/net/dp8393x.c95
-rw-r--r--hw/net/mcf_fec.c71
-rw-r--r--hw/net/vhost_net.c1
-rw-r--r--hw/nvram/fw_cfg.c110
-rw-r--r--hw/pci/pci.c9
-rw-r--r--hw/s390x/s390-pci-bus.c361
-rw-r--r--hw/s390x/s390-pci-bus.h46
-rw-r--r--hw/s390x/s390-pci-inst.c74
-rw-r--r--hw/s390x/s390-pci-inst.h2
-rw-r--r--hw/s390x/s390-virtio-ccw.c19
-rw-r--r--hw/s390x/virtio-ccw.c2
-rw-r--r--hw/sh4/sh7750.c2
-rw-r--r--hw/sparc64/Makefile.objs2
-rw-r--r--hw/sparc64/niagara.c177
-rw-r--r--hw/sparc64/sparc64.c378
-rw-r--r--hw/sparc64/sun4u.c379
-rw-r--r--hw/ssi/aspeed_smc.c325
-rw-r--r--hw/timer/Makefile.objs2
-rw-r--r--hw/timer/sun4v-rtc.c102
-rw-r--r--hw/virtio/vhost-backend.c99
-rw-r--r--hw/virtio/vhost.c166
-rw-r--r--hw/virtio/virtio-bus.c5
-rw-r--r--hw/virtio/virtio-mmio.c95
-rw-r--r--hw/virtio/virtio-pci.c3
-rw-r--r--hw/virtio/virtio.c59
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;
}