summaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorAnthony Liguori <anthony@codemonkey.ws>2013-10-31 17:01:12 +0100
committerAnthony Liguori <anthony@codemonkey.ws>2013-10-31 17:01:12 +0100
commit1ba1905abd72f34836b153f3348d618da6148f87 (patch)
tree357e9db6aff7f34bf33c167ee6ec07eabd30fd68 /hw
parente2cb2902bacb0efaa4adf680719aa77758dd33cd (diff)
parent3bbf37f2692652cc9d48030a9e7f34e2207429f6 (diff)
downloadqemu-1ba1905abd72f34836b153f3348d618da6148f87.tar.gz
Merge remote-tracking branch 'agraf/ppc-for-upstream' into staging
* agraf/ppc-for-upstream: (29 commits) spapr: Use DeviceClass::fw_name for device tree CPU node target-ppc: Fill in OpenFirmware names for some PowerPCCPU families target-ppc: dump-guest-memory support dump-guest-memory: Check for the correct return value target-ppc: Use #define for max slb entries target-ppc: Check for error on address translation in memsave command target-ppc: Update slb array with correct index values. spapr-pci: enable irqfd for INTx xics-kvm: enable irqfd for MSI xics: Implement H_XIRR_X xics: Implement H_IPOLL xics-kvm: Support for in-kernel XICS interrupt controller xics: add cpu_setup callback xics: split to xics and xics-common xics: add missing const specifiers to TypeInfo xics: convert init() to realize() xics: add pre_save/post_load dispatchers xics: replace fprintf with error_report spapr: move cpu_setup after kvmppc_set_papr xics: move reset and cpu_setup ... Message-id: 1382736474-32128-1-git-send-email-agraf@suse.de Signed-off-by: Anthony Liguori <anthony@codemonkey.ws>
Diffstat (limited to 'hw')
-rw-r--r--hw/intc/Makefile.objs1
-rw-r--r--hw/intc/xics.c327
-rw-r--r--hw/intc/xics_kvm.c494
-rw-r--r--hw/ppc/spapr.c72
-rw-r--r--hw/ppc/spapr_hcall.c6
-rw-r--r--hw/ppc/spapr_pci.c13
6 files changed, 828 insertions, 85 deletions
diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs
index 2851eed25f..47ac44264c 100644
--- a/hw/intc/Makefile.objs
+++ b/hw/intc/Makefile.objs
@@ -23,3 +23,4 @@ obj-$(CONFIG_OMAP) += omap_intc.o
obj-$(CONFIG_OPENPIC_KVM) += openpic_kvm.o
obj-$(CONFIG_SH4) += sh_intc.o
obj-$(CONFIG_XICS) += xics.o
+obj-$(CONFIG_XICS_KVM) += xics_kvm.o
diff --git a/hw/intc/xics.c b/hw/intc/xics.c
index bb018d1829..a333305d3d 100644
--- a/hw/intc/xics.c
+++ b/hw/intc/xics.c
@@ -27,8 +27,148 @@
#include "hw/hw.h"
#include "trace.h"
+#include "qemu/timer.h"
#include "hw/ppc/spapr.h"
#include "hw/ppc/xics.h"
+#include "qemu/error-report.h"
+#include "qapi/visitor.h"
+
+void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu)
+{
+ CPUState *cs = CPU(cpu);
+ CPUPPCState *env = &cpu->env;
+ ICPState *ss = &icp->ss[cs->cpu_index];
+ XICSStateClass *info = XICS_COMMON_GET_CLASS(icp);
+
+ assert(cs->cpu_index < icp->nr_servers);
+
+ if (info->cpu_setup) {
+ info->cpu_setup(icp, cpu);
+ }
+
+ switch (PPC_INPUT(env)) {
+ case PPC_FLAGS_INPUT_POWER7:
+ ss->output = env->irq_inputs[POWER7_INPUT_INT];
+ break;
+
+ case PPC_FLAGS_INPUT_970:
+ ss->output = env->irq_inputs[PPC970_INPUT_INT];
+ break;
+
+ default:
+ error_report("XICS interrupt controller does not support this CPU "
+ "bus model");
+ abort();
+ }
+}
+
+/*
+ * XICS Common class - parent for emulated XICS and KVM-XICS
+ */
+static void xics_common_reset(DeviceState *d)
+{
+ XICSState *icp = XICS_COMMON(d);
+ int i;
+
+ for (i = 0; i < icp->nr_servers; i++) {
+ device_reset(DEVICE(&icp->ss[i]));
+ }
+
+ device_reset(DEVICE(icp->ics));
+}
+
+static void xics_prop_get_nr_irqs(Object *obj, Visitor *v,
+ void *opaque, const char *name, Error **errp)
+{
+ XICSState *icp = XICS_COMMON(obj);
+ int64_t value = icp->nr_irqs;
+
+ visit_type_int(v, &value, name, errp);
+}
+
+static void xics_prop_set_nr_irqs(Object *obj, Visitor *v,
+ void *opaque, const char *name, Error **errp)
+{
+ XICSState *icp = XICS_COMMON(obj);
+ XICSStateClass *info = XICS_COMMON_GET_CLASS(icp);
+ Error *error = NULL;
+ int64_t value;
+
+ visit_type_int(v, &value, name, &error);
+ if (error) {
+ error_propagate(errp, error);
+ return;
+ }
+ if (icp->nr_irqs) {
+ error_setg(errp, "Number of interrupts is already set to %u",
+ icp->nr_irqs);
+ return;
+ }
+
+ assert(info->set_nr_irqs);
+ assert(icp->ics);
+ info->set_nr_irqs(icp, value, errp);
+}
+
+static void xics_prop_get_nr_servers(Object *obj, Visitor *v,
+ void *opaque, const char *name,
+ Error **errp)
+{
+ XICSState *icp = XICS_COMMON(obj);
+ int64_t value = icp->nr_servers;
+
+ visit_type_int(v, &value, name, errp);
+}
+
+static void xics_prop_set_nr_servers(Object *obj, Visitor *v,
+ void *opaque, const char *name,
+ Error **errp)
+{
+ XICSState *icp = XICS_COMMON(obj);
+ XICSStateClass *info = XICS_COMMON_GET_CLASS(icp);
+ Error *error = NULL;
+ int64_t value;
+
+ visit_type_int(v, &value, name, &error);
+ if (error) {
+ error_propagate(errp, error);
+ return;
+ }
+ if (icp->nr_servers) {
+ error_setg(errp, "Number of servers is already set to %u",
+ icp->nr_servers);
+ return;
+ }
+
+ assert(info->set_nr_servers);
+ info->set_nr_servers(icp, value, errp);
+}
+
+static void xics_common_initfn(Object *obj)
+{
+ object_property_add(obj, "nr_irqs", "int",
+ xics_prop_get_nr_irqs, xics_prop_set_nr_irqs,
+ NULL, NULL, NULL);
+ object_property_add(obj, "nr_servers", "int",
+ xics_prop_get_nr_servers, xics_prop_set_nr_servers,
+ NULL, NULL, NULL);
+}
+
+static void xics_common_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->reset = xics_common_reset;
+}
+
+static const TypeInfo xics_common_info = {
+ .name = TYPE_XICS_COMMON,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(XICSState),
+ .class_size = sizeof(XICSStateClass),
+ .instance_init = xics_common_initfn,
+ .class_init = xics_common_class_init,
+};
/*
* ICP: Presentation layer
@@ -153,11 +293,35 @@ static void icp_irq(XICSState *icp, int server, int nr, uint8_t priority)
}
}
+static void icp_dispatch_pre_save(void *opaque)
+{
+ ICPState *ss = opaque;
+ ICPStateClass *info = ICP_GET_CLASS(ss);
+
+ if (info->pre_save) {
+ info->pre_save(ss);
+ }
+}
+
+static int icp_dispatch_post_load(void *opaque, int version_id)
+{
+ ICPState *ss = opaque;
+ ICPStateClass *info = ICP_GET_CLASS(ss);
+
+ if (info->post_load) {
+ return info->post_load(ss, version_id);
+ }
+
+ return 0;
+}
+
static const VMStateDescription vmstate_icp_server = {
.name = "icp/server",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
+ .pre_save = icp_dispatch_pre_save,
+ .post_load = icp_dispatch_post_load,
.fields = (VMStateField []) {
/* Sanity check */
VMSTATE_UINT32(xirr, ICPState),
@@ -187,11 +351,12 @@ static void icp_class_init(ObjectClass *klass, void *data)
dc->vmsd = &vmstate_icp_server;
}
-static TypeInfo icp_info = {
+static const TypeInfo icp_info = {
.name = TYPE_ICP,
.parent = TYPE_DEVICE,
.instance_size = sizeof(ICPState),
.class_init = icp_class_init,
+ .class_size = sizeof(ICPStateClass),
};
/*
@@ -353,10 +518,9 @@ static void ics_reset(DeviceState *dev)
}
}
-static int ics_post_load(void *opaque, int version_id)
+static int ics_post_load(ICSState *ics, int version_id)
{
int i;
- ICSState *ics = opaque;
for (i = 0; i < ics->icp->nr_servers; i++) {
icp_resend(ics->icp, i);
@@ -365,6 +529,28 @@ static int ics_post_load(void *opaque, int version_id)
return 0;
}
+static void ics_dispatch_pre_save(void *opaque)
+{
+ ICSState *ics = opaque;
+ ICSStateClass *info = ICS_GET_CLASS(ics);
+
+ if (info->pre_save) {
+ info->pre_save(ics);
+ }
+}
+
+static int ics_dispatch_post_load(void *opaque, int version_id)
+{
+ ICSState *ics = opaque;
+ ICSStateClass *info = ICS_GET_CLASS(ics);
+
+ if (info->post_load) {
+ return info->post_load(ics, version_id);
+ }
+
+ return 0;
+}
+
static const VMStateDescription vmstate_ics_irq = {
.name = "ics/irq",
.version_id = 1,
@@ -384,7 +570,8 @@ static const VMStateDescription vmstate_ics = {
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
- .post_load = ics_post_load,
+ .pre_save = ics_dispatch_pre_save,
+ .post_load = ics_dispatch_post_load,
.fields = (VMStateField []) {
/* Sanity check */
VMSTATE_UINT32_EQUAL(nr_irqs, ICSState),
@@ -395,31 +582,44 @@ static const VMStateDescription vmstate_ics = {
},
};
-static int ics_realize(DeviceState *dev)
+static void ics_initfn(Object *obj)
+{
+ ICSState *ics = ICS(obj);
+
+ ics->offset = XICS_IRQ_BASE;
+}
+
+static void ics_realize(DeviceState *dev, Error **errp)
{
ICSState *ics = ICS(dev);
+ if (!ics->nr_irqs) {
+ error_setg(errp, "Number of interrupts needs to be greater 0");
+ return;
+ }
ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState));
ics->islsi = g_malloc0(ics->nr_irqs * sizeof(bool));
ics->qirqs = qemu_allocate_irqs(ics_set_irq, ics, ics->nr_irqs);
-
- return 0;
}
static void ics_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
+ ICSStateClass *isc = ICS_CLASS(klass);
- dc->init = ics_realize;
+ dc->realize = ics_realize;
dc->vmsd = &vmstate_ics;
dc->reset = ics_reset;
+ isc->post_load = ics_post_load;
}
-static TypeInfo ics_info = {
+static const TypeInfo ics_info = {
.name = TYPE_ICS,
.parent = TYPE_DEVICE,
.instance_size = sizeof(ICSState),
.class_init = ics_class_init,
+ .class_size = sizeof(ICSStateClass),
+ .instance_init = ics_initfn,
};
/*
@@ -480,6 +680,18 @@ static target_ulong h_xirr(PowerPCCPU *cpu, sPAPREnvironment *spapr,
return H_SUCCESS;
}
+static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ CPUState *cs = CPU(cpu);
+ ICPState *ss = &spapr->icp->ss[cs->cpu_index];
+ uint32_t xirr = icp_accept(ss);
+
+ args[0] = xirr;
+ args[1] = cpu_get_real_ticks();
+ return H_SUCCESS;
+}
+
static target_ulong h_eoi(PowerPCCPU *cpu, sPAPREnvironment *spapr,
target_ulong opcode, target_ulong *args)
{
@@ -490,6 +702,18 @@ static target_ulong h_eoi(PowerPCCPU *cpu, sPAPREnvironment *spapr,
return H_SUCCESS;
}
+static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ CPUState *cs = CPU(cpu);
+ ICPState *ss = &spapr->icp->ss[cs->cpu_index];
+
+ args[0] = ss->xirr;
+ args[1] = ss->mfrr;
+
+ return H_SUCCESS;
+}
+
static void rtas_set_xive(PowerPCCPU *cpu, sPAPREnvironment *spapr,
uint32_t token,
uint32_t nargs, target_ulong args,
@@ -600,48 +824,39 @@ static void rtas_int_on(PowerPCCPU *cpu, sPAPREnvironment *spapr,
* XICS
*/
-static void xics_reset(DeviceState *d)
+static void xics_set_nr_irqs(XICSState *icp, uint32_t nr_irqs, Error **errp)
{
- XICSState *icp = XICS(d);
- int i;
-
- for (i = 0; i < icp->nr_servers; i++) {
- device_reset(DEVICE(&icp->ss[i]));
- }
-
- device_reset(DEVICE(icp->ics));
+ icp->nr_irqs = icp->ics->nr_irqs = nr_irqs;
}
-void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu)
+static void xics_set_nr_servers(XICSState *icp, uint32_t nr_servers,
+ Error **errp)
{
- CPUState *cs = CPU(cpu);
- CPUPPCState *env = &cpu->env;
- ICPState *ss = &icp->ss[cs->cpu_index];
-
- assert(cs->cpu_index < icp->nr_servers);
-
- switch (PPC_INPUT(env)) {
- case PPC_FLAGS_INPUT_POWER7:
- ss->output = env->irq_inputs[POWER7_INPUT_INT];
- break;
+ int i;
- case PPC_FLAGS_INPUT_970:
- ss->output = env->irq_inputs[PPC970_INPUT_INT];
- break;
+ icp->nr_servers = nr_servers;
- default:
- fprintf(stderr, "XICS interrupt controller does not support this CPU "
- "bus model\n");
- abort();
+ icp->ss = g_malloc0(icp->nr_servers*sizeof(ICPState));
+ for (i = 0; i < icp->nr_servers; i++) {
+ char buffer[32];
+ object_initialize(&icp->ss[i], sizeof(icp->ss[i]), TYPE_ICP);
+ snprintf(buffer, sizeof(buffer), "icp[%d]", i);
+ object_property_add_child(OBJECT(icp), buffer, OBJECT(&icp->ss[i]),
+ errp);
}
}
static void xics_realize(DeviceState *dev, Error **errp)
{
XICSState *icp = XICS(dev);
- ICSState *ics = icp->ics;
+ Error *error = NULL;
int i;
+ if (!icp->nr_servers) {
+ error_setg(errp, "Number of servers needs to be greater 0");
+ return;
+ }
+
/* Registration of global state belongs into realize */
spapr_rtas_register("ibm,set-xive", rtas_set_xive);
spapr_rtas_register("ibm,get-xive", rtas_get_xive);
@@ -651,20 +866,22 @@ static void xics_realize(DeviceState *dev, Error **errp)
spapr_register_hypercall(H_CPPR, h_cppr);
spapr_register_hypercall(H_IPI, h_ipi);
spapr_register_hypercall(H_XIRR, h_xirr);
+ spapr_register_hypercall(H_XIRR_X, h_xirr_x);
spapr_register_hypercall(H_EOI, h_eoi);
+ spapr_register_hypercall(H_IPOLL, h_ipoll);
- ics->nr_irqs = icp->nr_irqs;
- ics->offset = XICS_IRQ_BASE;
- ics->icp = icp;
- qdev_init_nofail(DEVICE(ics));
+ object_property_set_bool(OBJECT(icp->ics), true, "realized", &error);
+ if (error) {
+ error_propagate(errp, error);
+ return;
+ }
- icp->ss = g_malloc0(icp->nr_servers*sizeof(ICPState));
for (i = 0; i < icp->nr_servers; i++) {
- char buffer[32];
- object_initialize(&icp->ss[i], sizeof(icp->ss[i]), TYPE_ICP);
- snprintf(buffer, sizeof(buffer), "icp[%d]", i);
- object_property_add_child(OBJECT(icp), buffer, OBJECT(&icp->ss[i]), NULL);
- qdev_init_nofail(DEVICE(&icp->ss[i]));
+ object_property_set_bool(OBJECT(&icp->ss[i]), true, "realized", &error);
+ if (error) {
+ error_propagate(errp, error);
+ return;
+ }
}
}
@@ -674,33 +891,31 @@ static void xics_initfn(Object *obj)
xics->ics = ICS(object_new(TYPE_ICS));
object_property_add_child(obj, "ics", OBJECT(xics->ics), NULL);
+ xics->ics->icp = xics;
}
-static Property xics_properties[] = {
- DEFINE_PROP_UINT32("nr_servers", XICSState, nr_servers, -1),
- DEFINE_PROP_UINT32("nr_irqs", XICSState, nr_irqs, -1),
- DEFINE_PROP_END_OF_LIST(),
-};
-
static void xics_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
+ XICSStateClass *xsc = XICS_CLASS(oc);
dc->realize = xics_realize;
- dc->props = xics_properties;
- dc->reset = xics_reset;
+ xsc->set_nr_irqs = xics_set_nr_irqs;
+ xsc->set_nr_servers = xics_set_nr_servers;
}
static const TypeInfo xics_info = {
.name = TYPE_XICS,
- .parent = TYPE_SYS_BUS_DEVICE,
+ .parent = TYPE_XICS_COMMON,
.instance_size = sizeof(XICSState),
+ .class_size = sizeof(XICSStateClass),
.class_init = xics_class_init,
.instance_init = xics_initfn,
};
static void xics_register_types(void)
{
+ type_register_static(&xics_common_info);
type_register_static(&xics_info);
type_register_static(&ics_info);
type_register_static(&icp_info);
diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c
new file mode 100644
index 0000000000..c203646bd6
--- /dev/null
+++ b/hw/intc/xics_kvm.c
@@ -0,0 +1,494 @@
+/*
+ * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator
+ *
+ * PAPR Virtualized Interrupt System, aka ICS/ICP aka xics, in-kernel emulation
+ *
+ * Copyright (c) 2013 David Gibson, IBM Corporation.
+ *
+ * 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 "hw/hw.h"
+#include "trace.h"
+#include "hw/ppc/spapr.h"
+#include "hw/ppc/xics.h"
+#include "kvm_ppc.h"
+#include "qemu/config-file.h"
+#include "qemu/error-report.h"
+
+#include <sys/ioctl.h>
+
+typedef struct KVMXICSState {
+ XICSState parent_obj;
+
+ uint32_t set_xive_token;
+ uint32_t get_xive_token;
+ uint32_t int_off_token;
+ uint32_t int_on_token;
+ int kernel_xics_fd;
+} KVMXICSState;
+
+/*
+ * ICP-KVM
+ */
+static void icp_get_kvm_state(ICPState *ss)
+{
+ uint64_t state;
+ struct kvm_one_reg reg = {
+ .id = KVM_REG_PPC_ICP_STATE,
+ .addr = (uintptr_t)&state,
+ };
+ int ret;
+
+ /* ICP for this CPU thread is not in use, exiting */
+ if (!ss->cs) {
+ return;
+ }
+
+ ret = kvm_vcpu_ioctl(ss->cs, KVM_GET_ONE_REG, &reg);
+ if (ret != 0) {
+ error_report("Unable to retrieve KVM interrupt controller state"
+ " for CPU %d: %s", ss->cs->cpu_index, strerror(errno));
+ exit(1);
+ }
+
+ ss->xirr = state >> KVM_REG_PPC_ICP_XISR_SHIFT;
+ ss->mfrr = (state >> KVM_REG_PPC_ICP_MFRR_SHIFT)
+ & KVM_REG_PPC_ICP_MFRR_MASK;
+ ss->pending_priority = (state >> KVM_REG_PPC_ICP_PPRI_SHIFT)
+ & KVM_REG_PPC_ICP_PPRI_MASK;
+}
+
+static int icp_set_kvm_state(ICPState *ss, int version_id)
+{
+ uint64_t state;
+ struct kvm_one_reg reg = {
+ .id = KVM_REG_PPC_ICP_STATE,
+ .addr = (uintptr_t)&state,
+ };
+ int ret;
+
+ /* ICP for this CPU thread is not in use, exiting */
+ if (!ss->cs) {
+ return 0;
+ }
+
+ state = ((uint64_t)ss->xirr << KVM_REG_PPC_ICP_XISR_SHIFT)
+ | ((uint64_t)ss->mfrr << KVM_REG_PPC_ICP_MFRR_SHIFT)
+ | ((uint64_t)ss->pending_priority << KVM_REG_PPC_ICP_PPRI_SHIFT);
+
+ ret = kvm_vcpu_ioctl(ss->cs, KVM_SET_ONE_REG, &reg);
+ if (ret != 0) {
+ error_report("Unable to restore KVM interrupt controller state (0x%"
+ PRIx64 ") for CPU %d: %s", state, ss->cs->cpu_index,
+ strerror(errno));
+ return ret;
+ }
+
+ return 0;
+}
+
+static void icp_kvm_reset(DeviceState *dev)
+{
+ ICPState *icp = ICP(dev);
+
+ icp->xirr = 0;
+ icp->pending_priority = 0xff;
+ icp->mfrr = 0xff;
+
+ /* Make all outputs are deasserted */
+ qemu_set_irq(icp->output, 0);
+
+ icp_set_kvm_state(icp, 1);
+}
+
+static void icp_kvm_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ICPStateClass *icpc = ICP_CLASS(klass);
+
+ dc->reset = icp_kvm_reset;
+ icpc->pre_save = icp_get_kvm_state;
+ icpc->post_load = icp_set_kvm_state;
+}
+
+static const TypeInfo icp_kvm_info = {
+ .name = TYPE_KVM_ICP,
+ .parent = TYPE_ICP,
+ .instance_size = sizeof(ICPState),
+ .class_init = icp_kvm_class_init,
+ .class_size = sizeof(ICPStateClass),
+};
+
+/*
+ * ICS-KVM
+ */
+static void ics_get_kvm_state(ICSState *ics)
+{
+ KVMXICSState *icpkvm = KVM_XICS(ics->icp);
+ uint64_t state;
+ struct kvm_device_attr attr = {
+ .flags = 0,
+ .group = KVM_DEV_XICS_GRP_SOURCES,
+ .addr = (uint64_t)(uintptr_t)&state,
+ };
+ int i;
+
+ for (i = 0; i < ics->nr_irqs; i++) {
+ ICSIRQState *irq = &ics->irqs[i];
+ int ret;
+
+ attr.attr = i + ics->offset;
+
+ ret = ioctl(icpkvm->kernel_xics_fd, KVM_GET_DEVICE_ATTR, &attr);
+ if (ret != 0) {
+ error_report("Unable to retrieve KVM interrupt controller state"
+ " for IRQ %d: %s", i + ics->offset, strerror(errno));
+ exit(1);
+ }
+
+ irq->server = state & KVM_XICS_DESTINATION_MASK;
+ irq->saved_priority = (state >> KVM_XICS_PRIORITY_SHIFT)
+ & KVM_XICS_PRIORITY_MASK;
+ /*
+ * To be consistent with the software emulation in xics.c, we
+ * split out the masked state + priority that we get from the
+ * kernel into 'current priority' (0xff if masked) and
+ * 'saved priority' (if masked, this is the priority the
+ * interrupt had before it was masked). Masking and unmasking
+ * are done with the ibm,int-off and ibm,int-on RTAS calls.
+ */
+ if (state & KVM_XICS_MASKED) {
+ irq->priority = 0xff;
+ } else {
+ irq->priority = irq->saved_priority;
+ }
+
+ if (state & KVM_XICS_PENDING) {
+ if (state & KVM_XICS_LEVEL_SENSITIVE) {
+ irq->status |= XICS_STATUS_ASSERTED;
+ } else {
+ /*
+ * A pending edge-triggered interrupt (or MSI)
+ * must have been rejected previously when we
+ * first detected it and tried to deliver it,
+ * so mark it as pending and previously rejected
+ * for consistency with how xics.c works.
+ */
+ irq->status |= XICS_STATUS_MASKED_PENDING
+ | XICS_STATUS_REJECTED;
+ }
+ }
+ }
+}
+
+static int ics_set_kvm_state(ICSState *ics, int version_id)
+{
+ KVMXICSState *icpkvm = KVM_XICS(ics->icp);
+ uint64_t state;
+ struct kvm_device_attr attr = {
+ .flags = 0,
+ .group = KVM_DEV_XICS_GRP_SOURCES,
+ .addr = (uint64_t)(uintptr_t)&state,
+ };
+ int i;
+
+ for (i = 0; i < ics->nr_irqs; i++) {
+ ICSIRQState *irq = &ics->irqs[i];
+ int ret;
+
+ attr.attr = i + ics->offset;
+
+ state = irq->server;
+ state |= (uint64_t)(irq->saved_priority & KVM_XICS_PRIORITY_MASK)
+ << KVM_XICS_PRIORITY_SHIFT;
+ if (irq->priority != irq->saved_priority) {
+ assert(irq->priority == 0xff);
+ state |= KVM_XICS_MASKED;
+ }
+
+ if (ics->islsi[i]) {
+ state |= KVM_XICS_LEVEL_SENSITIVE;
+ if (irq->status & XICS_STATUS_ASSERTED) {
+ state |= KVM_XICS_PENDING;
+ }
+ } else {
+ if (irq->status & XICS_STATUS_MASKED_PENDING) {
+ state |= KVM_XICS_PENDING;
+ }
+ }
+
+ ret = ioctl(icpkvm->kernel_xics_fd, KVM_SET_DEVICE_ATTR, &attr);
+ if (ret != 0) {
+ error_report("Unable to restore KVM interrupt controller state"
+ " for IRQs %d: %s", i + ics->offset, strerror(errno));
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void ics_kvm_set_irq(void *opaque, int srcno, int val)
+{
+ ICSState *ics = opaque;
+ struct kvm_irq_level args;
+ int rc;
+
+ args.irq = srcno + ics->offset;
+ if (!ics->islsi[srcno]) {
+ if (!val) {
+ return;
+ }
+ args.level = KVM_INTERRUPT_SET;
+ } else {
+ args.level = val ? KVM_INTERRUPT_SET_LEVEL : KVM_INTERRUPT_UNSET;
+ }
+ rc = kvm_vm_ioctl(kvm_state, KVM_IRQ_LINE, &args);
+ if (rc < 0) {
+ perror("kvm_irq_line");
+ }
+}
+
+static void ics_kvm_reset(DeviceState *dev)
+{
+ ics_set_kvm_state(ICS(dev), 1);
+}
+
+static void ics_kvm_realize(DeviceState *dev, Error **errp)
+{
+ ICSState *ics = ICS(dev);
+
+ if (!ics->nr_irqs) {
+ error_setg(errp, "Number of interrupts needs to be greater 0");
+ return;
+ }
+ ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState));
+ ics->islsi = g_malloc0(ics->nr_irqs * sizeof(bool));
+ ics->qirqs = qemu_allocate_irqs(ics_kvm_set_irq, ics, ics->nr_irqs);
+}
+
+static void ics_kvm_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ICSStateClass *icsc = ICS_CLASS(klass);
+
+ dc->realize = ics_kvm_realize;
+ dc->reset = ics_kvm_reset;
+ icsc->pre_save = ics_get_kvm_state;
+ icsc->post_load = ics_set_kvm_state;
+}
+
+static const TypeInfo ics_kvm_info = {
+ .name = TYPE_KVM_ICS,
+ .parent = TYPE_ICS,
+ .instance_size = sizeof(ICSState),
+ .class_init = ics_kvm_class_init,
+};
+
+/*
+ * XICS-KVM
+ */
+static void xics_kvm_cpu_setup(XICSState *icp, PowerPCCPU *cpu)
+{
+ CPUState *cs;
+ ICPState *ss;
+ KVMXICSState *icpkvm = KVM_XICS(icp);
+
+ cs = CPU(cpu);
+ ss = &icp->ss[cs->cpu_index];
+
+ assert(cs->cpu_index < icp->nr_servers);
+ if (icpkvm->kernel_xics_fd == -1) {
+ abort();
+ }
+
+ if (icpkvm->kernel_xics_fd != -1) {
+ int ret;
+ struct kvm_enable_cap xics_enable_cap = {
+ .cap = KVM_CAP_IRQ_XICS,
+ .flags = 0,
+ .args = {icpkvm->kernel_xics_fd, cs->cpu_index, 0, 0},
+ };
+
+ ss->cs = cs;
+
+ ret = kvm_vcpu_ioctl(ss->cs, KVM_ENABLE_CAP, &xics_enable_cap);
+ if (ret < 0) {
+ error_report("Unable to connect CPU%d to kernel XICS: %s",
+ cs->cpu_index, strerror(errno));
+ exit(1);
+ }
+ }
+}
+
+static void xics_kvm_set_nr_irqs(XICSState *icp, uint32_t nr_irqs, Error **errp)
+{
+ icp->nr_irqs = icp->ics->nr_irqs = nr_irqs;
+}
+
+static void xics_kvm_set_nr_servers(XICSState *icp, uint32_t nr_servers,
+ Error **errp)
+{
+ int i;
+
+ icp->nr_servers = nr_servers;
+
+ icp->ss = g_malloc0(icp->nr_servers*sizeof(ICPState));
+ for (i = 0; i < icp->nr_servers; i++) {
+ char buffer[32];
+ object_initialize(&icp->ss[i], sizeof(icp->ss[i]), TYPE_KVM_ICP);
+ snprintf(buffer, sizeof(buffer), "icp[%d]", i);
+ object_property_add_child(OBJECT(icp), buffer, OBJECT(&icp->ss[i]),
+ errp);
+ }
+}
+
+static void rtas_dummy(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+ uint32_t token,
+ uint32_t nargs, target_ulong args,
+ uint32_t nret, target_ulong rets)
+{
+ error_report("pseries: %s must never be called for in-kernel XICS",
+ __func__);
+}
+
+static void xics_kvm_realize(DeviceState *dev, Error **errp)
+{
+ KVMXICSState *icpkvm = KVM_XICS(dev);
+ XICSState *icp = XICS_COMMON(dev);
+ int i, rc;
+ Error *error = NULL;
+ struct kvm_create_device xics_create_device = {
+ .type = KVM_DEV_TYPE_XICS,
+ .flags = 0,
+ };
+
+ if (!kvm_enabled() || !kvm_check_extension(kvm_state, KVM_CAP_IRQ_XICS)) {
+ error_setg(errp,
+ "KVM and IRQ_XICS capability must be present for in-kernel XICS");
+ goto fail;
+ }
+
+ icpkvm->set_xive_token = spapr_rtas_register("ibm,set-xive", rtas_dummy);
+ icpkvm->get_xive_token = spapr_rtas_register("ibm,get-xive", rtas_dummy);
+ icpkvm->int_off_token = spapr_rtas_register("ibm,int-off", rtas_dummy);
+ icpkvm->int_on_token = spapr_rtas_register("ibm,int-on", rtas_dummy);
+
+ rc = kvmppc_define_rtas_kernel_token(icpkvm->set_xive_token,
+ "ibm,set-xive");
+ if (rc < 0) {
+ error_setg(errp, "kvmppc_define_rtas_kernel_token: ibm,set-xive");
+ goto fail;
+ }
+
+ rc = kvmppc_define_rtas_kernel_token(icpkvm->get_xive_token,
+ "ibm,get-xive");
+ if (rc < 0) {
+ error_setg(errp, "kvmppc_define_rtas_kernel_token: ibm,get-xive");
+ goto fail;
+ }
+
+ rc = kvmppc_define_rtas_kernel_token(icpkvm->int_on_token, "ibm,int-on");
+ if (rc < 0) {
+ error_setg(errp, "kvmppc_define_rtas_kernel_token: ibm,int-on");
+ goto fail;
+ }
+
+ rc = kvmppc_define_rtas_kernel_token(icpkvm->int_off_token, "ibm,int-off");
+ if (rc < 0) {
+ error_setg(errp, "kvmppc_define_rtas_kernel_token: ibm,int-off");
+ goto fail;
+ }
+
+ /* Create the kernel ICP */
+ rc = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &xics_create_device);
+ if (rc < 0) {
+ error_setg_errno(errp, -rc, "Error on KVM_CREATE_DEVICE for XICS");
+ goto fail;
+ }
+
+ icpkvm->kernel_xics_fd = xics_create_device.fd;
+
+ object_property_set_bool(OBJECT(icp->ics), true, "realized", &error);
+ if (error) {
+ error_propagate(errp, error);
+ goto fail;
+ }
+
+ assert(icp->nr_servers);
+ for (i = 0; i < icp->nr_servers; i++) {
+ object_property_set_bool(OBJECT(&icp->ss[i]), true, "realized", &error);
+ if (error) {
+ error_propagate(errp, error);
+ goto fail;
+ }
+ }
+
+ kvm_kernel_irqchip = true;
+ kvm_irqfds_allowed = true;
+ kvm_msi_via_irqfd_allowed = true;
+ kvm_gsi_direct_mapping = true;
+
+ return;
+
+fail:
+ kvmppc_define_rtas_kernel_token(0, "ibm,set-xive");
+ kvmppc_define_rtas_kernel_token(0, "ibm,get-xive");
+ kvmppc_define_rtas_kernel_token(0, "ibm,int-on");
+ kvmppc_define_rtas_kernel_token(0, "ibm,int-off");
+}
+
+static void xics_kvm_initfn(Object *obj)
+{
+ XICSState *xics = XICS_COMMON(obj);
+
+ xics->ics = ICS(object_new(TYPE_KVM_ICS));
+ object_property_add_child(obj, "ics", OBJECT(xics->ics), NULL);
+ xics->ics->icp = xics;
+}
+
+static void xics_kvm_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ XICSStateClass *xsc = XICS_COMMON_CLASS(oc);
+
+ dc->realize = xics_kvm_realize;
+ xsc->cpu_setup = xics_kvm_cpu_setup;
+ xsc->set_nr_irqs = xics_kvm_set_nr_irqs;
+ xsc->set_nr_servers = xics_kvm_set_nr_servers;
+}
+
+static const TypeInfo xics_kvm_info = {
+ .name = TYPE_KVM_XICS,
+ .parent = TYPE_XICS_COMMON,
+ .instance_size = sizeof(KVMXICSState),
+ .class_init = xics_kvm_class_init,
+ .instance_init = xics_kvm_initfn,
+};
+
+static void xics_kvm_register_types(void)
+{
+ type_register_static(&xics_kvm_info);
+ type_register_static(&ics_kvm_info);
+ type_register_static(&icp_kvm_info);
+}
+
+type_init(xics_kvm_register_types)
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index 004184d841..f76b355150 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -62,7 +62,7 @@
*
* We load our kernel at 4M, leaving space for SLOF initial image
*/
-#define FDT_MAX_SIZE 0x10000
+#define FDT_MAX_SIZE 0x40000
#define RTAS_MAX_SIZE 0x10000
#define FW_MAX_SIZE 0x400000
#define FW_FILE_NAME "slof.bin"
@@ -161,14 +161,33 @@ static XICSState *try_create_xics(const char *type, int nr_servers,
return NULL;
}
- return XICS(dev);
+ return XICS_COMMON(dev);
}
static XICSState *xics_system_init(int nr_servers, int nr_irqs)
{
XICSState *icp = NULL;
- icp = try_create_xics(TYPE_XICS, nr_servers, nr_irqs);
+ if (kvm_enabled()) {
+ QemuOpts *machine_opts = qemu_get_machine_opts();
+ bool irqchip_allowed = qemu_opt_get_bool(machine_opts,
+ "kernel_irqchip", true);
+ bool irqchip_required = qemu_opt_get_bool(machine_opts,
+ "kernel_irqchip", false);
+ if (irqchip_allowed) {
+ icp = try_create_xics(TYPE_KVM_XICS, nr_servers, nr_irqs);
+ }
+
+ if (irqchip_required && !icp) {
+ perror("Failed to create in-kernel XICS\n");
+ abort();
+ }
+ }
+
+ if (!icp) {
+ icp = try_create_xics(TYPE_XICS, nr_servers, nr_irqs);
+ }
+
if (!icp) {
perror("Failed to create XICS\n");
abort();
@@ -185,9 +204,8 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPREnvironment *spapr)
int smt = kvmppc_smt_threads();
uint32_t pft_size_prop[] = {0, cpu_to_be32(spapr->htab_shift)};
- assert(spapr->cpu_model);
-
CPU_FOREACH(cpu) {
+ DeviceClass *dc = DEVICE_GET_CLASS(cpu);
uint32_t associativity[] = {cpu_to_be32(0x5),
cpu_to_be32(0x0),
cpu_to_be32(0x0),
@@ -199,7 +217,7 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPREnvironment *spapr)
continue;
}
- snprintf(cpu_model, 32, "/cpus/%s@%x", spapr->cpu_model,
+ snprintf(cpu_model, 32, "/cpus/%s@%x", dc->fw_name,
cpu->cpu_index);
offset = fdt_path_offset(fdt, cpu_model);
@@ -269,10 +287,10 @@ static size_t create_page_sizes_prop(CPUPPCState *env, uint32_t *prop,
} while (0)
-static void *spapr_create_fdt_skel(const char *cpu_model,
- hwaddr initrd_base,
+static void *spapr_create_fdt_skel(hwaddr initrd_base,
hwaddr initrd_size,
hwaddr kernel_size,
+ bool little_endian,
const char *boot_device,
const char *kernel_cmdline,
uint32_t epow_irq)
@@ -286,7 +304,6 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
char qemu_hypertas_prop[] = "hcall-memop1";
uint32_t refpoints[] = {cpu_to_be32(0x4), cpu_to_be32(0x4)};
uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(smp_cpus)};
- char *modelname;
int i, smt = kvmppc_smt_threads();
unsigned char vec5[] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x80};
@@ -326,6 +343,9 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
cpu_to_be64(kernel_size) };
_FDT((fdt_property(fdt, "qemu,boot-kernel", &kprop, sizeof(kprop))));
+ if (little_endian) {
+ _FDT((fdt_property(fdt, "qemu,boot-kernel-le", NULL, 0)));
+ }
}
if (boot_device) {
_FDT((fdt_property_string(fdt, "qemu,boot-device", boot_device)));
@@ -342,18 +362,10 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
_FDT((fdt_property_cell(fdt, "#address-cells", 0x1)));
_FDT((fdt_property_cell(fdt, "#size-cells", 0x0)));
- modelname = g_strdup(cpu_model);
-
- for (i = 0; i < strlen(modelname); i++) {
- modelname[i] = toupper(modelname[i]);
- }
-
- /* This is needed during FDT finalization */
- spapr->cpu_model = g_strdup(modelname);
-
CPU_FOREACH(cs) {
PowerPCCPU *cpu = POWERPC_CPU(cs);
CPUPPCState *env = &cpu->env;
+ DeviceClass *dc = DEVICE_GET_CLASS(cs);
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs);
int index = cs->cpu_index;
uint32_t servers_prop[smp_threads];
@@ -370,7 +382,7 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
continue;
}
- nodename = g_strdup_printf("%s@%x", modelname, index);
+ nodename = g_strdup_printf("%s@%x", dc->fw_name, index);
_FDT((fdt_begin_node(fdt, nodename)));
@@ -418,6 +430,10 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
_FDT((fdt_property(fdt, "ibm,ppc-interrupt-gserver#s",
gservers_prop, sizeof(gservers_prop))));
+ if (env->spr_cb[SPR_PURR].oea_read) {
+ _FDT((fdt_property(fdt, "ibm,purr", NULL, 0)));
+ }
+
if (env->mmu_model & POWERPC_MMU_1TSEG) {
_FDT((fdt_property(fdt, "ibm,processor-segment-sizes",
segs, sizeof(segs))));
@@ -450,8 +466,6 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
_FDT((fdt_end_node(fdt)));
}
- g_free(modelname);
-
_FDT((fdt_end_node(fdt)));
/* RTAS */
@@ -1102,6 +1116,7 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args)
uint32_t initrd_base = 0;
long kernel_size = 0, initrd_size = 0;
long load_limit, rtas_limit, fw_size;
+ bool kernel_le = false;
char *filename;
msi_supported = true;
@@ -1175,8 +1190,6 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args)
}
env = &cpu->env;
- xics_cpu_setup(spapr->icp, cpu);
-
/* Set time-base frequency to 512 MHz */
cpu_ppc_tb_init(env, TIMEBASE_FREQ);
@@ -1190,6 +1203,8 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args)
kvmppc_set_papr(cpu);
}
+ xics_cpu_setup(spapr->icp, cpu);
+
qemu_register_reset(spapr_cpu_reset, cpu);
}
@@ -1282,6 +1297,12 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args)
kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL,
NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0);
if (kernel_size < 0) {
+ kernel_size = load_elf(kernel_filename,
+ translate_kernel_address, NULL,
+ NULL, &lowaddr, NULL, 0, ELF_MACHINE, 0);
+ kernel_le = kernel_size > 0;
+ }
+ if (kernel_size < 0) {
kernel_size = load_image_targphys(kernel_filename,
KERNEL_LOAD_ADDR,
load_limit - KERNEL_LOAD_ADDR);
@@ -1329,9 +1350,8 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args)
&savevm_htab_handlers, spapr);
/* Prepare the device tree */
- spapr->fdt_skel = spapr_create_fdt_skel(cpu_model,
- initrd_base, initrd_size,
- kernel_size,
+ spapr->fdt_skel = spapr_create_fdt_skel(initrd_base, initrd_size,
+ kernel_size, kernel_le,
boot_device, kernel_cmdline,
spapr->epow_irq);
assert(spapr->fdt_skel != NULL);
diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
index f10ba8a932..f755a53923 100644
--- a/hw/ppc/spapr_hcall.c
+++ b/hw/ppc/spapr_hcall.c
@@ -521,9 +521,9 @@ static target_ulong h_rtas(PowerPCCPU *cpu, sPAPREnvironment *spapr,
target_ulong opcode, target_ulong *args)
{
target_ulong rtas_r3 = args[0];
- uint32_t token = ldl_be_phys(rtas_r3);
- uint32_t nargs = ldl_be_phys(rtas_r3 + 4);
- uint32_t nret = ldl_be_phys(rtas_r3 + 8);
+ uint32_t token = rtas_ld(rtas_r3, 0);
+ uint32_t nargs = rtas_ld(rtas_r3, 1);
+ uint32_t nret = rtas_ld(rtas_r3, 2);
return spapr_rtas_call(cpu, spapr, token, nargs, rtas_r3 + 12,
nret, rtas_r3 + 12 + 4*nargs);
diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c
index 9b6ee32acf..edb4cb0413 100644
--- a/hw/ppc/spapr_pci.c
+++ b/hw/ppc/spapr_pci.c
@@ -432,6 +432,17 @@ static void pci_spapr_set_irq(void *opaque, int irq_num, int level)
qemu_set_irq(spapr_phb_lsi_qirq(phb, irq_num), level);
}
+static PCIINTxRoute spapr_route_intx_pin_to_irq(void *opaque, int pin)
+{
+ sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(opaque);
+ PCIINTxRoute route;
+
+ route.mode = PCI_INTX_ENABLED;
+ route.irq = sphb->lsi_table[pin].irq;
+
+ return route;
+}
+
/*
* MSI/MSIX memory region implementation.
* The handler handles both MSI and MSIX.
@@ -610,6 +621,8 @@ static int spapr_phb_init(SysBusDevice *s)
pci_setup_iommu(bus, spapr_pci_dma_iommu, sphb);
+ pci_bus_set_route_irq_fn(bus, spapr_route_intx_pin_to_irq);
+
QLIST_INSERT_HEAD(&spapr->phbs, sphb, list);
/* Initialize the LSI table */