summaryrefslogtreecommitdiffstats
path: root/hw/ppc
diff options
context:
space:
mode:
Diffstat (limited to 'hw/ppc')
-rw-r--r--hw/ppc/Makefile.objs6
-rw-r--r--hw/ppc/e500plat.c4
-rw-r--r--hw/ppc/pnv.c23
-rw-r--r--hw/ppc/pnv_core.c2
-rw-r--r--hw/ppc/pnv_xscom.c8
-rw-r--r--hw/ppc/ppc4xx_pci.c6
-rw-r--r--hw/ppc/spapr.c152
-rw-r--r--hw/ppc/spapr_caps.c315
-rw-r--r--hw/ppc/spapr_cpu_core.c7
-rw-r--r--hw/ppc/spapr_pci.c10
-rw-r--r--hw/ppc/spapr_rtas.c9
11 files changed, 461 insertions, 81 deletions
diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
index 7efc686748..ad1928c5d8 100644
--- a/hw/ppc/Makefile.objs
+++ b/hw/ppc/Makefile.objs
@@ -1,7 +1,7 @@
# shared objects
obj-y += ppc.o ppc_booke.o fdt.o
# IBM pSeries (sPAPR)
-obj-$(CONFIG_PSERIES) += spapr.o spapr_vio.o spapr_events.o
+obj-$(CONFIG_PSERIES) += spapr.o spapr_caps.o spapr_vio.o spapr_events.o
obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
obj-$(CONFIG_PSERIES) += spapr_cpu_core.o spapr_ovec.o
@@ -12,8 +12,8 @@ obj-y += spapr_pci_vfio.o
endif
obj-$(CONFIG_PSERIES) += spapr_rtas_ddw.o
# PowerPC 4xx boards
-obj-y += ppc405_boards.o ppc4xx_devs.o ppc405_uc.o ppc440_bamboo.o
-obj-y += ppc4xx_pci.o
+obj-y += ppc4xx_devs.o ppc405_uc.o
+obj-$(CONFIG_PPC4XX) += ppc4xx_pci.o ppc405_boards.o ppc440_bamboo.o
# PReP
obj-$(CONFIG_PREP) += prep.o
obj-$(CONFIG_PREP) += prep_systemio.o
diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c
index e59e80fb9e..81d03e1038 100644
--- a/hw/ppc/e500plat.c
+++ b/hw/ppc/e500plat.c
@@ -12,9 +12,11 @@
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "e500.h"
+#include "hw/net/fsl_etsec/etsec.h"
#include "hw/boards.h"
#include "sysemu/device_tree.h"
#include "sysemu/kvm.h"
+#include "hw/sysbus.h"
#include "hw/pci/pci.h"
#include "hw/ppc/openpic.h"
#include "kvm_ppc.h"
@@ -63,7 +65,7 @@ static void e500plat_machine_init(MachineClass *mc)
mc->desc = "generic paravirt e500 platform";
mc->init = e500plat_init;
mc->max_cpus = 32;
- mc->has_dynamic_sysbus = true;
+ machine_class_allow_dynamic_sysbus_dev(mc, TYPE_ETSEC_COMMON);
mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("e500v2_v30");
}
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 9475e8479c..98ee3c607a 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -53,7 +53,7 @@
#define FW_MAX_SIZE 0x00400000
#define KERNEL_LOAD_ADDR 0x20000000
-#define INITRD_LOAD_ADDR 0x40000000
+#define INITRD_LOAD_ADDR 0x60000000
static const char *pnv_chip_core_typename(const PnvChip *o)
{
@@ -707,9 +707,9 @@ static uint32_t pnv_chip_core_pir_p9(PnvChip *chip, uint32_t core_id)
#define POWER8_CORE_MASK (0x7e7eull)
/*
- * POWER9 has 24 cores, ids starting at 0x20
+ * POWER9 has 24 cores, ids starting at 0x0
*/
-#define POWER9_CORE_MASK (0xffffff00000000ull)
+#define POWER9_CORE_MASK (0xffffffffffffffull)
static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data)
{
@@ -721,7 +721,6 @@ static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data)
k->cores_mask = POWER8E_CORE_MASK;
k->core_pir = pnv_chip_core_pir_p8;
k->xscom_base = 0x003fc0000000000ull;
- k->xscom_core_base = 0x10000000ull;
dc->desc = "PowerNV Chip POWER8E";
}
@@ -735,7 +734,6 @@ static void pnv_chip_power8_class_init(ObjectClass *klass, void *data)
k->cores_mask = POWER8_CORE_MASK;
k->core_pir = pnv_chip_core_pir_p8;
k->xscom_base = 0x003fc0000000000ull;
- k->xscom_core_base = 0x10000000ull;
dc->desc = "PowerNV Chip POWER8";
}
@@ -749,7 +747,6 @@ static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data)
k->cores_mask = POWER8_CORE_MASK;
k->core_pir = pnv_chip_core_pir_p8;
k->xscom_base = 0x003fc0000000000ull;
- k->xscom_core_base = 0x10000000ull;
dc->desc = "PowerNV Chip POWER8NVL";
}
@@ -759,11 +756,10 @@ static void pnv_chip_power9_class_init(ObjectClass *klass, void *data)
PnvChipClass *k = PNV_CHIP_CLASS(klass);
k->chip_type = PNV_CHIP_POWER9;
- k->chip_cfam_id = 0x100d104980000000ull; /* P9 Nimbus DD1.0 */
+ k->chip_cfam_id = 0x220d104900008000ull; /* P9 Nimbus DD2.0 */
k->cores_mask = POWER9_CORE_MASK;
k->core_pir = pnv_chip_core_pir_p9;
k->xscom_base = 0x00603fc00000000ull;
- k->xscom_core_base = 0x0ull;
dc->desc = "PowerNV Chip POWER9";
}
@@ -887,6 +883,7 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
&& (i < chip->nr_cores); core_hwid++) {
char core_name[32];
void *pnv_core = chip->cores + i * typesize;
+ uint64_t xscom_core_base;
if (!(chip->cores_mask & (1ull << core_hwid))) {
continue;
@@ -910,9 +907,13 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
object_unref(OBJECT(pnv_core));
/* Each core has an XSCOM MMIO region */
- pnv_xscom_add_subregion(chip,
- PNV_XSCOM_EX_CORE_BASE(pcc->xscom_core_base,
- core_hwid),
+ if (!pnv_chip_is_power9(chip)) {
+ xscom_core_base = PNV_XSCOM_EX_BASE(core_hwid);
+ } else {
+ xscom_core_base = PNV_XSCOM_P9_EC_BASE(core_hwid);
+ }
+
+ pnv_xscom_add_subregion(chip, xscom_core_base,
&PNV_CORE(pnv_core)->xscom_regs);
i++;
}
diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c
index 7e8a76df44..cbb64ad9e7 100644
--- a/hw/ppc/pnv_core.c
+++ b/hw/ppc/pnv_core.c
@@ -192,7 +192,7 @@ static void pnv_core_realize(DeviceState *dev, Error **errp)
snprintf(name, sizeof(name), "xscom-core.%d", cc->core_id);
pnv_xscom_region_init(&pc->xscom_regs, OBJECT(dev), &pnv_core_xscom_ops,
- pc, name, PNV_XSCOM_EX_CORE_SIZE);
+ pc, name, PNV_XSCOM_EX_SIZE);
return;
err:
diff --git a/hw/ppc/pnv_xscom.c b/hw/ppc/pnv_xscom.c
index e51d634f40..99c40efecd 100644
--- a/hw/ppc/pnv_xscom.c
+++ b/hw/ppc/pnv_xscom.c
@@ -51,10 +51,9 @@ static void xscom_complete(CPUState *cs, uint64_t hmer_bits)
static uint32_t pnv_xscom_pcba(PnvChip *chip, uint64_t addr)
{
- PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
-
addr &= (PNV_XSCOM_SIZE - 1);
- if (pcc->chip_type == PNV_CHIP_POWER9) {
+
+ if (pnv_chip_is_power9(chip)) {
return addr >> 3;
} else {
return ((addr >> 4) & ~0xfull) | ((addr >> 3) & 0xf);
@@ -231,7 +230,6 @@ int pnv_dt_xscom(PnvChip *chip, void *fdt, int root_offset)
int xscom_offset;
ForeachPopulateArgs args;
char *name;
- PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
name = g_strdup_printf("xscom@%" PRIx64, be64_to_cpu(reg[0]));
xscom_offset = fdt_add_subnode(fdt, root_offset, name);
@@ -242,7 +240,7 @@ int pnv_dt_xscom(PnvChip *chip, void *fdt, int root_offset)
_FDT((fdt_setprop_cell(fdt, xscom_offset, "#size-cells", 1)));
_FDT((fdt_setprop(fdt, xscom_offset, "reg", reg, sizeof(reg))));
- if (pcc->chip_type == PNV_CHIP_POWER9) {
+ if (pnv_chip_is_power9(chip)) {
_FDT((fdt_setprop(fdt, xscom_offset, "compatible", compat_p9,
sizeof(compat_p9))));
} else {
diff --git a/hw/ppc/ppc4xx_pci.c b/hw/ppc/ppc4xx_pci.c
index 4765dcecca..b7642bac01 100644
--- a/hw/ppc/ppc4xx_pci.c
+++ b/hw/ppc/ppc4xx_pci.c
@@ -314,9 +314,9 @@ static int ppc4xx_pcihost_initfn(SysBusDevice *dev)
sysbus_init_irq(dev, &s->irq[i]);
}
- b = pci_register_bus(DEVICE(dev), NULL, ppc4xx_pci_set_irq,
- ppc4xx_pci_map_irq, s->irq, get_system_memory(),
- get_system_io(), 0, 4, TYPE_PCI_BUS);
+ b = pci_register_root_bus(DEVICE(dev), NULL, ppc4xx_pci_set_irq,
+ ppc4xx_pci_map_irq, s->irq, get_system_memory(),
+ get_system_io(), 0, 4, TYPE_PCI_BUS);
h->bus = b;
pci_create_simple(b, 0, "ppc4xx-host-bridge");
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index dfd352c473..88a78d31eb 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -253,7 +253,9 @@ static int spapr_fixup_cpu_numa_dt(void *fdt, int offset, PowerPCCPU *cpu)
}
/* Populate the "ibm,pa-features" property */
-static void spapr_populate_pa_features(PowerPCCPU *cpu, void *fdt, int offset,
+static void spapr_populate_pa_features(sPAPRMachineState *spapr,
+ PowerPCCPU *cpu,
+ void *fdt, int offset,
bool legacy_guest)
{
CPUPPCState *env = &cpu->env;
@@ -318,7 +320,7 @@ static void spapr_populate_pa_features(PowerPCCPU *cpu, void *fdt, int offset,
*/
pa_features[3] |= 0x20;
}
- if (kvmppc_has_cap_htm() && pa_size > 24) {
+ if ((spapr_get_cap(spapr, SPAPR_CAP_HTM) != 0) && pa_size > 24) {
pa_features[24] |= 0x80; /* Transactional memory support */
}
if (legacy_guest && pa_size > 40) {
@@ -343,7 +345,7 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPRMachineState *spapr)
PowerPCCPU *cpu = POWERPC_CPU(cs);
DeviceClass *dc = DEVICE_GET_CLASS(cs);
int index = spapr_vcpu_id(cpu);
- int compat_smt = MIN(smp_threads, ppc_compat_max_threads(cpu));
+ int compat_smt = MIN(smp_threads, ppc_compat_max_vthreads(cpu));
if ((index % smt) != 0) {
continue;
@@ -384,8 +386,8 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPRMachineState *spapr)
return ret;
}
- spapr_populate_pa_features(cpu, fdt, offset,
- spapr->cas_legacy_guest_workaround);
+ spapr_populate_pa_features(spapr, cpu, fdt, offset,
+ spapr->cas_legacy_guest_workaround);
}
return ret;
}
@@ -501,7 +503,7 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset,
size_t page_sizes_prop_size;
uint32_t vcpus_per_socket = smp_threads * smp_cores;
uint32_t pft_size_prop[] = {0, cpu_to_be32(spapr->htab_shift)};
- int compat_smt = MIN(smp_threads, ppc_compat_max_threads(cpu));
+ int compat_smt = MIN(smp_threads, ppc_compat_max_vthreads(cpu));
sPAPRDRConnector *drc;
int drc_index;
uint32_t radix_AP_encodings[PPC_PAGE_SIZES_MAX_SZ];
@@ -555,20 +557,22 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset,
segs, sizeof(segs))));
}
- /* Advertise VMX/VSX (vector extensions) if available
- * 0 / no property == no vector extensions
+ /* Advertise VSX (vector extensions) if available
* 1 == VMX / Altivec available
- * 2 == VSX available */
- if (env->insns_flags & PPC_ALTIVEC) {
- uint32_t vmx = (env->insns_flags2 & PPC2_VSX) ? 2 : 1;
-
- _FDT((fdt_setprop_cell(fdt, offset, "ibm,vmx", vmx)));
+ * 2 == VSX available
+ *
+ * Only CPUs for which we create core types in spapr_cpu_core.c
+ * are possible, and all of those have VMX */
+ if (spapr_get_cap(spapr, SPAPR_CAP_VSX) != 0) {
+ _FDT((fdt_setprop_cell(fdt, offset, "ibm,vmx", 2)));
+ } else {
+ _FDT((fdt_setprop_cell(fdt, offset, "ibm,vmx", 1)));
}
/* Advertise DFP (Decimal Floating Point) if available
* 0 / no property == no DFP
* 1 == DFP available */
- if (env->insns_flags2 & PPC2_DFP) {
+ if (spapr_get_cap(spapr, SPAPR_CAP_DFP) != 0) {
_FDT((fdt_setprop_cell(fdt, offset, "ibm,dfp", 1)));
}
@@ -579,7 +583,7 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset,
page_sizes_prop, page_sizes_prop_size)));
}
- spapr_populate_pa_features(cpu, fdt, offset, false);
+ spapr_populate_pa_features(spapr, cpu, fdt, offset, false);
_FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id",
cs->cpu_index / vcpus_per_socket)));
@@ -1466,6 +1470,8 @@ static void spapr_machine_reset(void)
/* Check for unknown sysbus devices */
foreach_dynamic_sysbus_device(find_unknown_sysbus_device, NULL);
+ spapr_caps_reset(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,
@@ -1478,6 +1484,15 @@ static void spapr_machine_reset(void)
spapr_setup_hpt_and_vrma(spapr);
}
+ /* if this reset wasn't generated by CAS, we should reset our
+ * negotiated options and start from scratch */
+ if (!spapr->cas_reboot) {
+ spapr_ovec_cleanup(spapr->ov5_cas);
+ spapr->ov5_cas = spapr_ovec_new();
+
+ ppc_set_compat(first_ppc_cpu, spapr->max_compat_pvr, &error_fatal);
+ }
+
qemu_devices_reset();
/* DRC reset may cause a device to be unplugged. This will cause troubles
@@ -1498,15 +1513,6 @@ static void spapr_machine_reset(void)
rtas_addr = rtas_limit - RTAS_MAX_SIZE;
fdt_addr = rtas_addr - FDT_MAX_SIZE;
- /* if this reset wasn't generated by CAS, we should reset our
- * negotiated options and start from scratch */
- if (!spapr->cas_reboot) {
- spapr_ovec_cleanup(spapr->ov5_cas);
- spapr->ov5_cas = spapr_ovec_new();
-
- ppc_set_compat(first_ppc_cpu, spapr->max_compat_pvr, &error_fatal);
- }
-
fdt = spapr_build_fdt(spapr, rtas_addr, spapr->rtas_size);
spapr_load_rtas(spapr, fdt, rtas_addr);
@@ -1580,11 +1586,28 @@ static bool spapr_vga_init(PCIBus *pci_bus, Error **errp)
}
}
+static int spapr_pre_load(void *opaque)
+{
+ int rc;
+
+ rc = spapr_caps_pre_load(opaque);
+ if (rc) {
+ return rc;
+ }
+
+ return 0;
+}
+
static int spapr_post_load(void *opaque, int version_id)
{
sPAPRMachineState *spapr = (sPAPRMachineState *)opaque;
int err = 0;
+ err = spapr_caps_post_migration(spapr);
+ if (err) {
+ return err;
+ }
+
if (!object_dynamic_cast(OBJECT(spapr->ics), TYPE_ICS_KVM)) {
CPUState *cs;
CPU_FOREACH(cs) {
@@ -1616,6 +1639,18 @@ static int spapr_post_load(void *opaque, int version_id)
return err;
}
+static int spapr_pre_save(void *opaque)
+{
+ int rc;
+
+ rc = spapr_caps_pre_save(opaque);
+ if (rc) {
+ return rc;
+ }
+
+ return 0;
+}
+
static bool version_before_3(void *opaque, int version_id)
{
return version_id < 3;
@@ -1736,7 +1771,9 @@ static const VMStateDescription vmstate_spapr = {
.name = "spapr",
.version_id = 3,
.minimum_version_id = 1,
+ .pre_load = spapr_pre_load,
.post_load = spapr_post_load,
+ .pre_save = spapr_pre_save,
.fields = (VMStateField[]) {
/* used to be @next_irq */
VMSTATE_UNUSED_BUFFER(version_before_3, 0, 4),
@@ -1751,6 +1788,9 @@ static const VMStateDescription vmstate_spapr = {
&vmstate_spapr_ov5_cas,
&vmstate_spapr_patb_entry,
&vmstate_spapr_pending_events,
+ &vmstate_spapr_cap_htm,
+ &vmstate_spapr_cap_vsx,
+ &vmstate_spapr_cap_dfp,
NULL
}
};
@@ -2186,11 +2226,6 @@ static void spapr_init_cpus(sPAPRMachineState *spapr)
int boot_cores_nr = smp_cpus / smp_threads;
int i;
- if (!type) {
- error_report("Unable to find sPAPR CPU Core definition");
- exit(1);
- }
-
possible_cpus = mc->possible_cpu_arch_ids(machine);
if (mc->has_hotpluggable_cpus) {
if (smp_cpus % smp_threads) {
@@ -2265,26 +2300,43 @@ static void spapr_set_vsmt_mode(sPAPRMachineState *spapr, Error **errp)
}
/* In this case, spapr->vsmt has been set by the command line */
} else {
- /* Choose a VSMT mode that may be higher than necessary but is
- * likely to be compatible with hosts that don't have VSMT. */
- spapr->vsmt = MAX(kvm_smt, smp_threads);
+ /*
+ * Default VSMT value is tricky, because we need it to be as
+ * consistent as possible (for migration), but this requires
+ * changing it for at least some existing cases. We pick 8 as
+ * the value that we'd get with KVM on POWER8, the
+ * overwhelmingly common case in production systems.
+ */
+ spapr->vsmt = 8;
}
/* KVM: If necessary, set the SMT mode: */
if (kvm_enabled() && (spapr->vsmt != kvm_smt)) {
ret = kvmppc_set_smt_threads(spapr->vsmt);
if (ret) {
+ /* Looks like KVM isn't able to change VSMT mode */
error_setg(&local_err,
"Failed to set KVM's VSMT mode to %d (errno %d)",
spapr->vsmt, ret);
- if (!vsmt_user) {
- error_append_hint(&local_err, "On PPC, a VM with %d threads/"
- "core on a host with %d threads/core requires "
- " the use of VSMT mode %d.\n",
- smp_threads, kvm_smt, spapr->vsmt);
+ /* We can live with that if the default one is big enough
+ * for the number of threads, and a submultiple of the one
+ * we want. In this case we'll waste some vcpu ids, but
+ * behaviour will be correct */
+ if ((kvm_smt >= smp_threads) && ((spapr->vsmt % kvm_smt) == 0)) {
+ warn_report_err(local_err);
+ local_err = NULL;
+ goto out;
+ } else {
+ if (!vsmt_user) {
+ error_append_hint(&local_err,
+ "On PPC, a VM with %d threads/core"
+ " on a host with %d threads/core"
+ " requires the use of VSMT mode %d.\n",
+ smp_threads, kvm_smt, spapr->vsmt);
+ }
+ kvmppc_hint_smt_possible(&local_err);
+ goto out;
}
- kvmppc_hint_smt_possible(&local_err);
- goto out;
}
}
/* else TCG: nothing to do currently */
@@ -3305,9 +3357,7 @@ static void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
int i;
for (i = 0; i < cc->nr_threads; i++) {
- sPAPRCPUCore *sc = SPAPR_CPU_CORE(dev);
-
- cs = CPU(sc->threads[i]);
+ cs = CPU(core->threads[i]);
pre_2_10_vmstate_unregister_dummy_icp(cs->cpu_index);
}
}
@@ -3488,6 +3538,7 @@ static int64_t spapr_get_default_cpu_node_id(const MachineState *ms, int idx)
static const CPUArchIdList *spapr_possible_cpu_arch_ids(MachineState *machine)
{
int i;
+ const char *core_type;
int spapr_max_cores = max_cpus / smp_threads;
MachineClass *mc = MACHINE_GET_CLASS(machine);
@@ -3499,12 +3550,19 @@ static const CPUArchIdList *spapr_possible_cpu_arch_ids(MachineState *machine)
return machine->possible_cpus;
}
+ core_type = spapr_get_cpu_core_type(machine->cpu_type);
+ if (!core_type) {
+ error_report("Unable to find sPAPR CPU Core definition");
+ exit(1);
+ }
+
machine->possible_cpus = g_malloc0(sizeof(CPUArchIdList) +
sizeof(CPUArchId) * spapr_max_cores);
machine->possible_cpus->len = spapr_max_cores;
for (i = 0; i < machine->possible_cpus->len; i++) {
int core_id = i * smp_threads;
+ machine->possible_cpus->cpus[i].type = core_type;
machine->possible_cpus->cpus[i].vcpus_count = smp_threads;
machine->possible_cpus->cpus[i].arch_id = core_id;
machine->possible_cpus->cpus[i].props.has_core_id = true;
@@ -3786,7 +3844,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
mc->default_boot_order = "";
mc->default_ram_size = 512 * M_BYTE;
mc->kvm_type = spapr_kvm_type;
- mc->has_dynamic_sysbus = true;
+ machine_class_allow_dynamic_sysbus_dev(mc, TYPE_SPAPR_PCI_HOST_BRIDGE);
mc->pci_allow_0_address = true;
mc->get_hotplug_handler = spapr_get_hotplug_handler;
hc->pre_plug = spapr_machine_device_pre_plug;
@@ -3819,6 +3877,11 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
* in which LMBs are represented and hot-added
*/
mc->numa_mem_align_shift = 28;
+
+ smc->default_caps.caps[SPAPR_CAP_HTM] = SPAPR_CAP_OFF;
+ smc->default_caps.caps[SPAPR_CAP_VSX] = SPAPR_CAP_ON;
+ smc->default_caps.caps[SPAPR_CAP_DFP] = SPAPR_CAP_ON;
+ spapr_caps_add_properties(smc, &error_abort);
}
static const TypeInfo spapr_machine_info = {
@@ -3896,7 +3959,10 @@ static void spapr_machine_2_11_instance_options(MachineState *machine)
static void spapr_machine_2_11_class_options(MachineClass *mc)
{
+ sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc);
+
spapr_machine_2_12_class_options(mc);
+ smc->default_caps.caps[SPAPR_CAP_HTM] = SPAPR_CAP_ON;
SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_11);
}
diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c
new file mode 100644
index 0000000000..5d52969bd5
--- /dev/null
+++ b/hw/ppc/spapr_caps.c
@@ -0,0 +1,315 @@
+/*
+ * QEMU PowerPC pSeries Logical Partition capabilities handling
+ *
+ * Copyright (c) 2017 David Gibson, Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+#include "sysemu/hw_accel.h"
+#include "target/ppc/cpu.h"
+#include "cpu-models.h"
+#include "kvm_ppc.h"
+
+#include "hw/ppc/spapr.h"
+
+typedef struct sPAPRCapabilityInfo {
+ const char *name;
+ const char *description;
+ int index;
+
+ /* Getter and Setter Function Pointers */
+ ObjectPropertyAccessor *get;
+ ObjectPropertyAccessor *set;
+ const char *type;
+ /* Make sure the virtual hardware can support this capability */
+ void (*apply)(sPAPRMachineState *spapr, uint8_t val, Error **errp);
+} sPAPRCapabilityInfo;
+
+static void spapr_cap_get_bool(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ sPAPRCapabilityInfo *cap = opaque;
+ sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
+ bool value = spapr_get_cap(spapr, cap->index) == SPAPR_CAP_ON;
+
+ visit_type_bool(v, name, &value, errp);
+}
+
+static void spapr_cap_set_bool(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ sPAPRCapabilityInfo *cap = opaque;
+ sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
+ bool value;
+ Error *local_err = NULL;
+
+ visit_type_bool(v, name, &value, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ spapr->cmd_line_caps[cap->index] = true;
+ spapr->eff.caps[cap->index] = value ? SPAPR_CAP_ON : SPAPR_CAP_OFF;
+}
+
+static void cap_htm_apply(sPAPRMachineState *spapr, uint8_t val, Error **errp)
+{
+ if (!val) {
+ /* TODO: We don't support disabling htm yet */
+ return;
+ }
+ if (tcg_enabled()) {
+ error_setg(errp,
+ "No Transactional Memory support in TCG, try cap-htm=off");
+ } else if (kvm_enabled() && !kvmppc_has_cap_htm()) {
+ error_setg(errp,
+"KVM implementation does not support Transactional Memory, try cap-htm=off"
+ );
+ }
+}
+
+static void cap_vsx_apply(sPAPRMachineState *spapr, uint8_t val, Error **errp)
+{
+ PowerPCCPU *cpu = POWERPC_CPU(first_cpu);
+ CPUPPCState *env = &cpu->env;
+
+ if (!val) {
+ /* TODO: We don't support disabling vsx yet */
+ return;
+ }
+ /* Allowable CPUs in spapr_cpu_core.c should already have gotten
+ * rid of anything that doesn't do VMX */
+ g_assert(env->insns_flags & PPC_ALTIVEC);
+ if (!(env->insns_flags2 & PPC2_VSX)) {
+ error_setg(errp, "VSX support not available, try cap-vsx=off");
+ }
+}
+
+static void cap_dfp_apply(sPAPRMachineState *spapr, uint8_t val, Error **errp)
+{
+ PowerPCCPU *cpu = POWERPC_CPU(first_cpu);
+ CPUPPCState *env = &cpu->env;
+
+ if (!val) {
+ /* TODO: We don't support disabling dfp yet */
+ return;
+ }
+ if (!(env->insns_flags2 & PPC2_DFP)) {
+ error_setg(errp, "DFP support not available, try cap-dfp=off");
+ }
+}
+
+
+sPAPRCapabilityInfo capability_table[SPAPR_CAP_NUM] = {
+ [SPAPR_CAP_HTM] = {
+ .name = "htm",
+ .description = "Allow Hardware Transactional Memory (HTM)",
+ .index = SPAPR_CAP_HTM,
+ .get = spapr_cap_get_bool,
+ .set = spapr_cap_set_bool,
+ .type = "bool",
+ .apply = cap_htm_apply,
+ },
+ [SPAPR_CAP_VSX] = {
+ .name = "vsx",
+ .description = "Allow Vector Scalar Extensions (VSX)",
+ .index = SPAPR_CAP_VSX,
+ .get = spapr_cap_get_bool,
+ .set = spapr_cap_set_bool,
+ .type = "bool",
+ .apply = cap_vsx_apply,
+ },
+ [SPAPR_CAP_DFP] = {
+ .name = "dfp",
+ .description = "Allow Decimal Floating Point (DFP)",
+ .index = SPAPR_CAP_DFP,
+ .get = spapr_cap_get_bool,
+ .set = spapr_cap_set_bool,
+ .type = "bool",
+ .apply = cap_dfp_apply,
+ },
+};
+
+static sPAPRCapabilities default_caps_with_cpu(sPAPRMachineState *spapr,
+ CPUState *cs)
+{
+ 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)) {
+ caps.caps[SPAPR_CAP_HTM] = SPAPR_CAP_OFF;
+ }
+
+ if (!ppc_check_compat(cpu, 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;
+ }
+
+ return caps;
+}
+
+int spapr_caps_pre_load(void *opaque)
+{
+ sPAPRMachineState *spapr = opaque;
+
+ /* Set to default so we can tell if this came in with the migration */
+ spapr->mig = spapr->def;
+ return 0;
+}
+
+int spapr_caps_pre_save(void *opaque)
+{
+ sPAPRMachineState *spapr = opaque;
+
+ spapr->mig = spapr->eff;
+ return 0;
+}
+
+/* This has to be called from the top-level spapr post_load, not the
+ * caps specific one. Otherwise it wouldn't be called when the source
+ * caps are all defaults, which could still conflict with overridden
+ * caps on the destination */
+int spapr_caps_post_migration(sPAPRMachineState *spapr)
+{
+ int i;
+ bool ok = true;
+ sPAPRCapabilities dstcaps = spapr->eff;
+ sPAPRCapabilities srccaps;
+
+ srccaps = default_caps_with_cpu(spapr, first_cpu);
+ 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]) {
+ srccaps.caps[i] = spapr->mig.caps[i];
+ }
+ }
+
+ for (i = 0; i < SPAPR_CAP_NUM; i++) {
+ sPAPRCapabilityInfo *info = &capability_table[i];
+
+ if (srccaps.caps[i] > dstcaps.caps[i]) {
+ error_report("cap-%s higher level (%d) in incoming stream than on destination (%d)",
+ info->name, srccaps.caps[i], dstcaps.caps[i]);
+ ok = false;
+ }
+
+ if (srccaps.caps[i] < dstcaps.caps[i]) {
+ warn_report("cap-%s lower level (%d) in incoming stream than on destination (%d)",
+ info->name, srccaps.caps[i], dstcaps.caps[i]);
+ }
+ }
+
+ return ok ? 0 : -EINVAL;
+}
+
+/* Used to generate the migration field and needed function for a spapr cap */
+#define SPAPR_CAP_MIG_STATE(cap, ccap) \
+static bool spapr_cap_##cap##_needed(void *opaque) \
+{ \
+ sPAPRMachineState *spapr = opaque; \
+ \
+ return spapr->cmd_line_caps[SPAPR_CAP_##ccap] && \
+ (spapr->eff.caps[SPAPR_CAP_##ccap] != \
+ spapr->def.caps[SPAPR_CAP_##ccap]); \
+} \
+ \
+const VMStateDescription vmstate_spapr_cap_##cap = { \
+ .name = "spapr/cap/" #cap, \
+ .version_id = 1, \
+ .minimum_version_id = 1, \
+ .needed = spapr_cap_##cap##_needed, \
+ .fields = (VMStateField[]) { \
+ VMSTATE_UINT8(mig.caps[SPAPR_CAP_##ccap], \
+ sPAPRMachineState), \
+ VMSTATE_END_OF_LIST() \
+ }, \
+}
+
+SPAPR_CAP_MIG_STATE(htm, HTM);
+SPAPR_CAP_MIG_STATE(vsx, VSX);
+SPAPR_CAP_MIG_STATE(dfp, DFP);
+
+void spapr_caps_reset(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);
+
+ for (i = 0; i < SPAPR_CAP_NUM; i++) {
+ /* Store the defaults */
+ spapr->def.caps[i] = default_caps.caps[i];
+ /* If not set on the command line then apply the default value */
+ if (!spapr->cmd_line_caps[i]) {
+ spapr->eff.caps[i] = default_caps.caps[i];
+ }
+ }
+
+ /* .. then apply those caps to the virtual hardware */
+
+ 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.
+ */
+ info->apply(spapr, spapr->eff.caps[i], &error_fatal);
+ }
+}
+
+void spapr_caps_add_properties(sPAPRMachineClass *smc, Error **errp)
+{
+ Error *local_err = NULL;
+ ObjectClass *klass = OBJECT_CLASS(smc);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(capability_table); i++) {
+ sPAPRCapabilityInfo *cap = &capability_table[i];
+ const char *name = g_strdup_printf("cap-%s", cap->name);
+ char *desc;
+
+ object_class_property_add(klass, name, cap->type,
+ cap->get, cap->set,
+ NULL, cap, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ desc = g_strdup_printf("%s", cap->description);
+ object_class_property_set_description(klass, name, desc, &local_err);
+ g_free(desc);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ }
+}
diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c
index ac19b2e0b7..590d167b04 100644
--- a/hw/ppc/spapr_cpu_core.c
+++ b/hw/ppc/spapr_cpu_core.c
@@ -44,6 +44,13 @@ static void spapr_cpu_reset(void *opaque)
if (cs != first_cpu) {
env->spr[SPR_LPCR] &= ~pcc->lpcr_pm;
}
+
+ /* Set compatibility mode to match the boot CPU, which was either set
+ * by the machine reset code or by CAS. This should never fail.
+ */
+ if (cs != first_cpu) {
+ ppc_set_compat(cpu, POWERPC_CPU(first_cpu)->compat_pvr, &error_abort);
+ }
}
static void spapr_cpu_destroy(PowerPCCPU *cpu)
diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c
index 695c820911..37f18b3d32 100644
--- a/hw/ppc/spapr_pci.c
+++ b/hw/ppc/spapr_pci.c
@@ -505,7 +505,7 @@ static void rtas_ibm_get_config_addr_info2(PowerPCCPU *cpu,
goto param_error_exit;
}
- rtas_st(rets, 1, (pci_bus_num(pdev->bus) << 16) + 1);
+ rtas_st(rets, 1, (pci_bus_num(pci_get_bus(pdev)) << 16) + 1);
break;
case RTAS_GET_PE_MODE:
rtas_st(rets, 1, RTAS_PE_MODE_SHARED);
@@ -1621,10 +1621,10 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
memory_region_add_subregion(get_system_memory(), sphb->io_win_addr,
&sphb->iowindow);
- bus = pci_register_bus(dev, NULL,
- pci_spapr_set_irq, pci_spapr_map_irq, sphb,
- &sphb->memspace, &sphb->iospace,
- PCI_DEVFN(0, 0), PCI_NUM_PINS, TYPE_PCI_BUS);
+ bus = pci_register_root_bus(dev, NULL,
+ pci_spapr_set_irq, pci_spapr_map_irq, sphb,
+ &sphb->memspace, &sphb->iospace,
+ PCI_DEVFN(0, 0), PCI_NUM_PINS, TYPE_PCI_BUS);
phb->bus = bus;
qbus_set_hotplug_handler(BUS(phb->bus), DEVICE(sphb), NULL);
diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c
index 2b89e1d448..4bb939d3d1 100644
--- a/hw/ppc/spapr_rtas.c
+++ b/hw/ppc/spapr_rtas.c
@@ -163,7 +163,6 @@ static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPRMachineState *spapr,
CPUState *cs = CPU(cpu);
CPUPPCState *env = &cpu->env;
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
- Error *local_err = NULL;
if (!cs->halted) {
rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
@@ -175,14 +174,6 @@ static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPRMachineState *spapr,
* new cpu enters */
kvm_cpu_synchronize_state(cs);
- /* Set compatibility mode to match existing cpus */
- ppc_set_compat(cpu, POWERPC_CPU(first_cpu)->compat_pvr, &local_err);
- if (local_err) {
- error_report_err(local_err);
- rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
- return;
- }
-
env->msr = (1ULL << MSR_SF) | (1ULL << MSR_ME);
/* Enable Power-saving mode Exit Cause exceptions for the new CPU */