F: include/hw/char/cmsdk-apb-uart.h
F: hw/misc/tz-ppc.c
F: include/hw/misc/tz-ppc.h
+F: hw/misc/tz-mpc.c
+F: include/hw/misc/tz-mpc.h
ARM cores
M: Peter Maydell <peter.maydell@linaro.org>
CONFIG_MPS2_FPGAIO=y
CONFIG_MPS2_SCC=y
+CONFIG_TZ_MPC=y
CONFIG_TZ_PPC=y
CONFIG_IOTKIT=y
CONFIG_IOTKIT_SECCTL=y
CONFIG_SM501=y
CONFIG_IDE_SII3112=y
CONFIG_I2C=y
+CONFIG_BITBANG_I2C=y
# For Macs
CONFIG_MAC=y
CONFIG_SM501=y
CONFIG_IDE_SII3112=y
CONFIG_I2C=y
+CONFIG_BITBANG_I2C=y
TYPE_TZ_PPC);
init_sysbus_child(obj, "apb-ppc1", &s->apb_ppc1, sizeof(s->apb_ppc1),
TYPE_TZ_PPC);
+ init_sysbus_child(obj, "mpc", &s->mpc, sizeof(s->mpc), TYPE_TZ_MPC);
+ object_initialize(&s->mpc_irq_orgate, sizeof(s->mpc_irq_orgate),
+ TYPE_OR_IRQ);
+ object_property_add_child(obj, "mpc-irq-orgate",
+ OBJECT(&s->mpc_irq_orgate), &error_abort);
+ for (i = 0; i < ARRAY_SIZE(s->mpc_irq_splitter); i++) {
+ char *name = g_strdup_printf("mpc-irq-splitter-%d", i);
+ SplitIRQ *splitter = &s->mpc_irq_splitter[i];
+
+ object_initialize(splitter, sizeof(*splitter), TYPE_SPLIT_IRQ);
+ object_property_add_child(obj, name, OBJECT(splitter), &error_abort);
+ g_free(name);
+ }
init_sysbus_child(obj, "timer0", &s->timer0, sizeof(s->timer0),
TYPE_CMSDK_APB_TIMER);
init_sysbus_child(obj, "timer1", &s->timer1, sizeof(s->timer1),
qemu_set_irq(s->exp_irqs[n], level);
}
+static void iotkit_mpcexp_status(void *opaque, int n, int level)
+{
+ IoTKit *s = IOTKIT(opaque);
+ qemu_set_irq(s->mpcexp_status_in[n], level);
+}
+
static void iotkit_realize(DeviceState *dev, Error **errp)
{
IoTKit *s = IOTKIT(dev);
*/
make_alias(s, &s->alias3, "alias 3", 0x50000000, 0x10000000, 0x40000000);
- /* This RAM should be behind a Memory Protection Controller, but we
- * don't implement that yet.
- */
- memory_region_init_ram(&s->sram0, NULL, "iotkit.sram0", 0x00008000, &err);
- if (err) {
- error_propagate(errp, err);
- return;
- }
- memory_region_add_subregion(&s->container, 0x20000000, &s->sram0);
/* Security controller */
object_property_set_bool(OBJECT(&s->secctl), true, "realized", &err);
qdev_connect_gpio_out_named(dev_secctl, "sec_resp_cfg", 0,
qdev_get_gpio_in(dev_splitter, 0));
+ /* This RAM lives behind the Memory Protection Controller */
+ memory_region_init_ram(&s->sram0, NULL, "iotkit.sram0", 0x00008000, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ object_property_set_link(OBJECT(&s->mpc), OBJECT(&s->sram0),
+ "downstream", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ object_property_set_bool(OBJECT(&s->mpc), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ /* Map the upstream end of the MPC into the right place... */
+ memory_region_add_subregion(&s->container, 0x20000000,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mpc),
+ 1));
+ /* ...and its register interface */
+ memory_region_add_subregion(&s->container, 0x50083000,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mpc),
+ 0));
+
+ /* We must OR together lines from the MPC splitters to go to the NVIC */
+ object_property_set_int(OBJECT(&s->mpc_irq_orgate),
+ IOTS_NUM_EXP_MPC + IOTS_NUM_MPC, "num-lines", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ object_property_set_bool(OBJECT(&s->mpc_irq_orgate), true,
+ "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ qdev_connect_gpio_out(DEVICE(&s->mpc_irq_orgate), 0,
+ qdev_get_gpio_in(DEVICE(&s->armv7m), 9));
+
/* Devices behind APB PPC0:
* 0x40000000: timer0
* 0x40001000: timer1
create_unimplemented_device("NS watchdog", 0x40081000, 0x1000);
create_unimplemented_device("S watchdog", 0x50081000, 0x1000);
- create_unimplemented_device("SRAM0 MPC", 0x50083000, 0x1000);
-
for (i = 0; i < ARRAY_SIZE(s->ppc_irq_splitter); i++) {
Object *splitter = OBJECT(&s->ppc_irq_splitter[i]);
g_free(gpioname);
}
+ /* Wire up the splitters for the MPC IRQs */
+ for (i = 0; i < IOTS_NUM_EXP_MPC + IOTS_NUM_MPC; i++) {
+ SplitIRQ *splitter = &s->mpc_irq_splitter[i];
+ DeviceState *dev_splitter = DEVICE(splitter);
+
+ object_property_set_int(OBJECT(splitter), 2, "num-lines", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ object_property_set_bool(OBJECT(splitter), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ if (i < IOTS_NUM_EXP_MPC) {
+ /* Splitter input is from GPIO input line */
+ s->mpcexp_status_in[i] = qdev_get_gpio_in(dev_splitter, 0);
+ qdev_connect_gpio_out(dev_splitter, 0,
+ qdev_get_gpio_in_named(dev_secctl,
+ "mpcexp_status", i));
+ } else {
+ /* Splitter input is from our own MPC */
+ qdev_connect_gpio_out_named(DEVICE(&s->mpc), "irq", 0,
+ qdev_get_gpio_in(dev_splitter, 0));
+ qdev_connect_gpio_out(dev_splitter, 0,
+ qdev_get_gpio_in_named(dev_secctl,
+ "mpc_status", 0));
+ }
+
+ qdev_connect_gpio_out(dev_splitter, 1,
+ qdev_get_gpio_in(DEVICE(&s->mpc_irq_orgate), i));
+ }
+ /* Create GPIO inputs which will pass the line state for our
+ * mpcexp_irq inputs to the correct splitter devices.
+ */
+ qdev_init_gpio_in_named(dev, iotkit_mpcexp_status, "mpcexp_status",
+ IOTS_NUM_EXP_MPC);
+
iotkit_forward_sec_resp_cfg(s);
system_clock_scale = NANOSECONDS_PER_SECOND / s->mainclk_frq;
#include "hw/timer/cmsdk-apb-timer.h"
#include "hw/misc/mps2-scc.h"
#include "hw/misc/mps2-fpgaio.h"
+#include "hw/misc/tz-mpc.h"
#include "hw/arm/iotkit.h"
#include "hw/devices.h"
#include "net/net.h"
IoTKit iotkit;
MemoryRegion psram;
- MemoryRegion ssram1;
+ MemoryRegion ssram[3];
MemoryRegion ssram1_m;
- MemoryRegion ssram23;
MPS2SCC scc;
MPS2FPGAIO fpgaio;
TZPPC ppc[5];
- UnimplementedDeviceState ssram_mpc[3];
+ TZMPC ssram_mpc[3];
UnimplementedDeviceState spi[5];
UnimplementedDeviceState i2c[4];
UnimplementedDeviceState i2s_audio;
/* Main SYSCLK frequency in Hz */
#define SYSCLK_FRQ 20000000
-/* Initialize the auxiliary RAM region @mr and map it into
- * the memory map at @base.
- */
-static void make_ram(MemoryRegion *mr, const char *name,
- hwaddr base, hwaddr size)
-{
- memory_region_init_ram(mr, NULL, name, size, &error_fatal);
- memory_region_add_subregion(get_system_memory(), base, mr);
-}
-
/* Create an alias of an entire original MemoryRegion @orig
* located at @base in the memory map.
*/
return sysbus_mmio_get_region(s, 0);
}
+static MemoryRegion *make_mpc(MPS2TZMachineState *mms, void *opaque,
+ const char *name, hwaddr size)
+{
+ TZMPC *mpc = opaque;
+ int i = mpc - &mms->ssram_mpc[0];
+ MemoryRegion *ssram = &mms->ssram[i];
+ MemoryRegion *upstream;
+ char *mpcname = g_strdup_printf("%s-mpc", name);
+ static uint32_t ramsize[] = { 0x00400000, 0x00200000, 0x00200000 };
+ static uint32_t rambase[] = { 0x00000000, 0x28000000, 0x28200000 };
+
+ memory_region_init_ram(ssram, NULL, name, ramsize[i], &error_fatal);
+
+ init_sysbus_child(OBJECT(mms), mpcname, mpc,
+ sizeof(mms->ssram_mpc[0]), TYPE_TZ_MPC);
+ object_property_set_link(OBJECT(mpc), OBJECT(ssram),
+ "downstream", &error_fatal);
+ object_property_set_bool(OBJECT(mpc), true, "realized", &error_fatal);
+ /* Map the upstream end of the MPC into system memory */
+ upstream = sysbus_mmio_get_region(SYS_BUS_DEVICE(mpc), 1);
+ memory_region_add_subregion(get_system_memory(), rambase[i], upstream);
+ /* and connect its interrupt to the IoTKit */
+ qdev_connect_gpio_out_named(DEVICE(mpc), "irq", 0,
+ qdev_get_gpio_in_named(DEVICE(&mms->iotkit),
+ "mpcexp_status", i));
+
+ /* The first SSRAM is a special case as it has an alias; accesses to
+ * the alias region at 0x00400000 must also go to the MPC upstream.
+ */
+ if (i == 0) {
+ make_ram_alias(&mms->ssram1_m, "mps.ssram1_m", upstream, 0x00400000);
+ }
+
+ g_free(mpcname);
+ /* Return the register interface MR for our caller to map behind the PPC */
+ return sysbus_mmio_get_region(SYS_BUS_DEVICE(mpc), 0);
+}
+
static void mps2tz_common_init(MachineState *machine)
{
MPS2TZMachineState *mms = MPS2TZ_MACHINE(machine);
NULL, "mps.ram", 0x01000000);
memory_region_add_subregion(system_memory, 0x80000000, &mms->psram);
- /* The SSRAM memories should all be behind Memory Protection Controllers,
- * but we don't implement that yet.
- */
- make_ram(&mms->ssram1, "mps.ssram1", 0x00000000, 0x00400000);
- make_ram_alias(&mms->ssram1_m, "mps.ssram1_m", &mms->ssram1, 0x00400000);
-
- make_ram(&mms->ssram23, "mps.ssram23", 0x28000000, 0x00400000);
-
/* The overflow IRQs for all UARTs are ORed together.
* Tx, Rx and "combined" IRQs are sent to the NVIC separately.
* Create the OR gate for this.
const PPCInfo ppcs[] = { {
.name = "apb_ppcexp0",
.ports = {
- { "ssram-mpc0", make_unimp_dev, &mms->ssram_mpc[0],
- 0x58007000, 0x1000 },
- { "ssram-mpc1", make_unimp_dev, &mms->ssram_mpc[1],
- 0x58008000, 0x1000 },
- { "ssram-mpc2", make_unimp_dev, &mms->ssram_mpc[2],
- 0x58009000, 0x1000 },
+ { "ssram-0", make_mpc, &mms->ssram_mpc[0], 0x58007000, 0x1000 },
+ { "ssram-1", make_mpc, &mms->ssram_mpc[1], 0x58008000, 0x1000 },
+ { "ssram-2", make_mpc, &mms->ssram_mpc[2], 0x58009000, 0x1000 },
},
}, {
.name = "apb_ppcexp1",
}
static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap,
- uint32_t irq, bool use_highmem)
+ uint32_t irq, bool use_highmem, bool highmem_ecam)
{
+ int ecam_id = VIRT_ECAM_ID(highmem_ecam);
Aml *method, *crs, *ifctx, *UUID, *ifctx1, *elsectx, *buf;
int i, bus_no;
hwaddr base_mmio = memmap[VIRT_PCIE_MMIO].base;
hwaddr size_mmio = memmap[VIRT_PCIE_MMIO].size;
hwaddr base_pio = memmap[VIRT_PCIE_PIO].base;
hwaddr size_pio = memmap[VIRT_PCIE_PIO].size;
- hwaddr base_ecam = memmap[VIRT_PCIE_ECAM].base;
- hwaddr size_ecam = memmap[VIRT_PCIE_ECAM].size;
+ hwaddr base_ecam = memmap[ecam_id].base;
+ hwaddr size_ecam = memmap[ecam_id].size;
int nr_pcie_buses = size_ecam / PCIE_MMCFG_SIZE_MIN;
Aml *dev = aml_device("%s", "PCI0");
aml_append(dev, aml_name_decl("_CCA", aml_int(1)));
/* Declare the PCI Routing Table. */
- Aml *rt_pkg = aml_package(nr_pcie_buses * PCI_NUM_PINS);
+ Aml *rt_pkg = aml_varpackage(nr_pcie_buses * PCI_NUM_PINS);
for (bus_no = 0; bus_no < nr_pcie_buses; bus_no++) {
for (i = 0; i < PCI_NUM_PINS; i++) {
int gsi = (i + bus_no) % PCI_NUM_PINS;
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(crs,
+ aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED,
+ AML_NON_CACHEABLE, AML_READ_WRITE, 0x0000, base_ecam,
+ base_ecam + size_ecam - 1, 0x0000, size_ecam));
aml_append(dev_res0, aml_name_decl("_CRS", crs));
aml_append(dev, dev_res0);
aml_append(scope, dev);
{
AcpiTableMcfg *mcfg;
const MemMapEntry *memmap = vms->memmap;
+ int ecam_id = VIRT_ECAM_ID(vms->highmem_ecam);
int len = sizeof(*mcfg) + sizeof(mcfg->allocation[0]);
int mcfg_start = table_data->len;
mcfg = acpi_data_push(table_data, len);
- mcfg->allocation[0].address = cpu_to_le64(memmap[VIRT_PCIE_ECAM].base);
+ mcfg->allocation[0].address = cpu_to_le64(memmap[ecam_id].base);
/* Only a single allocation so no need to play with segments */
mcfg->allocation[0].pci_segment = cpu_to_le16(0);
mcfg->allocation[0].start_bus_number = 0;
- mcfg->allocation[0].end_bus_number = (memmap[VIRT_PCIE_ECAM].size
+ mcfg->allocation[0].end_bus_number = (memmap[ecam_id].size
/ PCIE_MMCFG_SIZE_MIN) - 1;
build_header(linker, table_data, (void *)(table_data->data + mcfg_start),
if (vms->gic_version == 3) {
AcpiMadtGenericTranslator *gic_its;
+ int nb_redist_regions = virt_gicv3_redist_region_count(vms);
AcpiMadtGenericRedistributor *gicr = acpi_data_push(table_data,
sizeof *gicr);
gicr->base_address = cpu_to_le64(memmap[VIRT_GIC_REDIST].base);
gicr->range_length = cpu_to_le32(memmap[VIRT_GIC_REDIST].size);
+ if (nb_redist_regions == 2) {
+ gicr = acpi_data_push(table_data, sizeof(*gicr));
+ gicr->type = ACPI_APIC_GENERIC_REDISTRIBUTOR;
+ gicr->length = sizeof(*gicr);
+ gicr->base_address = cpu_to_le64(memmap[VIRT_GIC_REDIST2].base);
+ gicr->range_length = cpu_to_le32(memmap[VIRT_GIC_REDIST2].size);
+ }
+
if (its_class_name() && !vmc->no_its) {
gic_its = acpi_data_push(table_data, sizeof *gic_its);
gic_its->type = ACPI_APIC_GENERIC_TRANSLATOR;
acpi_dsdt_add_virtio(scope, &memmap[VIRT_MMIO],
(irqmap[VIRT_MMIO] + ARM_SPI_BASE), NUM_VIRTIO_TRANSPORTS);
acpi_dsdt_add_pci(scope, memmap, (irqmap[VIRT_PCIE] + ARM_SPI_BASE),
- vms->highmem);
+ vms->highmem, vms->highmem_ecam);
acpi_dsdt_add_gpio(scope, &memmap[VIRT_GPIO],
(irqmap[VIRT_GPIO] + ARM_SPI_BASE));
acpi_dsdt_add_power_button(scope);
[VIRT_PCIE_PIO] = { 0x3eff0000, 0x00010000 },
[VIRT_PCIE_ECAM] = { 0x3f000000, 0x01000000 },
[VIRT_MEM] = { 0x40000000, RAMLIMIT_BYTES },
+ /* Additional 64 MB redist region (can contain up to 512 redistributors) */
+ [VIRT_GIC_REDIST2] = { 0x4000000000ULL, 0x4000000 },
+ [VIRT_PCIE_ECAM_HIGH] = { 0x4010000000ULL, 0x10000000 },
/* Second PCIe window, 512GB wide at the 512GB boundary */
[VIRT_PCIE_MMIO_HIGH] = { 0x8000000000ULL, 0x8000000000ULL },
};
qemu_fdt_setprop_cell(vms->fdt, "/intc", "#size-cells", 0x2);
qemu_fdt_setprop(vms->fdt, "/intc", "ranges", NULL, 0);
if (vms->gic_version == 3) {
+ int nb_redist_regions = virt_gicv3_redist_region_count(vms);
+
qemu_fdt_setprop_string(vms->fdt, "/intc", "compatible",
"arm,gic-v3");
- qemu_fdt_setprop_sized_cells(vms->fdt, "/intc", "reg",
- 2, vms->memmap[VIRT_GIC_DIST].base,
- 2, vms->memmap[VIRT_GIC_DIST].size,
- 2, vms->memmap[VIRT_GIC_REDIST].base,
- 2, vms->memmap[VIRT_GIC_REDIST].size);
+
+ qemu_fdt_setprop_cell(vms->fdt, "/intc",
+ "#redistributor-regions", nb_redist_regions);
+
+ if (nb_redist_regions == 1) {
+ qemu_fdt_setprop_sized_cells(vms->fdt, "/intc", "reg",
+ 2, vms->memmap[VIRT_GIC_DIST].base,
+ 2, vms->memmap[VIRT_GIC_DIST].size,
+ 2, vms->memmap[VIRT_GIC_REDIST].base,
+ 2, vms->memmap[VIRT_GIC_REDIST].size);
+ } else {
+ qemu_fdt_setprop_sized_cells(vms->fdt, "/intc", "reg",
+ 2, vms->memmap[VIRT_GIC_DIST].base,
+ 2, vms->memmap[VIRT_GIC_DIST].size,
+ 2, vms->memmap[VIRT_GIC_REDIST].base,
+ 2, vms->memmap[VIRT_GIC_REDIST].size,
+ 2, vms->memmap[VIRT_GIC_REDIST2].base,
+ 2, vms->memmap[VIRT_GIC_REDIST2].size);
+ }
+
if (vms->virt) {
qemu_fdt_setprop_cells(vms->fdt, "/intc", "interrupts",
GIC_FDT_IRQ_TYPE_PPI, ARCH_GICV3_MAINT_IRQ,
SysBusDevice *gicbusdev;
const char *gictype;
int type = vms->gic_version, i;
+ uint32_t nb_redist_regions = 0;
gictype = (type == 3) ? gicv3_class_name() : gic_class_name();
if (!kvm_irqchip_in_kernel()) {
qdev_prop_set_bit(gicdev, "has-security-extensions", vms->secure);
}
+
+ if (type == 3) {
+ uint32_t redist0_capacity =
+ vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE;
+ uint32_t redist0_count = MIN(smp_cpus, redist0_capacity);
+
+ nb_redist_regions = virt_gicv3_redist_region_count(vms);
+
+ qdev_prop_set_uint32(gicdev, "len-redist-region-count",
+ nb_redist_regions);
+ qdev_prop_set_uint32(gicdev, "redist-region-count[0]", redist0_count);
+
+ if (nb_redist_regions == 2) {
+ uint32_t redist1_capacity =
+ vms->memmap[VIRT_GIC_REDIST2].size / GICV3_REDIST_SIZE;
+
+ qdev_prop_set_uint32(gicdev, "redist-region-count[1]",
+ MIN(smp_cpus - redist0_count, redist1_capacity));
+ }
+ }
qdev_init_nofail(gicdev);
gicbusdev = SYS_BUS_DEVICE(gicdev);
sysbus_mmio_map(gicbusdev, 0, vms->memmap[VIRT_GIC_DIST].base);
if (type == 3) {
sysbus_mmio_map(gicbusdev, 1, vms->memmap[VIRT_GIC_REDIST].base);
+ if (nb_redist_regions == 2) {
+ sysbus_mmio_map(gicbusdev, 2, vms->memmap[VIRT_GIC_REDIST2].base);
+ }
} else {
sysbus_mmio_map(gicbusdev, 1, vms->memmap[VIRT_GIC_CPU].base);
}
hwaddr size_mmio_high = vms->memmap[VIRT_PCIE_MMIO_HIGH].size;
hwaddr base_pio = vms->memmap[VIRT_PCIE_PIO].base;
hwaddr size_pio = vms->memmap[VIRT_PCIE_PIO].size;
- hwaddr base_ecam = vms->memmap[VIRT_PCIE_ECAM].base;
- hwaddr size_ecam = vms->memmap[VIRT_PCIE_ECAM].size;
+ hwaddr base_ecam, size_ecam;
hwaddr base = base_mmio;
- int nr_pcie_buses = size_ecam / PCIE_MMCFG_SIZE_MIN;
+ int nr_pcie_buses;
int irq = vms->irqmap[VIRT_PCIE];
MemoryRegion *mmio_alias;
MemoryRegion *mmio_reg;
MemoryRegion *ecam_reg;
DeviceState *dev;
char *nodename;
- int i;
+ int i, ecam_id;
PCIHostState *pci;
dev = qdev_create(NULL, TYPE_GPEX_HOST);
qdev_init_nofail(dev);
+ ecam_id = VIRT_ECAM_ID(vms->highmem_ecam);
+ base_ecam = vms->memmap[ecam_id].base;
+ size_ecam = vms->memmap[ecam_id].size;
+ nr_pcie_buses = size_ecam / PCIE_MMCFG_SIZE_MIN;
/* Map only the first size_ecam bytes of ECAM space */
ecam_alias = g_new0(MemoryRegion, 1);
ecam_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
int n, virt_max_cpus;
MemoryRegion *ram = g_new(MemoryRegion, 1);
bool firmware_loaded = bios_name || drive_get(IF_PFLASH, 0, 0);
+ bool aarch64 = true;
/* We can probe only here because during property set
* KVM is not available yet
* many redistributors we can fit into the memory map.
*/
if (vms->gic_version == 3) {
- virt_max_cpus = vms->memmap[VIRT_GIC_REDIST].size / 0x20000;
+ virt_max_cpus = vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE;
+ virt_max_cpus += vms->memmap[VIRT_GIC_REDIST2].size / GICV3_REDIST_SIZE;
} else {
virt_max_cpus = GIC_NCPU;
}
numa_cpu_pre_plug(&possible_cpus->cpus[cs->cpu_index], DEVICE(cpuobj),
&error_fatal);
+ aarch64 &= object_property_get_bool(cpuobj, "aarch64", NULL);
+
if (!vms->secure) {
object_property_set_bool(cpuobj, false, "has_el3", NULL);
}
create_uart(vms, pic, VIRT_SECURE_UART, secure_sysmem, serial_hd(1));
}
+ vms->highmem_ecam &= vms->highmem && (!firmware_loaded || aarch64);
+
create_rtc(vms, pic);
create_pcie(vms, pic);
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc);
mc->init = machvirt_init;
- /* Start max_cpus at the maximum QEMU supports. We'll further restrict
- * it later in machvirt_init, where we have more information about the
+ /* Start with max_cpus set to 512, which is the maximum supported by KVM.
+ * The value may be reduced later when we have more information about the
* configuration of the particular instance.
*/
- mc->max_cpus = 255;
+ mc->max_cpus = 512;
machine_class_allow_dynamic_sysbus_dev(mc, TYPE_VFIO_CALXEDA_XGMAC);
machine_class_allow_dynamic_sysbus_dev(mc, TYPE_VFIO_AMD_XGBE);
machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE);
#define VIRT_COMPAT_2_12 \
HW_COMPAT_2_12
-static void virt_2_12_instance_init(Object *obj)
+static void virt_3_0_instance_init(Object *obj)
{
VirtMachineState *vms = VIRT_MACHINE(obj);
VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms);
"Set GIC version. "
"Valid values are 2, 3 and host", NULL);
+ vms->highmem_ecam = !vmc->no_highmem_ecam;
+
if (vmc->no_its) {
vms->its = false;
} else {
vms->irqmap = a15irqmap;
}
+static void virt_machine_3_0_options(MachineClass *mc)
+{
+}
+DEFINE_VIRT_MACHINE_AS_LATEST(3, 0)
+
+static void virt_2_12_instance_init(Object *obj)
+{
+ virt_3_0_instance_init(obj);
+}
+
static void virt_machine_2_12_options(MachineClass *mc)
{
+ VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc));
+
+ virt_machine_3_0_options(mc);
SET_MACHINE_COMPAT(mc, VIRT_COMPAT_2_12);
+ vmc->no_highmem_ecam = true;
+ mc->max_cpus = 255;
}
-DEFINE_VIRT_MACHINE_AS_LATEST(2, 12)
+DEFINE_VIRT_MACHINE(2, 12)
#define VIRT_COMPAT_2_11 \
HW_COMPAT_2_11
{
MachineClass *mc = MACHINE_CLASS(oc);
- mc->desc = "Xilinx ZynqMP ZCU102 board with 4xA53s and 2xR5s based on " \
+ mc->desc = "Xilinx ZynqMP ZCU102 board with 4xA53s and 2xR5Fs based on " \
"the value of smp";
mc->init = xlnx_zcu102_init;
mc->block_default_type = IF_IDE;
char *name;
object_initialize(&s->rpu_cpu[i], sizeof(s->rpu_cpu[i]),
- "cortex-r5-" TYPE_ARM_CPU);
+ "cortex-r5f-" TYPE_ARM_CPU);
object_property_add_child(OBJECT(s), "rpu-cpu[*]",
OBJECT(&s->rpu_cpu[i]), &error_abort);
} else {
rgb565 = color_reg & 0xFFFF;
}
- palette[i * 3 + 0] = (rgb565 << 3) & 0xf8; /* red */
- palette[i * 3 + 1] = (rgb565 >> 3) & 0xfc; /* green */
- palette[i * 3 + 2] = (rgb565 >> 8) & 0xf8; /* blue */
+ palette[i * 3 + 0] = ((rgb565 >> 11) * 527 + 23) >> 6; /* r */
+ palette[i * 3 + 1] = (((rgb565 >> 5) & 0x3f) * 259 + 33) >> 6; /* g */
+ palette[i * 3 + 2] = ((rgb565 & 0x1f) * 527 + 23) >> 6; /* b */
}
}
*
* Copyright (c) 2007 Jocelyn Mayer
* Copyright (c) 2012 François Revol
- * Copyright (c) 2016 BALATON Zoltan
+ * Copyright (c) 2016-2018 BALATON Zoltan
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
#include "cpu.h"
#include "hw/hw.h"
#include "hw/i2c/ppc4xx_i2c.h"
+#include "bitbang_i2c.h"
#define PPC4xx_I2C_MEM_SIZE 18
#define IIC_XTCNTLSS_SRST (1 << 0)
+#define IIC_DIRECTCNTL_SDAC (1 << 3)
+#define IIC_DIRECTCNTL_SCLC (1 << 2)
+#define IIC_DIRECTCNTL_MSDA (1 << 1)
+#define IIC_DIRECTCNTL_MSCL (1 << 0)
+
static void ppc4xx_i2c_reset(DeviceState *s)
{
PPC4xxI2CState *i2c = PPC4xx_I2C(s);
i2c->mdcntl = 0;
i2c->sts = 0;
i2c->extsts = 0x8f;
- i2c->sdata = 0;
i2c->lsadr = 0;
i2c->hsadr = 0;
i2c->clkdiv = 0;
i2c->xfrcnt = 0;
i2c->xtcntlss = 0;
i2c->directcntl = 0xf;
- i2c->intr = 0;
}
static inline bool ppc4xx_i2c_is_master(PPC4xxI2CState *i2c)
TYPE_PPC4xx_I2C, __func__);
}
break;
- case 2:
- ret = i2c->sdata;
- break;
case 4:
ret = i2c->lmadr;
break;
case 16:
ret = i2c->directcntl;
break;
- case 17:
- ret = i2c->intr;
- break;
default:
if (addr < PPC4xx_I2C_MEM_SIZE) {
qemu_log_mask(LOG_UNIMP, "%s: Unimplemented register 0x%"
}
}
break;
- case 2:
- i2c->sdata = value;
- break;
case 4:
i2c->lmadr = value;
if (i2c_bus_busy(i2c->bus)) {
i2c->xtcntlss = value;
break;
case 16:
- i2c->directcntl = value & 0x7;
- break;
- case 17:
- i2c->intr = value;
+ i2c->directcntl = value & (IIC_DIRECTCNTL_SDAC & IIC_DIRECTCNTL_SCLC);
+ i2c->directcntl |= (value & IIC_DIRECTCNTL_SCLC ? 1 : 0);
+ bitbang_i2c_set(i2c->bitbang, BITBANG_I2C_SCL,
+ i2c->directcntl & IIC_DIRECTCNTL_MSCL);
+ i2c->directcntl |= bitbang_i2c_set(i2c->bitbang, BITBANG_I2C_SDA,
+ (value & IIC_DIRECTCNTL_SDAC) != 0) << 1;
break;
default:
if (addr < PPC4xx_I2C_MEM_SIZE) {
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq);
s->bus = i2c_init_bus(DEVICE(s), "i2c");
+ s->bitbang = bitbang_i2c_init(s->bus);
}
static void ppc4xx_i2c_class_init(ObjectClass *klass, void *data)
| KVM_VGIC_V2_ADDR_TYPE_DIST,
KVM_DEV_ARM_VGIC_GRP_ADDR,
KVM_VGIC_V2_ADDR_TYPE_DIST,
- s->dev_fd);
+ s->dev_fd, 0);
/* CPU interface for current core. Unlike arm_gic, we don't
* provide the "interface for core #N" memory regions, because
* cores with a VGIC don't have those.
| KVM_VGIC_V2_ADDR_TYPE_CPU,
KVM_DEV_ARM_VGIC_GRP_ADDR,
KVM_VGIC_V2_ADDR_TYPE_CPU,
- s->dev_fd);
+ s->dev_fd, 0);
if (kvm_has_gsi_routing()) {
/* set up irq routing */
return;
}
- gicv3_init_irqs_and_mmio(s, gicv3_set_irq, gic_ops);
+ if (s->nb_redist_regions != 1) {
+ error_setg(errp, "VGICv3 redist region number(%d) not equal to 1",
+ s->nb_redist_regions);
+ return;
+ }
+
+ gicv3_init_irqs_and_mmio(s, gicv3_set_irq, gic_ops, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
gicv3_init_cpuif(s);
}
};
void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler,
- const MemoryRegionOps *ops)
+ const MemoryRegionOps *ops, Error **errp)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(s);
+ int rdist_capacity = 0;
int i;
+ for (i = 0; i < s->nb_redist_regions; i++) {
+ rdist_capacity += s->redist_region_count[i];
+ }
+ if (rdist_capacity < s->num_cpu) {
+ error_setg(errp, "Capacity of the redist regions(%d) "
+ "is less than number of vcpus(%d)",
+ rdist_capacity, s->num_cpu);
+ return;
+ }
+
/* For the GIC, also expose incoming GPIO lines for PPIs for each CPU.
* GPIO array layout is thus:
* [0..N-1] spi
memory_region_init_io(&s->iomem_dist, OBJECT(s), ops, s,
"gicv3_dist", 0x10000);
- memory_region_init_io(&s->iomem_redist, OBJECT(s), ops ? &ops[1] : NULL, s,
- "gicv3_redist", 0x20000 * s->num_cpu);
-
sysbus_init_mmio(sbd, &s->iomem_dist);
- sysbus_init_mmio(sbd, &s->iomem_redist);
+
+ s->iomem_redist = g_new0(MemoryRegion, s->nb_redist_regions);
+ for (i = 0; i < s->nb_redist_regions; i++) {
+ char *name = g_strdup_printf("gicv3_redist_region[%d]", i);
+
+ memory_region_init_io(&s->iomem_redist[i], OBJECT(s),
+ ops ? &ops[1] : NULL, s, name,
+ s->redist_region_count[i] * GICV3_REDIST_SIZE);
+ sysbus_init_mmio(sbd, &s->iomem_redist[i]);
+ g_free(name);
+ }
}
static void arm_gicv3_common_realize(DeviceState *dev, Error **errp)
}
}
+static void arm_gicv3_finalize(Object *obj)
+{
+ GICv3State *s = ARM_GICV3_COMMON(obj);
+
+ g_free(s->redist_region_count);
+}
+
static void arm_gicv3_common_reset(DeviceState *dev)
{
GICv3State *s = ARM_GICV3_COMMON(dev);
DEFINE_PROP_UINT32("num-irq", GICv3State, num_irq, 32),
DEFINE_PROP_UINT32("revision", GICv3State, revision, 3),
DEFINE_PROP_BOOL("has-security-extensions", GICv3State, security_extn, 0),
+ DEFINE_PROP_ARRAY("redist-region-count", GICv3State, nb_redist_regions,
+ redist_region_count, qdev_prop_uint32, uint32_t),
DEFINE_PROP_END_OF_LIST(),
};
.instance_size = sizeof(GICv3State),
.class_size = sizeof(ARMGICv3CommonClass),
.class_init = arm_gicv3_common_class_init,
+ .instance_finalize = arm_gicv3_finalize,
.abstract = true,
.interfaces = (InterfaceInfo []) {
{ TYPE_ARM_LINUX_BOOT_IF },
int i, irq = offset - GICD_IPRIORITYR;
uint32_t value = 0;
- for (i = irq + 3; i >= irq; i--, value <<= 8) {
+ for (i = irq + 3; i >= irq; i--) {
+ value <<= 8;
value |= gicd_read_ipriorityr(s, attrs, i);
}
*data = value;
/* register the base address */
kvm_arm_register_device(&s->iomem_its_cntrl, -1, KVM_DEV_ARM_VGIC_GRP_ADDR,
- KVM_VGIC_ITS_ADDR_TYPE, s->dev_fd);
+ KVM_VGIC_ITS_ADDR_TYPE, s->dev_fd, 0);
gicv3_its_init_mmio(s, NULL);
{
GICv3State *s = KVM_ARM_GICV3(dev);
KVMARMGICv3Class *kgc = KVM_ARM_GICV3_GET_CLASS(s);
+ bool multiple_redist_region_allowed;
Error *local_err = NULL;
int i;
return;
}
- gicv3_init_irqs_and_mmio(s, kvm_arm_gicv3_set_irq, NULL);
+ gicv3_init_irqs_and_mmio(s, kvm_arm_gicv3_set_irq, NULL, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
for (i = 0; i < s->num_cpu; i++) {
ARMCPU *cpu = ARM_CPU(qemu_get_cpu(i));
return;
}
+ multiple_redist_region_allowed =
+ kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
+ KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION);
+
+ if (!multiple_redist_region_allowed && s->nb_redist_regions > 1) {
+ error_setg(errp, "Multiple VGICv3 redistributor regions are not "
+ "supported by this host kernel");
+ error_append_hint(errp, "A maximum of %d VCPUs can be used",
+ s->redist_region_count[0]);
+ return;
+ }
+
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
0, &s->num_irq, true, &error_abort);
KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true, &error_abort);
kvm_arm_register_device(&s->iomem_dist, -1, KVM_DEV_ARM_VGIC_GRP_ADDR,
- KVM_VGIC_V3_ADDR_TYPE_DIST, s->dev_fd);
- kvm_arm_register_device(&s->iomem_redist, -1, KVM_DEV_ARM_VGIC_GRP_ADDR,
- KVM_VGIC_V3_ADDR_TYPE_REDIST, s->dev_fd);
+ KVM_VGIC_V3_ADDR_TYPE_DIST, s->dev_fd, 0);
+
+ if (!multiple_redist_region_allowed) {
+ kvm_arm_register_device(&s->iomem_redist[0], -1,
+ KVM_DEV_ARM_VGIC_GRP_ADDR,
+ KVM_VGIC_V3_ADDR_TYPE_REDIST, s->dev_fd, 0);
+ } else {
+ /* we register regions in reverse order as "devices" are inserted at
+ * the head of a QSLIST and the list is then popped from the head
+ * onwards by kvm_arm_machine_init_done()
+ */
+ for (i = s->nb_redist_regions - 1; i >= 0; i--) {
+ /* Address mask made of the rdist region index and count */
+ uint64_t addr_ormask =
+ i | ((uint64_t)s->redist_region_count[i] << 52);
+
+ kvm_arm_register_device(&s->iomem_redist[i], -1,
+ KVM_DEV_ARM_VGIC_GRP_ADDR,
+ KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION,
+ s->dev_fd, addr_ormask);
+ }
+ }
if (kvm_has_gsi_routing()) {
/* set up irq routing */
int i, irq = offset - GICR_IPRIORITYR;
uint32_t value = 0;
- for (i = irq + 3; i >= irq; i--, value <<= 8) {
+ for (i = irq + 3; i >= irq; i--) {
+ value <<= 8;
value |= gicr_read_ipriorityr(cs, attrs, i);
}
*data = value;
obj-$(CONFIG_MPS2_FPGAIO) += mps2-fpgaio.o
obj-$(CONFIG_MPS2_SCC) += mps2-scc.o
+obj-$(CONFIG_TZ_MPC) += tz-mpc.o
obj-$(CONFIG_TZ_PPC) += tz-ppc.o
obj-$(CONFIG_IOTKIT_SECCTL) += iotkit-secctl.o
case A_NSCCFG:
r = s->nsccfg;
break;
+ case A_SECMPCINTSTATUS:
+ r = s->mpcintstatus;
+ break;
case A_SECPPCINTSTAT:
r = s->secppcintstat;
break;
case A_APBSPPPCEXP3:
r = s->apbexp[offset_to_ppc_idx(offset)].sp;
break;
- case A_SECMPCINTSTATUS:
case A_SECMSCINTSTAT:
case A_SECMSCINTEN:
case A_NSMSCEXP:
foreach_ppc(s, iotkit_secctl_reset_ppc);
}
+static void iotkit_secctl_mpc_status(void *opaque, int n, int level)
+{
+ IoTKitSecCtl *s = IOTKIT_SECCTL(opaque);
+
+ s->mpcintstatus = deposit32(s->mpcintstatus, 0, 1, !!level);
+}
+
+static void iotkit_secctl_mpcexp_status(void *opaque, int n, int level)
+{
+ IoTKitSecCtl *s = IOTKIT_SECCTL(opaque);
+
+ s->mpcintstatus = deposit32(s->mpcintstatus, n + 16, 1, !!level);
+}
+
static void iotkit_secctl_ppc_irqstatus(void *opaque, int n, int level)
{
IoTKitSecCtlPPC *ppc = opaque;
qdev_init_gpio_out_named(dev, &s->sec_resp_cfg, "sec_resp_cfg", 1);
qdev_init_gpio_out_named(dev, &s->nsc_cfg_irq, "nsc_cfg", 1);
+ qdev_init_gpio_in_named(dev, iotkit_secctl_mpc_status, "mpc_status", 1);
+ qdev_init_gpio_in_named(dev, iotkit_secctl_mpcexp_status,
+ "mpcexp_status", IOTS_NUM_EXP_MPC);
+
memory_region_init_io(&s->s_regs, obj, &iotkit_secctl_s_ops,
s, "iotkit-secctl-s-regs", 0x1000);
memory_region_init_io(&s->ns_regs, obj, &iotkit_secctl_ns_ops,
}
};
+static const VMStateDescription iotkit_secctl_mpcintstatus_vmstate = {
+ .name = "iotkit-secctl-mpcintstatus",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(mpcintstatus, IoTKitSecCtl),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription iotkit_secctl_vmstate = {
.name = "iotkit-secctl",
.version_id = 1,
VMSTATE_STRUCT_ARRAY(ahbexp, IoTKitSecCtl, IOTS_NUM_AHB_EXP_PPC, 1,
iotkit_secctl_ppc_vmstate, IoTKitSecCtlPPC),
VMSTATE_END_OF_LIST()
- }
+ },
+ .subsections = (const VMStateDescription*[]) {
+ &iotkit_secctl_mpcintstatus_vmstate,
+ NULL
+ },
};
static void iotkit_secctl_class_init(ObjectClass *klass, void *data)
mos6522_write(uint64_t addr, uint64_t val) "reg=0x%"PRIx64 " val=0x%"PRIx64
mos6522_read(uint64_t addr, unsigned val) "reg=0x%"PRIx64 " val=0x%x"
+# hw/misc/tz-mpc.c
+tz_mpc_reg_read(uint32_t offset, uint64_t data, unsigned size) "TZ MPC regs read: offset 0x%x data 0x%" PRIx64 " size %u"
+tz_mpc_reg_write(uint32_t offset, uint64_t data, unsigned size) "TZ MPC regs write: offset 0x%x data 0x%" PRIx64 " size %u"
+tz_mpc_mem_blocked_read(uint64_t addr, unsigned size, bool secure) "TZ MPC blocked read: offset 0x%" PRIx64 " size %u secure %d"
+tz_mpc_mem_blocked_write(uint64_t addr, uint64_t data, unsigned size, bool secure) "TZ MPC blocked write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u secure %d"
+tz_mpc_translate(uint64_t addr, int flags, const char *idx, const char *res) "TZ MPC translate: addr 0x%" PRIx64 " flags 0x%x iommu_idx %s: %s"
+tz_mpc_iommu_notify(uint64_t addr) "TZ MPC iommu: notifying UNMAP/MAP for 0x%" PRIx64
+
# hw/misc/tz-ppc.c
tz_ppc_reset(void) "TZ PPC: reset"
tz_ppc_cfg_nonsec(int n, int level) "TZ PPC: cfg_nonsec[%d] = %d"
--- /dev/null
+/*
+ * ARM AHB5 TrustZone Memory Protection Controller emulation
+ *
+ * Copyright (c) 2018 Linaro Limited
+ * Written by Peter Maydell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 or
+ * (at your option) any later version.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qapi/error.h"
+#include "trace.h"
+#include "hw/sysbus.h"
+#include "hw/registerfields.h"
+#include "hw/misc/tz-mpc.h"
+
+/* Our IOMMU has two IOMMU indexes, one for secure transactions and one for
+ * non-secure transactions.
+ */
+enum {
+ IOMMU_IDX_S,
+ IOMMU_IDX_NS,
+ IOMMU_NUM_INDEXES,
+};
+
+/* Config registers */
+REG32(CTRL, 0x00)
+ FIELD(CTRL, SEC_RESP, 4, 1)
+ FIELD(CTRL, AUTOINC, 8, 1)
+ FIELD(CTRL, LOCKDOWN, 31, 1)
+REG32(BLK_MAX, 0x10)
+REG32(BLK_CFG, 0x14)
+REG32(BLK_IDX, 0x18)
+REG32(BLK_LUT, 0x1c)
+REG32(INT_STAT, 0x20)
+ FIELD(INT_STAT, IRQ, 0, 1)
+REG32(INT_CLEAR, 0x24)
+ FIELD(INT_CLEAR, IRQ, 0, 1)
+REG32(INT_EN, 0x28)
+ FIELD(INT_EN, IRQ, 0, 1)
+REG32(INT_INFO1, 0x2c)
+REG32(INT_INFO2, 0x30)
+ FIELD(INT_INFO2, HMASTER, 0, 16)
+ FIELD(INT_INFO2, HNONSEC, 16, 1)
+ FIELD(INT_INFO2, CFG_NS, 17, 1)
+REG32(INT_SET, 0x34)
+ FIELD(INT_SET, IRQ, 0, 1)
+REG32(PIDR4, 0xfd0)
+REG32(PIDR5, 0xfd4)
+REG32(PIDR6, 0xfd8)
+REG32(PIDR7, 0xfdc)
+REG32(PIDR0, 0xfe0)
+REG32(PIDR1, 0xfe4)
+REG32(PIDR2, 0xfe8)
+REG32(PIDR3, 0xfec)
+REG32(CIDR0, 0xff0)
+REG32(CIDR1, 0xff4)
+REG32(CIDR2, 0xff8)
+REG32(CIDR3, 0xffc)
+
+static const uint8_t tz_mpc_idregs[] = {
+ 0x04, 0x00, 0x00, 0x00,
+ 0x60, 0xb8, 0x1b, 0x00,
+ 0x0d, 0xf0, 0x05, 0xb1,
+};
+
+static void tz_mpc_irq_update(TZMPC *s)
+{
+ qemu_set_irq(s->irq, s->int_stat && s->int_en);
+}
+
+static void tz_mpc_iommu_notify(TZMPC *s, uint32_t lutidx,
+ uint32_t oldlut, uint32_t newlut)
+{
+ /* Called when the LUT word at lutidx has changed from oldlut to newlut;
+ * must call the IOMMU notifiers for the changed blocks.
+ */
+ IOMMUTLBEntry entry = {
+ .addr_mask = s->blocksize - 1,
+ };
+ hwaddr addr = lutidx * s->blocksize * 32;
+ int i;
+
+ for (i = 0; i < 32; i++, addr += s->blocksize) {
+ bool block_is_ns;
+
+ if (!((oldlut ^ newlut) & (1 << i))) {
+ continue;
+ }
+ /* This changes the mappings for both the S and the NS space,
+ * so we need to do four notifies: an UNMAP then a MAP for each.
+ */
+ block_is_ns = newlut & (1 << i);
+
+ trace_tz_mpc_iommu_notify(addr);
+ entry.iova = addr;
+ entry.translated_addr = addr;
+
+ entry.perm = IOMMU_NONE;
+ memory_region_notify_iommu(&s->upstream, IOMMU_IDX_S, entry);
+ memory_region_notify_iommu(&s->upstream, IOMMU_IDX_NS, entry);
+
+ entry.perm = IOMMU_RW;
+ if (block_is_ns) {
+ entry.target_as = &s->blocked_io_as;
+ } else {
+ entry.target_as = &s->downstream_as;
+ }
+ memory_region_notify_iommu(&s->upstream, IOMMU_IDX_S, entry);
+ if (block_is_ns) {
+ entry.target_as = &s->downstream_as;
+ } else {
+ entry.target_as = &s->blocked_io_as;
+ }
+ memory_region_notify_iommu(&s->upstream, IOMMU_IDX_NS, entry);
+ }
+}
+
+static void tz_mpc_autoinc_idx(TZMPC *s, unsigned access_size)
+{
+ /* Auto-increment BLK_IDX if necessary */
+ if (access_size == 4 && (s->ctrl & R_CTRL_AUTOINC_MASK)) {
+ s->blk_idx++;
+ s->blk_idx %= s->blk_max;
+ }
+}
+
+static MemTxResult tz_mpc_reg_read(void *opaque, hwaddr addr,
+ uint64_t *pdata,
+ unsigned size, MemTxAttrs attrs)
+{
+ TZMPC *s = TZ_MPC(opaque);
+ uint64_t r;
+ uint32_t offset = addr & ~0x3;
+
+ if (!attrs.secure && offset < A_PIDR4) {
+ /* NS accesses can only see the ID registers */
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "TZ MPC register read: NS access to offset 0x%x\n",
+ offset);
+ r = 0;
+ goto read_out;
+ }
+
+ switch (offset) {
+ case A_CTRL:
+ r = s->ctrl;
+ break;
+ case A_BLK_MAX:
+ r = s->blk_max;
+ break;
+ case A_BLK_CFG:
+ /* We are never in "init in progress state", so this just indicates
+ * the block size. s->blocksize == (1 << BLK_CFG + 5), so
+ * BLK_CFG == ctz32(s->blocksize) - 5
+ */
+ r = ctz32(s->blocksize) - 5;
+ break;
+ case A_BLK_IDX:
+ r = s->blk_idx;
+ break;
+ case A_BLK_LUT:
+ r = s->blk_lut[s->blk_idx];
+ tz_mpc_autoinc_idx(s, size);
+ break;
+ case A_INT_STAT:
+ r = s->int_stat;
+ break;
+ case A_INT_EN:
+ r = s->int_en;
+ break;
+ case A_INT_INFO1:
+ r = s->int_info1;
+ break;
+ case A_INT_INFO2:
+ r = s->int_info2;
+ break;
+ case A_PIDR4:
+ case A_PIDR5:
+ case A_PIDR6:
+ case A_PIDR7:
+ case A_PIDR0:
+ case A_PIDR1:
+ case A_PIDR2:
+ case A_PIDR3:
+ case A_CIDR0:
+ case A_CIDR1:
+ case A_CIDR2:
+ case A_CIDR3:
+ r = tz_mpc_idregs[(offset - A_PIDR4) / 4];
+ break;
+ case A_INT_CLEAR:
+ case A_INT_SET:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "TZ MPC register read: write-only offset 0x%x\n",
+ offset);
+ r = 0;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "TZ MPC register read: bad offset 0x%x\n", offset);
+ r = 0;
+ break;
+ }
+
+ if (size != 4) {
+ /* None of our registers are read-sensitive (except BLK_LUT,
+ * which can special case the "size not 4" case), so just
+ * pull the right bytes out of the word read result.
+ */
+ r = extract32(r, (addr & 3) * 8, size * 8);
+ }
+
+read_out:
+ trace_tz_mpc_reg_read(addr, r, size);
+ *pdata = r;
+ return MEMTX_OK;
+}
+
+static MemTxResult tz_mpc_reg_write(void *opaque, hwaddr addr,
+ uint64_t value,
+ unsigned size, MemTxAttrs attrs)
+{
+ TZMPC *s = TZ_MPC(opaque);
+ uint32_t offset = addr & ~0x3;
+
+ trace_tz_mpc_reg_write(addr, value, size);
+
+ if (!attrs.secure && offset < A_PIDR4) {
+ /* NS accesses can only see the ID registers */
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "TZ MPC register write: NS access to offset 0x%x\n",
+ offset);
+ return MEMTX_OK;
+ }
+
+ if (size != 4) {
+ /* Expand the byte or halfword write to a full word size.
+ * In most cases we can do this with zeroes; the exceptions
+ * are CTRL, BLK_IDX and BLK_LUT.
+ */
+ uint32_t oldval;
+
+ switch (offset) {
+ case A_CTRL:
+ oldval = s->ctrl;
+ break;
+ case A_BLK_IDX:
+ oldval = s->blk_idx;
+ break;
+ case A_BLK_LUT:
+ oldval = s->blk_lut[s->blk_idx];
+ break;
+ default:
+ oldval = 0;
+ break;
+ }
+ value = deposit32(oldval, (addr & 3) * 8, size * 8, value);
+ }
+
+ if ((s->ctrl & R_CTRL_LOCKDOWN_MASK) &&
+ (offset == A_CTRL || offset == A_BLK_LUT || offset == A_INT_EN)) {
+ /* Lockdown mode makes these three registers read-only, and
+ * the only way out of it is to reset the device.
+ */
+ qemu_log_mask(LOG_GUEST_ERROR, "TZ MPC register write to offset 0x%x "
+ "while MPC is in lockdown mode\n", offset);
+ return MEMTX_OK;
+ }
+
+ switch (offset) {
+ case A_CTRL:
+ /* We don't implement the 'data gating' feature so all other bits
+ * are reserved and we make them RAZ/WI.
+ */
+ s->ctrl = value & (R_CTRL_SEC_RESP_MASK |
+ R_CTRL_AUTOINC_MASK |
+ R_CTRL_LOCKDOWN_MASK);
+ break;
+ case A_BLK_IDX:
+ s->blk_idx = value % s->blk_max;
+ break;
+ case A_BLK_LUT:
+ tz_mpc_iommu_notify(s, s->blk_idx, s->blk_lut[s->blk_idx], value);
+ s->blk_lut[s->blk_idx] = value;
+ tz_mpc_autoinc_idx(s, size);
+ break;
+ case A_INT_CLEAR:
+ if (value & R_INT_CLEAR_IRQ_MASK) {
+ s->int_stat = 0;
+ tz_mpc_irq_update(s);
+ }
+ break;
+ case A_INT_EN:
+ s->int_en = value & R_INT_EN_IRQ_MASK;
+ tz_mpc_irq_update(s);
+ break;
+ case A_INT_SET:
+ if (value & R_INT_SET_IRQ_MASK) {
+ s->int_stat = R_INT_STAT_IRQ_MASK;
+ tz_mpc_irq_update(s);
+ }
+ break;
+ case A_PIDR4:
+ case A_PIDR5:
+ case A_PIDR6:
+ case A_PIDR7:
+ case A_PIDR0:
+ case A_PIDR1:
+ case A_PIDR2:
+ case A_PIDR3:
+ case A_CIDR0:
+ case A_CIDR1:
+ case A_CIDR2:
+ case A_CIDR3:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "TZ MPC register write: read-only offset 0x%x\n", offset);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "TZ MPC register write: bad offset 0x%x\n", offset);
+ break;
+ }
+
+ return MEMTX_OK;
+}
+
+static const MemoryRegionOps tz_mpc_reg_ops = {
+ .read_with_attrs = tz_mpc_reg_read,
+ .write_with_attrs = tz_mpc_reg_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
+ .impl.min_access_size = 1,
+ .impl.max_access_size = 4,
+};
+
+static inline bool tz_mpc_cfg_ns(TZMPC *s, hwaddr addr)
+{
+ /* Return the cfg_ns bit from the LUT for the specified address */
+ hwaddr blknum = addr / s->blocksize;
+ hwaddr blkword = blknum / 32;
+ uint32_t blkbit = 1U << (blknum % 32);
+
+ /* This would imply the address was larger than the size we
+ * defined this memory region to be, so it can't happen.
+ */
+ assert(blkword < s->blk_max);
+ return s->blk_lut[blkword] & blkbit;
+}
+
+static MemTxResult tz_mpc_handle_block(TZMPC *s, hwaddr addr, MemTxAttrs attrs)
+{
+ /* Handle a blocked transaction: raise IRQ, capture info, etc */
+ if (!s->int_stat) {
+ /* First blocked transfer: capture information into INT_INFO1 and
+ * INT_INFO2. Subsequent transfers are still blocked but don't
+ * capture information until the guest clears the interrupt.
+ */
+
+ s->int_info1 = addr;
+ s->int_info2 = 0;
+ s->int_info2 = FIELD_DP32(s->int_info2, INT_INFO2, HMASTER,
+ attrs.requester_id & 0xffff);
+ s->int_info2 = FIELD_DP32(s->int_info2, INT_INFO2, HNONSEC,
+ ~attrs.secure);
+ s->int_info2 = FIELD_DP32(s->int_info2, INT_INFO2, CFG_NS,
+ tz_mpc_cfg_ns(s, addr));
+ s->int_stat |= R_INT_STAT_IRQ_MASK;
+ tz_mpc_irq_update(s);
+ }
+
+ /* Generate bus error if desired; otherwise RAZ/WI */
+ return (s->ctrl & R_CTRL_SEC_RESP_MASK) ? MEMTX_ERROR : MEMTX_OK;
+}
+
+/* Accesses only reach these read and write functions if the MPC is
+ * blocking them; non-blocked accesses go directly to the downstream
+ * memory region without passing through this code.
+ */
+static MemTxResult tz_mpc_mem_blocked_read(void *opaque, hwaddr addr,
+ uint64_t *pdata,
+ unsigned size, MemTxAttrs attrs)
+{
+ TZMPC *s = TZ_MPC(opaque);
+
+ trace_tz_mpc_mem_blocked_read(addr, size, attrs.secure);
+
+ *pdata = 0;
+ return tz_mpc_handle_block(s, addr, attrs);
+}
+
+static MemTxResult tz_mpc_mem_blocked_write(void *opaque, hwaddr addr,
+ uint64_t value,
+ unsigned size, MemTxAttrs attrs)
+{
+ TZMPC *s = TZ_MPC(opaque);
+
+ trace_tz_mpc_mem_blocked_write(addr, value, size, attrs.secure);
+
+ return tz_mpc_handle_block(s, addr, attrs);
+}
+
+static const MemoryRegionOps tz_mpc_mem_blocked_ops = {
+ .read_with_attrs = tz_mpc_mem_blocked_read,
+ .write_with_attrs = tz_mpc_mem_blocked_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 8,
+ .impl.min_access_size = 1,
+ .impl.max_access_size = 8,
+};
+
+static IOMMUTLBEntry tz_mpc_translate(IOMMUMemoryRegion *iommu,
+ hwaddr addr, IOMMUAccessFlags flags,
+ int iommu_idx)
+{
+ TZMPC *s = TZ_MPC(container_of(iommu, TZMPC, upstream));
+ bool ok;
+
+ IOMMUTLBEntry ret = {
+ .iova = addr & ~(s->blocksize - 1),
+ .translated_addr = addr & ~(s->blocksize - 1),
+ .addr_mask = s->blocksize - 1,
+ .perm = IOMMU_RW,
+ };
+
+ /* Look at the per-block configuration for this address, and
+ * return a TLB entry directing the transaction at either
+ * downstream_as or blocked_io_as, as appropriate.
+ * If the LUT cfg_ns bit is 1, only non-secure transactions
+ * may pass. If the bit is 0, only secure transactions may pass.
+ */
+ ok = tz_mpc_cfg_ns(s, addr) == (iommu_idx == IOMMU_IDX_NS);
+
+ trace_tz_mpc_translate(addr, flags,
+ iommu_idx == IOMMU_IDX_S ? "S" : "NS",
+ ok ? "pass" : "block");
+
+ ret.target_as = ok ? &s->downstream_as : &s->blocked_io_as;
+ return ret;
+}
+
+static int tz_mpc_attrs_to_index(IOMMUMemoryRegion *iommu, MemTxAttrs attrs)
+{
+ /* We treat unspecified attributes like secure. Transactions with
+ * unspecified attributes come from places like
+ * cpu_physical_memory_write_rom() for initial image load, and we want
+ * those to pass through the from-reset "everything is secure" config.
+ * All the real during-emulation transactions from the CPU will
+ * specify attributes.
+ */
+ return (attrs.unspecified || attrs.secure) ? IOMMU_IDX_S : IOMMU_IDX_NS;
+}
+
+static int tz_mpc_num_indexes(IOMMUMemoryRegion *iommu)
+{
+ return IOMMU_NUM_INDEXES;
+}
+
+static void tz_mpc_reset(DeviceState *dev)
+{
+ TZMPC *s = TZ_MPC(dev);
+
+ s->ctrl = 0x00000100;
+ s->blk_idx = 0;
+ s->int_stat = 0;
+ s->int_en = 1;
+ s->int_info1 = 0;
+ s->int_info2 = 0;
+
+ memset(s->blk_lut, 0, s->blk_max * sizeof(uint32_t));
+}
+
+static void tz_mpc_init(Object *obj)
+{
+ DeviceState *dev = DEVICE(obj);
+ TZMPC *s = TZ_MPC(obj);
+
+ qdev_init_gpio_out_named(dev, &s->irq, "irq", 1);
+}
+
+static void tz_mpc_realize(DeviceState *dev, Error **errp)
+{
+ Object *obj = OBJECT(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ TZMPC *s = TZ_MPC(dev);
+ uint64_t size;
+
+ /* We can't create the upstream end of the port until realize,
+ * as we don't know the size of the MR used as the downstream until then.
+ * We insist on having a downstream, to avoid complicating the code
+ * with handling the "don't know how big this is" case. It's easy
+ * enough for the user to create an unimplemented_device as downstream
+ * if they have nothing else to plug into this.
+ */
+ if (!s->downstream) {
+ error_setg(errp, "MPC 'downstream' link not set");
+ return;
+ }
+
+ size = memory_region_size(s->downstream);
+
+ memory_region_init_iommu(&s->upstream, sizeof(s->upstream),
+ TYPE_TZ_MPC_IOMMU_MEMORY_REGION,
+ obj, "tz-mpc-upstream", size);
+
+ /* In real hardware the block size is configurable. In QEMU we could
+ * make it configurable but will need it to be at least as big as the
+ * target page size so we can execute out of the resulting MRs. Guest
+ * software is supposed to check the block size using the BLK_CFG
+ * register, so make it fixed at the page size.
+ */
+ s->blocksize = memory_region_iommu_get_min_page_size(&s->upstream);
+ if (size % s->blocksize != 0) {
+ error_setg(errp,
+ "MPC 'downstream' size %" PRId64
+ " is not a multiple of %" HWADDR_PRIx " bytes",
+ size, s->blocksize);
+ object_unref(OBJECT(&s->upstream));
+ return;
+ }
+
+ /* BLK_MAX is the max value of BLK_IDX, which indexes an array of 32-bit
+ * words, each bit of which indicates one block.
+ */
+ s->blk_max = DIV_ROUND_UP(size / s->blocksize, 32);
+
+ memory_region_init_io(&s->regmr, obj, &tz_mpc_reg_ops,
+ s, "tz-mpc-regs", 0x1000);
+ sysbus_init_mmio(sbd, &s->regmr);
+
+ sysbus_init_mmio(sbd, MEMORY_REGION(&s->upstream));
+
+ /* This memory region is not exposed to users of this device as a
+ * sysbus MMIO region, but is instead used internally as something
+ * that our IOMMU translate function might direct accesses to.
+ */
+ memory_region_init_io(&s->blocked_io, obj, &tz_mpc_mem_blocked_ops,
+ s, "tz-mpc-blocked-io", size);
+
+ address_space_init(&s->downstream_as, s->downstream,
+ "tz-mpc-downstream");
+ address_space_init(&s->blocked_io_as, &s->blocked_io,
+ "tz-mpc-blocked-io");
+
+ s->blk_lut = g_new(uint32_t, s->blk_max);
+}
+
+static int tz_mpc_post_load(void *opaque, int version_id)
+{
+ TZMPC *s = TZ_MPC(opaque);
+
+ /* Check the incoming data doesn't point blk_idx off the end of blk_lut. */
+ if (s->blk_idx >= s->blk_max) {
+ return -1;
+ }
+ return 0;
+}
+
+static const VMStateDescription tz_mpc_vmstate = {
+ .name = "tz-mpc",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .post_load = tz_mpc_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(ctrl, TZMPC),
+ VMSTATE_UINT32(blk_idx, TZMPC),
+ VMSTATE_UINT32(int_stat, TZMPC),
+ VMSTATE_UINT32(int_en, TZMPC),
+ VMSTATE_UINT32(int_info1, TZMPC),
+ VMSTATE_UINT32(int_info2, TZMPC),
+ VMSTATE_VARRAY_UINT32(blk_lut, TZMPC, blk_max,
+ 0, vmstate_info_uint32, uint32_t),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property tz_mpc_properties[] = {
+ DEFINE_PROP_LINK("downstream", TZMPC, downstream,
+ TYPE_MEMORY_REGION, MemoryRegion *),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void tz_mpc_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = tz_mpc_realize;
+ dc->vmsd = &tz_mpc_vmstate;
+ dc->reset = tz_mpc_reset;
+ dc->props = tz_mpc_properties;
+}
+
+static const TypeInfo tz_mpc_info = {
+ .name = TYPE_TZ_MPC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(TZMPC),
+ .instance_init = tz_mpc_init,
+ .class_init = tz_mpc_class_init,
+};
+
+static void tz_mpc_iommu_memory_region_class_init(ObjectClass *klass,
+ void *data)
+{
+ IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass);
+
+ imrc->translate = tz_mpc_translate;
+ imrc->attrs_to_index = tz_mpc_attrs_to_index;
+ imrc->num_indexes = tz_mpc_num_indexes;
+}
+
+static const TypeInfo tz_mpc_iommu_memory_region_info = {
+ .name = TYPE_TZ_MPC_IOMMU_MEMORY_REGION,
+ .parent = TYPE_IOMMU_MEMORY_REGION,
+ .class_init = tz_mpc_iommu_memory_region_class_init,
+};
+
+static void tz_mpc_register_types(void)
+{
+ type_register_static(&tz_mpc_info);
+ type_register_static(&tz_mpc_iommu_memory_region_info);
+}
+
+type_init(tz_mpc_register_types);
g_free(reg);
}
-static int pnv_chip_lpc_offset(PnvChip *chip, void *fdt)
-{
- char *name;
- int offset;
-
- name = g_strdup_printf("/xscom@%" PRIx64 "/isa@%x",
- (uint64_t) PNV_XSCOM_BASE(chip), PNV_XSCOM_LPC_BASE);
- offset = fdt_path_offset(fdt, name);
- g_free(name);
- return offset;
-}
-
static void pnv_dt_chip(PnvChip *chip, void *fdt)
{
const char *typename = pnv_chip_core_typename(chip);
pnv_dt_xscom(chip, fdt, 0);
- /* The default LPC bus of a multichip system is on chip 0. It's
- * recognized by the firmware (skiboot) using a "primary"
- * property.
- */
- if (chip->chip_id == 0x0) {
- int lpc_offset = pnv_chip_lpc_offset(chip, fdt);
-
- _FDT((fdt_setprop(fdt, lpc_offset, "primary", NULL, 0)));
- }
-
for (i = 0; i < chip->nr_cores; i++) {
PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize);
return 0;
}
-static void pnv_dt_isa(ISABus *bus, void *fdt, int lpc_offset)
+static int pnv_chip_isa_offset(PnvChip *chip, void *fdt)
{
+ char *name;
+ int offset;
+
+ name = g_strdup_printf("/xscom@%" PRIx64 "/isa@%x",
+ (uint64_t) PNV_XSCOM_BASE(chip), PNV_XSCOM_LPC_BASE);
+ offset = fdt_path_offset(fdt, name);
+ g_free(name);
+ return offset;
+}
+
+/* The default LPC bus of a multichip system is on chip 0. It's
+ * recognized by the firmware (skiboot) using a "primary" property.
+ */
+static void pnv_dt_isa(PnvMachineState *pnv, void *fdt)
+{
+ int isa_offset = pnv_chip_isa_offset(pnv->chips[0], fdt);
ForeachPopulateArgs args = {
.fdt = fdt,
- .offset = lpc_offset,
+ .offset = isa_offset,
};
+ _FDT((fdt_setprop(fdt, isa_offset, "primary", NULL, 0)));
+
/* ISA devices are not necessarily parented to the ISA bus so we
* can not use object_child_foreach() */
- qbus_walk_children(BUS(bus), pnv_dt_isa_device, NULL, NULL, NULL, &args);
+ qbus_walk_children(BUS(pnv->isa_bus), pnv_dt_isa_device, NULL, NULL, NULL,
+ &args);
}
static void *pnv_dt_create(MachineState *machine)
char *buf;
int off;
int i;
- int lpc_offset;
fdt = g_malloc0(FDT_MAX_SIZE);
_FDT((fdt_create_empty_tree(fdt, FDT_MAX_SIZE)));
}
/* Populate ISA devices on chip 0 */
- lpc_offset = pnv_chip_lpc_offset(pnv->chips[0], fdt);
- pnv_dt_isa(pnv->isa_bus, fdt, lpc_offset);
+ pnv_dt_isa(pnv, fdt);
if (pnv->bmc) {
pnv_dt_bmc_sensors(pnv->bmc, fdt);
cpu_physical_memory_write(PNV_FDT_ADDR, fdt, fdt_totalsize(fdt));
}
-static ISABus *pnv_isa_create(PnvChip *chip)
+static ISABus *pnv_chip_power8_isa_create(PnvChip *chip, Error **errp)
{
- PnvLpcController *lpc = &chip->lpc;
- ISABus *isa_bus;
- qemu_irq *irqs;
- PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
+ Pnv8Chip *chip8 = PNV8_CHIP(chip);
+ return pnv_lpc_isa_create(&chip8->lpc, true, errp);
+}
- /* let isa_bus_new() create its own bridge on SysBus otherwise
- * devices speficied on the command line won't find the bus and
- * will fail to create.
- */
- isa_bus = isa_bus_new(NULL, &lpc->isa_mem, &lpc->isa_io,
- &error_fatal);
+static ISABus *pnv_chip_power8nvl_isa_create(PnvChip *chip, Error **errp)
+{
+ Pnv8Chip *chip8 = PNV8_CHIP(chip);
+ return pnv_lpc_isa_create(&chip8->lpc, false, errp);
+}
- irqs = pnv_lpc_isa_irq_create(lpc, pcc->chip_type, ISA_NUM_IRQS);
+static ISABus *pnv_chip_power9_isa_create(PnvChip *chip, Error **errp)
+{
+ return NULL;
+}
- isa_bus_irqs(isa_bus, irqs);
- return isa_bus;
+static ISABus *pnv_isa_create(PnvChip *chip, Error **errp)
+{
+ return PNV_CHIP_GET_CLASS(chip)->isa_create(chip, errp);
}
static void pnv_init(MachineState *machine)
g_free(chip_typename);
/* Instantiate ISA bus on chip 0 */
- pnv->isa_bus = pnv_isa_create(pnv->chips[0]);
+ pnv->isa_bus = pnv_isa_create(pnv->chips[0], &error_fatal);
/* Create serial port */
serial_hds_isa_init(pnv->isa_bus, 0, MAX_ISA_SERIAL_PORTS);
return (chip->chip_id << 7) | (core_id << 3);
}
+static Object *pnv_chip_power8_intc_create(PnvChip *chip, Object *child,
+ Error **errp)
+{
+ return icp_create(child, TYPE_PNV_ICP, XICS_FABRIC(qdev_get_machine()),
+ errp);
+}
+
/*
* 0:48 Reserved - Read as zeroes
* 49:52 Node ID
return (chip->chip_id << 8) | (core_id << 2);
}
+static Object *pnv_chip_power9_intc_create(PnvChip *chip, Object *child,
+ Error **errp)
+{
+ return NULL;
+}
+
/* Allowed core identifiers on a POWER8 Processor Chip :
*
* <EX0 reserved>
*/
#define POWER9_CORE_MASK (0xffffffffffffffull)
+static void pnv_chip_power8_instance_init(Object *obj)
+{
+ Pnv8Chip *chip8 = PNV8_CHIP(obj);
+
+ object_initialize(&chip8->psi, sizeof(chip8->psi), TYPE_PNV_PSI);
+ object_property_add_child(obj, "psi", OBJECT(&chip8->psi), NULL);
+ object_property_add_const_link(OBJECT(&chip8->psi), "xics",
+ OBJECT(qdev_get_machine()), &error_abort);
+
+ object_initialize(&chip8->lpc, sizeof(chip8->lpc), TYPE_PNV_LPC);
+ object_property_add_child(obj, "lpc", OBJECT(&chip8->lpc), NULL);
+ object_property_add_const_link(OBJECT(&chip8->lpc), "psi",
+ OBJECT(&chip8->psi), &error_abort);
+
+ object_initialize(&chip8->occ, sizeof(chip8->occ), TYPE_PNV_OCC);
+ object_property_add_child(obj, "occ", OBJECT(&chip8->occ), NULL);
+ object_property_add_const_link(OBJECT(&chip8->occ), "psi",
+ OBJECT(&chip8->psi), &error_abort);
+}
+
+static void pnv_chip_icp_realize(Pnv8Chip *chip8, Error **errp)
+ {
+ PnvChip *chip = PNV_CHIP(chip8);
+ PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
+ const char *typename = pnv_chip_core_typename(chip);
+ size_t typesize = object_type_get_instance_size(typename);
+ int i, j;
+ char *name;
+ XICSFabric *xi = XICS_FABRIC(qdev_get_machine());
+
+ name = g_strdup_printf("icp-%x", chip->chip_id);
+ memory_region_init(&chip8->icp_mmio, OBJECT(chip), name, PNV_ICP_SIZE);
+ sysbus_init_mmio(SYS_BUS_DEVICE(chip), &chip8->icp_mmio);
+ g_free(name);
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(chip), 1, PNV_ICP_BASE(chip));
+
+ /* Map the ICP registers for each thread */
+ for (i = 0; i < chip->nr_cores; i++) {
+ PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize);
+ int core_hwid = CPU_CORE(pnv_core)->core_id;
+
+ for (j = 0; j < CPU_CORE(pnv_core)->nr_threads; j++) {
+ uint32_t pir = pcc->core_pir(chip, core_hwid) + j;
+ PnvICPState *icp = PNV_ICP(xics_icp_get(xi, pir));
+
+ memory_region_add_subregion(&chip8->icp_mmio, pir << 12,
+ &icp->mmio);
+ }
+ }
+}
+
+static void pnv_chip_power8_realize(DeviceState *dev, Error **errp)
+{
+ PnvChipClass *pcc = PNV_CHIP_GET_CLASS(dev);
+ PnvChip *chip = PNV_CHIP(dev);
+ Pnv8Chip *chip8 = PNV8_CHIP(dev);
+ Error *local_err = NULL;
+
+ pcc->parent_realize(dev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ /* Processor Service Interface (PSI) Host Bridge */
+ object_property_set_int(OBJECT(&chip8->psi), PNV_PSIHB_BASE(chip),
+ "bar", &error_fatal);
+ object_property_set_bool(OBJECT(&chip8->psi), true, "realized", &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ pnv_xscom_add_subregion(chip, PNV_XSCOM_PSIHB_BASE, &chip8->psi.xscom_regs);
+
+ /* Create LPC controller */
+ object_property_set_bool(OBJECT(&chip8->lpc), true, "realized",
+ &error_fatal);
+ pnv_xscom_add_subregion(chip, PNV_XSCOM_LPC_BASE, &chip8->lpc.xscom_regs);
+
+ /* Interrupt Management Area. This is the memory region holding
+ * all the Interrupt Control Presenter (ICP) registers */
+ pnv_chip_icp_realize(chip8, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ /* Create the simplified OCC model */
+ object_property_set_bool(OBJECT(&chip8->occ), true, "realized", &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ pnv_xscom_add_subregion(chip, PNV_XSCOM_OCC_BASE, &chip8->occ.xscom_regs);
+}
+
static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
k->chip_cfam_id = 0x221ef04980000000ull; /* P8 Murano DD2.1 */
k->cores_mask = POWER8E_CORE_MASK;
k->core_pir = pnv_chip_core_pir_p8;
+ k->intc_create = pnv_chip_power8_intc_create;
+ k->isa_create = pnv_chip_power8_isa_create;
k->xscom_base = 0x003fc0000000000ull;
dc->desc = "PowerNV Chip POWER8E";
+
+ device_class_set_parent_realize(dc, pnv_chip_power8_realize,
+ &k->parent_realize);
}
static void pnv_chip_power8_class_init(ObjectClass *klass, void *data)
k->chip_cfam_id = 0x220ea04980000000ull; /* P8 Venice DD2.0 */
k->cores_mask = POWER8_CORE_MASK;
k->core_pir = pnv_chip_core_pir_p8;
+ k->intc_create = pnv_chip_power8_intc_create;
+ k->isa_create = pnv_chip_power8_isa_create;
k->xscom_base = 0x003fc0000000000ull;
dc->desc = "PowerNV Chip POWER8";
+
+ device_class_set_parent_realize(dc, pnv_chip_power8_realize,
+ &k->parent_realize);
}
static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data)
k->chip_cfam_id = 0x120d304980000000ull; /* P8 Naples DD1.0 */
k->cores_mask = POWER8_CORE_MASK;
k->core_pir = pnv_chip_core_pir_p8;
+ k->intc_create = pnv_chip_power8_intc_create;
+ k->isa_create = pnv_chip_power8nvl_isa_create;
k->xscom_base = 0x003fc0000000000ull;
dc->desc = "PowerNV Chip POWER8NVL";
+
+ device_class_set_parent_realize(dc, pnv_chip_power8_realize,
+ &k->parent_realize);
+}
+
+static void pnv_chip_power9_instance_init(Object *obj)
+{
+}
+
+static void pnv_chip_power9_realize(DeviceState *dev, Error **errp)
+{
+ PnvChipClass *pcc = PNV_CHIP_GET_CLASS(dev);
+ Error *local_err = NULL;
+
+ pcc->parent_realize(dev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
}
static void pnv_chip_power9_class_init(ObjectClass *klass, void *data)
k->chip_cfam_id = 0x220d104900008000ull; /* P9 Nimbus DD2.0 */
k->cores_mask = POWER9_CORE_MASK;
k->core_pir = pnv_chip_core_pir_p9;
+ k->intc_create = pnv_chip_power9_intc_create;
+ k->isa_create = pnv_chip_power9_isa_create;
k->xscom_base = 0x00603fc00000000ull;
dc->desc = "PowerNV Chip POWER9";
+
+ device_class_set_parent_realize(dc, pnv_chip_power9_realize,
+ &k->parent_realize);
}
static void pnv_chip_core_sanitize(PnvChip *chip, Error **errp)
}
}
-static void pnv_chip_init(Object *obj)
+static void pnv_chip_instance_init(Object *obj)
{
- PnvChip *chip = PNV_CHIP(obj);
- PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
-
- chip->xscom_base = pcc->xscom_base;
-
- object_initialize(&chip->lpc, sizeof(chip->lpc), TYPE_PNV_LPC);
- object_property_add_child(obj, "lpc", OBJECT(&chip->lpc), NULL);
-
- object_initialize(&chip->psi, sizeof(chip->psi), TYPE_PNV_PSI);
- object_property_add_child(obj, "psi", OBJECT(&chip->psi), NULL);
- object_property_add_const_link(OBJECT(&chip->psi), "xics",
- OBJECT(qdev_get_machine()), &error_abort);
-
- object_initialize(&chip->occ, sizeof(chip->occ), TYPE_PNV_OCC);
- object_property_add_child(obj, "occ", OBJECT(&chip->occ), NULL);
- object_property_add_const_link(OBJECT(&chip->occ), "psi",
- OBJECT(&chip->psi), &error_abort);
-
- /* The LPC controller needs PSI to generate interrupts */
- object_property_add_const_link(OBJECT(&chip->lpc), "psi",
- OBJECT(&chip->psi), &error_abort);
-}
-
-static void pnv_chip_icp_realize(PnvChip *chip, Error **errp)
-{
- PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
- const char *typename = pnv_chip_core_typename(chip);
- size_t typesize = object_type_get_instance_size(typename);
- int i, j;
- char *name;
- XICSFabric *xi = XICS_FABRIC(qdev_get_machine());
-
- name = g_strdup_printf("icp-%x", chip->chip_id);
- memory_region_init(&chip->icp_mmio, OBJECT(chip), name, PNV_ICP_SIZE);
- sysbus_init_mmio(SYS_BUS_DEVICE(chip), &chip->icp_mmio);
- g_free(name);
-
- sysbus_mmio_map(SYS_BUS_DEVICE(chip), 1, PNV_ICP_BASE(chip));
-
- /* Map the ICP registers for each thread */
- for (i = 0; i < chip->nr_cores; i++) {
- PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize);
- int core_hwid = CPU_CORE(pnv_core)->core_id;
-
- for (j = 0; j < CPU_CORE(pnv_core)->nr_threads; j++) {
- uint32_t pir = pcc->core_pir(chip, core_hwid) + j;
- PnvICPState *icp = PNV_ICP(xics_icp_get(xi, pir));
-
- memory_region_add_subregion(&chip->icp_mmio, pir << 12, &icp->mmio);
- }
- }
+ PNV_CHIP(obj)->xscom_base = PNV_CHIP_GET_CLASS(obj)->xscom_base;
}
static void pnv_chip_core_realize(PnvChip *chip, Error **errp)
object_property_set_int(OBJECT(pnv_core),
pcc->core_pir(chip, core_hwid),
"pir", &error_fatal);
- object_property_add_const_link(OBJECT(pnv_core), "xics",
- qdev_get_machine(), &error_fatal);
+ object_property_add_const_link(OBJECT(pnv_core), "chip",
+ OBJECT(chip), &error_fatal);
object_property_set_bool(OBJECT(pnv_core), true, "realized",
&error_fatal);
object_unref(OBJECT(pnv_core));
error_propagate(errp, error);
return;
}
-
- /* Create LPC controller */
- object_property_set_bool(OBJECT(&chip->lpc), true, "realized",
- &error_fatal);
- pnv_xscom_add_subregion(chip, PNV_XSCOM_LPC_BASE, &chip->lpc.xscom_regs);
-
- /* Interrupt Management Area. This is the memory region holding
- * all the Interrupt Control Presenter (ICP) registers */
- pnv_chip_icp_realize(chip, &error);
- if (error) {
- error_propagate(errp, error);
- return;
- }
-
- /* Processor Service Interface (PSI) Host Bridge */
- object_property_set_int(OBJECT(&chip->psi), PNV_PSIHB_BASE(chip),
- "bar", &error_fatal);
- object_property_set_bool(OBJECT(&chip->psi), true, "realized", &error);
- if (error) {
- error_propagate(errp, error);
- return;
- }
- pnv_xscom_add_subregion(chip, PNV_XSCOM_PSIHB_BASE, &chip->psi.xscom_regs);
-
- /* Create the simplified OCC model */
- object_property_set_bool(OBJECT(&chip->occ), true, "realized", &error);
- if (error) {
- error_propagate(errp, error);
- return;
- }
- pnv_xscom_add_subregion(chip, PNV_XSCOM_OCC_BASE, &chip->occ.xscom_regs);
}
static Property pnv_chip_properties[] = {
int i;
for (i = 0; i < pnv->num_chips; i++) {
- if (ics_valid_irq(&pnv->chips[i]->psi.ics, irq)) {
- return &pnv->chips[i]->psi.ics;
+ Pnv8Chip *chip8 = PNV8_CHIP(pnv->chips[i]);
+
+ if (ics_valid_irq(&chip8->psi.ics, irq)) {
+ return &chip8->psi.ics;
}
}
return NULL;
int i;
for (i = 0; i < pnv->num_chips; i++) {
- ics_resend(&pnv->chips[i]->psi.ics);
+ Pnv8Chip *chip8 = PNV8_CHIP(pnv->chips[i]);
+ ics_resend(&chip8->psi.ics);
}
}
}
for (i = 0; i < pnv->num_chips; i++) {
- ics_pic_print_info(&pnv->chips[i]->psi.ics, mon);
+ Pnv8Chip *chip8 = PNV8_CHIP(pnv->chips[i]);
+ ics_pic_print_info(&chip8->psi.ics, mon);
}
}
pnv->num_chips = num_chips;
}
-static void pnv_machine_initfn(Object *obj)
+static void pnv_machine_instance_init(Object *obj)
{
PnvMachineState *pnv = PNV_MACHINE(obj);
pnv->num_chips = 1;
pnv_machine_class_props_init(oc);
}
-#define DEFINE_PNV_CHIP_TYPE(type, class_initfn) \
- { \
- .name = type, \
- .class_init = class_initfn, \
- .parent = TYPE_PNV_CHIP, \
+#define DEFINE_PNV8_CHIP_TYPE(type, class_initfn) \
+ { \
+ .name = type, \
+ .class_init = class_initfn, \
+ .parent = TYPE_PNV8_CHIP, \
+ }
+
+#define DEFINE_PNV9_CHIP_TYPE(type, class_initfn) \
+ { \
+ .name = type, \
+ .class_init = class_initfn, \
+ .parent = TYPE_PNV9_CHIP, \
}
static const TypeInfo types[] = {
.name = TYPE_PNV_MACHINE,
.parent = TYPE_MACHINE,
.instance_size = sizeof(PnvMachineState),
- .instance_init = pnv_machine_initfn,
+ .instance_init = pnv_machine_instance_init,
.class_init = pnv_machine_class_init,
.interfaces = (InterfaceInfo[]) {
{ TYPE_XICS_FABRIC },
.name = TYPE_PNV_CHIP,
.parent = TYPE_SYS_BUS_DEVICE,
.class_init = pnv_chip_class_init,
- .instance_init = pnv_chip_init,
+ .instance_init = pnv_chip_instance_init,
.instance_size = sizeof(PnvChip),
.class_size = sizeof(PnvChipClass),
.abstract = true,
},
- DEFINE_PNV_CHIP_TYPE(TYPE_PNV_CHIP_POWER9, pnv_chip_power9_class_init),
- DEFINE_PNV_CHIP_TYPE(TYPE_PNV_CHIP_POWER8, pnv_chip_power8_class_init),
- DEFINE_PNV_CHIP_TYPE(TYPE_PNV_CHIP_POWER8E, pnv_chip_power8e_class_init),
- DEFINE_PNV_CHIP_TYPE(TYPE_PNV_CHIP_POWER8NVL,
- pnv_chip_power8nvl_class_init),
+
+ /*
+ * P9 chip and variants
+ */
+ {
+ .name = TYPE_PNV9_CHIP,
+ .parent = TYPE_PNV_CHIP,
+ .instance_init = pnv_chip_power9_instance_init,
+ .instance_size = sizeof(Pnv9Chip),
+ },
+ DEFINE_PNV9_CHIP_TYPE(TYPE_PNV_CHIP_POWER9, pnv_chip_power9_class_init),
+
+ /*
+ * P8 chip and variants
+ */
+ {
+ .name = TYPE_PNV8_CHIP,
+ .parent = TYPE_PNV_CHIP,
+ .instance_init = pnv_chip_power8_instance_init,
+ .instance_size = sizeof(Pnv8Chip),
+ },
+ DEFINE_PNV8_CHIP_TYPE(TYPE_PNV_CHIP_POWER8, pnv_chip_power8_class_init),
+ DEFINE_PNV8_CHIP_TYPE(TYPE_PNV_CHIP_POWER8E, pnv_chip_power8e_class_init),
+ DEFINE_PNV8_CHIP_TYPE(TYPE_PNV_CHIP_POWER8NVL,
+ pnv_chip_power8nvl_class_init),
};
DEFINE_TYPES(types)
.endianness = DEVICE_BIG_ENDIAN,
};
-static void pnv_realize_vcpu(PowerPCCPU *cpu, XICSFabric *xi, Error **errp)
+static void pnv_realize_vcpu(PowerPCCPU *cpu, PnvChip *chip, Error **errp)
{
CPUPPCState *env = &cpu->env;
int core_pir;
int thread_index = 0; /* TODO: TCG supports only one thread */
ppc_spr_t *pir = &env->spr_cb[SPR_PIR];
Error *local_err = NULL;
+ PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
object_property_set_bool(OBJECT(cpu), true, "realized", &local_err);
if (local_err) {
return;
}
- cpu->intc = icp_create(OBJECT(cpu), TYPE_PNV_ICP, xi, &local_err);
+ cpu->intc = pcc->intc_create(chip, OBJECT(cpu), &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
void *obj;
int i, j;
char name[32];
- Object *xi;
+ Object *chip;
- xi = object_property_get_link(OBJECT(dev), "xics", &local_err);
- if (!xi) {
- error_setg(errp, "%s: required link 'xics' not found: %s",
- __func__, error_get_pretty(local_err));
- return;
+ chip = object_property_get_link(OBJECT(dev), "chip", &local_err);
+ if (!chip) {
+ error_propagate(errp, local_err);
+ error_prepend(errp, "required link 'chip' not found: ");
}
pc->threads = g_new(PowerPCCPU *, cc->nr_threads);
}
for (j = 0; j < cc->nr_threads; j++) {
- pnv_realize_vcpu(pc->threads[j], XICS_FABRIC(xi), &local_err);
+ pnv_realize_vcpu(pc->threads[j], PNV_CHIP(chip), &local_err);
if (local_err) {
goto err;
}
#include "target/ppc/cpu.h"
#include "qapi/error.h"
#include "qemu/log.h"
+#include "hw/isa/isa.h"
#include "hw/ppc/pnv.h"
#include "hw/ppc/pnv_lpc.h"
}
}
-qemu_irq *pnv_lpc_isa_irq_create(PnvLpcController *lpc, int chip_type,
- int nirqs)
+ISABus *pnv_lpc_isa_create(PnvLpcController *lpc, bool use_cpld, Error **errp)
{
+ Error *local_err = NULL;
+ ISABus *isa_bus;
+ qemu_irq *irqs;
+ qemu_irq_handler handler;
+
+ /* let isa_bus_new() create its own bridge on SysBus otherwise
+ * devices speficied on the command line won't find the bus and
+ * will fail to create.
+ */
+ isa_bus = isa_bus_new(NULL, &lpc->isa_mem, &lpc->isa_io, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return NULL;
+ }
+
/* Not all variants have a working serial irq decoder. If not,
* handling of LPC interrupts becomes a platform issue (some
* platforms have a CPLD to do it).
*/
- if (chip_type == PNV_CHIP_POWER8NVL) {
- return qemu_allocate_irqs(pnv_lpc_isa_irq_handler, lpc, nirqs);
+ if (use_cpld) {
+ handler = pnv_lpc_isa_irq_handler_cpld;
} else {
- return qemu_allocate_irqs(pnv_lpc_isa_irq_handler_cpld, lpc, nirqs);
+ handler = pnv_lpc_isa_irq_handler;
}
+
+ irqs = qemu_allocate_irqs(handler, lpc, ISA_NUM_IRQS);
+
+ isa_bus_irqs(isa_bus, irqs);
+ return isa_bus;
}
#include "hw/virtio/vhost-scsi-common.h"
#include "exec/address-spaces.h"
+#include "exec/ram_addr.h"
#include "hw/usb.h"
#include "qemu/config-file.h"
#include "qemu/error-report.h"
void *fdt;
int rc;
- spapr_caps_reset(spapr);
+ spapr_caps_apply(spapr);
first_ppc_cpu = POWERPC_CPU(first_cpu);
if (kvm_enabled() && kvmppc_has_cap_mmu_radix() &&
- ppc_check_compat(first_ppc_cpu, CPU_POWERPC_LOGICAL_3_00, 0,
- spapr->max_compat_pvr)) {
+ ppc_type_check_compat(machine->cpu_type, CPU_POWERPC_LOGICAL_3_00, 0,
+ spapr->max_compat_pvr)) {
/* If using KVM with radix mode available, VCPUs can be started
* without a HPT because KVM will start them in radix mode.
* Set the GR bit in PATB so that we know there is no HPT. */
long load_limit, fw_size;
char *filename;
Error *resize_hpt_err = NULL;
- PowerPCCPU *first_ppc_cpu;
msi_nonbroken = true;
QLIST_INIT(&spapr->phbs);
QTAILQ_INIT(&spapr->pending_dimm_unplugs);
- /* Check HPT resizing availability */
+ /* Determine capabilities to run with */
+ spapr_caps_init(spapr);
+
kvmppc_check_papr_resize_hpt(&resize_hpt_err);
if (spapr->resize_hpt == SPAPR_RESIZE_HPT_DEFAULT) {
/*
/* init CPUs */
spapr_init_cpus(spapr);
- first_ppc_cpu = POWERPC_CPU(first_cpu);
if ((!kvm_enabled() || kvmppc_has_cap_mmu_radix()) &&
- ppc_check_compat(first_ppc_cpu, CPU_POWERPC_LOGICAL_3_00, 0,
- spapr->max_compat_pvr)) {
+ ppc_type_check_compat(machine->cpu_type, CPU_POWERPC_LOGICAL_3_00, 0,
+ spapr->max_compat_pvr)) {
/* KVM and TCG always allow GTSE with radix... */
spapr_ovec_set(spapr->ov5, OV5_MMU_RADIX_GTSE);
}
Error **errp)
{
const sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(hotplug_dev);
+ sPAPRMachineState *spapr = SPAPR_MACHINE(hotplug_dev);
PCDIMMDevice *dimm = PC_DIMM(dev);
PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
MemoryRegion *mr;
uint64_t size;
- char *mem_dev;
+ Object *memdev;
+ hwaddr pagesize;
if (!smc->dr_lmb_enabled) {
error_setg(errp, "Memory hotplug not supported for this machine");
return;
}
- mem_dev = object_property_get_str(OBJECT(dimm), PC_DIMM_MEMDEV_PROP, NULL);
- if (mem_dev && !kvmppc_is_mem_backend_page_size_ok(mem_dev)) {
- error_setg(errp, "Memory backend has bad page size. "
- "Use 'memory-backend-file' with correct mem-path.");
- goto out;
- }
-
-out:
- g_free(mem_dev);
+ memdev = object_property_get_link(OBJECT(dimm), PC_DIMM_MEMDEV_PROP,
+ &error_abort);
+ pagesize = host_memory_backend_pagesize(MEMORY_BACKEND(memdev));
+ spapr_check_pagesize(spapr, pagesize, errp);
}
struct sPAPRDIMMState {
return -1;
}
-/*
- * Allocate the IRQ number and set the IRQ type, LSI or MSI
- */
-static void spapr_irq_set_lsi(sPAPRMachineState *spapr, int irq, bool lsi)
-{
- ics_set_irq_type(spapr->ics, irq - spapr->ics->offset, lsi);
-}
-
-int spapr_irq_alloc(sPAPRMachineState *spapr, int irq_hint, bool lsi,
- Error **errp)
-{
- ICSState *ics = spapr->ics;
- int irq;
-
- assert(ics);
-
- if (irq_hint) {
- if (!ICS_IRQ_FREE(ics, irq_hint - ics->offset)) {
- error_setg(errp, "can't allocate IRQ %d: already in use", irq_hint);
- return -1;
- }
- irq = irq_hint;
- } else {
- irq = ics_find_free_block(ics, 1, 1);
- if (irq < 0) {
- error_setg(errp, "can't allocate IRQ: no IRQ left");
- return -1;
- }
- irq += ics->offset;
- }
-
- spapr_irq_set_lsi(spapr, irq, lsi);
- trace_spapr_irq_alloc(irq);
-
- return irq;
-}
-
-/*
- * Allocate block of consecutive IRQs, and return the number of the first IRQ in
- * the block. If align==true, aligns the first IRQ number to num.
- */
-int spapr_irq_alloc_block(sPAPRMachineState *spapr, int num, bool lsi,
- bool align, Error **errp)
+int spapr_irq_find(sPAPRMachineState *spapr, int num, bool align, Error **errp)
{
ICSState *ics = spapr->ics;
- int i, first = -1;
+ int first = -1;
assert(ics);
} else {
first = ics_find_free_block(ics, num, 1);
}
+
if (first < 0) {
error_setg(errp, "can't find a free %d-IRQ block", num);
return -1;
}
- first += ics->offset;
- for (i = first; i < first + num; ++i) {
- spapr_irq_set_lsi(spapr, i, lsi);
+ return first + ics->offset;
+}
+
+int spapr_irq_claim(sPAPRMachineState *spapr, int irq, bool lsi, Error **errp)
+{
+ ICSState *ics = spapr->ics;
+
+ assert(ics);
+
+ if (!ics_valid_irq(ics, irq)) {
+ error_setg(errp, "IRQ %d is invalid", irq);
+ return -1;
}
- trace_spapr_irq_alloc_block(first, num, lsi, align);
+ if (!ICS_IRQ_FREE(ics, irq - ics->offset)) {
+ error_setg(errp, "IRQ %d is not free", irq);
+ return -1;
+ }
- return first;
+ ics_set_irq_type(ics, irq - ics->offset, lsi);
+ return 0;
}
void spapr_irq_free(sPAPRMachineState *spapr, int irq, int num)
smc->default_caps.caps[SPAPR_CAP_CFPC] = SPAPR_CAP_BROKEN;
smc->default_caps.caps[SPAPR_CAP_SBBC] = SPAPR_CAP_BROKEN;
smc->default_caps.caps[SPAPR_CAP_IBS] = SPAPR_CAP_BROKEN;
+ smc->default_caps.caps[SPAPR_CAP_HPT_MAXPAGESIZE] = 16; /* 64kiB */
spapr_caps_add_properties(smc, &error_abort);
}
HW_COMPAT_2_12 \
{ \
.driver = TYPE_POWERPC_CPU, \
- .property = "pre-3.0-migration", \
+ .property = "pre-3.0-migration", \
+ .value = "on", \
+ }, \
+ { \
+ .driver = TYPE_SPAPR_CPU_CORE, \
+ .property = "pre-3.0-migration", \
.value = "on", \
},
static void spapr_machine_2_12_class_options(MachineClass *mc)
{
+ sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc);
+ uint8_t mps;
+
spapr_machine_3_0_class_options(mc);
SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_12);
+
+ if (kvmppc_hpt_needs_host_contiguous_pages()) {
+ mps = ctz64(qemu_getrampagesize());
+ } else {
+ mps = 34; /* allow everything up to 16GiB, i.e. everything */
+ }
+ smc->default_caps.caps[SPAPR_CAP_HPT_MAXPAGESIZE] = mps;
}
DEFINE_SPAPR_MACHINE(2_12, "2.12", false);
#include "qapi/error.h"
#include "qapi/visitor.h"
#include "sysemu/hw_accel.h"
+#include "exec/ram_addr.h"
#include "target/ppc/cpu.h"
+#include "target/ppc/mmu-hash64.h"
#include "cpu-models.h"
#include "kvm_ppc.h"
sPAPRCapPossible *possible;
/* Make sure the virtual hardware can support this capability */
void (*apply)(sPAPRMachineState *spapr, uint8_t val, Error **errp);
+ void (*cpu_apply)(sPAPRMachineState *spapr, PowerPCCPU *cpu,
+ uint8_t val, Error **errp);
} sPAPRCapabilityInfo;
static void spapr_cap_get_bool(Object *obj, Visitor *v, const char *name,
g_free(val);
}
+static void spapr_cap_get_pagesize(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ sPAPRCapabilityInfo *cap = opaque;
+ sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
+ uint8_t val = spapr_get_cap(spapr, cap->index);
+ uint64_t pagesize = (1ULL << val);
+
+ visit_type_size(v, name, &pagesize, errp);
+}
+
+static void spapr_cap_set_pagesize(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ sPAPRCapabilityInfo *cap = opaque;
+ sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
+ uint64_t pagesize;
+ uint8_t val;
+ Error *local_err = NULL;
+
+ visit_type_size(v, name, &pagesize, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ if (!is_power_of_2(pagesize)) {
+ error_setg(errp, "cap-%s must be a power of 2", cap->name);
+ return;
+ }
+
+ val = ctz64(pagesize);
+ spapr->cmd_line_caps[cap->index] = true;
+ spapr->eff.caps[cap->index] = val;
+}
+
static void cap_htm_apply(sPAPRMachineState *spapr, uint8_t val, Error **errp)
{
if (!val) {
#define VALUE_DESC_TRISTATE " (broken, workaround, fixed)"
+void spapr_check_pagesize(sPAPRMachineState *spapr, hwaddr pagesize,
+ Error **errp)
+{
+ hwaddr maxpagesize = (1ULL << spapr->eff.caps[SPAPR_CAP_HPT_MAXPAGESIZE]);
+
+ if (!kvmppc_hpt_needs_host_contiguous_pages()) {
+ return;
+ }
+
+ if (maxpagesize > pagesize) {
+ error_setg(errp,
+ "Can't support %"HWADDR_PRIu" kiB guest pages with %"
+ HWADDR_PRIu" kiB host pages with this KVM implementation",
+ maxpagesize >> 10, pagesize >> 10);
+ }
+}
+
+static void cap_hpt_maxpagesize_apply(sPAPRMachineState *spapr,
+ uint8_t val, Error **errp)
+{
+ if (val < 12) {
+ error_setg(errp, "Require at least 4kiB hpt-max-page-size");
+ return;
+ } else if (val < 16) {
+ warn_report("Many guests require at least 64kiB hpt-max-page-size");
+ }
+
+ spapr_check_pagesize(spapr, qemu_getrampagesize(), errp);
+}
+
+static bool spapr_pagesize_cb(void *opaque, uint32_t seg_pshift,
+ uint32_t pshift)
+{
+ unsigned maxshift = *((unsigned *)opaque);
+
+ assert(pshift >= seg_pshift);
+
+ /* Don't allow the guest to use pages bigger than the configured
+ * maximum size */
+ if (pshift > maxshift) {
+ return false;
+ }
+
+ /* For whatever reason, KVM doesn't allow multiple pagesizes
+ * within a segment, *except* for the case of 16M pages in a 4k or
+ * 64k segment. Always exclude other cases, so that TCG and KVM
+ * guests see a consistent environment */
+ if ((pshift != seg_pshift) && (pshift != 24)) {
+ return false;
+ }
+
+ return true;
+}
+
+static void cap_hpt_maxpagesize_cpu_apply(sPAPRMachineState *spapr,
+ PowerPCCPU *cpu,
+ uint8_t val, Error **errp)
+{
+ unsigned maxshift = val;
+
+ ppc_hash64_filter_pagesizes(cpu, spapr_pagesize_cb, &maxshift);
+}
+
sPAPRCapabilityInfo capability_table[SPAPR_CAP_NUM] = {
[SPAPR_CAP_HTM] = {
.name = "htm",
.possible = &cap_ibs_possible,
.apply = cap_safe_indirect_branch_apply,
},
+ [SPAPR_CAP_HPT_MAXPAGESIZE] = {
+ .name = "hpt-max-page-size",
+ .description = "Maximum page size for Hash Page Table guests",
+ .index = SPAPR_CAP_HPT_MAXPAGESIZE,
+ .get = spapr_cap_get_pagesize,
+ .set = spapr_cap_set_pagesize,
+ .type = "int",
+ .apply = cap_hpt_maxpagesize_apply,
+ .cpu_apply = cap_hpt_maxpagesize_cpu_apply,
+ },
};
static sPAPRCapabilities default_caps_with_cpu(sPAPRMachineState *spapr,
- CPUState *cs)
+ const char *cputype)
{
sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
- PowerPCCPU *cpu = POWERPC_CPU(cs);
sPAPRCapabilities caps;
caps = smc->default_caps;
- if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_2_07,
- 0, spapr->max_compat_pvr)) {
+ if (!ppc_type_check_compat(cputype, CPU_POWERPC_LOGICAL_2_07,
+ 0, spapr->max_compat_pvr)) {
caps.caps[SPAPR_CAP_HTM] = SPAPR_CAP_OFF;
caps.caps[SPAPR_CAP_CFPC] = SPAPR_CAP_BROKEN;
}
- if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_2_06_PLUS,
- 0, spapr->max_compat_pvr)) {
+ if (!ppc_type_check_compat(cputype, CPU_POWERPC_LOGICAL_2_06_PLUS,
+ 0, spapr->max_compat_pvr)) {
caps.caps[SPAPR_CAP_SBBC] = SPAPR_CAP_BROKEN;
}
- if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_2_06,
- 0, spapr->max_compat_pvr)) {
+ if (!ppc_type_check_compat(cputype, CPU_POWERPC_LOGICAL_2_06,
+ 0, spapr->max_compat_pvr)) {
caps.caps[SPAPR_CAP_VSX] = SPAPR_CAP_OFF;
caps.caps[SPAPR_CAP_DFP] = SPAPR_CAP_OFF;
caps.caps[SPAPR_CAP_IBS] = SPAPR_CAP_BROKEN;
sPAPRCapabilities dstcaps = spapr->eff;
sPAPRCapabilities srccaps;
- srccaps = default_caps_with_cpu(spapr, first_cpu);
+ srccaps = default_caps_with_cpu(spapr, MACHINE(spapr)->cpu_type);
for (i = 0; i < SPAPR_CAP_NUM; i++) {
/* If not default value then assume came in with the migration */
if (spapr->mig.caps[i] != spapr->def.caps[i]) {
SPAPR_CAP_MIG_STATE(sbbc, SPAPR_CAP_SBBC);
SPAPR_CAP_MIG_STATE(ibs, SPAPR_CAP_IBS);
-void spapr_caps_reset(sPAPRMachineState *spapr)
+void spapr_caps_init(sPAPRMachineState *spapr)
{
sPAPRCapabilities default_caps;
int i;
- /* First compute the actual set of caps we're running with.. */
- default_caps = default_caps_with_cpu(spapr, first_cpu);
+ /* Compute the actual set of caps we should run with */
+ default_caps = default_caps_with_cpu(spapr, MACHINE(spapr)->cpu_type);
for (i = 0; i < SPAPR_CAP_NUM; i++) {
/* Store the defaults */
spapr->eff.caps[i] = default_caps.caps[i];
}
}
+}
- /* .. then apply those caps to the virtual hardware */
+void spapr_caps_apply(sPAPRMachineState *spapr)
+{
+ int i;
for (i = 0; i < SPAPR_CAP_NUM; i++) {
sPAPRCapabilityInfo *info = &capability_table[i];
}
}
+void spapr_caps_cpu_apply(sPAPRMachineState *spapr, PowerPCCPU *cpu)
+{
+ int i;
+
+ for (i = 0; i < SPAPR_CAP_NUM; i++) {
+ sPAPRCapabilityInfo *info = &capability_table[i];
+
+ /*
+ * If the apply function can't set the desired level and thinks it's
+ * fatal, it should cause that.
+ */
+ if (info->cpu_apply) {
+ info->cpu_apply(spapr, cpu, spapr->eff.caps[i], &error_fatal);
+ }
+ }
+}
+
void spapr_caps_add_properties(sPAPRMachineClass *smc, Error **errp)
{
Error *local_err = NULL;
spapr_cpu->slb_shadow_size = 0;
spapr_cpu->dtl_addr = 0;
spapr_cpu->dtl_size = 0;
+
+ spapr_caps_cpu_apply(SPAPR_MACHINE(qdev_get_machine()), cpu);
+
+ kvm_check_mmu(cpu, &error_fatal);
}
void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, target_ulong r3)
g_free(sc->threads);
}
+static bool slb_shadow_needed(void *opaque)
+{
+ sPAPRCPUState *spapr_cpu = opaque;
+
+ return spapr_cpu->slb_shadow_addr != 0;
+}
+
+static const VMStateDescription vmstate_spapr_cpu_slb_shadow = {
+ .name = "spapr_cpu/vpa/slb_shadow",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = slb_shadow_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(slb_shadow_addr, sPAPRCPUState),
+ VMSTATE_UINT64(slb_shadow_size, sPAPRCPUState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static bool dtl_needed(void *opaque)
+{
+ sPAPRCPUState *spapr_cpu = opaque;
+
+ return spapr_cpu->dtl_addr != 0;
+}
+
+static const VMStateDescription vmstate_spapr_cpu_dtl = {
+ .name = "spapr_cpu/vpa/dtl",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = dtl_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(dtl_addr, sPAPRCPUState),
+ VMSTATE_UINT64(dtl_size, sPAPRCPUState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static bool vpa_needed(void *opaque)
+{
+ sPAPRCPUState *spapr_cpu = opaque;
+
+ return spapr_cpu->vpa_addr != 0;
+}
+
+static const VMStateDescription vmstate_spapr_cpu_vpa = {
+ .name = "spapr_cpu/vpa",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = vpa_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(vpa_addr, sPAPRCPUState),
+ VMSTATE_END_OF_LIST()
+ },
+ .subsections = (const VMStateDescription * []) {
+ &vmstate_spapr_cpu_slb_shadow,
+ &vmstate_spapr_cpu_dtl,
+ NULL
+ }
+};
+
+static const VMStateDescription vmstate_spapr_cpu_state = {
+ .name = "spapr_cpu",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_END_OF_LIST()
+ },
+ .subsections = (const VMStateDescription * []) {
+ &vmstate_spapr_cpu_vpa,
+ NULL
+ }
+};
+
static void spapr_realize_vcpu(PowerPCCPU *cpu, sPAPRMachineState *spapr,
Error **errp)
{
}
cpu->machine_data = g_new0(sPAPRCPUState, 1);
+ if (!sc->pre_3_0_migration) {
+ vmstate_register(NULL, cs->cpu_index, &vmstate_spapr_cpu_state,
+ cpu->machine_data);
+ }
object_unref(obj);
return cpu;
return NULL;
}
-static void spapr_delete_vcpu(PowerPCCPU *cpu)
+static void spapr_delete_vcpu(PowerPCCPU *cpu, sPAPRCPUCore *sc)
{
sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu);
+ if (!sc->pre_3_0_migration) {
+ vmstate_unregister(NULL, &vmstate_spapr_cpu_state, cpu->machine_data);
+ }
cpu->machine_data = NULL;
g_free(spapr_cpu);
object_unparent(OBJECT(cpu));
}
err:
while (--i >= 0) {
- spapr_delete_vcpu(sc->threads[i]);
+ spapr_delete_vcpu(sc->threads[i], sc);
}
g_free(sc->threads);
error_propagate(errp, local_err);
static Property spapr_cpu_core_properties[] = {
DEFINE_PROP_INT32("node-id", sPAPRCPUCore, node_id, CPU_UNSET_NUMA_NODE_ID),
+ DEFINE_PROP_BOOL("pre-3.0-migration", sPAPRCPUCore, pre_3_0_migration,
+ false),
DEFINE_PROP_END_OF_LIST()
};
void spapr_events_init(sPAPRMachineState *spapr)
{
+ int epow_irq;
+
+ epow_irq = spapr_irq_findone(spapr, &error_fatal);
+
+ spapr_irq_claim(spapr, epow_irq, false, &error_fatal);
+
QTAILQ_INIT(&spapr->pending_events);
spapr->event_sources = spapr_event_sources_new();
spapr_event_sources_register(spapr->event_sources, EVENT_CLASS_EPOW,
- spapr_irq_alloc(spapr, 0, false,
- &error_fatal));
+ epow_irq);
/* NOTE: if machine supports modern/dedicated hotplug event source,
* we add it to the device-tree unconditionally. This means we may
* checking that it's enabled.
*/
if (spapr->use_hotplug_event_source) {
+ int hp_irq;
+
+ hp_irq = spapr_irq_findone(spapr, &error_fatal);
+
+ spapr_irq_claim(spapr, hp_irq, false, &error_fatal);
+
spapr_event_sources_register(spapr->event_sources, EVENT_CLASS_HOT_PLUG,
- spapr_irq_alloc(spapr, 0, false,
- &error_fatal));
+ hp_irq);
}
spapr->epow_notifier.notify = spapr_powerdown_req;
spapr_pci_msi *msi;
int *config_addr_key;
Error *err = NULL;
+ int i;
/* Fins sPAPRPHBState */
phb = spapr_pci_find_phb(spapr, buid);
}
/* Allocate MSIs */
- irq = spapr_irq_alloc_block(spapr, req_num, false,
- ret_intr_type == RTAS_TYPE_MSI, &err);
+ irq = spapr_irq_find(spapr, req_num, ret_intr_type == RTAS_TYPE_MSI, &err);
if (err) {
error_reportf_err(err, "Can't allocate MSIs for device %x: ",
config_addr);
return;
}
+ for (i = 0; i < req_num; i++) {
+ spapr_irq_claim(spapr, irq + i, false, &err);
+ if (err) {
+ error_reportf_err(err, "Can't allocate MSIs for device %x: ",
+ config_addr);
+ rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
+ return;
+ }
+ }
+
/* Release previous MSIs */
if (msi) {
spapr_irq_free(spapr, msi->first_irq, msi->num);
uint32_t irq;
Error *local_err = NULL;
- irq = spapr_irq_alloc_block(spapr, 1, true, false, &local_err);
+ irq = spapr_irq_findone(spapr, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ error_prepend(errp, "can't allocate LSIs: ");
+ return;
+ }
+
+ spapr_irq_claim(spapr, irq, true, &local_err);
if (local_err) {
error_propagate(errp, local_err);
error_prepend(errp, "can't allocate LSIs: ");
dev->qdev.id = id;
}
- dev->irq = spapr_irq_alloc(spapr, dev->irq, false, &local_err);
+ if (!dev->irq) {
+ dev->irq = spapr_irq_findone(spapr, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ }
+
+ spapr_irq_claim(spapr, dev->irq, false, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
extern void *pci_assign_dev_load_option_rom(PCIDevice *dev,
- struct Object *owner, int *size,
+ int *size,
unsigned int domain,
unsigned int bus, unsigned int slot,
unsigned int function);
static void *get_vgabios(XenPCIPassthroughState *s, int *size,
XenHostPCIDevice *dev)
{
- return pci_assign_dev_load_option_rom(&s->dev, OBJECT(&s->dev), size,
+ return pci_assign_dev_load_option_rom(&s->dev, size,
dev->domain, dev->bus,
dev->dev, dev->func);
}
* load the corresponding ROM data to RAM. If an error occurs while loading an
* option ROM, we just ignore that option ROM and continue with the next one.
*/
-void *pci_assign_dev_load_option_rom(PCIDevice *dev, struct Object *owner,
+void *pci_assign_dev_load_option_rom(PCIDevice *dev,
int *size, unsigned int domain,
unsigned int bus, unsigned int slot,
unsigned int function)
uint8_t val;
struct stat st;
void *ptr = NULL;
+ Object *owner = OBJECT(dev);
/* If loading ROM from file, pci handles it */
if (dev->romfile || !dev->rom_bar) {
fseek(fp, 0, SEEK_SET);
snprintf(name, sizeof(name), "%s.rom", object_get_typename(owner));
- memory_region_init_ram_nomigrate(&dev->rom, owner, name, st.st_size, &error_abort);
- vmstate_register_ram(&dev->rom, &dev->qdev);
+ memory_region_init_ram(&dev->rom, owner, name, st.st_size, &error_abort);
ptr = memory_region_get_ram_ptr(&dev->rom);
memset(ptr, 0xff, st.st_size);
* + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_enable
* + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_clear
* + named GPIO inputs ahb_ppcexp{0,1,2,3}_irq_status
+ * Controlling each of the 16 expansion MPCs which a system using the IoTKit
+ * might provide:
+ * + named GPIO inputs mpcexp_status[0..15]
*/
#ifndef IOTKIT_H
#include "hw/arm/armv7m.h"
#include "hw/misc/iotkit-secctl.h"
#include "hw/misc/tz-ppc.h"
+#include "hw/misc/tz-mpc.h"
#include "hw/timer/cmsdk-apb-timer.h"
#include "hw/misc/unimp.h"
#include "hw/or-irq.h"
IoTKitSecCtl secctl;
TZPPC apb_ppc0;
TZPPC apb_ppc1;
+ TZMPC mpc;
CMSDKAPBTIMER timer0;
CMSDKAPBTIMER timer1;
qemu_or_irq ppc_irq_orgate;
SplitIRQ sec_resp_splitter;
SplitIRQ ppc_irq_splitter[NUM_PPCS];
+ SplitIRQ mpc_irq_splitter[IOTS_NUM_EXP_MPC + IOTS_NUM_MPC];
+ qemu_or_irq mpc_irq_orgate;
UnimplementedDeviceState dualtimer;
UnimplementedDeviceState s32ktimer;
qemu_irq nsc_cfg_in;
qemu_irq irq_status_in[NUM_EXTERNAL_PPCS];
+ qemu_irq mpcexp_status_in[IOTS_NUM_EXP_MPC];
uint32_t nsccfg;
#include "qemu/notify.h"
#include "hw/boards.h"
#include "hw/arm/arm.h"
+#include "sysemu/kvm.h"
+#include "hw/intc/arm_gicv3_common.h"
#define NUM_GICV2M_SPIS 64
#define NUM_VIRTIO_TRANSPORTS 32
VIRT_GIC_V2M,
VIRT_GIC_ITS,
VIRT_GIC_REDIST,
+ VIRT_GIC_REDIST2,
VIRT_SMMU,
VIRT_UART,
VIRT_MMIO,
VIRT_PCIE_MMIO,
VIRT_PCIE_PIO,
VIRT_PCIE_ECAM,
+ VIRT_PCIE_ECAM_HIGH,
VIRT_PLATFORM_BUS,
VIRT_PCIE_MMIO_HIGH,
VIRT_GPIO,
bool no_pmu;
bool claim_edge_triggered_timers;
bool smbios_old_sys_ver;
+ bool no_highmem_ecam;
} VirtMachineClass;
typedef struct {
FWCfgState *fw_cfg;
bool secure;
bool highmem;
+ bool highmem_ecam;
bool its;
bool virt;
int32_t gic_version;
int psci_conduit;
} VirtMachineState;
+#define VIRT_ECAM_ID(high) (high ? VIRT_PCIE_ECAM_HIGH : VIRT_PCIE_ECAM)
+
#define TYPE_VIRT_MACHINE MACHINE_TYPE_NAME("virt")
#define VIRT_MACHINE(obj) \
OBJECT_CHECK(VirtMachineState, (obj), TYPE_VIRT_MACHINE)
void virt_acpi_setup(VirtMachineState *vms);
+/* Return the number of used redistributor regions */
+static inline int virt_gicv3_redist_region_count(VirtMachineState *vms)
+{
+ uint32_t redist0_capacity =
+ vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE;
+
+ assert(vms->gic_version == 3);
+
+ return vms->smp_cpus > redist0_capacity ? 2 : 1;
+}
+
#endif /* QEMU_ARM_VIRT_H */
*
* Copyright (c) 2007 Jocelyn Mayer
* Copyright (c) 2012 François Revol
- * Copyright (c) 2016 BALATON Zoltan
+ * Copyright (c) 2016-2018 BALATON Zoltan
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
#include "hw/sysbus.h"
#include "hw/i2c/i2c.h"
+/* from hw/i2c/bitbang_i2c.h */
+typedef struct bitbang_i2c_interface bitbang_i2c_interface;
+
#define TYPE_PPC4xx_I2C "ppc4xx-i2c"
#define PPC4xx_I2C(obj) OBJECT_CHECK(PPC4xxI2CState, (obj), TYPE_PPC4xx_I2C)
I2CBus *bus;
qemu_irq irq;
MemoryRegion iomem;
+ bitbang_i2c_interface *bitbang;
uint8_t mdata;
uint8_t lmadr;
uint8_t hmadr;
uint8_t mdcntl;
uint8_t sts;
uint8_t extsts;
- uint8_t sdata;
uint8_t lsadr;
uint8_t hsadr;
uint8_t clkdiv;
uint8_t xfrcnt;
uint8_t xtcntlss;
uint8_t directcntl;
- uint8_t intr;
} PPC4xxI2CState;
#endif /* PPC4XX_I2C_H */
#define GICV3_MAXIRQ 1020
#define GICV3_MAXSPI (GICV3_MAXIRQ - GIC_INTERNAL)
+#define GICV3_REDIST_SIZE 0x20000
+
/* Number of SGI target-list bits */
#define GICV3_TARGETLIST_BITS 16
/*< public >*/
MemoryRegion iomem_dist; /* Distributor */
- MemoryRegion iomem_redist; /* Redistributors */
+ MemoryRegion *iomem_redist; /* Redistributor Regions */
+ uint32_t *redist_region_count; /* redistributor count within each region */
+ uint32_t nb_redist_regions; /* number of redist regions */
uint32_t num_cpu;
uint32_t num_irq;
} ARMGICv3CommonClass;
void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler,
- const MemoryRegionOps *ops);
+ const MemoryRegionOps *ops, Error **errp);
#endif
* + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_enable
* + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_clear
* + named GPIO inputs ahb_ppcexp{0,1,2,3}_irq_status
+ * Controlling the MPC in the IoTKit:
+ * + named GPIO input mpc_status
+ * Controlling each of the 16 expansion MPCs which a system using the IoTKit
+ * might provide:
+ * + named GPIO inputs mpcexp_status[0..15]
*/
#ifndef IOTKIT_SECCTL_H
#define IOTS_NUM_APB_PPC 2
#define IOTS_NUM_APB_EXP_PPC 4
#define IOTS_NUM_AHB_EXP_PPC 4
+#define IOTS_NUM_EXP_MPC 16
+#define IOTS_NUM_MPC 1
typedef struct IoTKitSecCtl IoTKitSecCtl;
uint32_t secrespcfg;
uint32_t nsccfg;
uint32_t brginten;
+ uint32_t mpcintstatus;
IoTKitSecCtlPPC apb[IOTS_NUM_APB_PPC];
IoTKitSecCtlPPC apbexp[IOTS_NUM_APB_EXP_PPC];
--- /dev/null
+/*
+ * ARM AHB5 TrustZone Memory Protection Controller emulation
+ *
+ * Copyright (c) 2018 Linaro Limited
+ * Written by Peter Maydell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 or
+ * (at your option) any later version.
+ */
+
+/* This is a model of the TrustZone memory protection controller (MPC).
+ * It is documented in the ARM CoreLink SIE-200 System IP for Embedded TRM
+ * (DDI 0571G):
+ * https://developer.arm.com/products/architecture/m-profile/docs/ddi0571/g
+ *
+ * The MPC sits in front of memory and allows secure software to
+ * configure it to either pass through or reject transactions.
+ * Rejected transactions may be configured to either be aborted, or to
+ * behave as RAZ/WI. An interrupt can be signalled for a rejected transaction.
+ *
+ * The MPC has a register interface which the guest uses to configure it.
+ *
+ * QEMU interface:
+ * + sysbus MMIO region 0: MemoryRegion for the MPC's config registers
+ * + sysbus MMIO region 1: MemoryRegion for the upstream end of the MPC
+ * + Property "downstream": MemoryRegion defining the downstream memory
+ * + Named GPIO output "irq": set for a transaction-failed interrupt
+ */
+
+#ifndef TZ_MPC_H
+#define TZ_MPC_H
+
+#include "hw/sysbus.h"
+
+#define TYPE_TZ_MPC "tz-mpc"
+#define TZ_MPC(obj) OBJECT_CHECK(TZMPC, (obj), TYPE_TZ_MPC)
+
+#define TZ_NUM_PORTS 16
+
+#define TYPE_TZ_MPC_IOMMU_MEMORY_REGION "tz-mpc-iommu-memory-region"
+
+typedef struct TZMPC TZMPC;
+
+struct TZMPC {
+ /*< private >*/
+ SysBusDevice parent_obj;
+
+ /*< public >*/
+
+ /* State */
+ uint32_t ctrl;
+ uint32_t blk_idx;
+ uint32_t int_stat;
+ uint32_t int_en;
+ uint32_t int_info1;
+ uint32_t int_info2;
+
+ uint32_t *blk_lut;
+
+ qemu_irq irq;
+
+ /* Properties */
+ MemoryRegion *downstream;
+
+ hwaddr blocksize;
+ uint32_t blk_max;
+
+ /* MemoryRegions exposed to user */
+ MemoryRegion regmr;
+ IOMMUMemoryRegion upstream;
+
+ /* MemoryRegion used internally */
+ MemoryRegion blocked_io;
+
+ AddressSpace downstream_as;
+ AddressSpace blocked_io_as;
+};
+
+#endif
MemoryRegion xscom_mmio;
MemoryRegion xscom;
AddressSpace xscom_as;
+} PnvChip;
+
+#define TYPE_PNV8_CHIP "pnv8-chip"
+#define PNV8_CHIP(obj) OBJECT_CHECK(Pnv8Chip, (obj), TYPE_PNV8_CHIP)
+
+typedef struct Pnv8Chip {
+ /*< private >*/
+ PnvChip parent_obj;
+
+ /*< public >*/
MemoryRegion icp_mmio;
PnvLpcController lpc;
PnvPsi psi;
PnvOCC occ;
-} PnvChip;
+} Pnv8Chip;
+
+#define TYPE_PNV9_CHIP "pnv9-chip"
+#define PNV9_CHIP(obj) OBJECT_CHECK(Pnv9Chip, (obj), TYPE_PNV9_CHIP)
+
+typedef struct Pnv9Chip {
+ /*< private >*/
+ PnvChip parent_obj;
+
+ /*< public >*/
+} Pnv9Chip;
typedef struct PnvChipClass {
/*< private >*/
hwaddr xscom_base;
+ DeviceRealize parent_realize;
+
uint32_t (*core_pir)(PnvChip *chip, uint32_t core_id);
+ Object *(*intc_create)(PnvChip *chip, Object *child, Error **errp);
+ ISABus *(*isa_create)(PnvChip *chip, Error **errp);
} PnvChipClass;
#define PNV_CHIP_TYPE_SUFFIX "-" TYPE_PNV_CHIP
PnvPsi *psi;
} PnvLpcController;
-qemu_irq *pnv_lpc_isa_irq_create(PnvLpcController *lpc, int chip_type,
- int nirqs);
+ISABus *pnv_lpc_isa_create(PnvLpcController *lpc, bool use_cpld, Error **errp);
#endif /* _PPC_PNV_LPC_H */
#define SPAPR_CAP_SBBC 0x04
/* Indirect Branch Serialisation */
#define SPAPR_CAP_IBS 0x05
+/* HPT Maximum Page Size (encoded as a shift) */
+#define SPAPR_CAP_HPT_MAXPAGESIZE 0x06
/* Num Caps */
-#define SPAPR_CAP_NUM (SPAPR_CAP_IBS + 1)
+#define SPAPR_CAP_NUM (SPAPR_CAP_HPT_MAXPAGESIZE + 1)
/*
* Capability Values
void spapr_set_vcpu_id(PowerPCCPU *cpu, int cpu_index, Error **errp);
PowerPCCPU *spapr_find_cpu(int vcpu_id);
-int spapr_irq_alloc(sPAPRMachineState *spapr, int irq_hint, bool lsi,
- Error **errp);
-int spapr_irq_alloc_block(sPAPRMachineState *spapr, int num, bool lsi,
- bool align, Error **errp);
+int spapr_irq_find(sPAPRMachineState *spapr, int num, bool align,
+ Error **errp);
+#define spapr_irq_findone(spapr, errp) spapr_irq_find(spapr, 1, false, errp)
+int spapr_irq_claim(sPAPRMachineState *spapr, int irq, bool lsi, Error **errp);
void spapr_irq_free(sPAPRMachineState *spapr, int irq, int num);
qemu_irq spapr_qirq(sPAPRMachineState *spapr, int irq);
return spapr->eff.caps[cap];
}
-void spapr_caps_reset(sPAPRMachineState *spapr);
+void spapr_caps_init(sPAPRMachineState *spapr);
+void spapr_caps_apply(sPAPRMachineState *spapr);
+void spapr_caps_cpu_apply(sPAPRMachineState *spapr, PowerPCCPU *cpu);
void spapr_caps_add_properties(sPAPRMachineClass *smc, Error **errp);
int spapr_caps_post_migration(sPAPRMachineState *spapr);
+void spapr_check_pagesize(sPAPRMachineState *spapr, hwaddr pagesize,
+ Error **errp);
+
#endif /* HW_SPAPR_H */
/*< public >*/
PowerPCCPU **threads;
int node_id;
+ bool pre_3_0_migration; /* older machine don't know about sPAPRCPUState */
} sPAPRCPUCore;
typedef struct sPAPRCPUCoreClass {
#define PCI_EXP_DEVCTL_READRQ_256B 0x1000 /* 256 Bytes */
#define PCI_EXP_DEVCTL_READRQ_512B 0x2000 /* 512 Bytes */
#define PCI_EXP_DEVCTL_READRQ_1024B 0x3000 /* 1024 Bytes */
+#define PCI_EXP_DEVCTL_READRQ_2048B 0x4000 /* 2048 Bytes */
+#define PCI_EXP_DEVCTL_READRQ_4096B 0x5000 /* 4096 Bytes */
#define PCI_EXP_DEVCTL_BCR_FLR 0x8000 /* Bridge Configuration Retry / FLR */
#define PCI_EXP_DEVSTA 10 /* Device Status */
#define PCI_EXP_DEVSTA_CED 0x0001 /* Correctable Error Detected */
#define PCI_EXP_LNKCAP2_SLS_16_0GB 0x00000010 /* Supported Speed 16GT/s */
#define PCI_EXP_LNKCAP2_CROSSLINK 0x00000100 /* Crosslink supported */
#define PCI_EXP_LNKCTL2 48 /* Link Control 2 */
+#define PCI_EXP_LNKCTL2_TLS 0x000f
+#define PCI_EXP_LNKCTL2_TLS_2_5GT 0x0001 /* Supported Speed 2.5GT/s */
+#define PCI_EXP_LNKCTL2_TLS_5_0GT 0x0002 /* Supported Speed 5GT/s */
+#define PCI_EXP_LNKCTL2_TLS_8_0GT 0x0003 /* Supported Speed 8GT/s */
+#define PCI_EXP_LNKCTL2_TLS_16_0GT 0x0004 /* Supported Speed 16GT/s */
#define PCI_EXP_LNKSTA2 50 /* Link Status 2 */
#define PCI_CAP_EXP_ENDPOINT_SIZEOF_V2 52 /* v2 endpoints with link end here */
#define PCI_EXP_SLTCAP2 52 /* Slot Capabilities 2 */
#define PCI_EXP_DPC_CAP_DL_ACTIVE 0x1000 /* ERR_COR signal on DL_Active supported */
#define PCI_EXP_DPC_CTL 6 /* DPC control */
+#define PCI_EXP_DPC_CTL_EN_FATAL 0x0001 /* Enable trigger on ERR_FATAL message */
#define PCI_EXP_DPC_CTL_EN_NONFATAL 0x0002 /* Enable trigger on ERR_NONFATAL message */
#define PCI_EXP_DPC_CTL_INT_EN 0x0008 /* DPC Interrupt Enable */
};
#define VIRTIO_GPU_CAPSET_VIRGL 1
+#define VIRTIO_GPU_CAPSET_VIRGL2 2
/* VIRTIO_GPU_CMD_GET_CAPSET_INFO */
struct virtio_gpu_get_capset_info {
* Steering */
#define VIRTIO_NET_F_CTRL_MAC_ADDR 23 /* Set MAC address */
+#define VIRTIO_NET_F_STANDBY 62 /* Act as standby for another device
+ * with the same MAC.
+ */
#define VIRTIO_NET_F_SPEED_DUPLEX 63 /* Device set linkspeed and duplex */
#ifndef VIRTIO_NET_NO_LEGACY
SPDX-Exception-Identifier: Linux-syscall-note
SPDX-URL: https://spdx.org/licenses/Linux-syscall-note.html
-SPDX-Licenses: GPL-2.0, GPL-2.0+, GPL-1.0+, LGPL-2.0, LGPL-2.0+, LGPL-2.1, LGPL-2.1+
+SPDX-Licenses: GPL-2.0, GPL-2.0+, GPL-1.0+, LGPL-2.0, LGPL-2.0+, LGPL-2.1, LGPL-2.1+, GPL-2.0-only, GPL-2.0-or-later
Usage-Guide:
This exception is used together with one of the above SPDX-Licenses
to mark user space API (uapi) header files so they can be included
Valid-License-Identifier: GPL-2.0
+Valid-License-Identifier: GPL-2.0-only
Valid-License-Identifier: GPL-2.0+
+Valid-License-Identifier: GPL-2.0-or-later
SPDX-URL: https://spdx.org/licenses/GPL-2.0.html
Usage-Guide:
To use this license in source code, put one of the following SPDX
guidelines in the licensing rules documentation.
For 'GNU General Public License (GPL) version 2 only' use:
SPDX-License-Identifier: GPL-2.0
+ or
+ SPDX-License-Identifier: GPL-2.0-only
For 'GNU General Public License (GPL) version 2 or any later version' use:
SPDX-License-Identifier: GPL-2.0+
+ or
+ SPDX-License-Identifier: GPL-2.0-or-later
License-Text:
GNU GENERAL PUBLIC LICENSE
#define KVM_VGIC_V3_ADDR_TYPE_DIST 2
#define KVM_VGIC_V3_ADDR_TYPE_REDIST 3
#define KVM_VGIC_ITS_ADDR_TYPE 4
+#define KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION 5
#define KVM_VGIC_V3_DIST_SIZE SZ_64K
#define KVM_VGIC_V3_REDIST_SIZE (2 * SZ_64K)
#define __NR_pkey_alloc (__NR_SYSCALL_BASE + 395)
#define __NR_pkey_free (__NR_SYSCALL_BASE + 396)
#define __NR_statx (__NR_SYSCALL_BASE + 397)
+#define __NR_rseq (__NR_SYSCALL_BASE + 398)
#endif /* _ASM_ARM_UNISTD_COMMON_H */
#define KVM_VGIC_V3_ADDR_TYPE_DIST 2
#define KVM_VGIC_V3_ADDR_TYPE_REDIST 3
#define KVM_VGIC_ITS_ADDR_TYPE 4
+#define KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION 5
#define KVM_VGIC_V3_DIST_SIZE SZ_64K
#define KVM_VGIC_V3_REDIST_SIZE (2 * SZ_64K)
__SYSCALL(__NR_pkey_free, sys_pkey_free)
#define __NR_statx 291
__SYSCALL(__NR_statx, sys_statx)
+#define __NR_io_pgetevents 292
+__SC_COMP(__NR_io_pgetevents, sys_io_pgetevents, compat_sys_io_pgetevents)
#undef __NR_syscalls
-#define __NR_syscalls 292
+#define __NR_syscalls 293
/*
* 32 bit systems traditionally used different
#define __NR_pkey_alloc 384
#define __NR_pkey_free 385
#define __NR_pkey_mprotect 386
+#define __NR_rseq 387
#endif /* _ASM_POWERPC_UNISTD_H_ */
#define __NR_pkey_free 382
#define __NR_statx 383
#define __NR_arch_prctl 384
+#define __NR_io_pgetevents 385
+#define __NR_rseq 386
#endif /* _ASM_X86_UNISTD_32_H */
#define __NR_pkey_alloc 330
#define __NR_pkey_free 331
#define __NR_statx 332
+#define __NR_io_pgetevents 333
+#define __NR_rseq 334
#endif /* _ASM_X86_UNISTD_64_H */
#define __NR_pkey_alloc (__X32_SYSCALL_BIT + 330)
#define __NR_pkey_free (__X32_SYSCALL_BIT + 331)
#define __NR_statx (__X32_SYSCALL_BIT + 332)
+#define __NR_io_pgetevents (__X32_SYSCALL_BIT + 333)
+#define __NR_rseq (__X32_SYSCALL_BIT + 334)
#define __NR_rt_sigaction (__X32_SYSCALL_BIT + 512)
#define __NR_rt_sigreturn (__X32_SYSCALL_BIT + 513)
#define __NR_ioctl (__X32_SYSCALL_BIT + 514)
};
#define KVM_X86_DISABLE_EXITS_MWAIT (1 << 0)
-#define KVM_X86_DISABLE_EXITS_HTL (1 << 1)
+#define KVM_X86_DISABLE_EXITS_HLT (1 << 1)
#define KVM_X86_DISABLE_EXITS_PAUSE (1 << 2)
#define KVM_X86_DISABLE_VALID_EXITS (KVM_X86_DISABLE_EXITS_MWAIT | \
- KVM_X86_DISABLE_EXITS_HTL | \
+ KVM_X86_DISABLE_EXITS_HLT | \
KVM_X86_DISABLE_EXITS_PAUSE)
/* for KVM_ENABLE_CAP */
#define KVM_CAP_S390_BPB 152
#define KVM_CAP_GET_MSR_FEATURES 153
#define KVM_CAP_HYPERV_EVENTFD 154
+#define KVM_CAP_HYPERV_TLBFLUSH 155
#ifdef KVM_CAP_IRQ_ROUTING
SEV_PDH_GEN,
SEV_PDH_CERT_EXPORT,
SEV_PEK_CERT_IMPORT,
+ SEV_GET_ID,
SEV_MAX,
};
__u32 cert_chain_len; /* In/Out */
} __attribute__((packed));
+/**
+ * struct sev_user_data_get_id - GET_ID command parameters
+ *
+ * @socket1: Buffer to pass unique ID of first socket
+ * @socket2: Buffer to pass unique ID of second socket
+ */
+struct sev_user_data_get_id {
+ __u8 socket1[64]; /* Out */
+ __u8 socket2[64]; /* Out */
+} __attribute__((packed));
+
/**
* struct sev_issue_cmd - SEV ioctl parameters
*
- SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware
implementation for certain IBM POWER hardware. The sources are at
https://github.com/aik/SLOF, and the image currently in qemu is
- built from git tag qemu-slof-20171214.
+ built from git tag qemu-slof-20180621.
- sgabios (the Serial Graphics Adapter option ROM) provides a means for
legacy x86 software to communicate with an attached serial console as
-Subproject commit 2317427ce76006723f7ae103a6998ab41dd79c68
+Subproject commit 7d37babcfa48a6eb08e726a8d13b745cb2eebe1c
ARMCPU *cpu = ARM_CPU(obj);
set_feature(&cpu->env, ARM_FEATURE_V7);
set_feature(&cpu->env, ARM_FEATURE_M);
+ set_feature(&cpu->env, ARM_FEATURE_M_MAIN);
cpu->midr = 0x410fc231;
cpu->pmsav7_dregion = 8;
cpu->id_pfr0 = 0x00000030;
set_feature(&cpu->env, ARM_FEATURE_V7);
set_feature(&cpu->env, ARM_FEATURE_M);
+ set_feature(&cpu->env, ARM_FEATURE_M_MAIN);
set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP);
cpu->midr = 0x410fc240; /* r0p0 */
cpu->pmsav7_dregion = 8;
set_feature(&cpu->env, ARM_FEATURE_V8);
set_feature(&cpu->env, ARM_FEATURE_M);
+ set_feature(&cpu->env, ARM_FEATURE_M_MAIN);
set_feature(&cpu->env, ARM_FEATURE_M_SECURITY);
set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP);
cpu->midr = 0x410fd213; /* r0p3 */
define_arm_cp_regs(cpu, cortexr5_cp_reginfo);
}
+static void cortex_r5f_initfn(Object *obj)
+{
+ ARMCPU *cpu = ARM_CPU(obj);
+
+ cortex_r5_initfn(obj);
+ set_feature(&cpu->env, ARM_FEATURE_VFP3);
+}
+
static const ARMCPRegInfo cortexa8_cp_reginfo[] = {
{ .name = "L2LOCKDOWN", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 0,
.access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
{ .name = "cortex-m33", .initfn = cortex_m33_initfn,
.class_init = arm_v7m_class_init },
{ .name = "cortex-r5", .initfn = cortex_r5_initfn },
+ { .name = "cortex-r5f", .initfn = cortex_r5f_initfn },
{ .name = "cortex-a7", .initfn = cortex_a7_initfn },
{ .name = "cortex-a8", .initfn = cortex_a8_initfn },
{ .name = "cortex-a9", .initfn = cortex_a9_initfn },
ARM_FEATURE_V8_RDM, /* implements v8.1 simd round multiply */
ARM_FEATURE_V8_FP16, /* implements v8.2 half-precision float */
ARM_FEATURE_V8_FCMA, /* has complex number part of v8.3 extensions. */
+ ARM_FEATURE_M_MAIN, /* M profile Main Extension */
};
static inline int arm_feature(CPUARMState *env, int feature)
* We use a MemoryListener to track mapping and unmapping of
* the regions during board creation, so the board models don't
* need to do anything special for the KVM case.
+ *
+ * Sometimes the address must be OR'ed with some other fields
+ * (for example for KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION).
+ * @kda_addr_ormask aims at storing the value of those fields.
*/
typedef struct KVMDevice {
struct kvm_arm_device_addr kda;
struct kvm_device_attr kdattr;
+ uint64_t kda_addr_ormask;
MemoryRegion *mr;
QSLIST_ENTRY(KVMDevice) entries;
int dev_fd;
*/
if (kd->dev_fd >= 0) {
uint64_t addr = kd->kda.addr;
+
+ addr |= kd->kda_addr_ormask;
attr->addr = (uintptr_t)&addr;
ret = kvm_device_ioctl(kd->dev_fd, KVM_SET_DEVICE_ATTR, attr);
} else {
kvm_arm_set_device_addr(kd);
}
memory_region_unref(kd->mr);
+ QSLIST_REMOVE_HEAD(&kvm_devices_head, entries);
g_free(kd);
}
memory_listener_unregister(&devlistener);
};
void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid, uint64_t group,
- uint64_t attr, int dev_fd)
+ uint64_t attr, int dev_fd, uint64_t addr_ormask)
{
KVMDevice *kd;
kd->kdattr.group = group;
kd->kdattr.attr = attr;
kd->dev_fd = dev_fd;
+ kd->kda_addr_ormask = addr_ormask;
QSLIST_INSERT_HEAD(&kvm_devices_head, kd, entries);
memory_region_ref(kd->mr);
}
* @group: device control API group for setting addresses
* @attr: device control API address type
* @dev_fd: device control device file descriptor (or -1 if not supported)
+ * @addr_ormask: value to be OR'ed with resolved address
*
* Remember the memory region @mr, and when it is mapped by the
* machine model, tell the kernel that base address using the
* address at the point where machine init is complete.
*/
void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid, uint64_t group,
- uint64_t attr, int dev_fd);
+ uint64_t attr, int dev_fd, uint64_t addr_ormask);
/**
* kvm_arm_init_cpreg_list:
static void gen_aa32_ld_i32(DisasContext *s, TCGv_i32 val, TCGv_i32 a32,
int index, TCGMemOp opc)
{
- TCGv addr = gen_aa32_addr(s, a32, opc);
+ TCGv addr;
+
+ if (arm_dc_feature(s, ARM_FEATURE_M) &&
+ !arm_dc_feature(s, ARM_FEATURE_M_MAIN)) {
+ opc |= MO_ALIGN;
+ }
+
+ addr = gen_aa32_addr(s, a32, opc);
tcg_gen_qemu_ld_i32(val, addr, index, opc);
tcg_temp_free(addr);
}
static void gen_aa32_st_i32(DisasContext *s, TCGv_i32 val, TCGv_i32 a32,
int index, TCGMemOp opc)
{
- TCGv addr = gen_aa32_addr(s, a32, opc);
+ TCGv addr;
+
+ if (arm_dc_feature(s, ARM_FEATURE_M) &&
+ !arm_dc_feature(s, ARM_FEATURE_M_MAIN)) {
+ opc |= MO_ALIGN;
+ }
+
+ addr = gen_aa32_addr(s, a32, opc);
tcg_gen_qemu_st_i32(val, addr, index, opc);
tcg_temp_free(addr);
}
!arm_dc_feature(s, ARM_FEATURE_V7)) {
int i;
bool found = false;
- const uint32_t armv6m_insn[] = {0xf3808000 /* msr */,
- 0xf3b08040 /* dsb */,
- 0xf3b08050 /* dmb */,
- 0xf3b08060 /* isb */,
- 0xf3e08000 /* mrs */,
- 0xf000d000 /* bl */};
- const uint32_t armv6m_mask[] = {0xffe0d000,
- 0xfff0d0f0,
- 0xfff0d0f0,
- 0xfff0d0f0,
- 0xffe0d000,
- 0xf800d000};
+ static const uint32_t armv6m_insn[] = {0xf3808000 /* msr */,
+ 0xf3b08040 /* dsb */,
+ 0xf3b08050 /* dmb */,
+ 0xf3b08060 /* isb */,
+ 0xf3e08000 /* mrs */,
+ 0xf000d000 /* bl */};
+ static const uint32_t armv6m_mask[] = {0xffe0d000,
+ 0xfff0d0f0,
+ 0xfff0d0f0,
+ 0xfff0d0f0,
+ 0xffe0d000,
+ 0xf800d000};
for (i = 0; i < ARRAY_SIZE(armv6m_insn); i++) {
if ((insn & armv6m_mask[i]) == armv6m_insn[i]) {
break;
case 3: /* Special control operations. */
if (!arm_dc_feature(s, ARM_FEATURE_V7) &&
- !(arm_dc_feature(s, ARM_FEATURE_V6) &&
- arm_dc_feature(s, ARM_FEATURE_M))) {
+ !arm_dc_feature(s, ARM_FEATURE_M)) {
goto illegal_op;
}
op = (insn >> 4) & 0xf;
return NULL;
}
-bool ppc_check_compat(PowerPCCPU *cpu, uint32_t compat_pvr,
- uint32_t min_compat_pvr, uint32_t max_compat_pvr)
+static bool pcc_compat(PowerPCCPUClass *pcc, uint32_t compat_pvr,
+ uint32_t min_compat_pvr, uint32_t max_compat_pvr)
{
- PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
const CompatInfo *compat = compat_by_pvr(compat_pvr);
const CompatInfo *min = compat_by_pvr(min_compat_pvr);
const CompatInfo *max = compat_by_pvr(max_compat_pvr);
-#if !defined(CONFIG_USER_ONLY)
- g_assert(cpu->vhyp);
-#endif
g_assert(!min_compat_pvr || min);
g_assert(!max_compat_pvr || max);
return true;
}
+bool ppc_check_compat(PowerPCCPU *cpu, uint32_t compat_pvr,
+ uint32_t min_compat_pvr, uint32_t max_compat_pvr)
+{
+ PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
+
+#if !defined(CONFIG_USER_ONLY)
+ g_assert(cpu->vhyp);
+#endif
+
+ return pcc_compat(pcc, compat_pvr, min_compat_pvr, max_compat_pvr);
+}
+
+bool ppc_type_check_compat(const char *cputype, uint32_t compat_pvr,
+ uint32_t min_compat_pvr, uint32_t max_compat_pvr)
+{
+ PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(object_class_by_name(cputype));
+ return pcc_compat(pcc, compat_pvr, min_compat_pvr, max_compat_pvr);
+}
+
void ppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr, Error **errp)
{
const CompatInfo *compat = compat_by_pvr(compat_pvr);
#if defined(TARGET_PPC64)
bool ppc_check_compat(PowerPCCPU *cpu, uint32_t compat_pvr,
uint32_t min_compat_pvr, uint32_t max_compat_pvr);
+bool ppc_type_check_compat(const char *cputype, uint32_t compat_pvr,
+ uint32_t min_compat_pvr, uint32_t max_compat_pvr);
+
void ppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr, Error **errp);
+
#if !defined(CONFIG_USER_ONLY)
void ppc_set_compat_all(uint32_t compat_pvr, Error **errp);
#endif
case FPSCR_RN:
fpscr_set_rounding_mode(env);
break;
+ case FPSCR_VXSNAN:
+ case FPSCR_VXISI:
+ case FPSCR_VXIDI:
+ case FPSCR_VXZDZ:
+ case FPSCR_VXIMZ:
+ case FPSCR_VXVC:
+ case FPSCR_VXSOFT:
+ case FPSCR_VXSQRT:
+ case FPSCR_VXCVI:
+ if (!fpscr_ix) {
+ /* Set VX bit to zero */
+ env->fpscr &= ~(1 << FPSCR_VX);
+ }
+ break;
+ case FPSCR_OX:
+ case FPSCR_UX:
+ case FPSCR_ZX:
+ case FPSCR_XX:
+ case FPSCR_VE:
+ case FPSCR_OE:
+ case FPSCR_UE:
+ case FPSCR_ZE:
+ case FPSCR_XE:
+ if (!fpscr_eex) {
+ /* Set the FEX bit */
+ env->fpscr &= ~(1 << FPSCR_FEX);
+ }
+ break;
default:
break;
}
}
}
-static bool kvm_valid_page_size(uint32_t flags, long rampgsize, uint32_t shift)
+bool kvmppc_hpt_needs_host_contiguous_pages(void)
{
- if (!(flags & KVM_PPC_PAGE_SIZES_REAL)) {
- return true;
+ PowerPCCPU *cpu = POWERPC_CPU(first_cpu);
+ static struct kvm_ppc_smmu_info smmu_info;
+
+ if (!kvm_enabled()) {
+ return false;
}
- return (1ul << shift) <= rampgsize;
+ kvm_get_smmu_info(cpu, &smmu_info);
+ return !!(smmu_info.flags & KVM_PPC_PAGE_SIZES_REAL);
}
-static long max_cpu_page_size;
-
-static void kvm_fixup_page_sizes(PowerPCCPU *cpu)
+void kvm_check_mmu(PowerPCCPU *cpu, Error **errp)
{
- static struct kvm_ppc_smmu_info smmu_info;
- static bool has_smmu_info;
- CPUPPCState *env = &cpu->env;
+ struct kvm_ppc_smmu_info smmu_info;
int iq, ik, jq, jk;
- /* We only handle page sizes for 64-bit server guests for now */
- if (!(env->mmu_model & POWERPC_MMU_64)) {
+ /* For now, we only have anything to check on hash64 MMUs */
+ if (!cpu->hash64_opts || !kvm_enabled()) {
return;
}
- /* Collect MMU info from kernel if not already */
- if (!has_smmu_info) {
- kvm_get_smmu_info(cpu, &smmu_info);
- has_smmu_info = true;
- }
+ kvm_get_smmu_info(cpu, &smmu_info);
- if (!max_cpu_page_size) {
- max_cpu_page_size = qemu_getrampagesize();
+ if (ppc_hash64_has(cpu, PPC_HASH64_1TSEG)
+ && !(smmu_info.flags & KVM_PPC_1T_SEGMENTS)) {
+ error_setg(errp,
+ "KVM does not support 1TiB segments which guest expects");
+ return;
}
- /* Convert to QEMU form */
- memset(cpu->hash64_opts->sps, 0, sizeof(*cpu->hash64_opts->sps));
-
- /* If we have HV KVM, we need to forbid CI large pages if our
- * host page size is smaller than 64K.
- */
- if (smmu_info.flags & KVM_PPC_PAGE_SIZES_REAL) {
- if (getpagesize() >= 0x10000) {
- cpu->hash64_opts->flags |= PPC_HASH64_CI_LARGEPAGE;
- } else {
- cpu->hash64_opts->flags &= ~PPC_HASH64_CI_LARGEPAGE;
- }
+ if (smmu_info.slb_size < cpu->hash64_opts->slb_size) {
+ error_setg(errp, "KVM only supports %u SLB entries, but guest needs %u",
+ smmu_info.slb_size, cpu->hash64_opts->slb_size);
+ return;
}
/*
- * XXX This loop should be an entry wide AND of the capabilities that
- * the selected CPU has with the capabilities that KVM supports.
+ * Verify that every pagesize supported by the cpu model is
+ * supported by KVM with the same encodings
*/
- for (ik = iq = 0; ik < KVM_PPC_PAGE_SIZES_MAX_SZ; ik++) {
+ for (iq = 0; iq < ARRAY_SIZE(cpu->hash64_opts->sps); iq++) {
PPCHash64SegmentPageSizes *qsps = &cpu->hash64_opts->sps[iq];
- struct kvm_ppc_one_seg_page_size *ksps = &smmu_info.sps[ik];
+ struct kvm_ppc_one_seg_page_size *ksps;
- if (!kvm_valid_page_size(smmu_info.flags, max_cpu_page_size,
- ksps->page_shift)) {
- continue;
- }
- qsps->page_shift = ksps->page_shift;
- qsps->slb_enc = ksps->slb_enc;
- for (jk = jq = 0; jk < KVM_PPC_PAGE_SIZES_MAX_SZ; jk++) {
- if (!kvm_valid_page_size(smmu_info.flags, max_cpu_page_size,
- ksps->enc[jk].page_shift)) {
- continue;
- }
- qsps->enc[jq].page_shift = ksps->enc[jk].page_shift;
- qsps->enc[jq].pte_enc = ksps->enc[jk].pte_enc;
- if (++jq >= PPC_PAGE_SIZES_MAX_SZ) {
+ for (ik = 0; ik < ARRAY_SIZE(smmu_info.sps); ik++) {
+ if (qsps->page_shift == smmu_info.sps[ik].page_shift) {
break;
}
}
- if (++iq >= PPC_PAGE_SIZES_MAX_SZ) {
- break;
+ if (ik >= ARRAY_SIZE(smmu_info.sps)) {
+ error_setg(errp, "KVM doesn't support for base page shift %u",
+ qsps->page_shift);
+ return;
}
- }
- cpu->hash64_opts->slb_size = smmu_info.slb_size;
- if (!(smmu_info.flags & KVM_PPC_1T_SEGMENTS)) {
- cpu->hash64_opts->flags &= ~PPC_HASH64_1TSEG;
- }
-}
-
-bool kvmppc_is_mem_backend_page_size_ok(const char *obj_path)
-{
- Object *mem_obj = object_resolve_path(obj_path, NULL);
- long pagesize = host_memory_backend_pagesize(MEMORY_BACKEND(mem_obj));
- return pagesize >= max_cpu_page_size;
-}
+ ksps = &smmu_info.sps[ik];
+ if (ksps->slb_enc != qsps->slb_enc) {
+ error_setg(errp,
+"KVM uses SLB encoding 0x%x for page shift %u, but guest expects 0x%x",
+ ksps->slb_enc, ksps->page_shift, qsps->slb_enc);
+ return;
+ }
-#else /* defined (TARGET_PPC64) */
+ for (jq = 0; jq < ARRAY_SIZE(qsps->enc); jq++) {
+ for (jk = 0; jk < ARRAY_SIZE(ksps->enc); jk++) {
+ if (qsps->enc[jq].page_shift == ksps->enc[jk].page_shift) {
+ break;
+ }
+ }
-static inline void kvm_fixup_page_sizes(PowerPCCPU *cpu)
-{
-}
+ if (jk >= ARRAY_SIZE(ksps->enc)) {
+ error_setg(errp, "KVM doesn't support page shift %u/%u",
+ qsps->enc[jq].page_shift, qsps->page_shift);
+ return;
+ }
+ if (qsps->enc[jq].pte_enc != ksps->enc[jk].pte_enc) {
+ error_setg(errp,
+"KVM uses PTE encoding 0x%x for page shift %u/%u, but guest expects 0x%x",
+ ksps->enc[jk].pte_enc, qsps->enc[jq].page_shift,
+ qsps->page_shift, qsps->enc[jq].pte_enc);
+ return;
+ }
+ }
+ }
-bool kvmppc_is_mem_backend_page_size_ok(const char *obj_path)
-{
- return true;
+ if (ppc_hash64_has(cpu, PPC_HASH64_CI_LARGEPAGE)) {
+ /* Mostly what guest pagesizes we can use are related to the
+ * host pages used to map guest RAM, which is handled in the
+ * platform code. Cache-Inhibited largepages (64k) however are
+ * used for I/O, so if they're mapped to the host at all it
+ * will be a normal mapping, not a special hugepage one used
+ * for RAM. */
+ if (getpagesize() < 0x10000) {
+ error_setg(errp,
+ "KVM can't supply 64kiB CI pages, which guest expects");
+ }
+ }
}
-
#endif /* !defined (TARGET_PPC64) */
unsigned long kvm_arch_vcpu_id(CPUState *cpu)
CPUPPCState *cenv = &cpu->env;
int ret;
- /* Gather server mmu info from KVM and update the CPU state */
- kvm_fixup_page_sizes(cpu);
-
/* Synchronize sregs with kvm */
ret = kvm_arch_sync_sregs(cpu);
if (ret) {
int kvmppc_resize_hpt_commit(PowerPCCPU *cpu, target_ulong flags, int shift);
bool kvmppc_pvr_workaround_required(PowerPCCPU *cpu);
-bool kvmppc_is_mem_backend_page_size_ok(const char *obj_path);
+bool kvmppc_hpt_needs_host_contiguous_pages(void);
+void kvm_check_mmu(PowerPCCPU *cpu, Error **errp);
#else
return ram_size;
}
-static inline bool kvmppc_is_mem_backend_page_size_ok(const char *obj_path)
+static inline bool kvmppc_hpt_needs_host_contiguous_pages(void)
+{
+ return false;
+}
+
+static inline void kvm_check_mmu(PowerPCCPU *cpu, Error **errp)
{
- return true;
}
static inline bool kvmppc_has_cap_spapr_vfio(void)
},
}
};
+
+void ppc_hash64_filter_pagesizes(PowerPCCPU *cpu,
+ bool (*cb)(void *, uint32_t, uint32_t),
+ void *opaque)
+{
+ PPCHash64Options *opts = cpu->hash64_opts;
+ int i;
+ int n = 0;
+ bool ci_largepage = false;
+
+ assert(opts);
+
+ n = 0;
+ for (i = 0; i < ARRAY_SIZE(opts->sps); i++) {
+ PPCHash64SegmentPageSizes *sps = &opts->sps[i];
+ int j;
+ int m = 0;
+
+ assert(n <= i);
+
+ if (!sps->page_shift) {
+ break;
+ }
+
+ for (j = 0; j < ARRAY_SIZE(sps->enc); j++) {
+ PPCHash64PageSize *ps = &sps->enc[j];
+
+ assert(m <= j);
+ if (!ps->page_shift) {
+ break;
+ }
+
+ if (cb(opaque, sps->page_shift, ps->page_shift)) {
+ if (ps->page_shift >= 16) {
+ ci_largepage = true;
+ }
+ sps->enc[m++] = *ps;
+ }
+ }
+
+ /* Clear rest of the row */
+ for (j = m; j < ARRAY_SIZE(sps->enc); j++) {
+ memset(&sps->enc[j], 0, sizeof(sps->enc[j]));
+ }
+
+ if (m) {
+ n++;
+ }
+ }
+
+ /* Clear the rest of the table */
+ for (i = n; i < ARRAY_SIZE(opts->sps); i++) {
+ memset(&opts->sps[i], 0, sizeof(opts->sps[i]));
+ }
+
+ if (!ci_largepage) {
+ opts->flags &= ~PPC_HASH64_CI_LARGEPAGE;
+ }
+}
void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val);
void ppc_hash64_init(PowerPCCPU *cpu);
void ppc_hash64_finalize(PowerPCCPU *cpu);
+void ppc_hash64_filter_pagesizes(PowerPCCPU *cpu,
+ bool (*cb)(void *, uint32_t, uint32_t),
+ void *opaque);
#endif
/*
GEN_HANDLER(msync_4xx, 0x1F, 0x16, 0x12, 0x03FFF801, PPC_BOOKE),
GEN_HANDLER2_E(icbt_440, "icbt", 0x1F, 0x16, 0x00, 0x03E00001,
PPC_BOOKE, PPC2_BOOKE206),
+GEN_HANDLER2(icbt_440, "icbt", 0x1F, 0x06, 0x08, 0x03E00001,
+ PPC_440_SPEC),
GEN_HANDLER(lvsl, 0x1f, 0x06, 0x00, 0x00000001, PPC_ALTIVEC),
GEN_HANDLER(lvsr, 0x1f, 0x06, 0x01, 0x00000001, PPC_ALTIVEC),
GEN_HANDLER(mfvscr, 0x04, 0x2, 0x18, 0x001ff800, PPC_ALTIVEC),
int vga_interface_type = VGA_NONE;
static DisplayOptions dpy;
int no_frame;
-static int num_serial_hds = 0;
-static Chardev **serial_hds = NULL;
+static int num_serial_hds;
+static Chardev **serial_hds;
Chardev *parallel_hds[MAX_PARALLEL_PORTS];
Chardev *virtcon_hds[MAX_VIRTIO_CONSOLES];
int win2k_install_hack = 0;