ppc patch queue 2016-07-01
Here's the current ppc patch queue. This is a fairly large batch, containing: * A number of further preliminary patches towards full hypervisor mode emulation * Some further fixes / cleanups for the recently merged device_add based CPU hotplug * Preliminary patches towards supporting a native (rather than paravirtualized) XICS device. This will be needed to emulate a physical Power machine, including hypervisor capabilities * Assorted bug fixes -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJXdgYTAAoJEGw4ysog2bOS8qIQAKjNHf4U1KAf1cI6SWqtrtOJ y6PVbSZc/ywc3KF/on/o6owt1VidoLe8mkd1rTlQminY0/RXL5Fv+kwIqQ4F54h9 0kKf9GDrLwjKI6IXEeQawzSa8wlfU1b3zjQJhJpztjwfdeTKAydOh5XEujF+hoCB NkESZbJbH1gdWlxSOUD+1+DOxOfcHXsE4aJmqaw9F6l2GSHLdO//t9+KPxYfSY02 b9v6FTUkvZ/2xIQ1k8/0laE0tWqfLpTNCwkRbacHauZu1zUQVZa+fJDx7XgIlcGr 5undbLWy9/kBONQbjN9BeU9pc7XUPeNWKYZx7MX6ClbV2V74OPgjNdDHgcpTQim8 /ThiF8389qZ9tzoL+LPIdaIlOok0/I8NEcp2jfa2W2Z+AQnyjJ7nFJcjDhtRmhsI YHUYtTrceY40scUcucDth4osZ0w/PDNr94qb6HwnYxrekXmptfTMf2dq4i6yoH31 VHQYTyBZ5dYg4DjzYuZLpNjC1Bok683Y5g4MtKf0h0A/LUaKdcHYLtuGw+jV9RtP qC1RZMv2ZmU2jT0Jp2hUcj+S9M1OcitUUwemT2YrMDV0iXiWV6yiK7Rw28VBDLCV TPYoDDK9xlqYsw0wOAM3uS9ywLn4tIRSG6hp6iIh8ZWt8z66KSyRikWnSjAVI+GO Fm6xNz0a/r9UomjPB0wa =KhVy -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/dgibson/tags/ppc-for-2.7-20160701' into staging ppc patch queue 2016-07-01 Here's the current ppc patch queue. This is a fairly large batch, containing: * A number of further preliminary patches towards full hypervisor mode emulation * Some further fixes / cleanups for the recently merged device_add based CPU hotplug * Preliminary patches towards supporting a native (rather than paravirtualized) XICS device. This will be needed to emulate a physical Power machine, including hypervisor capabilities * Assorted bug fixes # gpg: Signature made Fri 01 Jul 2016 06:56:35 BST # gpg: using RSA key 0x6C38CACA20D9B392 # gpg: Good signature from "David Gibson <david@gibson.dropbear.id.au>" # gpg: aka "David Gibson (Red Hat) <dgibson@redhat.com>" # gpg: aka "David Gibson (ozlabs.org) <dgibson@ozlabs.org>" # gpg: WARNING: This key is not certified with sufficiently trusted signatures! # gpg: It is not certain that the signature belongs to the owner. # Primary key fingerprint: 75F4 6586 AE61 A66C C44E 87DC 6C38 CACA 20D9 B392 * remotes/dgibson/tags/ppc-for-2.7-20160701: (23 commits) qmp: fix spapr example of query-hotpluggable-cpus spapr: drop duplicate variable in spapr_core_release() spapr: do proper error propagation in spapr_cpu_core_realize_child() spapr: drop reference on child object during core realization spapr: Restore support for 970MP and POWER8NVL CPU cores target-ppc: gen_pause for instructions: yield, mdoio, mdoom, miso ppc/xics: Replace "icp" with "xics" in most places ppc/xics: Implement H_IPOLL using an accessor ppc/xics: Move SPAPR specific code to a separate file ppc/xics: Rename existing xics to xics_spapr ppc: Fix 64K pages support in full emulation target-ppc: Eliminate redundant and incorrect function booke206_page_size_to_tlb spapr: Restore support for older PowerPC CPU cores spapr: fix write-past-end-of-array error in cpu core device init code hw/ppc/spapr: Add some missing hcall function set strings ppc: Print HSRR0/HSRR1 in "info registers" ppc: LPCR is a HV resource ppc: Initial HDEC support ppc: Enforce setting MSR:EE,IR and DR when MSR:PR is set ppc: Fix conditions for delivering external interrupts to a guest ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
1b756f1abf
@ -49,6 +49,7 @@ CONFIG_ETSEC=y
|
||||
CONFIG_LIBDECNUMBER=y
|
||||
# For pSeries
|
||||
CONFIG_XICS=$(CONFIG_PSERIES)
|
||||
CONFIG_XICS_SPAPR=$(CONFIG_PSERIES)
|
||||
CONFIG_XICS_KVM=$(and $(CONFIG_PSERIES),$(CONFIG_KVM))
|
||||
# For PReP
|
||||
CONFIG_MC146818RTC=y
|
||||
|
@ -30,6 +30,7 @@ obj-$(CONFIG_OPENPIC_KVM) += openpic_kvm.o
|
||||
obj-$(CONFIG_RASPI) += bcm2835_ic.o bcm2836_control.o
|
||||
obj-$(CONFIG_SH4) += sh_intc.o
|
||||
obj-$(CONFIG_XICS) += xics.o
|
||||
obj-$(CONFIG_XICS_SPAPR) += xics_spapr.o
|
||||
obj-$(CONFIG_XICS_KVM) += xics_kvm.o
|
||||
obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
|
||||
obj-$(CONFIG_S390_FLIC) += s390_flic.o
|
||||
|
537
hw/intc/xics.c
537
hw/intc/xics.c
@ -32,12 +32,11 @@
|
||||
#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"
|
||||
|
||||
static int get_cpu_index_by_dt_id(int cpu_dt_id)
|
||||
int xics_get_cpu_index_by_dt_id(int cpu_dt_id)
|
||||
{
|
||||
PowerPCCPU *cpu = ppc_get_vcpu_by_dt_id(cpu_dt_id);
|
||||
|
||||
@ -48,31 +47,31 @@ static int get_cpu_index_by_dt_id(int cpu_dt_id)
|
||||
return -1;
|
||||
}
|
||||
|
||||
void xics_cpu_destroy(XICSState *icp, PowerPCCPU *cpu)
|
||||
void xics_cpu_destroy(XICSState *xics, PowerPCCPU *cpu)
|
||||
{
|
||||
CPUState *cs = CPU(cpu);
|
||||
ICPState *ss = &icp->ss[cs->cpu_index];
|
||||
ICPState *ss = &xics->ss[cs->cpu_index];
|
||||
|
||||
assert(cs->cpu_index < icp->nr_servers);
|
||||
assert(cs->cpu_index < xics->nr_servers);
|
||||
assert(cs == ss->cs);
|
||||
|
||||
ss->output = NULL;
|
||||
ss->cs = NULL;
|
||||
}
|
||||
|
||||
void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu)
|
||||
void xics_cpu_setup(XICSState *xics, PowerPCCPU *cpu)
|
||||
{
|
||||
CPUState *cs = CPU(cpu);
|
||||
CPUPPCState *env = &cpu->env;
|
||||
ICPState *ss = &icp->ss[cs->cpu_index];
|
||||
XICSStateClass *info = XICS_COMMON_GET_CLASS(icp);
|
||||
ICPState *ss = &xics->ss[cs->cpu_index];
|
||||
XICSStateClass *info = XICS_COMMON_GET_CLASS(xics);
|
||||
|
||||
assert(cs->cpu_index < icp->nr_servers);
|
||||
assert(cs->cpu_index < xics->nr_servers);
|
||||
|
||||
ss->cs = cs;
|
||||
|
||||
if (info->cpu_setup) {
|
||||
info->cpu_setup(icp, cpu);
|
||||
info->cpu_setup(xics, cpu);
|
||||
}
|
||||
|
||||
switch (PPC_INPUT(env)) {
|
||||
@ -96,21 +95,21 @@ void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu)
|
||||
*/
|
||||
static void xics_common_reset(DeviceState *d)
|
||||
{
|
||||
XICSState *icp = XICS_COMMON(d);
|
||||
XICSState *xics = XICS_COMMON(d);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < icp->nr_servers; i++) {
|
||||
device_reset(DEVICE(&icp->ss[i]));
|
||||
for (i = 0; i < xics->nr_servers; i++) {
|
||||
device_reset(DEVICE(&xics->ss[i]));
|
||||
}
|
||||
|
||||
device_reset(DEVICE(icp->ics));
|
||||
device_reset(DEVICE(xics->ics));
|
||||
}
|
||||
|
||||
static void xics_prop_get_nr_irqs(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
XICSState *icp = XICS_COMMON(obj);
|
||||
int64_t value = icp->nr_irqs;
|
||||
XICSState *xics = XICS_COMMON(obj);
|
||||
int64_t value = xics->nr_irqs;
|
||||
|
||||
visit_type_int(v, name, &value, errp);
|
||||
}
|
||||
@ -118,8 +117,8 @@ static void xics_prop_get_nr_irqs(Object *obj, Visitor *v, const char *name,
|
||||
static void xics_prop_set_nr_irqs(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
XICSState *icp = XICS_COMMON(obj);
|
||||
XICSStateClass *info = XICS_COMMON_GET_CLASS(icp);
|
||||
XICSState *xics = XICS_COMMON(obj);
|
||||
XICSStateClass *info = XICS_COMMON_GET_CLASS(xics);
|
||||
Error *error = NULL;
|
||||
int64_t value;
|
||||
|
||||
@ -128,23 +127,23 @@ static void xics_prop_set_nr_irqs(Object *obj, Visitor *v, const char *name,
|
||||
error_propagate(errp, error);
|
||||
return;
|
||||
}
|
||||
if (icp->nr_irqs) {
|
||||
if (xics->nr_irqs) {
|
||||
error_setg(errp, "Number of interrupts is already set to %u",
|
||||
icp->nr_irqs);
|
||||
xics->nr_irqs);
|
||||
return;
|
||||
}
|
||||
|
||||
assert(info->set_nr_irqs);
|
||||
assert(icp->ics);
|
||||
info->set_nr_irqs(icp, value, errp);
|
||||
assert(xics->ics);
|
||||
info->set_nr_irqs(xics, value, errp);
|
||||
}
|
||||
|
||||
static void xics_prop_get_nr_servers(Object *obj, Visitor *v,
|
||||
const char *name, void *opaque,
|
||||
Error **errp)
|
||||
{
|
||||
XICSState *icp = XICS_COMMON(obj);
|
||||
int64_t value = icp->nr_servers;
|
||||
XICSState *xics = XICS_COMMON(obj);
|
||||
int64_t value = xics->nr_servers;
|
||||
|
||||
visit_type_int(v, name, &value, errp);
|
||||
}
|
||||
@ -153,8 +152,8 @@ static void xics_prop_set_nr_servers(Object *obj, Visitor *v,
|
||||
const char *name, void *opaque,
|
||||
Error **errp)
|
||||
{
|
||||
XICSState *icp = XICS_COMMON(obj);
|
||||
XICSStateClass *info = XICS_COMMON_GET_CLASS(icp);
|
||||
XICSState *xics = XICS_COMMON(obj);
|
||||
XICSStateClass *info = XICS_COMMON_GET_CLASS(xics);
|
||||
Error *error = NULL;
|
||||
int64_t value;
|
||||
|
||||
@ -163,14 +162,14 @@ static void xics_prop_set_nr_servers(Object *obj, Visitor *v,
|
||||
error_propagate(errp, error);
|
||||
return;
|
||||
}
|
||||
if (icp->nr_servers) {
|
||||
if (xics->nr_servers) {
|
||||
error_setg(errp, "Number of servers is already set to %u",
|
||||
icp->nr_servers);
|
||||
xics->nr_servers);
|
||||
return;
|
||||
}
|
||||
|
||||
assert(info->set_nr_servers);
|
||||
info->set_nr_servers(icp, value, errp);
|
||||
info->set_nr_servers(xics, value, errp);
|
||||
}
|
||||
|
||||
static void xics_common_initfn(Object *obj)
|
||||
@ -213,9 +212,9 @@ static void ics_reject(ICSState *ics, int nr);
|
||||
static void ics_resend(ICSState *ics);
|
||||
static void ics_eoi(ICSState *ics, int nr);
|
||||
|
||||
static void icp_check_ipi(XICSState *icp, int server)
|
||||
static void icp_check_ipi(XICSState *xics, int server)
|
||||
{
|
||||
ICPState *ss = icp->ss + server;
|
||||
ICPState *ss = xics->ss + server;
|
||||
|
||||
if (XISR(ss) && (ss->pending_priority <= ss->mfrr)) {
|
||||
return;
|
||||
@ -224,7 +223,7 @@ static void icp_check_ipi(XICSState *icp, int server)
|
||||
trace_xics_icp_check_ipi(server, ss->mfrr);
|
||||
|
||||
if (XISR(ss)) {
|
||||
ics_reject(icp->ics, XISR(ss));
|
||||
ics_reject(xics->ics, XISR(ss));
|
||||
}
|
||||
|
||||
ss->xirr = (ss->xirr & ~XISR_MASK) | XICS_IPI;
|
||||
@ -232,19 +231,19 @@ static void icp_check_ipi(XICSState *icp, int server)
|
||||
qemu_irq_raise(ss->output);
|
||||
}
|
||||
|
||||
static void icp_resend(XICSState *icp, int server)
|
||||
static void icp_resend(XICSState *xics, int server)
|
||||
{
|
||||
ICPState *ss = icp->ss + server;
|
||||
ICPState *ss = xics->ss + server;
|
||||
|
||||
if (ss->mfrr < CPPR(ss)) {
|
||||
icp_check_ipi(icp, server);
|
||||
icp_check_ipi(xics, server);
|
||||
}
|
||||
ics_resend(icp->ics);
|
||||
ics_resend(xics->ics);
|
||||
}
|
||||
|
||||
static void icp_set_cppr(XICSState *icp, int server, uint8_t cppr)
|
||||
void icp_set_cppr(XICSState *xics, int server, uint8_t cppr)
|
||||
{
|
||||
ICPState *ss = icp->ss + server;
|
||||
ICPState *ss = xics->ss + server;
|
||||
uint8_t old_cppr;
|
||||
uint32_t old_xisr;
|
||||
|
||||
@ -257,26 +256,26 @@ static void icp_set_cppr(XICSState *icp, int server, uint8_t cppr)
|
||||
ss->xirr &= ~XISR_MASK; /* Clear XISR */
|
||||
ss->pending_priority = 0xff;
|
||||
qemu_irq_lower(ss->output);
|
||||
ics_reject(icp->ics, old_xisr);
|
||||
ics_reject(xics->ics, old_xisr);
|
||||
}
|
||||
} else {
|
||||
if (!XISR(ss)) {
|
||||
icp_resend(icp, server);
|
||||
icp_resend(xics, server);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void icp_set_mfrr(XICSState *icp, int server, uint8_t mfrr)
|
||||
void icp_set_mfrr(XICSState *xics, int server, uint8_t mfrr)
|
||||
{
|
||||
ICPState *ss = icp->ss + server;
|
||||
ICPState *ss = xics->ss + server;
|
||||
|
||||
ss->mfrr = mfrr;
|
||||
if (mfrr < CPPR(ss)) {
|
||||
icp_check_ipi(icp, server);
|
||||
icp_check_ipi(xics, server);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t icp_accept(ICPState *ss)
|
||||
uint32_t icp_accept(ICPState *ss)
|
||||
{
|
||||
uint32_t xirr = ss->xirr;
|
||||
|
||||
@ -289,31 +288,39 @@ static uint32_t icp_accept(ICPState *ss)
|
||||
return xirr;
|
||||
}
|
||||
|
||||
static void icp_eoi(XICSState *icp, int server, uint32_t xirr)
|
||||
uint32_t icp_ipoll(ICPState *ss, uint32_t *mfrr)
|
||||
{
|
||||
ICPState *ss = icp->ss + server;
|
||||
if (mfrr) {
|
||||
*mfrr = ss->mfrr;
|
||||
}
|
||||
return ss->xirr;
|
||||
}
|
||||
|
||||
void icp_eoi(XICSState *xics, int server, uint32_t xirr)
|
||||
{
|
||||
ICPState *ss = xics->ss + server;
|
||||
|
||||
/* Send EOI -> ICS */
|
||||
ss->xirr = (ss->xirr & ~CPPR_MASK) | (xirr & CPPR_MASK);
|
||||
trace_xics_icp_eoi(server, xirr, ss->xirr);
|
||||
ics_eoi(icp->ics, xirr & XISR_MASK);
|
||||
ics_eoi(xics->ics, xirr & XISR_MASK);
|
||||
if (!XISR(ss)) {
|
||||
icp_resend(icp, server);
|
||||
icp_resend(xics, server);
|
||||
}
|
||||
}
|
||||
|
||||
static void icp_irq(XICSState *icp, int server, int nr, uint8_t priority)
|
||||
static void icp_irq(XICSState *xics, int server, int nr, uint8_t priority)
|
||||
{
|
||||
ICPState *ss = icp->ss + server;
|
||||
ICPState *ss = xics->ss + server;
|
||||
|
||||
trace_xics_icp_irq(server, nr, priority);
|
||||
|
||||
if ((priority >= CPPR(ss))
|
||||
|| (XISR(ss) && (ss->pending_priority <= priority))) {
|
||||
ics_reject(icp->ics, nr);
|
||||
ics_reject(xics->ics, nr);
|
||||
} else {
|
||||
if (XISR(ss)) {
|
||||
ics_reject(icp->ics, XISR(ss));
|
||||
ics_reject(xics->ics, XISR(ss));
|
||||
}
|
||||
ss->xirr = (ss->xirr & ~XISR_MASK) | (nr & XISR_MASK);
|
||||
ss->pending_priority = priority;
|
||||
@ -390,12 +397,6 @@ static const TypeInfo icp_info = {
|
||||
/*
|
||||
* ICS: Source layer
|
||||
*/
|
||||
static int ics_valid_irq(ICSState *ics, uint32_t nr)
|
||||
{
|
||||
return (nr >= ics->offset)
|
||||
&& (nr < (ics->offset + ics->nr_irqs));
|
||||
}
|
||||
|
||||
static void resend_msi(ICSState *ics, int srcno)
|
||||
{
|
||||
ICSIRQState *irq = ics->irqs + srcno;
|
||||
@ -404,7 +405,7 @@ static void resend_msi(ICSState *ics, int srcno)
|
||||
if (irq->status & XICS_STATUS_REJECTED) {
|
||||
irq->status &= ~XICS_STATUS_REJECTED;
|
||||
if (irq->priority != 0xff) {
|
||||
icp_irq(ics->icp, irq->server, srcno + ics->offset,
|
||||
icp_irq(ics->xics, irq->server, srcno + ics->offset,
|
||||
irq->priority);
|
||||
}
|
||||
}
|
||||
@ -418,7 +419,7 @@ static void resend_lsi(ICSState *ics, int srcno)
|
||||
&& (irq->status & XICS_STATUS_ASSERTED)
|
||||
&& !(irq->status & XICS_STATUS_SENT)) {
|
||||
irq->status |= XICS_STATUS_SENT;
|
||||
icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority);
|
||||
icp_irq(ics->xics, irq->server, srcno + ics->offset, irq->priority);
|
||||
}
|
||||
}
|
||||
|
||||
@ -433,7 +434,7 @@ static void set_irq_msi(ICSState *ics, int srcno, int val)
|
||||
irq->status |= XICS_STATUS_MASKED_PENDING;
|
||||
trace_xics_masked_pending();
|
||||
} else {
|
||||
icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority);
|
||||
icp_irq(ics->xics, irq->server, srcno + ics->offset, irq->priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -472,7 +473,7 @@ static void write_xive_msi(ICSState *ics, int srcno)
|
||||
}
|
||||
|
||||
irq->status &= ~XICS_STATUS_MASKED_PENDING;
|
||||
icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority);
|
||||
icp_irq(ics->xics, irq->server, srcno + ics->offset, irq->priority);
|
||||
}
|
||||
|
||||
static void write_xive_lsi(ICSState *ics, int srcno)
|
||||
@ -480,8 +481,8 @@ static void write_xive_lsi(ICSState *ics, int srcno)
|
||||
resend_lsi(ics, srcno);
|
||||
}
|
||||
|
||||
static void ics_write_xive(ICSState *ics, int nr, int server,
|
||||
uint8_t priority, uint8_t saved_priority)
|
||||
void ics_write_xive(ICSState *ics, int nr, int server,
|
||||
uint8_t priority, uint8_t saved_priority)
|
||||
{
|
||||
int srcno = nr - ics->offset;
|
||||
ICSIRQState *irq = ics->irqs + srcno;
|
||||
@ -557,8 +558,8 @@ static int ics_post_load(ICSState *ics, int version_id)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ics->icp->nr_servers; i++) {
|
||||
icp_resend(ics->icp, i);
|
||||
for (i = 0; i < ics->xics->nr_servers; i++) {
|
||||
icp_resend(ics->xics, i);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -658,14 +659,14 @@ static const TypeInfo ics_info = {
|
||||
/*
|
||||
* Exported functions
|
||||
*/
|
||||
static int xics_find_source(XICSState *icp, int irq)
|
||||
int xics_find_source(XICSState *xics, int irq)
|
||||
{
|
||||
int sources = 1;
|
||||
int src;
|
||||
|
||||
/* FIXME: implement multiple sources */
|
||||
for (src = 0; src < sources; ++src) {
|
||||
ICSState *ics = &icp->ics[src];
|
||||
ICSState *ics = &xics->ics[src];
|
||||
if (ics_valid_irq(ics, irq)) {
|
||||
return src;
|
||||
}
|
||||
@ -674,19 +675,19 @@ static int xics_find_source(XICSState *icp, int irq)
|
||||
return -1;
|
||||
}
|
||||
|
||||
qemu_irq xics_get_qirq(XICSState *icp, int irq)
|
||||
qemu_irq xics_get_qirq(XICSState *xics, int irq)
|
||||
{
|
||||
int src = xics_find_source(icp, irq);
|
||||
int src = xics_find_source(xics, irq);
|
||||
|
||||
if (src >= 0) {
|
||||
ICSState *ics = &icp->ics[src];
|
||||
ICSState *ics = &xics->ics[src];
|
||||
return ics->qirqs[irq - ics->offset];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void ics_set_irq_type(ICSState *ics, int srcno, bool lsi)
|
||||
void ics_set_irq_type(ICSState *ics, int srcno, bool lsi)
|
||||
{
|
||||
assert(!(ics->irqs[srcno].flags & XICS_FLAGS_IRQ_MASK));
|
||||
|
||||
@ -694,401 +695,9 @@ static void ics_set_irq_type(ICSState *ics, int srcno, bool lsi)
|
||||
lsi ? XICS_FLAGS_IRQ_LSI : XICS_FLAGS_IRQ_MSI;
|
||||
}
|
||||
|
||||
#define ICS_IRQ_FREE(ics, srcno) \
|
||||
(!((ics)->irqs[(srcno)].flags & (XICS_FLAGS_IRQ_MASK)))
|
||||
|
||||
static int ics_find_free_block(ICSState *ics, int num, int alignnum)
|
||||
{
|
||||
int first, i;
|
||||
|
||||
for (first = 0; first < ics->nr_irqs; first += alignnum) {
|
||||
if (num > (ics->nr_irqs - first)) {
|
||||
return -1;
|
||||
}
|
||||
for (i = first; i < first + num; ++i) {
|
||||
if (!ICS_IRQ_FREE(ics, i)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == (first + num)) {
|
||||
return first;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int xics_alloc(XICSState *icp, int src, int irq_hint, bool lsi, Error **errp)
|
||||
{
|
||||
ICSState *ics = &icp->ics[src];
|
||||
int irq;
|
||||
|
||||
if (irq_hint) {
|
||||
assert(src == xics_find_source(icp, 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;
|
||||
}
|
||||
|
||||
ics_set_irq_type(ics, irq - ics->offset, lsi);
|
||||
trace_xics_alloc(src, 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 xics_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align,
|
||||
Error **errp)
|
||||
{
|
||||
int i, first = -1;
|
||||
ICSState *ics = &icp->ics[src];
|
||||
|
||||
assert(src == 0);
|
||||
/*
|
||||
* MSIMesage::data is used for storing VIRQ so
|
||||
* it has to be aligned to num to support multiple
|
||||
* MSI vectors. MSI-X is not affected by this.
|
||||
* The hint is used for the first IRQ, the rest should
|
||||
* be allocated continuously.
|
||||
*/
|
||||
if (align) {
|
||||
assert((num == 1) || (num == 2) || (num == 4) ||
|
||||
(num == 8) || (num == 16) || (num == 32));
|
||||
first = ics_find_free_block(ics, num, num);
|
||||
} 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;
|
||||
}
|
||||
|
||||
if (first >= 0) {
|
||||
for (i = first; i < first + num; ++i) {
|
||||
ics_set_irq_type(ics, i, lsi);
|
||||
}
|
||||
}
|
||||
first += ics->offset;
|
||||
|
||||
trace_xics_alloc_block(src, first, num, lsi, align);
|
||||
|
||||
return first;
|
||||
}
|
||||
|
||||
static void ics_free(ICSState *ics, int srcno, int num)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = srcno; i < srcno + num; ++i) {
|
||||
if (ICS_IRQ_FREE(ics, i)) {
|
||||
trace_xics_ics_free_warn(ics - ics->icp->ics, i + ics->offset);
|
||||
}
|
||||
memset(&ics->irqs[i], 0, sizeof(ICSIRQState));
|
||||
}
|
||||
}
|
||||
|
||||
void xics_free(XICSState *icp, int irq, int num)
|
||||
{
|
||||
int src = xics_find_source(icp, irq);
|
||||
|
||||
if (src >= 0) {
|
||||
ICSState *ics = &icp->ics[src];
|
||||
|
||||
/* FIXME: implement multiple sources */
|
||||
assert(src == 0);
|
||||
|
||||
trace_xics_ics_free(ics - icp->ics, irq, num);
|
||||
ics_free(ics, irq - ics->offset, num);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Guest interfaces
|
||||
*/
|
||||
|
||||
static target_ulong h_cppr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||
target_ulong opcode, target_ulong *args)
|
||||
{
|
||||
CPUState *cs = CPU(cpu);
|
||||
target_ulong cppr = args[0];
|
||||
|
||||
icp_set_cppr(spapr->icp, cs->cpu_index, cppr);
|
||||
return H_SUCCESS;
|
||||
}
|
||||
|
||||
static target_ulong h_ipi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||
target_ulong opcode, target_ulong *args)
|
||||
{
|
||||
target_ulong server = get_cpu_index_by_dt_id(args[0]);
|
||||
target_ulong mfrr = args[1];
|
||||
|
||||
if (server >= spapr->icp->nr_servers) {
|
||||
return H_PARAMETER;
|
||||
}
|
||||
|
||||
icp_set_mfrr(spapr->icp, server, mfrr);
|
||||
return H_SUCCESS;
|
||||
}
|
||||
|
||||
static target_ulong h_xirr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||
target_ulong opcode, target_ulong *args)
|
||||
{
|
||||
CPUState *cs = CPU(cpu);
|
||||
uint32_t xirr = icp_accept(spapr->icp->ss + cs->cpu_index);
|
||||
|
||||
args[0] = xirr;
|
||||
return H_SUCCESS;
|
||||
}
|
||||
|
||||
static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *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_host_ticks();
|
||||
return H_SUCCESS;
|
||||
}
|
||||
|
||||
static target_ulong h_eoi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||
target_ulong opcode, target_ulong *args)
|
||||
{
|
||||
CPUState *cs = CPU(cpu);
|
||||
target_ulong xirr = args[0];
|
||||
|
||||
icp_eoi(spapr->icp, cs->cpu_index, xirr);
|
||||
return H_SUCCESS;
|
||||
}
|
||||
|
||||
static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPRMachineState *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, sPAPRMachineState *spapr,
|
||||
uint32_t token,
|
||||
uint32_t nargs, target_ulong args,
|
||||
uint32_t nret, target_ulong rets)
|
||||
{
|
||||
ICSState *ics = spapr->icp->ics;
|
||||
uint32_t nr, server, priority;
|
||||
|
||||
if ((nargs != 3) || (nret != 1)) {
|
||||
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
nr = rtas_ld(args, 0);
|
||||
server = get_cpu_index_by_dt_id(rtas_ld(args, 1));
|
||||
priority = rtas_ld(args, 2);
|
||||
|
||||
if (!ics_valid_irq(ics, nr) || (server >= ics->icp->nr_servers)
|
||||
|| (priority > 0xff)) {
|
||||
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
ics_write_xive(ics, nr, server, priority, priority);
|
||||
|
||||
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
|
||||
}
|
||||
|
||||
static void rtas_get_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||
uint32_t token,
|
||||
uint32_t nargs, target_ulong args,
|
||||
uint32_t nret, target_ulong rets)
|
||||
{
|
||||
ICSState *ics = spapr->icp->ics;
|
||||
uint32_t nr;
|
||||
|
||||
if ((nargs != 1) || (nret != 3)) {
|
||||
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
nr = rtas_ld(args, 0);
|
||||
|
||||
if (!ics_valid_irq(ics, nr)) {
|
||||
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
|
||||
rtas_st(rets, 1, ics->irqs[nr - ics->offset].server);
|
||||
rtas_st(rets, 2, ics->irqs[nr - ics->offset].priority);
|
||||
}
|
||||
|
||||
static void rtas_int_off(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||
uint32_t token,
|
||||
uint32_t nargs, target_ulong args,
|
||||
uint32_t nret, target_ulong rets)
|
||||
{
|
||||
ICSState *ics = spapr->icp->ics;
|
||||
uint32_t nr;
|
||||
|
||||
if ((nargs != 1) || (nret != 1)) {
|
||||
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
nr = rtas_ld(args, 0);
|
||||
|
||||
if (!ics_valid_irq(ics, nr)) {
|
||||
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server, 0xff,
|
||||
ics->irqs[nr - ics->offset].priority);
|
||||
|
||||
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
|
||||
}
|
||||
|
||||
static void rtas_int_on(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||
uint32_t token,
|
||||
uint32_t nargs, target_ulong args,
|
||||
uint32_t nret, target_ulong rets)
|
||||
{
|
||||
ICSState *ics = spapr->icp->ics;
|
||||
uint32_t nr;
|
||||
|
||||
if ((nargs != 1) || (nret != 1)) {
|
||||
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
nr = rtas_ld(args, 0);
|
||||
|
||||
if (!ics_valid_irq(ics, nr)) {
|
||||
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server,
|
||||
ics->irqs[nr - ics->offset].saved_priority,
|
||||
ics->irqs[nr - ics->offset].saved_priority);
|
||||
|
||||
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
|
||||
}
|
||||
|
||||
/*
|
||||
* XICS
|
||||
*/
|
||||
|
||||
static void xics_set_nr_irqs(XICSState *icp, uint32_t nr_irqs, Error **errp)
|
||||
{
|
||||
icp->nr_irqs = icp->ics->nr_irqs = nr_irqs;
|
||||
}
|
||||
|
||||
static void xics_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_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);
|
||||
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(RTAS_IBM_SET_XIVE, "ibm,set-xive", rtas_set_xive);
|
||||
spapr_rtas_register(RTAS_IBM_GET_XIVE, "ibm,get-xive", rtas_get_xive);
|
||||
spapr_rtas_register(RTAS_IBM_INT_OFF, "ibm,int-off", rtas_int_off);
|
||||
spapr_rtas_register(RTAS_IBM_INT_ON, "ibm,int-on", rtas_int_on);
|
||||
|
||||
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);
|
||||
|
||||
object_property_set_bool(OBJECT(icp->ics), true, "realized", &error);
|
||||
if (error) {
|
||||
error_propagate(errp, error);
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void xics_initfn(Object *obj)
|
||||
{
|
||||
XICSState *xics = XICS(obj);
|
||||
|
||||
xics->ics = ICS(object_new(TYPE_ICS));
|
||||
object_property_add_child(obj, "ics", OBJECT(xics->ics), NULL);
|
||||
xics->ics->icp = xics;
|
||||
}
|
||||
|
||||
static void xics_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
XICSStateClass *xsc = XICS_CLASS(oc);
|
||||
|
||||
dc->realize = xics_realize;
|
||||
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_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);
|
||||
}
|
||||
|
@ -145,7 +145,7 @@ static const TypeInfo icp_kvm_info = {
|
||||
*/
|
||||
static void ics_get_kvm_state(ICSState *ics)
|
||||
{
|
||||
KVMXICSState *icpkvm = KVM_XICS(ics->icp);
|
||||
KVMXICSState *xicskvm = XICS_SPAPR_KVM(ics->xics);
|
||||
uint64_t state;
|
||||
struct kvm_device_attr attr = {
|
||||
.flags = 0,
|
||||
@ -160,7 +160,7 @@ static void ics_get_kvm_state(ICSState *ics)
|
||||
|
||||
attr.attr = i + ics->offset;
|
||||
|
||||
ret = ioctl(icpkvm->kernel_xics_fd, KVM_GET_DEVICE_ATTR, &attr);
|
||||
ret = ioctl(xicskvm->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));
|
||||
@ -204,7 +204,7 @@ static void ics_get_kvm_state(ICSState *ics)
|
||||
|
||||
static int ics_set_kvm_state(ICSState *ics, int version_id)
|
||||
{
|
||||
KVMXICSState *icpkvm = KVM_XICS(ics->icp);
|
||||
KVMXICSState *xicskvm = XICS_SPAPR_KVM(ics->xics);
|
||||
uint64_t state;
|
||||
struct kvm_device_attr attr = {
|
||||
.flags = 0,
|
||||
@ -238,7 +238,7 @@ static int ics_set_kvm_state(ICSState *ics, int version_id)
|
||||
}
|
||||
}
|
||||
|
||||
ret = ioctl(icpkvm->kernel_xics_fd, KVM_SET_DEVICE_ATTR, &attr);
|
||||
ret = ioctl(xicskvm->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));
|
||||
@ -324,17 +324,17 @@ static const TypeInfo ics_kvm_info = {
|
||||
/*
|
||||
* XICS-KVM
|
||||
*/
|
||||
static void xics_kvm_cpu_setup(XICSState *icp, PowerPCCPU *cpu)
|
||||
static void xics_kvm_cpu_setup(XICSState *xics, PowerPCCPU *cpu)
|
||||
{
|
||||
CPUState *cs;
|
||||
ICPState *ss;
|
||||
KVMXICSState *icpkvm = KVM_XICS(icp);
|
||||
KVMXICSState *xicskvm = XICS_SPAPR_KVM(xics);
|
||||
|
||||
cs = CPU(cpu);
|
||||
ss = &icp->ss[cs->cpu_index];
|
||||
ss = &xics->ss[cs->cpu_index];
|
||||
|
||||
assert(cs->cpu_index < icp->nr_servers);
|
||||
if (icpkvm->kernel_xics_fd == -1) {
|
||||
assert(cs->cpu_index < xics->nr_servers);
|
||||
if (xicskvm->kernel_xics_fd == -1) {
|
||||
abort();
|
||||
}
|
||||
|
||||
@ -347,11 +347,12 @@ static void xics_kvm_cpu_setup(XICSState *icp, PowerPCCPU *cpu)
|
||||
return;
|
||||
}
|
||||
|
||||
if (icpkvm->kernel_xics_fd != -1) {
|
||||
if (xicskvm->kernel_xics_fd != -1) {
|
||||
int ret;
|
||||
|
||||
ret = kvm_vcpu_enable_cap(cs, KVM_CAP_IRQ_XICS, 0,
|
||||
icpkvm->kernel_xics_fd, kvm_arch_vcpu_id(cs));
|
||||
xicskvm->kernel_xics_fd,
|
||||
kvm_arch_vcpu_id(cs));
|
||||
if (ret < 0) {
|
||||
error_report("Unable to connect CPU%ld to kernel XICS: %s",
|
||||
kvm_arch_vcpu_id(cs), strerror(errno));
|
||||
@ -361,24 +362,25 @@ static void xics_kvm_cpu_setup(XICSState *icp, PowerPCCPU *cpu)
|
||||
}
|
||||
}
|
||||
|
||||
static void xics_kvm_set_nr_irqs(XICSState *icp, uint32_t nr_irqs, Error **errp)
|
||||
static void xics_kvm_set_nr_irqs(XICSState *xics, uint32_t nr_irqs,
|
||||
Error **errp)
|
||||
{
|
||||
icp->nr_irqs = icp->ics->nr_irqs = nr_irqs;
|
||||
xics->nr_irqs = xics->ics->nr_irqs = nr_irqs;
|
||||
}
|
||||
|
||||
static void xics_kvm_set_nr_servers(XICSState *icp, uint32_t nr_servers,
|
||||
static void xics_kvm_set_nr_servers(XICSState *xics, uint32_t nr_servers,
|
||||
Error **errp)
|
||||
{
|
||||
int i;
|
||||
|
||||
icp->nr_servers = nr_servers;
|
||||
xics->nr_servers = nr_servers;
|
||||
|
||||
icp->ss = g_malloc0(icp->nr_servers*sizeof(ICPState));
|
||||
for (i = 0; i < icp->nr_servers; i++) {
|
||||
xics->ss = g_malloc0(xics->nr_servers * sizeof(ICPState));
|
||||
for (i = 0; i < xics->nr_servers; i++) {
|
||||
char buffer[32];
|
||||
object_initialize(&icp->ss[i], sizeof(icp->ss[i]), TYPE_KVM_ICP);
|
||||
object_initialize(&xics->ss[i], sizeof(xics->ss[i]), TYPE_KVM_ICP);
|
||||
snprintf(buffer, sizeof(buffer), "icp[%d]", i);
|
||||
object_property_add_child(OBJECT(icp), buffer, OBJECT(&icp->ss[i]),
|
||||
object_property_add_child(OBJECT(xics), buffer, OBJECT(&xics->ss[i]),
|
||||
errp);
|
||||
}
|
||||
}
|
||||
@ -394,8 +396,8 @@ static void rtas_dummy(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||
|
||||
static void xics_kvm_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
KVMXICSState *icpkvm = KVM_XICS(dev);
|
||||
XICSState *icp = XICS_COMMON(dev);
|
||||
KVMXICSState *xicskvm = XICS_SPAPR_KVM(dev);
|
||||
XICSState *xics = XICS_COMMON(dev);
|
||||
int i, rc;
|
||||
Error *error = NULL;
|
||||
struct kvm_create_device xics_create_device = {
|
||||
@ -445,17 +447,18 @@ static void xics_kvm_realize(DeviceState *dev, Error **errp)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
icpkvm->kernel_xics_fd = xics_create_device.fd;
|
||||
xicskvm->kernel_xics_fd = xics_create_device.fd;
|
||||
|
||||
object_property_set_bool(OBJECT(icp->ics), true, "realized", &error);
|
||||
object_property_set_bool(OBJECT(xics->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);
|
||||
assert(xics->nr_servers);
|
||||
for (i = 0; i < xics->nr_servers; i++) {
|
||||
object_property_set_bool(OBJECT(&xics->ss[i]), true, "realized",
|
||||
&error);
|
||||
if (error) {
|
||||
error_propagate(errp, error);
|
||||
goto fail;
|
||||
@ -481,7 +484,7 @@ static void xics_kvm_initfn(Object *obj)
|
||||
|
||||
xics->ics = ICS(object_new(TYPE_KVM_ICS));
|
||||
object_property_add_child(obj, "ics", OBJECT(xics->ics), NULL);
|
||||
xics->ics->icp = xics;
|
||||
xics->ics->xics = xics;
|
||||
}
|
||||
|
||||
static void xics_kvm_class_init(ObjectClass *oc, void *data)
|
||||
@ -495,8 +498,8 @@ static void xics_kvm_class_init(ObjectClass *oc, void *data)
|
||||
xsc->set_nr_servers = xics_kvm_set_nr_servers;
|
||||
}
|
||||
|
||||
static const TypeInfo xics_kvm_info = {
|
||||
.name = TYPE_KVM_XICS,
|
||||
static const TypeInfo xics_spapr_kvm_info = {
|
||||
.name = TYPE_XICS_SPAPR_KVM,
|
||||
.parent = TYPE_XICS_COMMON,
|
||||
.instance_size = sizeof(KVMXICSState),
|
||||
.class_init = xics_kvm_class_init,
|
||||
@ -505,7 +508,7 @@ static const TypeInfo xics_kvm_info = {
|
||||
|
||||
static void xics_kvm_register_types(void)
|
||||
{
|
||||
type_register_static(&xics_kvm_info);
|
||||
type_register_static(&xics_spapr_kvm_info);
|
||||
type_register_static(&ics_kvm_info);
|
||||
type_register_static(&icp_kvm_info);
|
||||
}
|
||||
|
434
hw/intc/xics_spapr.c
Normal file
434
hw/intc/xics_spapr.c
Normal file
@ -0,0 +1,434 @@
|
||||
/*
|
||||
* QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator
|
||||
*
|
||||
* PAPR Virtualized Interrupt System, aka ICS/ICP aka xics
|
||||
*
|
||||
* Copyright (c) 2010,2011 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 "qemu/osdep.h"
|
||||
#include "cpu.h"
|
||||
#include "hw/hw.h"
|
||||
#include "trace.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/ppc/spapr.h"
|
||||
#include "hw/ppc/xics.h"
|
||||
#include "qapi/visitor.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
/*
|
||||
* Guest interfaces
|
||||
*/
|
||||
|
||||
static target_ulong h_cppr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||
target_ulong opcode, target_ulong *args)
|
||||
{
|
||||
CPUState *cs = CPU(cpu);
|
||||
target_ulong cppr = args[0];
|
||||
|
||||
icp_set_cppr(spapr->xics, cs->cpu_index, cppr);
|
||||
return H_SUCCESS;
|
||||
}
|
||||
|
||||
static target_ulong h_ipi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||
target_ulong opcode, target_ulong *args)
|
||||
{
|
||||
target_ulong server = xics_get_cpu_index_by_dt_id(args[0]);
|
||||
target_ulong mfrr = args[1];
|
||||
|
||||
if (server >= spapr->xics->nr_servers) {
|
||||
return H_PARAMETER;
|
||||
}
|
||||
|
||||
icp_set_mfrr(spapr->xics, server, mfrr);
|
||||
return H_SUCCESS;
|
||||
}
|
||||
|
||||
static target_ulong h_xirr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||
target_ulong opcode, target_ulong *args)
|
||||
{
|
||||
CPUState *cs = CPU(cpu);
|
||||
uint32_t xirr = icp_accept(spapr->xics->ss + cs->cpu_index);
|
||||
|
||||
args[0] = xirr;
|
||||
return H_SUCCESS;
|
||||
}
|
||||
|
||||
static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||
target_ulong opcode, target_ulong *args)
|
||||
{
|
||||
CPUState *cs = CPU(cpu);
|
||||
ICPState *ss = &spapr->xics->ss[cs->cpu_index];
|
||||
uint32_t xirr = icp_accept(ss);
|
||||
|
||||
args[0] = xirr;
|
||||
args[1] = cpu_get_host_ticks();
|
||||
return H_SUCCESS;
|
||||
}
|
||||
|
||||
static target_ulong h_eoi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||
target_ulong opcode, target_ulong *args)
|
||||
{
|
||||
CPUState *cs = CPU(cpu);
|
||||
target_ulong xirr = args[0];
|
||||
|
||||
icp_eoi(spapr->xics, cs->cpu_index, xirr);
|
||||
return H_SUCCESS;
|
||||
}
|
||||
|
||||
static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||
target_ulong opcode, target_ulong *args)
|
||||
{
|
||||
CPUState *cs = CPU(cpu);
|
||||
uint32_t mfrr;
|
||||
uint32_t xirr = icp_ipoll(spapr->xics->ss + cs->cpu_index, &mfrr);
|
||||
|
||||
args[0] = xirr;
|
||||
args[1] = mfrr;
|
||||
|
||||
return H_SUCCESS;
|
||||
}
|
||||
|
||||
static void rtas_set_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||
uint32_t token,
|
||||
uint32_t nargs, target_ulong args,
|
||||
uint32_t nret, target_ulong rets)
|
||||
{
|
||||
ICSState *ics = spapr->xics->ics;
|
||||
uint32_t nr, server, priority;
|
||||
|
||||
if ((nargs != 3) || (nret != 1)) {
|
||||
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
nr = rtas_ld(args, 0);
|
||||
server = xics_get_cpu_index_by_dt_id(rtas_ld(args, 1));
|
||||
priority = rtas_ld(args, 2);
|
||||
|
||||
if (!ics_valid_irq(ics, nr) || (server >= ics->xics->nr_servers)
|
||||
|| (priority > 0xff)) {
|
||||
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
ics_write_xive(ics, nr, server, priority, priority);
|
||||
|
||||
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
|
||||
}
|
||||
|
||||
static void rtas_get_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||
uint32_t token,
|
||||
uint32_t nargs, target_ulong args,
|
||||
uint32_t nret, target_ulong rets)
|
||||
{
|
||||
ICSState *ics = spapr->xics->ics;
|
||||
uint32_t nr;
|
||||
|
||||
if ((nargs != 1) || (nret != 3)) {
|
||||
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
nr = rtas_ld(args, 0);
|
||||
|
||||
if (!ics_valid_irq(ics, nr)) {
|
||||
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
|
||||
rtas_st(rets, 1, ics->irqs[nr - ics->offset].server);
|
||||
rtas_st(rets, 2, ics->irqs[nr - ics->offset].priority);
|
||||
}
|
||||
|
||||
static void rtas_int_off(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||
uint32_t token,
|
||||
uint32_t nargs, target_ulong args,
|
||||
uint32_t nret, target_ulong rets)
|
||||
{
|
||||
ICSState *ics = spapr->xics->ics;
|
||||
uint32_t nr;
|
||||
|
||||
if ((nargs != 1) || (nret != 1)) {
|
||||
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
nr = rtas_ld(args, 0);
|
||||
|
||||
if (!ics_valid_irq(ics, nr)) {
|
||||
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server, 0xff,
|
||||
ics->irqs[nr - ics->offset].priority);
|
||||
|
||||
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
|
||||
}
|
||||
|
||||
static void rtas_int_on(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||
uint32_t token,
|
||||
uint32_t nargs, target_ulong args,
|
||||
uint32_t nret, target_ulong rets)
|
||||
{
|
||||
ICSState *ics = spapr->xics->ics;
|
||||
uint32_t nr;
|
||||
|
||||
if ((nargs != 1) || (nret != 1)) {
|
||||
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
nr = rtas_ld(args, 0);
|
||||
|
||||
if (!ics_valid_irq(ics, nr)) {
|
||||
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server,
|
||||
ics->irqs[nr - ics->offset].saved_priority,
|
||||
ics->irqs[nr - ics->offset].saved_priority);
|
||||
|
||||
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
|
||||
}
|
||||
|
||||
static void xics_spapr_set_nr_irqs(XICSState *xics, uint32_t nr_irqs,
|
||||
Error **errp)
|
||||
{
|
||||
xics->nr_irqs = xics->ics->nr_irqs = nr_irqs;
|
||||
}
|
||||
|
||||
static void xics_spapr_set_nr_servers(XICSState *xics, uint32_t nr_servers,
|
||||
Error **errp)
|
||||
{
|
||||
int i;
|
||||
|
||||
xics->nr_servers = nr_servers;
|
||||
|
||||
xics->ss = g_malloc0(xics->nr_servers * sizeof(ICPState));
|
||||
for (i = 0; i < xics->nr_servers; i++) {
|
||||
char buffer[32];
|
||||
object_initialize(&xics->ss[i], sizeof(xics->ss[i]), TYPE_ICP);
|
||||
snprintf(buffer, sizeof(buffer), "icp[%d]", i);
|
||||
object_property_add_child(OBJECT(xics), buffer, OBJECT(&xics->ss[i]),
|
||||
errp);
|
||||
}
|
||||
}
|
||||
|
||||
static void xics_spapr_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
XICSState *xics = XICS_SPAPR(dev);
|
||||
Error *error = NULL;
|
||||
int i;
|
||||
|
||||
if (!xics->nr_servers) {
|
||||
error_setg(errp, "Number of servers needs to be greater 0");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Registration of global state belongs into realize */
|
||||
spapr_rtas_register(RTAS_IBM_SET_XIVE, "ibm,set-xive", rtas_set_xive);
|
||||
spapr_rtas_register(RTAS_IBM_GET_XIVE, "ibm,get-xive", rtas_get_xive);
|
||||
spapr_rtas_register(RTAS_IBM_INT_OFF, "ibm,int-off", rtas_int_off);
|
||||
spapr_rtas_register(RTAS_IBM_INT_ON, "ibm,int-on", rtas_int_on);
|
||||
|
||||
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);
|
||||
|
||||
object_property_set_bool(OBJECT(xics->ics), true, "realized", &error);
|
||||
if (error) {
|
||||
error_propagate(errp, error);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < xics->nr_servers; i++) {
|
||||
object_property_set_bool(OBJECT(&xics->ss[i]), true, "realized",
|
||||
&error);
|
||||
if (error) {
|
||||
error_propagate(errp, error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void xics_spapr_initfn(Object *obj)
|
||||
{
|
||||
XICSState *xics = XICS_SPAPR(obj);
|
||||
|
||||
xics->ics = ICS(object_new(TYPE_ICS));
|
||||
object_property_add_child(obj, "ics", OBJECT(xics->ics), NULL);
|
||||
xics->ics->xics = xics;
|
||||
}
|
||||
|
||||
static void xics_spapr_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
XICSStateClass *xsc = XICS_SPAPR_CLASS(oc);
|
||||
|
||||
dc->realize = xics_spapr_realize;
|
||||
xsc->set_nr_irqs = xics_spapr_set_nr_irqs;
|
||||
xsc->set_nr_servers = xics_spapr_set_nr_servers;
|
||||
}
|
||||
|
||||
static const TypeInfo xics_spapr_info = {
|
||||
.name = TYPE_XICS_SPAPR,
|
||||
.parent = TYPE_XICS_COMMON,
|
||||
.instance_size = sizeof(XICSState),
|
||||
.class_size = sizeof(XICSStateClass),
|
||||
.class_init = xics_spapr_class_init,
|
||||
.instance_init = xics_spapr_initfn,
|
||||
};
|
||||
|
||||
#define ICS_IRQ_FREE(ics, srcno) \
|
||||
(!((ics)->irqs[(srcno)].flags & (XICS_FLAGS_IRQ_MASK)))
|
||||
|
||||
static int ics_find_free_block(ICSState *ics, int num, int alignnum)
|
||||
{
|
||||
int first, i;
|
||||
|
||||
for (first = 0; first < ics->nr_irqs; first += alignnum) {
|
||||
if (num > (ics->nr_irqs - first)) {
|
||||
return -1;
|
||||
}
|
||||
for (i = first; i < first + num; ++i) {
|
||||
if (!ICS_IRQ_FREE(ics, i)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == (first + num)) {
|
||||
return first;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int xics_spapr_alloc(XICSState *xics, int src, int irq_hint, bool lsi,
|
||||
Error **errp)
|
||||
{
|
||||
ICSState *ics = &xics->ics[src];
|
||||
int irq;
|
||||
|
||||
if (irq_hint) {
|
||||
assert(src == xics_find_source(xics, 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;
|
||||
}
|
||||
|
||||
ics_set_irq_type(ics, irq - ics->offset, lsi);
|
||||
trace_xics_alloc(src, 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 xics_spapr_alloc_block(XICSState *xics, int src, int num, bool lsi,
|
||||
bool align, Error **errp)
|
||||
{
|
||||
int i, first = -1;
|
||||
ICSState *ics = &xics->ics[src];
|
||||
|
||||
assert(src == 0);
|
||||
/*
|
||||
* MSIMesage::data is used for storing VIRQ so
|
||||
* it has to be aligned to num to support multiple
|
||||
* MSI vectors. MSI-X is not affected by this.
|
||||
* The hint is used for the first IRQ, the rest should
|
||||
* be allocated continuously.
|
||||
*/
|
||||
if (align) {
|
||||
assert((num == 1) || (num == 2) || (num == 4) ||
|
||||
(num == 8) || (num == 16) || (num == 32));
|
||||
first = ics_find_free_block(ics, num, num);
|
||||
} 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;
|
||||
}
|
||||
|
||||
if (first >= 0) {
|
||||
for (i = first; i < first + num; ++i) {
|
||||
ics_set_irq_type(ics, i, lsi);
|
||||
}
|
||||
}
|
||||
first += ics->offset;
|
||||
|
||||
trace_xics_alloc_block(src, first, num, lsi, align);
|
||||
|
||||
return first;
|
||||
}
|
||||
|
||||
static void ics_free(ICSState *ics, int srcno, int num)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = srcno; i < srcno + num; ++i) {
|
||||
if (ICS_IRQ_FREE(ics, i)) {
|
||||
trace_xics_ics_free_warn(ics - ics->xics->ics, i + ics->offset);
|
||||
}
|
||||
memset(&ics->irqs[i], 0, sizeof(ICSIRQState));
|
||||
}
|
||||
}
|
||||
|
||||
void xics_spapr_free(XICSState *xics, int irq, int num)
|
||||
{
|
||||
int src = xics_find_source(xics, irq);
|
||||
|
||||
if (src >= 0) {
|
||||
ICSState *ics = &xics->ics[src];
|
||||
|
||||
/* FIXME: implement multiple sources */
|
||||
assert(src == 0);
|
||||
|
||||
trace_xics_ics_free(ics - xics->ics, irq, num);
|
||||
ics_free(ics, irq - ics->offset, num);
|
||||
}
|
||||
}
|
||||
|
||||
static void xics_spapr_register_types(void)
|
||||
{
|
||||
type_register_static(&xics_spapr_info);
|
||||
}
|
||||
|
||||
type_init(xics_spapr_register_types)
|
@ -601,7 +601,7 @@ static int ppce500_prep_device_tree(MachineState *machine,
|
||||
}
|
||||
|
||||
/* Create -kernel TLB entries for BookE. */
|
||||
static inline hwaddr booke206_page_size_to_tlb(uint64_t size)
|
||||
hwaddr booke206_page_size_to_tlb(uint64_t size)
|
||||
{
|
||||
return 63 - clz64(size >> 10);
|
||||
}
|
||||
|
@ -26,4 +26,6 @@ typedef struct PPCE500Params {
|
||||
|
||||
void ppce500_init(MachineState *machine, PPCE500Params *params);
|
||||
|
||||
hwaddr booke206_page_size_to_tlb(uint64_t size);
|
||||
|
||||
#endif
|
||||
|
17
hw/ppc/ppc.c
17
hw/ppc/ppc.c
@ -699,9 +699,18 @@ static inline void cpu_ppc_decr_lower(PowerPCCPU *cpu)
|
||||
|
||||
static inline void cpu_ppc_hdecr_excp(PowerPCCPU *cpu)
|
||||
{
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
/* Raise it */
|
||||
LOG_TB("raise decrementer exception\n");
|
||||
ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 1);
|
||||
LOG_TB("raise hv decrementer exception\n");
|
||||
|
||||
/* The architecture specifies that we don't deliver HDEC
|
||||
* interrupts in a PM state. Not only they don't cause a
|
||||
* wakeup but they also get effectively discarded.
|
||||
*/
|
||||
if (!env->in_pm_state) {
|
||||
ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void cpu_ppc_hdecr_lower(PowerPCCPU *cpu)
|
||||
@ -928,9 +937,7 @@ clk_setup_cb cpu_ppc_tb_init (CPUPPCState *env, uint32_t freq)
|
||||
}
|
||||
/* Create new timer */
|
||||
tb_env->decr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_ppc_decr_cb, cpu);
|
||||
if (0) {
|
||||
/* XXX: find a suitable condition to enable the hypervisor decrementer
|
||||
*/
|
||||
if (env->has_hv_mode) {
|
||||
tb_env->hdecr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_ppc_hdecr_cb,
|
||||
cpu);
|
||||
} else {
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "e500.h"
|
||||
|
||||
#define MAX_CPUS 32
|
||||
|
||||
@ -72,12 +73,6 @@ static void spin_reset(void *opaque)
|
||||
}
|
||||
}
|
||||
|
||||
/* Create -kernel TLB entries for BookE, linearly spanning 256MB. */
|
||||
static inline hwaddr booke206_page_size_to_tlb(uint64_t size)
|
||||
{
|
||||
return ctz32(size >> 10) >> 1;
|
||||
}
|
||||
|
||||
static void mmubooke_create_initial_mapping(CPUPPCState *env,
|
||||
target_ulong va,
|
||||
hwaddr pa,
|
||||
|
@ -116,15 +116,16 @@ static XICSState *try_create_xics(const char *type, int nr_servers,
|
||||
static XICSState *xics_system_init(MachineState *machine,
|
||||
int nr_servers, int nr_irqs, Error **errp)
|
||||
{
|
||||
XICSState *icp = NULL;
|
||||
XICSState *xics = NULL;
|
||||
|
||||
if (kvm_enabled()) {
|
||||
Error *err = NULL;
|
||||
|
||||
if (machine_kernel_irqchip_allowed(machine)) {
|
||||
icp = try_create_xics(TYPE_KVM_XICS, nr_servers, nr_irqs, &err);
|
||||
xics = try_create_xics(TYPE_XICS_SPAPR_KVM, nr_servers, nr_irqs,
|
||||
&err);
|
||||
}
|
||||
if (machine_kernel_irqchip_required(machine) && !icp) {
|
||||
if (machine_kernel_irqchip_required(machine) && !xics) {
|
||||
error_reportf_err(err,
|
||||
"kernel_irqchip requested but unavailable: ");
|
||||
} else {
|
||||
@ -132,11 +133,11 @@ static XICSState *xics_system_init(MachineState *machine,
|
||||
}
|
||||
}
|
||||
|
||||
if (!icp) {
|
||||
icp = try_create_xics(TYPE_XICS, nr_servers, nr_irqs, errp);
|
||||
if (!xics) {
|
||||
xics = try_create_xics(TYPE_XICS_SPAPR, nr_servers, nr_irqs, errp);
|
||||
}
|
||||
|
||||
return icp;
|
||||
return xics;
|
||||
}
|
||||
|
||||
static int spapr_fixup_cpu_smt_dt(void *fdt, int offset, PowerPCCPU *cpu,
|
||||
@ -339,6 +340,9 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base,
|
||||
add_str(hypertas, "hcall-splpar");
|
||||
add_str(hypertas, "hcall-bulk");
|
||||
add_str(hypertas, "hcall-set-mode");
|
||||
add_str(hypertas, "hcall-sprg0");
|
||||
add_str(hypertas, "hcall-copy");
|
||||
add_str(hypertas, "hcall-debug");
|
||||
add_str(qemu_hypertas, "hcall-memop1");
|
||||
|
||||
fdt = g_malloc0(FDT_MAX_SIZE);
|
||||
@ -1779,9 +1783,9 @@ static void ppc_spapr_init(MachineState *machine)
|
||||
load_limit = MIN(spapr->rma_size, RTAS_MAX_ADDR) - FW_OVERHEAD;
|
||||
|
||||
/* Set up Interrupt Controller before we create the VCPUs */
|
||||
spapr->icp = xics_system_init(machine,
|
||||
DIV_ROUND_UP(max_cpus * smt, smp_threads),
|
||||
XICS_IRQS, &error_fatal);
|
||||
spapr->xics = xics_system_init(machine,
|
||||
DIV_ROUND_UP(max_cpus * smt, smp_threads),
|
||||
XICS_IRQS_SPAPR, &error_fatal);
|
||||
|
||||
if (smc->dr_lmb_enabled) {
|
||||
spapr_validate_node_memory(machine, &error_fatal);
|
||||
|
@ -42,7 +42,7 @@ static void spapr_cpu_destroy(PowerPCCPU *cpu)
|
||||
{
|
||||
sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
|
||||
|
||||
xics_cpu_destroy(spapr->icp, cpu);
|
||||
xics_cpu_destroy(spapr->xics, cpu);
|
||||
qemu_unregister_reset(spapr_cpu_reset, cpu);
|
||||
}
|
||||
|
||||
@ -76,7 +76,7 @@ void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
xics_cpu_setup(spapr->icp, cpu);
|
||||
xics_cpu_setup(spapr->xics, cpu);
|
||||
|
||||
qemu_register_reset(spapr_cpu_reset, cpu);
|
||||
spapr_cpu_reset(cpu);
|
||||
@ -102,7 +102,6 @@ static void spapr_core_release(DeviceState *dev, void *opaque)
|
||||
const char *typename = object_class_get_name(sc->cpu_class);
|
||||
size_t size = object_type_get_instance_size(typename);
|
||||
sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
|
||||
sPAPRCPUCore *core = SPAPR_CPU_CORE(OBJECT(dev));
|
||||
CPUCore *cc = CPU_CORE(dev);
|
||||
int smt = kvmppc_smt_threads();
|
||||
int i;
|
||||
@ -120,7 +119,7 @@ static void spapr_core_release(DeviceState *dev, void *opaque)
|
||||
|
||||
spapr->cores[cc->core_id / smt] = NULL;
|
||||
|
||||
g_free(core->threads);
|
||||
g_free(sc->threads);
|
||||
object_unparent(OBJECT(dev));
|
||||
}
|
||||
|
||||
@ -262,18 +261,20 @@ out:
|
||||
|
||||
static int spapr_cpu_core_realize_child(Object *child, void *opaque)
|
||||
{
|
||||
Error **errp = opaque;
|
||||
Error **errp = opaque, *local_err = NULL;
|
||||
sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
|
||||
CPUState *cs = CPU(child);
|
||||
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||
|
||||
object_property_set_bool(child, true, "realized", errp);
|
||||
if (*errp) {
|
||||
object_property_set_bool(child, true, "realized", &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
spapr_cpu_init(spapr, cpu, errp);
|
||||
if (*errp) {
|
||||
spapr_cpu_init(spapr, cpu, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@ -300,6 +301,7 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp)
|
||||
if (local_err) {
|
||||
goto err;
|
||||
}
|
||||
object_unref(obj);
|
||||
}
|
||||
object_child_foreach(OBJECT(dev), spapr_cpu_core_realize_child, &local_err);
|
||||
if (local_err) {
|
||||
@ -309,10 +311,9 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp)
|
||||
}
|
||||
|
||||
err:
|
||||
while (i >= 0) {
|
||||
while (--i >= 0) {
|
||||
obj = sc->threads + i * size;
|
||||
object_unparent(obj);
|
||||
i--;
|
||||
}
|
||||
g_free(sc->threads);
|
||||
error_propagate(errp, local_err);
|
||||
@ -326,7 +327,6 @@ static void spapr_cpu_core_class_init(ObjectClass *oc, void *data)
|
||||
|
||||
/*
|
||||
* instance_init routines from different flavours of sPAPR CPU cores.
|
||||
* TODO: Add support for 'host' core type.
|
||||
*/
|
||||
#define SPAPR_CPU_CORE_INITFN(_type, _fname) \
|
||||
static void glue(glue(spapr_cpu_core_, _fname), _initfn(Object *obj)) \
|
||||
@ -339,10 +339,15 @@ static void glue(glue(spapr_cpu_core_, _fname), _initfn(Object *obj)) \
|
||||
core->cpu_class = oc; \
|
||||
}
|
||||
|
||||
SPAPR_CPU_CORE_INITFN(970mp_v1.0, 970MP_v10);
|
||||
SPAPR_CPU_CORE_INITFN(970mp_v1.1, 970MP_v11);
|
||||
SPAPR_CPU_CORE_INITFN(970_v2.2, 970);
|
||||
SPAPR_CPU_CORE_INITFN(POWER5+_v2.1, POWER5plus);
|
||||
SPAPR_CPU_CORE_INITFN(POWER7_v2.3, POWER7);
|
||||
SPAPR_CPU_CORE_INITFN(POWER7+_v2.1, POWER7plus);
|
||||
SPAPR_CPU_CORE_INITFN(POWER8_v2.0, POWER8);
|
||||
SPAPR_CPU_CORE_INITFN(POWER8E_v2.1, POWER8E);
|
||||
SPAPR_CPU_CORE_INITFN(POWER8NVL_v1.0, POWER8NVL);
|
||||
|
||||
typedef struct SPAPRCoreInfo {
|
||||
const char *name;
|
||||
@ -350,6 +355,21 @@ typedef struct SPAPRCoreInfo {
|
||||
} SPAPRCoreInfo;
|
||||
|
||||
static const SPAPRCoreInfo spapr_cores[] = {
|
||||
/* 970 and aliaes */
|
||||
{ .name = "970_v2.2", .initfn = spapr_cpu_core_970_initfn },
|
||||
{ .name = "970", .initfn = spapr_cpu_core_970_initfn },
|
||||
|
||||
/* 970MP variants and aliases */
|
||||
{ .name = "970MP_v1.0", .initfn = spapr_cpu_core_970MP_v10_initfn },
|
||||
{ .name = "970mp_v1.0", .initfn = spapr_cpu_core_970MP_v10_initfn },
|
||||
{ .name = "970MP_v1.1", .initfn = spapr_cpu_core_970MP_v11_initfn },
|
||||
{ .name = "970mp_v1.1", .initfn = spapr_cpu_core_970MP_v11_initfn },
|
||||
{ .name = "970mp", .initfn = spapr_cpu_core_970MP_v11_initfn },
|
||||
|
||||
/* POWER5 and aliases */
|
||||
{ .name = "POWER5+_v2.1", .initfn = spapr_cpu_core_POWER5plus_initfn },
|
||||
{ .name = "POWER5+", .initfn = spapr_cpu_core_POWER5plus_initfn },
|
||||
|
||||
/* POWER7 and aliases */
|
||||
{ .name = "POWER7_v2.3", .initfn = spapr_cpu_core_POWER7_initfn },
|
||||
{ .name = "POWER7", .initfn = spapr_cpu_core_POWER7_initfn },
|
||||
@ -367,6 +387,10 @@ static const SPAPRCoreInfo spapr_cores[] = {
|
||||
{ .name = "POWER8E_v2.1", .initfn = spapr_cpu_core_POWER8E_initfn },
|
||||
{ .name = "POWER8E", .initfn = spapr_cpu_core_POWER8E_initfn },
|
||||
|
||||
/* POWER8NVL and aliases */
|
||||
{ .name = "POWER8NVL_v1.0", .initfn = spapr_cpu_core_POWER8NVL_initfn },
|
||||
{ .name = "POWER8NVL", .initfn = spapr_cpu_core_POWER8NVL_initfn },
|
||||
|
||||
{ .name = NULL }
|
||||
};
|
||||
|
||||
|
@ -386,7 +386,7 @@ static void spapr_powerdown_req(Notifier *n, void *opaque)
|
||||
|
||||
rtas_event_log_queue(RTAS_LOG_TYPE_EPOW, new_epow, true);
|
||||
|
||||
qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq));
|
||||
qemu_irq_pulse(xics_get_qirq(spapr->xics, spapr->check_exception_irq));
|
||||
}
|
||||
|
||||
static void spapr_hotplug_set_signalled(uint32_t drc_index)
|
||||
@ -468,7 +468,7 @@ static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action,
|
||||
|
||||
rtas_event_log_queue(RTAS_LOG_TYPE_HOTPLUG, new_hp, true);
|
||||
|
||||
qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq));
|
||||
qemu_irq_pulse(xics_get_qirq(spapr->xics, spapr->check_exception_irq));
|
||||
}
|
||||
|
||||
void spapr_hotplug_req_add_by_index(sPAPRDRConnector *drc)
|
||||
@ -551,7 +551,7 @@ static void check_exception(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||
* interrupts.
|
||||
*/
|
||||
if (rtas_event_log_contains(mask, true)) {
|
||||
qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq));
|
||||
qemu_irq_pulse(xics_get_qirq(spapr->xics, spapr->check_exception_irq));
|
||||
}
|
||||
|
||||
return;
|
||||
@ -603,7 +603,7 @@ out_no_events:
|
||||
void spapr_events_init(sPAPRMachineState *spapr)
|
||||
{
|
||||
QTAILQ_INIT(&spapr->pending_events);
|
||||
spapr->check_exception_irq = xics_alloc(spapr->icp, 0, 0, false,
|
||||
spapr->check_exception_irq = xics_spapr_alloc(spapr->xics, 0, 0, false,
|
||||
&error_fatal);
|
||||
spapr->epow_notifier.notify = spapr_powerdown_req;
|
||||
qemu_register_powerdown_notifier(&spapr->epow_notifier);
|
||||
|
@ -322,7 +322,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||
return;
|
||||
}
|
||||
|
||||
xics_free(spapr->icp, msi->first_irq, msi->num);
|
||||
xics_spapr_free(spapr->xics, msi->first_irq, msi->num);
|
||||
if (msi_present(pdev)) {
|
||||
spapr_msi_setmsg(pdev, 0, false, 0, 0);
|
||||
}
|
||||
@ -360,7 +360,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||
}
|
||||
|
||||
/* Allocate MSIs */
|
||||
irq = xics_alloc_block(spapr->icp, 0, req_num, false,
|
||||
irq = xics_spapr_alloc_block(spapr->xics, 0, req_num, false,
|
||||
ret_intr_type == RTAS_TYPE_MSI, &err);
|
||||
if (err) {
|
||||
error_reportf_err(err, "Can't allocate MSIs for device %x: ",
|
||||
@ -371,7 +371,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||
|
||||
/* Release previous MSIs */
|
||||
if (msi) {
|
||||
xics_free(spapr->icp, msi->first_irq, msi->num);
|
||||
xics_spapr_free(spapr->xics, msi->first_irq, msi->num);
|
||||
g_hash_table_remove(phb->msi, &config_addr);
|
||||
}
|
||||
|
||||
@ -733,7 +733,7 @@ static void spapr_msi_write(void *opaque, hwaddr addr,
|
||||
|
||||
trace_spapr_pci_msi_write(addr, data, irq);
|
||||
|
||||
qemu_irq_pulse(xics_get_qirq(spapr->icp, irq));
|
||||
qemu_irq_pulse(xics_get_qirq(spapr->xics, irq));
|
||||
}
|
||||
|
||||
static const MemoryRegionOps spapr_msi_ops = {
|
||||
@ -1442,7 +1442,8 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
|
||||
uint32_t irq;
|
||||
Error *local_err = NULL;
|
||||
|
||||
irq = xics_alloc_block(spapr->icp, 0, 1, true, false, &local_err);
|
||||
irq = xics_spapr_alloc_block(spapr->xics, 0, 1, true, false,
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
error_prepend(errp, "can't allocate LSIs: ");
|
||||
@ -1801,7 +1802,7 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb,
|
||||
_FDT(fdt_setprop(fdt, bus_off, "ranges", &ranges, sizeof_ranges));
|
||||
_FDT(fdt_setprop(fdt, bus_off, "reg", &bus_reg, sizeof(bus_reg)));
|
||||
_FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pci-config-space-type", 0x1));
|
||||
_FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pe-total-#msi", XICS_IRQS));
|
||||
_FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pe-total-#msi", XICS_IRQS_SPAPR));
|
||||
|
||||
/* Build the interrupt-map, this must matches what is done
|
||||
* in pci_spapr_map_irq
|
||||
|
@ -463,7 +463,7 @@ static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp)
|
||||
dev->qdev.id = id;
|
||||
}
|
||||
|
||||
dev->irq = xics_alloc(spapr->icp, 0, dev->irq, false, &local_err);
|
||||
dev->irq = xics_spapr_alloc(spapr->xics, 0, dev->irq, false, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
|
@ -93,7 +93,7 @@ static inline qemu_irq spapr_phb_lsi_qirq(struct sPAPRPHBState *phb, int pin)
|
||||
{
|
||||
sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
|
||||
|
||||
return xics_get_qirq(spapr->icp, phb->lsi_table[pin].irq);
|
||||
return xics_get_qirq(spapr->xics, phb->lsi_table[pin].irq);
|
||||
}
|
||||
|
||||
PCIHostState *spapr_create_phb(sPAPRMachineState *spapr, int index);
|
||||
|
@ -52,7 +52,7 @@ struct sPAPRMachineState {
|
||||
struct VIOsPAPRBus *vio_bus;
|
||||
QLIST_HEAD(, sPAPRPHBState) phbs;
|
||||
struct sPAPRNVRAM *nvram;
|
||||
XICSState *icp;
|
||||
XICSState *xics;
|
||||
DeviceState *rtc;
|
||||
|
||||
void *htab;
|
||||
|
@ -90,7 +90,7 @@ static inline qemu_irq spapr_vio_qirq(VIOsPAPRDevice *dev)
|
||||
{
|
||||
sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
|
||||
|
||||
return xics_get_qirq(spapr->icp, dev->irq);
|
||||
return xics_get_qirq(spapr->xics, dev->irq);
|
||||
}
|
||||
|
||||
static inline bool spapr_vio_dma_valid(VIOsPAPRDevice *dev, uint64_t taddr,
|
||||
|
@ -32,20 +32,25 @@
|
||||
#define TYPE_XICS_COMMON "xics-common"
|
||||
#define XICS_COMMON(obj) OBJECT_CHECK(XICSState, (obj), TYPE_XICS_COMMON)
|
||||
|
||||
#define TYPE_XICS "xics"
|
||||
#define XICS(obj) OBJECT_CHECK(XICSState, (obj), TYPE_XICS)
|
||||
/*
|
||||
* Retain xics as the type name to be compatible for migration. Rest all the
|
||||
* functions, class and variables are renamed as xics_spapr.
|
||||
*/
|
||||
#define TYPE_XICS_SPAPR "xics"
|
||||
#define XICS_SPAPR(obj) OBJECT_CHECK(XICSState, (obj), TYPE_XICS_SPAPR)
|
||||
|
||||
#define TYPE_KVM_XICS "xics-kvm"
|
||||
#define KVM_XICS(obj) OBJECT_CHECK(KVMXICSState, (obj), TYPE_KVM_XICS)
|
||||
#define TYPE_XICS_SPAPR_KVM "xics-spapr-kvm"
|
||||
#define XICS_SPAPR_KVM(obj) \
|
||||
OBJECT_CHECK(KVMXICSState, (obj), TYPE_XICS_SPAPR_KVM)
|
||||
|
||||
#define XICS_COMMON_CLASS(klass) \
|
||||
OBJECT_CLASS_CHECK(XICSStateClass, (klass), TYPE_XICS_COMMON)
|
||||
#define XICS_CLASS(klass) \
|
||||
OBJECT_CLASS_CHECK(XICSStateClass, (klass), TYPE_XICS)
|
||||
#define XICS_SPAPR_CLASS(klass) \
|
||||
OBJECT_CLASS_CHECK(XICSStateClass, (klass), TYPE_XICS_SPAPR)
|
||||
#define XICS_COMMON_GET_CLASS(obj) \
|
||||
OBJECT_GET_CLASS(XICSStateClass, (obj), TYPE_XICS_COMMON)
|
||||
#define XICS_GET_CLASS(obj) \
|
||||
OBJECT_GET_CLASS(XICSStateClass, (obj), TYPE_XICS)
|
||||
#define XICS_SPAPR_GET_CLASS(obj) \
|
||||
OBJECT_GET_CLASS(XICSStateClass, (obj), TYPE_XICS_SPAPR)
|
||||
|
||||
#define XICS_IPI 0x2
|
||||
#define XICS_BUID 0x1
|
||||
@ -138,9 +143,15 @@ struct ICSState {
|
||||
uint32_t offset;
|
||||
qemu_irq *qirqs;
|
||||
ICSIRQState *irqs;
|
||||
XICSState *icp;
|
||||
XICSState *xics;
|
||||
};
|
||||
|
||||
static inline bool ics_valid_irq(ICSState *ics, uint32_t nr)
|
||||
{
|
||||
return (nr >= ics->offset)
|
||||
&& (nr < (ics->offset + ics->nr_irqs));
|
||||
}
|
||||
|
||||
struct ICSIRQState {
|
||||
uint32_t server;
|
||||
uint8_t priority;
|
||||
@ -157,15 +168,32 @@ struct ICSIRQState {
|
||||
uint8_t flags;
|
||||
};
|
||||
|
||||
#define XICS_IRQS 1024
|
||||
#define XICS_IRQS_SPAPR 1024
|
||||
|
||||
qemu_irq xics_get_qirq(XICSState *icp, int irq);
|
||||
int xics_alloc(XICSState *icp, int src, int irq_hint, bool lsi, Error **errp);
|
||||
int xics_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align,
|
||||
int xics_spapr_alloc(XICSState *icp, int src, int irq_hint, bool lsi,
|
||||
Error **errp);
|
||||
void xics_free(XICSState *icp, int irq, int num);
|
||||
int xics_spapr_alloc_block(XICSState *icp, int src, int num, bool lsi,
|
||||
bool align, Error **errp);
|
||||
void xics_spapr_free(XICSState *icp, int irq, int num);
|
||||
|
||||
void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu);
|
||||
void xics_cpu_destroy(XICSState *icp, PowerPCCPU *cpu);
|
||||
|
||||
/* Internal XICS interfaces */
|
||||
int xics_get_cpu_index_by_dt_id(int cpu_dt_id);
|
||||
|
||||
void icp_set_cppr(XICSState *icp, int server, uint8_t cppr);
|
||||
void icp_set_mfrr(XICSState *icp, int server, uint8_t mfrr);
|
||||
uint32_t icp_accept(ICPState *ss);
|
||||
uint32_t icp_ipoll(ICPState *ss, uint32_t *mfrr);
|
||||
void icp_eoi(XICSState *icp, int server, uint32_t xirr);
|
||||
|
||||
void ics_write_xive(ICSState *ics, int nr, int server,
|
||||
uint8_t priority, uint8_t saved_priority);
|
||||
|
||||
void ics_set_irq_type(ICSState *ics, int srcno, bool lsi);
|
||||
|
||||
int xics_find_source(XICSState *icp, int irq);
|
||||
|
||||
#endif /* __XICS_H__ */
|
||||
|
@ -4978,8 +4978,8 @@ Example for pseries machine type started with
|
||||
|
||||
-> { "execute": "query-hotpluggable-cpus" }
|
||||
<- {"return": [
|
||||
{ "props": { "core": 8 }, "type": "POWER8-spapr-cpu-core",
|
||||
{ "props": { "core-id": 8 }, "type": "POWER8-spapr-cpu-core",
|
||||
"vcpus-count": 1 },
|
||||
{ "props": { "core": 0 }, "type": "POWER8-spapr-cpu-core",
|
||||
{ "props": { "core-id": 0 }, "type": "POWER8-spapr-cpu-core",
|
||||
"vcpus-count": 1, "qom-path": "/machine/unattached/device[0]"}
|
||||
]}'
|
||||
|
@ -70,18 +70,21 @@ enum powerpc_mmu_t {
|
||||
#define POWERPC_MMU_64 0x00010000
|
||||
#define POWERPC_MMU_1TSEG 0x00020000
|
||||
#define POWERPC_MMU_AMR 0x00040000
|
||||
#define POWERPC_MMU_64K 0x00080000
|
||||
/* 64 bits PowerPC MMU */
|
||||
POWERPC_MMU_64B = POWERPC_MMU_64 | 0x00000001,
|
||||
/* Architecture 2.03 and later (has LPCR) */
|
||||
POWERPC_MMU_2_03 = POWERPC_MMU_64 | 0x00000002,
|
||||
/* Architecture 2.06 variant */
|
||||
POWERPC_MMU_2_06 = POWERPC_MMU_64 | POWERPC_MMU_1TSEG
|
||||
| POWERPC_MMU_64K
|
||||
| POWERPC_MMU_AMR | 0x00000003,
|
||||
/* Architecture 2.06 "degraded" (no 1T segments) */
|
||||
POWERPC_MMU_2_06a = POWERPC_MMU_64 | POWERPC_MMU_AMR
|
||||
| 0x00000003,
|
||||
/* Architecture 2.07 variant */
|
||||
POWERPC_MMU_2_07 = POWERPC_MMU_64 | POWERPC_MMU_1TSEG
|
||||
| POWERPC_MMU_64K
|
||||
| POWERPC_MMU_AMR | 0x00000004,
|
||||
/* Architecture 2.07 "degraded" (no 1T segments) */
|
||||
POWERPC_MMU_2_07a = POWERPC_MMU_64 | POWERPC_MMU_AMR
|
||||
|
@ -377,12 +377,16 @@ struct ppc_slb_t {
|
||||
#define LPCR_VPM1 (1ull << (63 - 1))
|
||||
#define LPCR_ISL (1ull << (63 - 2))
|
||||
#define LPCR_KBV (1ull << (63 - 3))
|
||||
#define LPCR_DPFD_SHIFT (63 - 11)
|
||||
#define LPCR_DPFD (0x3ull << LPCR_DPFD_SHIFT)
|
||||
#define LPCR_VRMASD_SHIFT (63 - 16)
|
||||
#define LPCR_VRMASD (0x1full << LPCR_VRMASD_SHIFT)
|
||||
#define LPCR_RMLS_SHIFT (63 - 37)
|
||||
#define LPCR_RMLS (0xfull << LPCR_RMLS_SHIFT)
|
||||
#define LPCR_ILE (1ull << (63 - 38))
|
||||
#define LPCR_MER (1ull << (63 - 52))
|
||||
#define LPCR_LPES0 (1ull << (63 - 60))
|
||||
#define LPCR_LPES1 (1ull << (63 - 61))
|
||||
#define LPCR_AIL_SHIFT (63 - 40) /* Alternate interrupt location */
|
||||
#define LPCR_AIL (3ull << LPCR_AIL_SHIFT)
|
||||
#define LPCR_ONL (1ull << (63 - 45))
|
||||
#define LPCR_P7_PECE0 (1ull << (63 - 49))
|
||||
#define LPCR_P7_PECE1 (1ull << (63 - 50))
|
||||
#define LPCR_P7_PECE2 (1ull << (63 - 51))
|
||||
@ -391,6 +395,12 @@ struct ppc_slb_t {
|
||||
#define LPCR_P8_PECE2 (1ull << (63 - 49))
|
||||
#define LPCR_P8_PECE3 (1ull << (63 - 50))
|
||||
#define LPCR_P8_PECE4 (1ull << (63 - 51))
|
||||
#define LPCR_MER (1ull << (63 - 52))
|
||||
#define LPCR_TC (1ull << (63 - 54))
|
||||
#define LPCR_LPES0 (1ull << (63 - 60))
|
||||
#define LPCR_LPES1 (1ull << (63 - 61))
|
||||
#define LPCR_RMI (1ull << (63 - 62))
|
||||
#define LPCR_HDICE (1ull << (63 - 63))
|
||||
|
||||
#define msr_sf ((env->msr >> MSR_SF) & 1)
|
||||
#define msr_isf ((env->msr >> MSR_ISF) & 1)
|
||||
|
@ -753,7 +753,6 @@ void ppc_cpu_do_interrupt(CPUState *cs)
|
||||
static void ppc_hw_interrupt(CPUPPCState *env)
|
||||
{
|
||||
PowerPCCPU *cpu = ppc_env_get_cpu(env);
|
||||
int hdice;
|
||||
#if 0
|
||||
CPUState *cs = CPU(cpu);
|
||||
|
||||
@ -781,19 +780,25 @@ static void ppc_hw_interrupt(CPUPPCState *env)
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (0) {
|
||||
/* XXX: find a suitable condition to enable the hypervisor mode */
|
||||
hdice = env->spr[SPR_LPCR] & 1;
|
||||
} else {
|
||||
hdice = 0;
|
||||
}
|
||||
if ((msr_ee != 0 || msr_hv == 0 || msr_pr != 0) && hdice != 0) {
|
||||
/* Hypervisor decrementer exception */
|
||||
if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDECR)) {
|
||||
/* Hypervisor decrementer exception */
|
||||
if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDECR)) {
|
||||
/* LPCR will be clear when not supported so this will work */
|
||||
bool hdice = !!(env->spr[SPR_LPCR] & LPCR_HDICE);
|
||||
if ((msr_ee != 0 || msr_hv == 0) && hdice) {
|
||||
/* HDEC clears on delivery */
|
||||
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR);
|
||||
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_HDECR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Extermal interrupt can ignore MSR:EE under some circumstances */
|
||||
if (env->pending_interrupts & (1 << PPC_INTERRUPT_EXT)) {
|
||||
bool lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0);
|
||||
if (msr_ee != 0 || (env->has_hv_mode && msr_hv == 0 && !lpes0)) {
|
||||
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_EXTERNAL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (msr_ce != 0) {
|
||||
/* External critical interrupt */
|
||||
if (env->pending_interrupts & (1 << PPC_INTERRUPT_CEXT)) {
|
||||
@ -839,17 +844,6 @@ static void ppc_hw_interrupt(CPUPPCState *env)
|
||||
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_DECR);
|
||||
return;
|
||||
}
|
||||
/* External interrupt */
|
||||
if (env->pending_interrupts & (1 << PPC_INTERRUPT_EXT)) {
|
||||
/* Taking an external interrupt does not clear the external
|
||||
* interrupt status
|
||||
*/
|
||||
#if 0
|
||||
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_EXT);
|
||||
#endif
|
||||
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_EXTERNAL);
|
||||
return;
|
||||
}
|
||||
if (env->pending_interrupts & (1 << PPC_INTERRUPT_DOORBELL)) {
|
||||
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL);
|
||||
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_DOORI);
|
||||
@ -944,6 +938,11 @@ void helper_pminsn(CPUPPCState *env, powerpc_pm_insn_t insn)
|
||||
cs->halted = 1;
|
||||
env->in_pm_state = true;
|
||||
|
||||
/* The architecture specifies that HDEC interrupts are
|
||||
* discarded in PM states
|
||||
*/
|
||||
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR);
|
||||
|
||||
/* Technically, nap doesn't set EE, but if we don't set it
|
||||
* then ppc_hw_interrupt() won't deliver. We could add some
|
||||
* other tests there based on LPCR but it's simpler to just
|
||||
|
@ -16,6 +16,7 @@ DEF_HELPER_1(rfmci, void, env)
|
||||
DEF_HELPER_2(pminsn, void, env, i32)
|
||||
DEF_HELPER_1(rfid, void, env)
|
||||
DEF_HELPER_1(hrfid, void, env)
|
||||
DEF_HELPER_2(store_lpcr, void, env, tl)
|
||||
#endif
|
||||
DEF_HELPER_1(check_tlb_flush, void, env)
|
||||
#endif
|
||||
@ -599,6 +600,8 @@ DEF_HELPER_2(store_601_rtcl, void, env, tl)
|
||||
DEF_HELPER_2(store_601_rtcu, void, env, tl)
|
||||
DEF_HELPER_1(load_decr, tl, env)
|
||||
DEF_HELPER_2(store_decr, void, env, tl)
|
||||
DEF_HELPER_1(load_hdecr, tl, env)
|
||||
DEF_HELPER_2(store_hdecr, void, env, tl)
|
||||
DEF_HELPER_2(store_hid0_601, void, env, tl)
|
||||
DEF_HELPER_3(store_403_pbr, void, env, i32, tl)
|
||||
DEF_HELPER_1(load_40x_pit, tl, env)
|
||||
|
@ -136,6 +136,10 @@ static inline int hreg_store_msr(CPUPPCState *env, target_ulong value,
|
||||
/* Change the exception prefix on PowerPC 601 */
|
||||
env->excp_prefix = ((value >> MSR_EP) & 1) * 0xFFF00000;
|
||||
}
|
||||
/* If PR=1 then EE, IR and DR must be 1 */
|
||||
if ((value >> MSR_PR) & 1) {
|
||||
value |= (1 << MSR_EE) | (1 << MSR_DR) | (1 << MSR_IR);
|
||||
}
|
||||
#endif
|
||||
env->msr = value;
|
||||
hreg_compute_hflags(env);
|
||||
|
@ -450,9 +450,31 @@ void ppc_hash64_stop_access(PowerPCCPU *cpu, uint64_t token)
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns the effective page shift or 0. MPSS isn't supported yet so
|
||||
* this will always be the slb_pshift or 0
|
||||
*/
|
||||
static uint32_t ppc_hash64_pte_size_decode(uint64_t pte1, uint32_t slb_pshift)
|
||||
{
|
||||
switch (slb_pshift) {
|
||||
case 12:
|
||||
return 12;
|
||||
case 16:
|
||||
if ((pte1 & 0xf000) == 0x1000) {
|
||||
return 16;
|
||||
}
|
||||
return 0;
|
||||
case 24:
|
||||
if ((pte1 & 0xff000) == 0) {
|
||||
return 24;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static hwaddr ppc_hash64_pteg_search(PowerPCCPU *cpu, hwaddr hash,
|
||||
bool secondary, target_ulong ptem,
|
||||
ppc_hash_pte64_t *pte)
|
||||
uint32_t slb_pshift, bool secondary,
|
||||
target_ulong ptem, ppc_hash_pte64_t *pte)
|
||||
{
|
||||
CPUPPCState *env = &cpu->env;
|
||||
int i;
|
||||
@ -472,6 +494,13 @@ static hwaddr ppc_hash64_pteg_search(PowerPCCPU *cpu, hwaddr hash,
|
||||
if ((pte0 & HPTE64_V_VALID)
|
||||
&& (secondary == !!(pte0 & HPTE64_V_SECONDARY))
|
||||
&& HPTE64_V_COMPARE(pte0, ptem)) {
|
||||
uint32_t pshift = ppc_hash64_pte_size_decode(pte1, slb_pshift);
|
||||
if (pshift == 0) {
|
||||
continue;
|
||||
}
|
||||
/* We don't do anything with pshift yet as qemu TLB only deals
|
||||
* with 4K pages anyway
|
||||
*/
|
||||
pte->pte0 = pte0;
|
||||
pte->pte1 = pte1;
|
||||
ppc_hash64_stop_access(cpu, token);
|
||||
@ -525,7 +554,8 @@ static hwaddr ppc_hash64_htab_lookup(PowerPCCPU *cpu,
|
||||
" vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx
|
||||
" hash=" TARGET_FMT_plx "\n",
|
||||
env->htab_base, env->htab_mask, vsid, ptem, hash);
|
||||
pte_offset = ppc_hash64_pteg_search(cpu, hash, 0, ptem, pte);
|
||||
pte_offset = ppc_hash64_pteg_search(cpu, hash, slb->sps->page_shift,
|
||||
0, ptem, pte);
|
||||
|
||||
if (pte_offset == -1) {
|
||||
/* Secondary PTEG lookup */
|
||||
@ -535,7 +565,8 @@ static hwaddr ppc_hash64_htab_lookup(PowerPCCPU *cpu,
|
||||
" hash=" TARGET_FMT_plx "\n", env->htab_base,
|
||||
env->htab_mask, vsid, ptem, ~hash);
|
||||
|
||||
pte_offset = ppc_hash64_pteg_search(cpu, ~hash, 1, ptem, pte);
|
||||
pte_offset = ppc_hash64_pteg_search(cpu, ~hash, slb->sps->page_shift, 1,
|
||||
ptem, pte);
|
||||
}
|
||||
|
||||
return pte_offset;
|
||||
@ -851,3 +882,60 @@ void ppc_hash64_tlb_flush_hpte(PowerPCCPU *cpu,
|
||||
*/
|
||||
tlb_flush(CPU(cpu), 1);
|
||||
}
|
||||
|
||||
void helper_store_lpcr(CPUPPCState *env, target_ulong val)
|
||||
{
|
||||
uint64_t lpcr = 0;
|
||||
|
||||
/* Filter out bits */
|
||||
switch (env->mmu_model) {
|
||||
case POWERPC_MMU_64B: /* 970 */
|
||||
if (val & 0x40) {
|
||||
lpcr |= LPCR_LPES0;
|
||||
}
|
||||
if (val & 0x8000000000000000ull) {
|
||||
lpcr |= LPCR_LPES1;
|
||||
}
|
||||
if (val & 0x20) {
|
||||
lpcr |= (0x4ull << LPCR_RMLS_SHIFT);
|
||||
}
|
||||
if (val & 0x4000000000000000ull) {
|
||||
lpcr |= (0x2ull << LPCR_RMLS_SHIFT);
|
||||
}
|
||||
if (val & 0x2000000000000000ull) {
|
||||
lpcr |= (0x1ull << LPCR_RMLS_SHIFT);
|
||||
}
|
||||
env->spr[SPR_RMOR] = ((lpcr >> 41) & 0xffffull) << 26;
|
||||
|
||||
/* XXX We could also write LPID from HID4 here
|
||||
* but since we don't tag any translation on it
|
||||
* it doesn't actually matter
|
||||
*/
|
||||
/* XXX For proper emulation of 970 we also need
|
||||
* to dig HRMOR out of HID5
|
||||
*/
|
||||
break;
|
||||
case POWERPC_MMU_2_03: /* P5p */
|
||||
lpcr = val & (LPCR_RMLS | LPCR_ILE |
|
||||
LPCR_LPES0 | LPCR_LPES1 |
|
||||
LPCR_RMI | LPCR_HDICE);
|
||||
break;
|
||||
case POWERPC_MMU_2_06: /* P7 */
|
||||
lpcr = val & (LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_DPFD |
|
||||
LPCR_VRMASD | LPCR_RMLS | LPCR_ILE |
|
||||
LPCR_P7_PECE0 | LPCR_P7_PECE1 | LPCR_P7_PECE2 |
|
||||
LPCR_MER | LPCR_TC |
|
||||
LPCR_LPES0 | LPCR_LPES1 | LPCR_HDICE);
|
||||
break;
|
||||
case POWERPC_MMU_2_07: /* P8 */
|
||||
lpcr = val & (LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV |
|
||||
LPCR_DPFD | LPCR_VRMASD | LPCR_RMLS | LPCR_ILE |
|
||||
LPCR_AIL | LPCR_ONL | LPCR_P8_PECE0 | LPCR_P8_PECE1 |
|
||||
LPCR_P8_PECE2 | LPCR_P8_PECE3 | LPCR_P8_PECE4 |
|
||||
LPCR_MER | LPCR_TC | LPCR_LPES0 | LPCR_HDICE);
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
env->spr[SPR_LPCR] = lpcr;
|
||||
}
|
||||
|
@ -102,6 +102,16 @@ void helper_store_decr(CPUPPCState *env, target_ulong val)
|
||||
cpu_ppc_store_decr(env, val);
|
||||
}
|
||||
|
||||
target_ulong helper_load_hdecr(CPUPPCState *env)
|
||||
{
|
||||
return cpu_ppc_load_hdecr(env);
|
||||
}
|
||||
|
||||
void helper_store_hdecr(CPUPPCState *env, target_ulong val)
|
||||
{
|
||||
cpu_ppc_store_hdecr(env, val);
|
||||
}
|
||||
|
||||
target_ulong helper_load_40x_pit(CPUPPCState *env)
|
||||
{
|
||||
return load_40x_pit(env);
|
||||
|
@ -1471,7 +1471,7 @@ static void gen_or(DisasContext *ctx)
|
||||
} else if (unlikely(Rc(ctx->opcode) != 0)) {
|
||||
gen_set_Rc0(ctx, cpu_gpr[rs]);
|
||||
#if defined(TARGET_PPC64)
|
||||
} else {
|
||||
} else if (rs != 0) { /* 0 is nop */
|
||||
int prio = 0;
|
||||
|
||||
switch (rs) {
|
||||
@ -1514,7 +1514,6 @@ static void gen_or(DisasContext *ctx)
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
/* nop */
|
||||
break;
|
||||
}
|
||||
if (prio) {
|
||||
@ -1524,13 +1523,15 @@ static void gen_or(DisasContext *ctx)
|
||||
tcg_gen_ori_tl(t0, t0, ((uint64_t)prio) << 50);
|
||||
gen_store_spr(SPR_PPR, t0);
|
||||
tcg_temp_free(t0);
|
||||
/* Pause us out of TCG otherwise spin loops with smt_low
|
||||
* eat too much CPU and the kernel hangs
|
||||
*/
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
gen_pause(ctx);
|
||||
#endif
|
||||
}
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
/* Pause out of TCG otherwise spin loops with smt_low eat too much
|
||||
* CPU and the kernel hangs. This applies to all encodings other
|
||||
* than no-op, e.g., miso(rs=26), yield(27), mdoio(29), mdoom(30),
|
||||
* and all currently undefined.
|
||||
*/
|
||||
gen_pause(ctx);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -11407,6 +11408,13 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
|
||||
env->spr[SPR_SPRG4], env->spr[SPR_SPRG5],
|
||||
env->spr[SPR_SPRG6], env->spr[SPR_SPRG7]);
|
||||
|
||||
#if defined(TARGET_PPC64)
|
||||
if (env->excp_model == POWERPC_EXCP_POWER7 ||
|
||||
env->excp_model == POWERPC_EXCP_POWER8) {
|
||||
cpu_fprintf(f, "HSRR0 " TARGET_FMT_lx " HSRR1 " TARGET_FMT_lx "\n",
|
||||
env->spr[SPR_HSRR0], env->spr[SPR_HSRR1]);
|
||||
}
|
||||
#endif
|
||||
if (env->excp_model == POWERPC_EXCP_BOOKE) {
|
||||
cpu_fprintf(f, "CSRR0 " TARGET_FMT_lx " CSRR1 " TARGET_FMT_lx
|
||||
" MCSRR0 " TARGET_FMT_lx " MCSRR1 " TARGET_FMT_lx "\n",
|
||||
|
@ -277,6 +277,32 @@ static void spr_read_purr (DisasContext *ctx, int gprn, int sprn)
|
||||
{
|
||||
gen_helper_load_purr(cpu_gpr[gprn], cpu_env);
|
||||
}
|
||||
|
||||
/* HDECR */
|
||||
static void spr_read_hdecr(DisasContext *ctx, int gprn, int sprn)
|
||||
{
|
||||
if (ctx->tb->cflags & CF_USE_ICOUNT) {
|
||||
gen_io_start();
|
||||
}
|
||||
gen_helper_load_hdecr(cpu_gpr[gprn], cpu_env);
|
||||
if (ctx->tb->cflags & CF_USE_ICOUNT) {
|
||||
gen_io_end();
|
||||
gen_stop_exception(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static void spr_write_hdecr(DisasContext *ctx, int sprn, int gprn)
|
||||
{
|
||||
if (ctx->tb->cflags & CF_USE_ICOUNT) {
|
||||
gen_io_start();
|
||||
}
|
||||
gen_helper_store_hdecr(cpu_env, cpu_gpr[gprn]);
|
||||
if (ctx->tb->cflags & CF_USE_ICOUNT) {
|
||||
gen_io_end();
|
||||
gen_stop_exception(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -7525,16 +7551,6 @@ static void gen_spr_970_hior(CPUPPCState *env)
|
||||
0x00000000);
|
||||
}
|
||||
|
||||
static void gen_spr_970_lpar(CPUPPCState *env)
|
||||
{
|
||||
/* Logical partitionning */
|
||||
/* PPC970: HID4 is effectively the LPCR */
|
||||
spr_register(env, SPR_970_HID4, "HID4",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
}
|
||||
|
||||
static void gen_spr_book3s_common(CPUPPCState *env)
|
||||
{
|
||||
spr_register(env, SPR_CTRL, "SPR_CTRL",
|
||||
@ -7787,21 +7803,155 @@ static void gen_spr_power5p_ear(CPUPPCState *env)
|
||||
0x00000000);
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
static void spr_write_hmer(DisasContext *ctx, int sprn, int gprn)
|
||||
{
|
||||
TCGv hmer = tcg_temp_new();
|
||||
|
||||
gen_load_spr(hmer, sprn);
|
||||
tcg_gen_and_tl(hmer, cpu_gpr[gprn], hmer);
|
||||
gen_store_spr(sprn, hmer);
|
||||
spr_store_dump_spr(sprn);
|
||||
tcg_temp_free(hmer);
|
||||
}
|
||||
|
||||
static void spr_write_lpcr(DisasContext *ctx, int sprn, int gprn)
|
||||
{
|
||||
gen_helper_store_lpcr(cpu_env, cpu_gpr[gprn]);
|
||||
}
|
||||
|
||||
static void spr_write_970_hid4(DisasContext *ctx, int sprn, int gprn)
|
||||
{
|
||||
#if defined(TARGET_PPC64)
|
||||
spr_write_generic(ctx, sprn, gprn);
|
||||
gen_helper_store_lpcr(cpu_env, cpu_gpr[gprn]);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* !defined(CONFIG_USER_ONLY) */
|
||||
|
||||
static void gen_spr_970_lpar(CPUPPCState *env)
|
||||
{
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
/* Logical partitionning */
|
||||
/* PPC970: HID4 is effectively the LPCR */
|
||||
spr_register(env, SPR_970_HID4, "HID4",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_970_hid4,
|
||||
0x00000000);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void gen_spr_power5p_lpar(CPUPPCState *env)
|
||||
{
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
/* Logical partitionning */
|
||||
spr_register_kvm(env, SPR_LPCR, "LPCR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
KVM_REG_PPC_LPCR, 0x00000000);
|
||||
spr_register_kvm_hv(env, SPR_LPCR, "LPCR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_lpcr,
|
||||
KVM_REG_PPC_LPCR, LPCR_LPES0 | LPCR_LPES1);
|
||||
spr_register_hv(env, SPR_HDEC, "HDEC",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_hdecr, &spr_write_hdecr, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void gen_spr_book3s_ids(CPUPPCState *env)
|
||||
{
|
||||
/* FIXME: Will need to deal with thread vs core only SPRs */
|
||||
|
||||
/* Processor identification */
|
||||
spr_register(env, SPR_PIR, "PIR",
|
||||
spr_register_hv(env, SPR_PIR, "PIR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_pir,
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, NULL,
|
||||
0x00000000);
|
||||
spr_register_hv(env, SPR_HID0, "HID0",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
spr_register_hv(env, SPR_TSCR, "TSCR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
spr_register_hv(env, SPR_HMER, "HMER",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_hmer,
|
||||
0x00000000);
|
||||
spr_register_hv(env, SPR_HMEER, "HMEER",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
spr_register_hv(env, SPR_TFMR, "TFMR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
spr_register_hv(env, SPR_LPIDR, "LPIDR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
spr_register_hv(env, SPR_HFSCR, "HFSCR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
spr_register_hv(env, SPR_MMCRC, "MMCRC",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
spr_register_hv(env, SPR_MMCRH, "MMCRH",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
spr_register_hv(env, SPR_HSPRG0, "HSPRG0",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
spr_register_hv(env, SPR_HSPRG1, "HSPRG1",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
spr_register_hv(env, SPR_HSRR0, "HSRR0",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
spr_register_hv(env, SPR_HSRR1, "HSRR1",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
spr_register_hv(env, SPR_HDAR, "HDAR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
spr_register_hv(env, SPR_HDSISR, "HDSISR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
spr_register_hv(env, SPR_RMOR, "RMOR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
spr_register_hv(env, SPR_HRMOR, "HRMOR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
}
|
||||
|
||||
@ -8060,6 +8210,17 @@ static void gen_spr_power7_book4(CPUPPCState *env)
|
||||
#endif
|
||||
}
|
||||
|
||||
static void gen_spr_power8_rpr(CPUPPCState *env)
|
||||
{
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
spr_register_hv(env, SPR_RPR, "RPR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000103070F1F3F);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void init_proc_book3s_64(CPUPPCState *env, int version)
|
||||
{
|
||||
gen_spr_ne_601(env);
|
||||
@ -8117,6 +8278,7 @@ static void init_proc_book3s_64(CPUPPCState *env, int version)
|
||||
gen_spr_vtb(env);
|
||||
gen_spr_power8_ic(env);
|
||||
gen_spr_power8_book4(env);
|
||||
gen_spr_power8_rpr(env);
|
||||
}
|
||||
if (version < BOOK3S_CPU_POWER8) {
|
||||
gen_spr_book3s_dbg(env);
|
||||
@ -10131,8 +10293,8 @@ static void ppc_cpu_initfn(Object *obj)
|
||||
if (pcc->sps) {
|
||||
env->sps = *pcc->sps;
|
||||
} else if (env->mmu_model & POWERPC_MMU_64) {
|
||||
/* Use default sets of page sizes */
|
||||
static const struct ppc_segment_page_sizes defsps = {
|
||||
/* Use default sets of page sizes. We don't support MPSS */
|
||||
static const struct ppc_segment_page_sizes defsps_4k = {
|
||||
.sps = {
|
||||
{ .page_shift = 12, /* 4K */
|
||||
.slb_enc = 0,
|
||||
@ -10144,7 +10306,23 @@ static void ppc_cpu_initfn(Object *obj)
|
||||
},
|
||||
},
|
||||
};
|
||||
env->sps = defsps;
|
||||
static const struct ppc_segment_page_sizes defsps_64k = {
|
||||
.sps = {
|
||||
{ .page_shift = 12, /* 4K */
|
||||
.slb_enc = 0,
|
||||
.enc = { { .page_shift = 12, .pte_enc = 0 } }
|
||||
},
|
||||
{ .page_shift = 16, /* 64K */
|
||||
.slb_enc = 0x110,
|
||||
.enc = { { .page_shift = 16, .pte_enc = 1 } }
|
||||
},
|
||||
{ .page_shift = 24, /* 16M */
|
||||
.slb_enc = 0x100,
|
||||
.enc = { { .page_shift = 24, .pte_enc = 0 } }
|
||||
},
|
||||
},
|
||||
};
|
||||
env->sps = (env->mmu_model & POWERPC_MMU_64K) ? defsps_64k : defsps_4k;
|
||||
}
|
||||
#endif /* defined(TARGET_PPC64) */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user