#include "qemu/error-report.h"
#include "trace.h"
#include "hw/nmi.h"
+#include "hw/intc/intc.h"
#include "hw/compat.h"
#include "qemu/cutils.h"
#define HTAB_SIZE(spapr) (1ULL << ((spapr)->htab_shift))
-static XICSState *try_create_xics(const char *type, int nr_servers,
- int nr_irqs, Error **errp)
+static int try_create_xics(sPAPRMachineState *spapr, const char *type_ics,
+ const char *type_icp, int nr_servers,
+ int nr_irqs, Error **errp)
{
- Error *err = NULL;
- DeviceState *dev;
+ XICSFabric *xi = XICS_FABRIC(spapr);
+ Error *err = NULL, *local_err = NULL;
+ ICSState *ics = NULL;
+ int i;
- dev = qdev_create(NULL, type);
- qdev_prop_set_uint32(dev, "nr_servers", nr_servers);
- qdev_prop_set_uint32(dev, "nr_irqs", nr_irqs);
- object_property_set_bool(OBJECT(dev), true, "realized", &err);
+ ics = ICS_SIMPLE(object_new(type_ics));
+ object_property_add_child(OBJECT(spapr), "ics", OBJECT(ics), NULL);
+ object_property_set_int(OBJECT(ics), nr_irqs, "nr-irqs", &err);
+ object_property_add_const_link(OBJECT(ics), "xics", OBJECT(xi), NULL);
+ object_property_set_bool(OBJECT(ics), true, "realized", &local_err);
+ error_propagate(&err, local_err);
if (err) {
- error_propagate(errp, err);
- object_unparent(OBJECT(dev));
- return NULL;
+ goto error;
+ }
+
+ spapr->icps = g_malloc0(nr_servers * sizeof(ICPState));
+ spapr->nr_servers = nr_servers;
+
+ for (i = 0; i < nr_servers; i++) {
+ ICPState *icp = &spapr->icps[i];
+
+ object_initialize(icp, sizeof(*icp), type_icp);
+ object_property_add_child(OBJECT(spapr), "icp[*]", OBJECT(icp), NULL);
+ object_property_add_const_link(OBJECT(icp), "xics", OBJECT(xi), NULL);
+ object_property_set_bool(OBJECT(icp), true, "realized", &err);
+ if (err) {
+ goto error;
+ }
+ object_unref(OBJECT(icp));
+ }
+
+ spapr->ics = ics;
+ return 0;
+
+error:
+ error_propagate(errp, err);
+ if (ics) {
+ object_unparent(OBJECT(ics));
}
- return XICS_COMMON(dev);
+ return -1;
}
-static XICSState *xics_system_init(MachineState *machine,
- int nr_servers, int nr_irqs, Error **errp)
+static int xics_system_init(MachineState *machine,
+ int nr_servers, int nr_irqs, Error **errp)
{
- XICSState *xics = NULL;
+ int rc = -1;
if (kvm_enabled()) {
Error *err = NULL;
- if (machine_kernel_irqchip_allowed(machine)) {
- xics = try_create_xics(TYPE_XICS_SPAPR_KVM, nr_servers, nr_irqs,
- &err);
+ if (machine_kernel_irqchip_allowed(machine) &&
+ !xics_kvm_init(SPAPR_MACHINE(machine), errp)) {
+ rc = try_create_xics(SPAPR_MACHINE(machine), TYPE_ICS_KVM,
+ TYPE_KVM_ICP, nr_servers, nr_irqs, &err);
}
- if (machine_kernel_irqchip_required(machine) && !xics) {
+ if (machine_kernel_irqchip_required(machine) && rc < 0) {
error_reportf_err(err,
"kernel_irqchip requested but unavailable: ");
} else {
}
}
- if (!xics) {
- xics = try_create_xics(TYPE_XICS_SPAPR, nr_servers, nr_irqs, errp);
+ if (rc < 0) {
+ xics_spapr_init(SPAPR_MACHINE(machine), errp);
+ rc = try_create_xics(SPAPR_MACHINE(machine), TYPE_ICS_SIMPLE,
+ TYPE_ICP, nr_servers, nr_irqs, errp);
}
- return xics;
+ return rc;
}
static int spapr_fixup_cpu_smt_dt(void *fdt, int offset, PowerPCCPU *cpu,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x80, 0x00,
0x80, 0x00, 0x80, 0x00, 0x00, 0x00 };
+ /* Currently we don't advertise any of the "new" ISAv3.00 functionality */
+ uint8_t pa_features_300[] = { 64, 0,
+ 0xf6, 0x1f, 0xc7, 0xc0, 0x80, 0xf0, /* 0 - 5 */
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, /* 6 - 11 */
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, /* 12 - 17 */
+ 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, /* 18 - 23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 24 - 29 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 30 - 35 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 36 - 41 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 42 - 47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 48 - 53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 54 - 59 */
+ 0x00, 0x00, 0x00, 0x00 }; /* 60 - 63 */
+
uint8_t *pa_features;
size_t pa_size;
- switch (env->mmu_model) {
- case POWERPC_MMU_2_06:
- case POWERPC_MMU_2_06a:
+ switch (POWERPC_MMU_VER(env->mmu_model)) {
+ case POWERPC_MMU_VER_2_06:
pa_features = pa_features_206;
pa_size = sizeof(pa_features_206);
break;
- case POWERPC_MMU_2_07:
- case POWERPC_MMU_2_07a:
+ case POWERPC_MMU_VER_2_07:
pa_features = pa_features_207;
pa_size = sizeof(pa_features_207);
break;
+ case POWERPC_MMU_VER_3_00:
+ pa_features = pa_features_300;
+ pa_size = sizeof(pa_features_300);
+ break;
default:
return;
}
_FDT(fdt_setprop_cell(fdt, 0, "#size-cells", 2));
/* /interrupt controller */
- spapr_dt_xics(spapr->xics, fdt, PHANDLE_XICP);
+ spapr_dt_xics(spapr->nr_servers, fdt, PHANDLE_XICP);
ret = spapr_populate_memory(spapr, fdt);
if (ret < 0) {
}
}
+static uint64_t spapr_get_patbe(PPCVirtualHypervisor *vhyp)
+{
+ sPAPRMachineState *spapr = SPAPR_MACHINE(vhyp);
+
+ return spapr->patb_entry;
+}
+
#define HPTE(_table, _i) (void *)(((uint64_t *)(_table)) + ((_i) * 2))
#define HPTE_VALID(_hpte) (tswap64(*((uint64_t *)(_hpte))) & HPTE64_V_VALID)
#define HPTE_DIRTY(_hpte) (tswap64(*((uint64_t *)(_hpte))) & HPTE64_V_HPTE_DIRTY)
spapr->htab_fd = -1;
}
+static hwaddr spapr_hpt_mask(PPCVirtualHypervisor *vhyp)
+{
+ sPAPRMachineState *spapr = SPAPR_MACHINE(vhyp);
+
+ return HTAB_SIZE(spapr) / HASH_PTEG_SIZE_64 - 1;
+}
+
+static const ppc_hash_pte64_t *spapr_map_hptes(PPCVirtualHypervisor *vhyp,
+ hwaddr ptex, int n)
+{
+ sPAPRMachineState *spapr = SPAPR_MACHINE(vhyp);
+ hwaddr pte_offset = ptex * HASH_PTE_SIZE_64;
+
+ if (!spapr->htab) {
+ /*
+ * HTAB is controlled by KVM. Fetch into temporary buffer
+ */
+ ppc_hash_pte64_t *hptes = g_malloc(n * HASH_PTE_SIZE_64);
+ kvmppc_read_hptes(hptes, ptex, n);
+ return hptes;
+ }
+
+ /*
+ * HTAB is controlled by QEMU. Just point to the internally
+ * accessible PTEG.
+ */
+ return (const ppc_hash_pte64_t *)(spapr->htab + pte_offset);
+}
+
+static void spapr_unmap_hptes(PPCVirtualHypervisor *vhyp,
+ const ppc_hash_pte64_t *hptes,
+ hwaddr ptex, int n)
+{
+ sPAPRMachineState *spapr = SPAPR_MACHINE(vhyp);
+
+ if (!spapr->htab) {
+ g_free((void *)hptes);
+ }
+
+ /* Nothing to do for qemu managed HPT */
+}
+
+static void spapr_store_hpte(PPCVirtualHypervisor *vhyp, hwaddr ptex,
+ uint64_t pte0, uint64_t pte1)
+{
+ sPAPRMachineState *spapr = SPAPR_MACHINE(vhyp);
+ hwaddr offset = ptex * HASH_PTE_SIZE_64;
+
+ if (!spapr->htab) {
+ kvmppc_write_hpte(ptex, pte0, pte1);
+ } else {
+ stq_p(spapr->htab + offset, pte0);
+ stq_p(spapr->htab + offset + HASH_PTE_SIZE_64 / 2, pte1);
+ }
+}
+
static int spapr_hpt_shift_for_ramsize(uint64_t ramsize)
{
int shift;
/* Check for unknown sysbus devices */
foreach_dynamic_sysbus_device(find_unknown_sysbus_device, NULL);
+ spapr->patb_entry = 0;
+
/* Allocate and/or reset the hash page table */
spapr_reallocate_hpt(spapr,
spapr_hpt_shift_for_ramsize(machine->maxram_size),
sPAPRMachineState *spapr = (sPAPRMachineState *)opaque;
int err = 0;
+ if (!object_dynamic_cast(OBJECT(spapr->ics), TYPE_ICS_KVM)) {
+ int i;
+ for (i = 0; i < spapr->nr_servers; i++) {
+ icp_resend(&spapr->icps[i]);
+ }
+ }
+
/* In earlier versions, there was no separate qdev for the PAPR
* RTC, so the RTC offset was stored directly in sPAPREnvironment.
* So when migrating from those versions, poke the incoming offset
},
};
+static bool spapr_patb_entry_needed(void *opaque)
+{
+ sPAPRMachineState *spapr = opaque;
+
+ return !!spapr->patb_entry;
+}
+
+static const VMStateDescription vmstate_spapr_patb_entry = {
+ .name = "spapr_patb_entry",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = spapr_patb_entry_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(patb_entry, sPAPRMachineState),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
static const VMStateDescription vmstate_spapr = {
.name = "spapr",
.version_id = 3,
},
.subsections = (const VMStateDescription*[]) {
&vmstate_spapr_ov5_cas,
+ &vmstate_spapr_patb_entry,
NULL
}
};
/* Consume invalid HPTEs */
while ((index < htabslots)
&& !HPTE_VALID(HPTE(spapr->htab, index))) {
- index++;
CLEAN_HPTE(HPTE(spapr->htab, index));
+ index++;
}
/* Consume valid HPTEs */
chunkstart = index;
while ((index < htabslots) && (index - chunkstart < USHRT_MAX)
&& HPTE_VALID(HPTE(spapr->htab, index))) {
- index++;
CLEAN_HPTE(HPTE(spapr->htab, index));
+ index++;
}
if (index > chunkstart) {
load_limit = MIN(spapr->rma_size, RTAS_MAX_ADDR) - FW_OVERHEAD;
/* Set up Interrupt Controller before we create the VCPUs */
- spapr->xics = xics_system_init(machine,
- DIV_ROUND_UP(max_cpus * smt, smp_threads),
- XICS_IRQS_SPAPR, &error_fatal);
+ xics_system_init(machine, DIV_ROUND_UP(max_cpus * smt, smp_threads),
+ XICS_IRQS_SPAPR, &error_fatal);
/* Set up containers for ibm,client-set-architecture negotiated options */
spapr->ov5 = spapr_ovec_new();
*mmio64 = SPAPR_PCI_BASE + (index + 1) * SPAPR_PCI_MEM64_WIN_SIZE;
}
+static ICSState *spapr_ics_get(XICSFabric *dev, int irq)
+{
+ sPAPRMachineState *spapr = SPAPR_MACHINE(dev);
+
+ return ics_valid_irq(spapr->ics, irq) ? spapr->ics : NULL;
+}
+
+static void spapr_ics_resend(XICSFabric *dev)
+{
+ sPAPRMachineState *spapr = SPAPR_MACHINE(dev);
+
+ ics_resend(spapr->ics);
+}
+
+static ICPState *spapr_icp_get(XICSFabric *xi, int server)
+{
+ sPAPRMachineState *spapr = SPAPR_MACHINE(xi);
+
+ return (server < spapr->nr_servers) ? &spapr->icps[server] : NULL;
+}
+
+static void spapr_pic_print_info(InterruptStatsProvider *obj,
+ Monitor *mon)
+{
+ sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
+ int i;
+
+ for (i = 0; i < spapr->nr_servers; i++) {
+ icp_pic_print_info(&spapr->icps[i], mon);
+ }
+
+ ics_pic_print_info(spapr->ics, mon);
+}
+
static void spapr_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
NMIClass *nc = NMI_CLASS(oc);
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc);
PPCVirtualHypervisorClass *vhc = PPC_VIRTUAL_HYPERVISOR_CLASS(oc);
+ XICSFabricClass *xic = XICS_FABRIC_CLASS(oc);
+ InterruptStatsProviderClass *ispc = INTERRUPT_STATS_PROVIDER_CLASS(oc);
mc->desc = "pSeries Logical Partition (PAPR compliant)";
mc->init = ppc_spapr_init;
mc->reset = ppc_spapr_reset;
mc->block_default_type = IF_SCSI;
- mc->max_cpus = 255;
+ mc->max_cpus = 1024;
mc->no_parallel = 1;
mc->default_boot_order = "";
mc->default_ram_size = 512 * M_BYTE;
nc->nmi_monitor_handler = spapr_nmi;
smc->phb_placement = spapr_phb_placement;
vhc->hypercall = emulate_spapr_hypercall;
+ vhc->hpt_mask = spapr_hpt_mask;
+ vhc->map_hptes = spapr_map_hptes;
+ vhc->unmap_hptes = spapr_unmap_hptes;
+ vhc->store_hpte = spapr_store_hpte;
+ vhc->get_patbe = spapr_get_patbe;
+ xic->ics_get = spapr_ics_get;
+ xic->ics_resend = spapr_ics_resend;
+ xic->icp_get = spapr_icp_get;
+ ispc->print_info = spapr_pic_print_info;
+ /* Force NUMA node memory size to be a multiple of
+ * SPAPR_MEMORY_BLOCK_SIZE (256M) since that's the granularity
+ * in which LMBs are represented and hot-added
+ */
+ mc->numa_mem_align_shift = 28;
}
static const TypeInfo spapr_machine_info = {
{ TYPE_NMI },
{ TYPE_HOTPLUG_HANDLER },
{ TYPE_PPC_VIRTUAL_HYPERVISOR },
+ { TYPE_XICS_FABRIC },
+ { TYPE_INTERRUPT_STATS_PROVIDER },
{ }
},
};
/*
* pseries-2.8
*/
-#define SPAPR_COMPAT_2_8 \
- HW_COMPAT_2_8
+#define SPAPR_COMPAT_2_8 \
+ HW_COMPAT_2_8 \
+ { \
+ .driver = TYPE_SPAPR_PCI_HOST_BRIDGE, \
+ .property = "pcie-extended-configuration-space", \
+ .value = "off", \
+ },
static void spapr_machine_2_8_instance_options(MachineState *machine)
{
{
spapr_machine_2_9_class_options(mc);
SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_8);
+ mc->numa_mem_align_shift = 23;
}
DEFINE_SPAPR_MACHINE(2_8, "2.8", false);