diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak index bb71b23ee78a3..e80aa476aadf6 100644 --- a/default-configs/ppc64-softmmu.mak +++ b/default-configs/ppc64-softmmu.mak @@ -7,6 +7,10 @@ CONFIG_VIRTIO_VGA=y CONFIG_ISA_MMIO=y CONFIG_ESCC=y CONFIG_M48T59=y +CONFIG_IPMI=y +CONFIG_IPMI_LOCAL=y +CONFIG_IPMI_EXTERN=y +CONFIG_ISA_IPMI_BT=y CONFIG_SERIAL=y CONFIG_PARALLEL=y CONFIG_I8254=y @@ -40,6 +44,7 @@ CONFIG_I8259=y CONFIG_XILINX=y CONFIG_XILINX_ETHLITE=y CONFIG_PSERIES=y +CONFIG_POWERNV=y CONFIG_PREP=y CONFIG_MAC=y CONFIG_E500=y @@ -48,7 +53,9 @@ CONFIG_PLATFORM_BUS=y CONFIG_ETSEC=y CONFIG_LIBDECNUMBER=y # For pSeries -CONFIG_XICS=$(CONFIG_PSERIES) +CONFIG_XICS=$(or $(CONFIG_PSERIES),$(CONFIG_POWERNV)) +CONFIG_XICS_SPAPR=$(CONFIG_PSERIES) +CONFIG_XICS_NATIVE=$(CONFIG_POWERNV) CONFIG_XICS_KVM=$(and $(CONFIG_PSERIES),$(CONFIG_KVM)) # For PReP CONFIG_MC146818RTC=y diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index 52539c3109176..33122644a24e9 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -203,6 +203,8 @@ ETEXI .mhandler.cmd = sun4m_hmp_info_pic, #elif defined(TARGET_LM32) .mhandler.cmd = lm32_hmp_info_pic, +#elif defined(TARGET_PPC) + .mhandler.cmd = ppc_hmp_info_pic, #else .mhandler.cmd = hmp_info_pic, #endif diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs index 0e47f0f9ec1a0..4d3b180a5b7d4 100644 --- a/hw/intc/Makefile.objs +++ b/hw/intc/Makefile.objs @@ -27,6 +27,8 @@ 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_NATIVE) += xics_native.o obj-$(CONFIG_XICS_KVM) += xics_kvm.o obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o obj-$(CONFIG_S390_FLIC) += s390_flic.o diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 8659be0171be9..8130ba590a860 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -32,12 +32,14 @@ #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" +#include "monitor/monitor.h" -static int get_cpu_index_by_dt_id(int cpu_dt_id) +static XICSState *g_xics; + +int get_cpu_index_by_dt_id(int cpu_dt_id) { PowerPCCPU *cpu = ppc_get_vcpu_by_dt_id(cpu_dt_id); @@ -48,17 +50,17 @@ static int get_cpu_index_by_dt_id(int cpu_dt_id) return -1; } -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); if (info->cpu_setup) { - info->cpu_setup(icp, cpu); + info->cpu_setup(xics, cpu); } switch (PPC_INPUT(env)) { @@ -82,21 +84,30 @@ 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); + ICSState *ics; 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])); + } + + QLIST_FOREACH(ics, &xics->ics, list) { + device_reset(DEVICE(ics)); } +} - device_reset(DEVICE(icp->ics)); +void xics_add_ics(XICSState *xics, ICSState *ics) +{ + ics->xics = xics; + QLIST_INSERT_HEAD(&xics->ics, ics, list); } 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); } @@ -104,8 +115,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; @@ -114,23 +125,22 @@ 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); + 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); } @@ -139,8 +149,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; @@ -149,14 +159,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) @@ -167,6 +177,9 @@ static void xics_common_initfn(Object *obj) object_property_add(obj, "nr_servers", "int", xics_prop_get_nr_servers, xics_prop_set_nr_servers, NULL, NULL, NULL); + + /* For exclusive use of monitor command */ + g_xics = XICS_COMMON(obj); } static void xics_common_class_init(ObjectClass *oc, void *data) @@ -195,42 +208,67 @@ static const TypeInfo xics_common_info = { #define XISR(ss) (((ss)->xirr) & XISR_MASK) #define CPPR(ss) (((ss)->xirr) >> 24) -static void ics_reject(ICSState *ics, int nr); -static void ics_resend(ICSState *ics); -static void ics_eoi(ICSState *ics, int nr); +static void ics_reject(ICSState *ics, uint32_t nr) +{ + ICSStateClass *k = ICS_GET_CLASS(ics); -static void icp_check_ipi(XICSState *icp, int server) + if (k->reject) { + k->reject(ics, nr); + } +} + +static void ics_resend(ICSState *ics) { - ICPState *ss = icp->ss + server; + ICSStateClass *k = ICS_GET_CLASS(ics); + + if (k->resend) { + k->resend(ics); + } +} +static void ics_eoi(ICSState *ics, int nr) +{ + ICSStateClass *k = ICS_GET_CLASS(ics); + + if (k->eoi) { + k->eoi(ics, nr); + } +} + +static void icp_check_ipi(ICPState *ss, int server) +{ if (XISR(ss) && (ss->pending_priority <= ss->mfrr)) { return; } trace_xics_icp_check_ipi(server, ss->mfrr); - if (XISR(ss)) { - ics_reject(icp->ics, XISR(ss)); + if (XISR(ss) && ss->xirr_owner) { + ics_reject(ss->xirr_owner, XISR(ss)); } ss->xirr = (ss->xirr & ~XISR_MASK) | XICS_IPI; ss->pending_priority = ss->mfrr; + ss->xirr_owner = NULL; 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; + ICSState *ics; if (ss->mfrr < CPPR(ss)) { - icp_check_ipi(icp, server); + icp_check_ipi(ss, server); + } + QLIST_FOREACH(ics, &xics->ics, list) { + ics_resend(ics); } - ics_resend(icp->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; @@ -243,65 +281,87 @@ 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); + if (ss->xirr_owner) { + ics_reject(ss->xirr_owner, old_xisr); + ss->xirr_owner = NULL; + } } } 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(ss, server); } } -static uint32_t icp_accept(ICPState *ss) +uint32_t icp_accept(ICPState *ss) { uint32_t xirr = ss->xirr; qemu_irq_lower(ss->output); ss->xirr = ss->pending_priority << 24; ss->pending_priority = 0xff; + ss->xirr_owner = NULL; trace_xics_icp_accept(xirr, ss->xirr); 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; + ICSState *ics; + uint32_t irq; /* 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); + irq = xirr & XISR_MASK; + QLIST_FOREACH(ics, &xics->ics, list) { + if (ics_valid_irq(ics, irq)) { + ics_eoi(ics, irq); + } + } if (!XISR(ss)) { - icp_resend(icp, server); + icp_resend(xics, server); } } -static void icp_irq(XICSState *icp, int server, int nr, uint8_t priority) +void icp_irq(ICSState *ics, int server, int nr, uint8_t priority) { - ICPState *ss = icp->ss + server; + XICSState *xics = ics->xics; + 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(ics, nr); } else { - if (XISR(ss)) { - ics_reject(icp->ics, XISR(ss)); + if (XISR(ss) && ss->xirr_owner) { + ics_reject(ss->xirr_owner, XISR(ss)); + ss->xirr_owner = NULL; } ss->xirr = (ss->xirr & ~XISR_MASK) | (nr & XISR_MASK); + ss->xirr_owner = ics; ss->pending_priority = priority; trace_xics_icp_raise(ss->xirr, ss->pending_priority); qemu_irq_raise(ss->output); @@ -376,13 +436,7 @@ 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) +static void ics_simple_resend_msi(ICSState *ics, int srcno) { ICSIRQState *irq = ics->irqs + srcno; @@ -390,13 +444,12 @@ 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, - irq->priority); + icp_irq(ics, irq->server, srcno + ics->offset, irq->priority); } } } -static void resend_lsi(ICSState *ics, int srcno) +static void ics_simple_resend_lsi(ICSState *ics, int srcno) { ICSIRQState *irq = ics->irqs + srcno; @@ -404,11 +457,11 @@ 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, irq->server, srcno + ics->offset, irq->priority); } } -static void set_irq_msi(ICSState *ics, int srcno, int val) +static void ics_simple_set_irq_msi(ICSState *ics, int srcno, int val) { ICSIRQState *irq = ics->irqs + srcno; @@ -419,12 +472,12 @@ 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, irq->server, srcno + ics->offset, irq->priority); } } } -static void set_irq_lsi(ICSState *ics, int srcno, int val) +static void ics_simple_set_irq_lsi(ICSState *ics, int srcno, int val) { ICSIRQState *irq = ics->irqs + srcno; @@ -434,21 +487,21 @@ static void set_irq_lsi(ICSState *ics, int srcno, int val) } else { irq->status &= ~XICS_STATUS_ASSERTED; } - resend_lsi(ics, srcno); + ics_simple_resend_lsi(ics, srcno); } -static void ics_set_irq(void *opaque, int srcno, int val) +static void ics_simple_set_irq(void *opaque, int srcno, int val) { ICSState *ics = (ICSState *)opaque; if (ics->irqs[srcno].flags & XICS_FLAGS_IRQ_LSI) { - set_irq_lsi(ics, srcno, val); + ics_simple_set_irq_lsi(ics, srcno, val); } else { - set_irq_msi(ics, srcno, val); + ics_simple_set_irq_msi(ics, srcno, val); } } -static void write_xive_msi(ICSState *ics, int srcno) +static void ics_simple_write_xive_msi(ICSState *ics, int srcno) { ICSIRQState *irq = ics->irqs + srcno; @@ -458,34 +511,33 @@ 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, irq->server, srcno + ics->offset, irq->priority); } -static void write_xive_lsi(ICSState *ics, int srcno) +static void ics_simple_write_xive_lsi(ICSState *ics, int srcno) { - resend_lsi(ics, srcno); + ics_simple_resend_lsi(ics, srcno); } -static void ics_write_xive(ICSState *ics, int nr, int server, +void ics_simple_write_xive(ICSState *ics, int srcno, int server, uint8_t priority, uint8_t saved_priority) { - int srcno = nr - ics->offset; ICSIRQState *irq = ics->irqs + srcno; irq->server = server; irq->priority = priority; irq->saved_priority = saved_priority; - trace_xics_ics_write_xive(nr, srcno, server, priority); + trace_xics_ics_write_xive(ics->offset + srcno, srcno, server, priority); if (ics->irqs[srcno].flags & XICS_FLAGS_IRQ_LSI) { - write_xive_lsi(ics, srcno); + ics_simple_write_xive_lsi(ics, srcno); } else { - write_xive_msi(ics, srcno); + ics_simple_write_xive_msi(ics, srcno); } } -static void ics_reject(ICSState *ics, int nr) +static void ics_simple_reject(ICSState *ics, uint32_t nr) { ICSIRQState *irq = ics->irqs + nr - ics->offset; @@ -494,21 +546,21 @@ static void ics_reject(ICSState *ics, int nr) irq->status &= ~XICS_STATUS_SENT; /* Irrelevant but harmless for MSI */ } -static void ics_resend(ICSState *ics) +static void ics_simple_resend(ICSState *ics) { int i; for (i = 0; i < ics->nr_irqs; i++) { /* FIXME: filter by server#? */ if (ics->irqs[i].flags & XICS_FLAGS_IRQ_LSI) { - resend_lsi(ics, i); + ics_simple_resend_lsi(ics, i); } else { - resend_msi(ics, i); + ics_simple_resend_msi(ics, i); } } } -static void ics_eoi(ICSState *ics, int nr) +static void ics_simple_eoi(ICSState *ics, uint32_t nr) { int srcno = nr - ics->offset; ICSIRQState *irq = ics->irqs + srcno; @@ -520,9 +572,9 @@ static void ics_eoi(ICSState *ics, int nr) } } -static void ics_reset(DeviceState *dev) +static void ics_simple_reset(DeviceState *dev) { - ICSState *ics = ICS(dev); + ICSState *ics = ICS_SIMPLE(dev); int i; uint8_t flags[ics->nr_irqs]; @@ -539,12 +591,12 @@ static void ics_reset(DeviceState *dev) } } -static int ics_post_load(ICSState *ics, int version_id) +static int ics_simple_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; @@ -572,7 +624,39 @@ static int ics_dispatch_post_load(void *opaque, int version_id) return 0; } -static const VMStateDescription vmstate_ics_irq = { +void xics_hmp_info_pic(Monitor *mon, const QDict *qdict) +{ + ICSState *ics; + uint32_t i; + + for (i = 0; i < g_xics->nr_servers; i++) { + ICPState *icp = &g_xics->ss[i]; + + if (!icp->output) { + continue; + } + monitor_printf(mon, "CPU %d XIRR=%08x (%p) PP=%02x MFRR=%02x\n", + i, icp->xirr, icp->xirr_owner, + icp->pending_priority, icp->mfrr); + } + QLIST_FOREACH(ics, &g_xics->ics, list) { + monitor_printf(mon, "ICS %4x..%4x %p\n", + ics->offset, ics->offset + ics->nr_irqs - 1, ics); + for (i = 0; i < ics->nr_irqs; i++) { + ICSIRQState *irq = ics->irqs + i; + + if (!(irq->flags & XICS_FLAGS_IRQ_MASK)) { + continue; + } + monitor_printf(mon, " %4x %s %02x %02x\n", + ics->offset + i, + (irq->flags & XICS_FLAGS_IRQ_LSI) ? "LSI" : "MSI", + irq->priority, irq->status); + } + } +} + +static const VMStateDescription vmstate_ics_simple_irq = { .name = "ics/irq", .version_id = 2, .minimum_version_id = 1, @@ -586,7 +670,7 @@ static const VMStateDescription vmstate_ics_irq = { }, }; -static const VMStateDescription vmstate_ics = { +static const VMStateDescription vmstate_ics_simple = { .name = "ics", .version_id = 1, .minimum_version_id = 1, @@ -597,82 +681,89 @@ static const VMStateDescription vmstate_ics = { VMSTATE_UINT32_EQUAL(nr_irqs, ICSState), VMSTATE_STRUCT_VARRAY_POINTER_UINT32(irqs, ICSState, nr_irqs, - vmstate_ics_irq, ICSIRQState), + vmstate_ics_simple_irq, + ICSIRQState), VMSTATE_END_OF_LIST() }, }; -static void ics_initfn(Object *obj) +static void ics_simple_initfn(Object *obj) { - ICSState *ics = ICS(obj); + ICSState *ics = ICS_SIMPLE(obj); ics->offset = XICS_IRQ_BASE; } -static void ics_realize(DeviceState *dev, Error **errp) +static void ics_simple_realize(DeviceState *dev, Error **errp) { - ICSState *ics = ICS(dev); + ICSState *ics = ICS_SIMPLE(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->qirqs = qemu_allocate_irqs(ics_set_irq, ics, ics->nr_irqs); + ics->qirqs = qemu_allocate_irqs(ics_simple_set_irq, ics, ics->nr_irqs); } -static void ics_class_init(ObjectClass *klass, void *data) +static void ics_simple_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ICSStateClass *isc = ICS_CLASS(klass); - dc->realize = ics_realize; - dc->vmsd = &vmstate_ics; - dc->reset = ics_reset; - isc->post_load = ics_post_load; + dc->realize = ics_simple_realize; + dc->vmsd = &vmstate_ics_simple; + dc->reset = ics_simple_reset; + isc->post_load = ics_simple_post_load; + isc->reject = ics_simple_reject; + isc->resend = ics_simple_resend; + isc->eoi = ics_simple_eoi; } +static const TypeInfo ics_simple_info = { + .name = TYPE_ICS_SIMPLE, + .parent = TYPE_ICS, + .instance_size = sizeof(ICSState), + .class_init = ics_simple_class_init, + .class_size = sizeof(ICSStateClass), + .instance_init = ics_simple_initfn, +}; + static const TypeInfo ics_info = { .name = TYPE_ICS, .parent = TYPE_DEVICE, + .abstract = true, .instance_size = sizeof(ICSState), - .class_init = ics_class_init, .class_size = sizeof(ICSStateClass), - .instance_init = ics_initfn, }; /* * Exported functions */ -static int xics_find_source(XICSState *icp, int irq) +ICSState *xics_find_source(XICSState *xics, int irq) { - int sources = 1; - int src; + ICSState *ics; - /* FIXME: implement multiple sources */ - for (src = 0; src < sources; ++src) { - ICSState *ics = &icp->ics[src]; + QLIST_FOREACH(ics, &xics->ics, list) { if (ics_valid_irq(ics, irq)) { - return src; + return ics; } } - - return -1; + return NULL; } -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); + ICSState *ics = xics_find_source(xics, irq); - if (src >= 0) { - ICSState *ics = &icp->ics[src]; + if (ics) { return ics->qirqs[irq - ics->offset]; } return NULL; } -static void ics_set_irq_type(ICSState *ics, int srcno, bool lsi) +void ics_simple_set_irq_type(ICSState *ics, int srcno, bool lsi) { assert(!(ics->irqs[srcno].flags & XICS_FLAGS_IRQ_MASK)); @@ -680,412 +771,30 @@ static void ics_set_irq_type(ICSState *ics, int srcno, bool lsi) lsi ? XICS_FLAGS_IRQ_LSI : XICS_FLAGS_IRQ_MSI; } -void xics_set_irq_type(XICSState *icp, int irq, bool lsi) -{ - int src = xics_find_source(icp, irq); - ICSState *ics; - - assert(src >= 0); - - ics = &icp->ics[src]; - ics_set_irq_type(ics, irq - ics->offset, lsi); -} - -#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) +void xics_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_ICP); + object_initialize(&xics->ss[i], sizeof(xics->ss[i]), TYPE_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); } } -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_simple_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 index 9029d9ee0bd28..8d300c99cfdcb 100644 --- a/hw/intc/xics_kvm.c +++ b/hw/intc/xics_kvm.c @@ -142,7 +142,7 @@ static const TypeInfo icp_kvm_info = { */ static void ics_get_kvm_state(ICSState *ics) { - KVMXICSState *icpkvm = KVM_XICS(ics->icp); + KVMXICSState *xicskvm = KVM_XICS(ics->xics); uint64_t state; struct kvm_device_attr attr = { .flags = 0, @@ -157,7 +157,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)); @@ -201,7 +201,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 = KVM_XICS(ics->xics); uint64_t state; struct kvm_device_attr attr = { .flags = 0, @@ -235,7 +235,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)); @@ -313,7 +313,7 @@ static void ics_kvm_class_init(ObjectClass *klass, void *data) static const TypeInfo ics_kvm_info = { .name = TYPE_KVM_ICS, - .parent = TYPE_ICS, + .parent = TYPE_ICS_SIMPLE, .instance_size = sizeof(ICSState), .class_init = ics_kvm_class_init, }; @@ -321,17 +321,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 = KVM_XICS(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(); } @@ -344,13 +344,13 @@ 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; ss->cs = cs; 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)); @@ -360,24 +360,19 @@ 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) -{ - icp->nr_irqs = icp->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); } } @@ -393,8 +388,9 @@ 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 = KVM_XICS(dev); + XICSState *xics = XICS_COMMON(dev); + ICSState *ics; int i, rc; Error *error = NULL; struct kvm_create_device xics_create_device = { @@ -444,17 +440,19 @@ 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); - if (error) { - error_propagate(errp, error); - goto fail; + QLIST_FOREACH(ics, &xics->ics, list) { + object_property_set_bool(OBJECT(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; @@ -474,13 +472,28 @@ static void xics_kvm_realize(DeviceState *dev, Error **errp) kvmppc_define_rtas_kernel_token(0, "ibm,int-off"); } +static void xics_kvm_set_nr_irqs(XICSState *xics, uint32_t nr_irqs, Error **errp) +{ + ICSState *ics = QLIST_FIRST(&xics->ics); + + /* This needs to be deprecated ... */ + xics->nr_irqs = nr_irqs; + if (ics) { + ics->nr_irqs = nr_irqs; + } +} + static void xics_kvm_initfn(Object *obj) { XICSState *xics = XICS_COMMON(obj); + ICSState *ics; + + QLIST_INIT(&xics->ics); - xics->ics = ICS(object_new(TYPE_KVM_ICS)); - object_property_add_child(obj, "ics", OBJECT(xics->ics), NULL); - xics->ics->icp = xics; + ics = ICS(object_new(TYPE_KVM_ICS)); + object_property_add_child(obj, "ics", OBJECT(ics), NULL); + ics->xics = xics; + QLIST_INSERT_HEAD(&xics->ics, ics, list); } static void xics_kvm_class_init(ObjectClass *oc, void *data) @@ -494,8 +507,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, @@ -504,7 +517,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); } diff --git a/hw/intc/xics_native.c b/hw/intc/xics_native.c new file mode 100644 index 0000000000000..90cae1ecc8dbf --- /dev/null +++ b/hw/intc/xics_native.c @@ -0,0 +1,295 @@ +/* + * QEMU PowerPC hardware System Emulator + * + * Native version of ICS/ICP + * + * 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 "hw/hw.h" +#include "trace.h" +#include "qemu/timer.h" +#include "hw/ppc/xics.h" +#include "qapi/visitor.h" +#include "qapi/error.h" + +#include + +//#define DEBUG_MM(fmt...) printf(fmt) +#define DEBUG_MM(fmt...) do { } while(0) + +static void xics_native_initfn(Object *obj) +{ + XICSState *xics = XICS_NATIVE(obj); + + QLIST_INIT(&xics->ics); +} + +static uint64_t icp_mm_read(void *opaque, hwaddr addr, unsigned width) +{ + XICSState *s = opaque; + int32_t cpu_id, server; + uint32_t val; + ICPState *ss; + bool byte0 = (width == 1 && (addr & 0x3) == 0); + + cpu_id = (addr & (ICP_MM_SIZE - 1)) >> 12; + server = get_cpu_index_by_dt_id(cpu_id); + if (server < 0) { + fprintf(stderr, "XICS: Bad ICP server %d\n", server); + goto bad_access; + } + ss = &s->ss[server]; + + switch(addr & 0xffc) { + case 0: /* poll */ + val = icp_ipoll(ss, NULL); + if (byte0) { + val >>= 24; + } else if (width != 4) { + goto bad_access; + } + break; + case 4: /* xirr */ + if (byte0) { + val = icp_ipoll(ss, NULL) >> 24; + } else if (width == 4) { + val = icp_accept(ss); + } else { + goto bad_access; + } + break; + case 12: + if (byte0) { + val = ss->mfrr; + } else { + goto bad_access; + } + break; + case 16: + if (width == 4) { + val = ss->links[0]; + } else { + goto bad_access; + } + break; + case 20: + if (width == 4) { + val = ss->links[1]; + } else { + goto bad_access; + } + break; + case 24: + if (width == 4) { + val = ss->links[2]; + } else { + goto bad_access; + } + break; + default: +bad_access: + fprintf(stderr, "XICS: Bad ICP access %llx/%d\n", + (unsigned long long)addr, width); + val = 0xffffffff; + } + DEBUG_MM("icp_mm_read(addr=%016llx,serv=0x%x/%d,off=%d,w=%d,val=0x%08x)\n", + (unsigned long long)addr, cpu_id, server, (int)(addr & 0xffc), + width, val); + + return val; +} + +static void icp_mm_write(void *opaque, hwaddr addr, uint64_t val, + unsigned width) +{ + XICSState *s = opaque; + int32_t cpu_id, server; + ICPState *ss; + bool byte0 = (width == 1 && (addr & 0x3) == 0); + + cpu_id = (addr & (ICP_MM_SIZE - 1)) >> 12; + server = get_cpu_index_by_dt_id(cpu_id); + if (server < 0) { + fprintf(stderr, "XICS: Bad ICP server %d\n", server); + goto bad_access; + } + ss = &s->ss[server]; + + DEBUG_MM("icp_mm_write(addr=%016llx,serv=0x%x/%d,off=%d,w=%d,val=0x%08x)\n", + (unsigned long long)addr, cpu_id, server, + (int)(addr & 0xffc), width, (uint32_t)val); + + switch(addr & 0xffc) { + case 4: /* xirr */ + if (byte0) { + icp_set_cppr(s, server, val); + } else if (width == 4) { + icp_eoi(s, server, val); + } else { + goto bad_access; + } + break; + case 12: + if (byte0) { + icp_set_mfrr(s, server, val); + } else { + goto bad_access; + } + break; + case 16: + if (width == 4) { + ss->links[0] = val; + } else { + goto bad_access; + } + break; + case 20: + if (width == 4) { + ss->links[1] = val; + } else { + goto bad_access; + } + break; + case 24: + if (width == 4) { + ss->links[2] = val; + } else { + goto bad_access; + } + break; + default: + bad_access: + val = 0xffffffff; + } +} + +static const MemoryRegionOps icp_mm_ops = { + .read = icp_mm_read, + .write = icp_mm_write, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .impl.min_access_size = 1, + .impl.max_access_size = 4, + .endianness = DEVICE_BIG_ENDIAN, +}; + +#define _FDT(exp) \ + do { \ + int ret = (exp); \ + if (ret < 0) { \ + fprintf(stderr, "qemu: error creating device tree: %s: %s\n", \ + #exp, fdt_strerror(ret)); \ + exit(1); \ + } \ + } while (0) + +void xics_create_native_icp_node(XICSState *s, void *fdt, + uint32_t base, uint32_t count) +{ + uint64_t addr; + char *name; + const char compat[] = "IBM,power8-icp\0IBM,ppc-xicp"; + uint32_t irange[2], i, rsize; + uint64_t *reg; + + addr = ICP_MM_BASE | (base << 12); + + irange[0] = cpu_to_be32(base); + irange[1] = cpu_to_be32(count); + + rsize = sizeof(uint64_t) * 2 * count; + reg = g_malloc(rsize); + for (i = 0; i < count; i++) { + reg[i * 2] = cpu_to_be64(addr | ((base + i) * 0x1000)); + reg[i * 2 + 1] = cpu_to_be64(0x1000); + } + + name = g_strdup_printf("interrupt-controller@%"PRIX64, addr); + + /* interrupt controller */ + _FDT((fdt_begin_node(fdt, name))); + g_free(name); + + _FDT((fdt_property(fdt, "compatible", compat, sizeof(compat)))); + _FDT((fdt_property(fdt, "reg", reg, rsize))); + _FDT((fdt_property_string(fdt, "device_type", + "PowerPC-External-Interrupt-Presentation"))); + _FDT((fdt_property(fdt, "interrupt-controller", NULL, 0))); + _FDT((fdt_property(fdt, "ibm,interrupt-server-ranges", + irange, sizeof(irange)))); + _FDT((fdt_property_cell(fdt, "#interrupt-cells", 1))); + _FDT((fdt_property_cell(fdt, "#address-cells", 0))); + _FDT((fdt_end_node(fdt))); +} + +static void xics_native_realize(DeviceState *dev, Error **errp) +{ + XICSState *s = XICS_NATIVE(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Error *error = NULL; + int i; + + if (!s->nr_servers) { + error_setg(errp, "Number of servers needs to be greater 0"); + return; + } + + /* Register MMIO regions */ + memory_region_init_io(&s->icp_mmio, OBJECT(s), &icp_mm_ops, s, "icp", + ICP_MM_SIZE); + sysbus_init_mmio(sbd, &s->icp_mmio); + sysbus_mmio_map(sbd, 0, ICP_MM_BASE); + + for (i = 0; i < s->nr_servers; i++) { + object_property_set_bool(OBJECT(&s->ss[i]), true, "realized", &error); + if (error) { + error_propagate(errp, error); + return; + } + } +} + +static void xics_native_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + XICSStateClass *xsc = XICS_NATIVE_CLASS(oc); + + dc->realize = xics_native_realize; + xsc->set_nr_servers = xics_set_nr_servers; +} + +static const TypeInfo xics_native_info = { + .name = TYPE_XICS_NATIVE, + .parent = TYPE_XICS_COMMON, + .instance_size = sizeof(XICSState), + .class_size = sizeof(XICSStateClass), + .class_init = xics_native_class_init, + .instance_init = xics_native_initfn, +}; + +static void xics_native_register_types(void) +{ + type_register_static(&xics_native_info); +} +type_init(xics_native_register_types) diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c new file mode 100644 index 0000000000000..f4376eebef731 --- /dev/null +++ b/hw/intc/xics_spapr.c @@ -0,0 +1,429 @@ +/* + * 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 "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 = 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 = QLIST_FIRST(&spapr->xics->ics); + uint32_t nr, src_no, server, priority; + + if ((nargs != 3) || (nret != 1) || !ics) { + 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->xics->nr_servers) + || (priority > 0xff)) { + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); + return; + } + + src_no = nr - ics->offset; + ics_simple_write_xive(ics, src_no, 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 = QLIST_FIRST(&spapr->xics->ics); + uint32_t nr, src_no; + + if ((nargs != 1) || (nret != 3) || !ics) { + 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); + src_no = nr - ics->offset; + rtas_st(rets, 1, ics->irqs[src_no].server); + rtas_st(rets, 2, ics->irqs[src_no].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 = QLIST_FIRST(&spapr->xics->ics); + uint32_t nr, src_no; + + if ((nargs != 1) || (nret != 1) || !ics) { + 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; + } + + src_no = nr - ics->offset; + ics_simple_write_xive(ics, src_no, ics->irqs[src_no].server, 0xff, + ics->irqs[src_no].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 = QLIST_FIRST(&spapr->xics->ics); + uint32_t nr, src_no; + + if ((nargs != 1) || (nret != 1) || !ics) { + 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; + } + + src_no = nr - ics->offset; + ics_simple_write_xive(ics, src_no, ics->irqs[src_no].server, + ics->irqs[src_no].saved_priority, + ics->irqs[src_no].saved_priority); + + rtas_st(rets, 0, RTAS_OUT_SUCCESS); +} + +static void xics_spapr_realize(DeviceState *dev, Error **errp) +{ + XICSState *xics = XICS(dev); + ICSState *ics; + 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); + + QLIST_FOREACH(ics, &xics->ics, list) { + object_property_set_bool(OBJECT(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(obj); + ICSState *ics; + + QLIST_INIT(&xics->ics); + + ics = ICS(object_new(TYPE_ICS_SIMPLE)); + object_property_add_child(obj, "ics", OBJECT(ics), NULL); + xics_add_ics(xics, ics); +} + +static void xics_spapr_set_nr_irqs(XICSState *xics, uint32_t nr_irqs, Error **errp) +{ + ICSState *ics = QLIST_FIRST(&xics->ics); + + /* This needs to be deprecated ... */ + xics->nr_irqs = nr_irqs; + if (ics) { + ics->nr_irqs = nr_irqs; + } +} + +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_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 irq_hint, bool lsi, Error **errp) +{ + ICSState *ics = QLIST_FIRST(&xics->ics); + int irq; + + if (!ics) { + return -1; + } + if (irq_hint) { + if (!ICS_IRQ_FREE(ics, irq_hint - ics->offset)) { + error_setg(errp, "can't allocate IRQ %d: already in use", irq_hint); + return -1; + } + irq = irq_hint; + } else { + irq = ics_find_free_block(ics, 1, 1); + if (irq < 0) { + error_setg(errp, "can't allocate IRQ: no IRQ left"); + return -1; + } + irq += ics->offset; + } + + ics_simple_set_irq_type(ics, irq - ics->offset, lsi); + trace_xics_alloc(0, 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 num, bool lsi, bool align, + Error **errp) +{ + ICSState *ics = QLIST_FIRST(&xics->ics); + int i, first = -1; + + if (!ics) { + return -1; + } + + /* + * 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_simple_set_irq_type(ics, i, lsi); + } + } + first += ics->offset; + + trace_xics_alloc_block(0, 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(0, i + ics->offset); + } + memset(&ics->irqs[i], 0, sizeof(ICSIRQState)); + } +} + +void xics_spapr_free(XICSState *xics, int irq, int num) +{ + ICSState *ics = xics_find_source(xics, irq); + + if (ics) { + trace_xics_ics_free(0, 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) diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c index dc9c14cd29317..dcafa3fa0ac0c 100644 --- a/hw/ipmi/ipmi_bmc_sim.c +++ b/hw/ipmi/ipmi_bmc_sim.c @@ -27,6 +27,7 @@ #include "qemu/timer.h" #include "hw/ipmi/ipmi.h" #include "qemu/error-report.h" +#include "hw/loader.h" #define IPMI_NETFN_CHASSIS 0x00 @@ -44,6 +45,7 @@ #define IPMI_CMD_GET_SENSOR_READING 0x2d #define IPMI_CMD_SET_SENSOR_TYPE 0x2e #define IPMI_CMD_GET_SENSOR_TYPE 0x2f +#define IPMI_CMD_SET_SENSOR_READING 0x30 /* #define IPMI_NETFN_APP 0x06 In ipmi.h */ @@ -79,6 +81,9 @@ #define IPMI_CMD_ENTER_SDR_REP_UPD_MODE 0x2A #define IPMI_CMD_EXIT_SDR_REP_UPD_MODE 0x2B #define IPMI_CMD_RUN_INIT_AGENT 0x2C +#define IPMI_CMD_GET_FRU_AREA_INFO 0x10 +#define IPMI_CMD_READ_FRU_DATA 0x11 +#define IPMI_CMD_WRITE_FRU_DATA 0x12 #define IPMI_CMD_GET_SEL_INFO 0x40 #define IPMI_CMD_GET_SEL_ALLOC_INFO 0x41 #define IPMI_CMD_RESERVE_SEL 0x42 @@ -121,6 +126,13 @@ typedef struct IPMISdr { uint8_t overflow; } IPMISdr; +typedef struct IPMIFru { + char *filename; + unsigned int nentries; + uint16_t areasize; + uint8_t *data; +} IPMIFru; + typedef struct IPMISensor { uint8_t status; uint8_t reading; @@ -212,7 +224,9 @@ struct IPMIBmcSim { IPMISel sel; IPMISdr sdr; + IPMIFru fru; IPMISensor sensors[MAX_SENSORS]; + char *sdr_filename; /* Odd netfns are for responses, so we only need the even ones. */ const IPMINetfn *netfns[MAX_NETFNS / 2]; @@ -404,6 +418,22 @@ static int sdr_find_entry(IPMISdr *sdr, uint16_t recid, return 1; } +int ipmi_bmc_sdr_find(IPMIBmc *b, uint16_t recid, + const struct ipmi_sdr_compact **sdr, uint16_t *nextrec) + +{ + IPMIBmcSim *ibs = IPMI_BMC_SIMULATOR(b); + unsigned int pos; + + pos = 0; + if (sdr_find_entry(&ibs->sdr, recid, &pos, nextrec)) { + return -1; + } + + *sdr = (const struct ipmi_sdr_compact *) &ibs->sdr.sdr[pos]; + return 0; +} + static void sel_inc_reservation(IPMISel *sel) { sel->reservation++; @@ -445,6 +475,30 @@ static int attn_irq_enabled(IPMIBmcSim *ibs) IPMI_BMC_MSG_FLAG_EVT_BUF_FULL_SET(ibs)); } +void ipmi_bmc_gen_event(IPMIBmc *b, uint8_t *evt, bool log) +{ + IPMIBmcSim *ibs = IPMI_BMC_SIMULATOR(b); + IPMIInterface *s = ibs->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + + if (!IPMI_BMC_EVENT_MSG_BUF_ENABLED(ibs)) { + return; + } + + if (log && IPMI_BMC_EVENT_LOG_ENABLED(ibs)) { + sel_add_event(ibs, evt); + } + + if (ibs->msg_flags & IPMI_BMC_MSG_FLAG_EVT_BUF_FULL) { + goto out; + } + + memcpy(ibs->evtbuf, evt, 16); + ibs->msg_flags |= IPMI_BMC_MSG_FLAG_EVT_BUF_FULL; + k->set_atn(s, 1, attn_irq_enabled(ibs)); + out: + return; +} static void gen_event(IPMIBmcSim *ibs, unsigned int sens_num, uint8_t deassert, uint8_t evd1, uint8_t evd2, uint8_t evd3) { @@ -1320,6 +1374,91 @@ static void get_sel_info(IPMIBmcSim *ibs, rsp_buffer_push(rsp, (ibs->sel.overflow << 7) | 0x02); } +static void get_fru_area_info(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + uint8_t fruid; + uint16_t fru_entry_size; + + fruid = cmd[2]; + + if (fruid >= ibs->fru.nentries) { + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } + + fru_entry_size = ibs->fru.areasize; + + rsp_buffer_push(rsp, fru_entry_size & 0xff); + rsp_buffer_push(rsp, fru_entry_size >> 8 & 0xff); + rsp_buffer_push(rsp, 0x0); +} + +static void read_fru_data(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + uint8_t fruid; + uint16_t offset; + int i; + uint8_t *fru_entry; + unsigned int count; + + fruid = cmd[2]; + offset = (cmd[3] | cmd[4] << 8); + + if (fruid >= ibs->fru.nentries) { + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } + + if (offset >= ibs->fru.areasize - 1) { + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } + + fru_entry = &ibs->fru.data[fruid * ibs->fru.areasize]; + + count = MIN(cmd[5], ibs->fru.areasize - offset); + + rsp_buffer_push(rsp, count & 0xff); + for (i = 0; i < count; i++) { + rsp_buffer_push(rsp, fru_entry[offset + i]); + } +} + +static void write_fru_data(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + uint8_t fruid; + uint16_t offset; + uint8_t *fru_entry; + unsigned int count; + + fruid = cmd[2]; + offset = (cmd[3] | cmd[4] << 8); + + if (fruid >= ibs->fru.nentries) { + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } + + if (offset >= ibs->fru.areasize - 1) { + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } + + fru_entry = &ibs->fru.data[fruid * ibs->fru.areasize]; + + count = MIN(cmd_len - 5, ibs->fru.areasize - offset); + + memcpy(fru_entry + offset, cmd + 5, count); + + rsp_buffer_push(rsp, count & 0xff); +} + static void reserve_sel(IPMIBmcSim *ibs, uint8_t *cmd, unsigned int cmd_len, RspBuffer *rsp) @@ -1606,6 +1745,137 @@ static void get_sensor_type(IPMIBmcSim *ibs, rsp_buffer_push(rsp, sens->evt_reading_type_code); } +static void set_sensor_reading(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + IPMISensor *sens; + uint8_t evd1; + uint8_t evd2; + uint8_t evd3; + + if ((cmd[2] > MAX_SENSORS) || + !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) { + rsp_buffer_set_error(rsp, IPMI_CC_REQ_ENTRY_NOT_PRESENT); + return; + } + + sens = ibs->sensors + cmd[2]; + + /* Sensor Reading operation */ + switch ((cmd[3]) & 0x3) { + case 0: /* Do not change */ + break; + case 1: /* write given value to sensor reading byte */ + sens->reading = cmd[4]; + break; + case 2: + case 3: + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } + + /* Deassertion bits operation */ + switch ((cmd[3] >> 2) & 0x3) { + case 0: /* Do not change */ + break; + case 1: /* write given value */ + if (cmd_len > 7) { + sens->deassert_states = cmd[7]; + } + if (cmd_len > 8) { + sens->deassert_states = cmd[8] << 8; + } + + case 2: /* mask on */ + if (cmd_len > 7) { + sens->deassert_states |= cmd[7]; + } + if (cmd_len > 8) { + sens->deassert_states |= cmd[8] << 8; + } + break; + case 3: /* mask off */ + if (cmd_len > 7) { + sens->deassert_states &= cmd[7]; + } + if (cmd_len > 8) { + sens->deassert_states &= (cmd[8] << 8); + } + break; + } + + /* Assertion bits operation */ + switch ((cmd[3] >> 4) & 0x3) { + case 0: /* Do not change */ + break; + case 1: /* write given value */ + if (cmd_len > 5) { + sens->assert_states = cmd[5]; + } + if (cmd_len > 6) { + sens->assert_states = cmd[6] << 8; + } + + case 2: /* mask on */ + if (cmd_len > 5) { + sens->assert_states |= cmd[5]; + } + if (cmd_len > 6) { + sens->assert_states |= cmd[6] << 8; + } + break; + case 3: /* mask off */ + if (cmd_len > 5) { + sens->assert_states &= cmd[5]; + } + if (cmd_len > 6) { + sens->assert_states &= (cmd[6] << 8); + } + break; + } + + evd1 = evd2 = evd3 = 0x0; + if (cmd_len > 9) { + evd1 = cmd[9]; + } + if (cmd_len > 10) { + evd2 = cmd[10]; + } + if (cmd_len > 11) { + evd3 = cmd[11]; + } + + /* Event Data Bytes operation */ + switch ((cmd[3] >> 6) & 0x3) { + case 0: /* Do not use the event data in message */ + evd1 = evd2 = evd3 = 0x0; + break; + case 1: /* Write given values to event data bytes excluding bits + * [3:0] Event Data 1. */ + evd1 &= 0xf0; + break; + case 2: /* Write given values to event data bytes including bits + * [3:0] Event Data 1. */ + break; + case 3: + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } + + if (IPMI_SENSOR_IS_DISCRETE(sens)) { + unsigned int bit = evd1 & 0xf; + uint16_t mask = (1 << bit); + + if (sens->assert_states & mask & sens->assert_enable) { + gen_event(ibs, cmd[2], 0, evd1, evd2, evd3); + } + + if (sens->deassert_states & mask & sens->deassert_enable) { + gen_event(ibs, cmd[2], 1, evd1, evd2, evd3); + } + } +} static const IPMICmdHandler chassis_cmds[] = { [IPMI_CMD_GET_CHASSIS_CAPABILITIES] = { chassis_capabilities }, @@ -1626,6 +1896,7 @@ static const IPMICmdHandler sensor_event_cmds[] = { [IPMI_CMD_GET_SENSOR_READING] = { get_sensor_reading, 3 }, [IPMI_CMD_SET_SENSOR_TYPE] = { set_sensor_type, 5 }, [IPMI_CMD_GET_SENSOR_TYPE] = { get_sensor_type, 3 }, + [IPMI_CMD_SET_SENSOR_READING] = { set_sensor_reading, 5 }, }; static const IPMINetfn sensor_event_netfn = { .cmd_nums = ARRAY_SIZE(sensor_event_cmds), @@ -1656,6 +1927,9 @@ static const IPMINetfn app_netfn = { }; static const IPMICmdHandler storage_cmds[] = { + [IPMI_CMD_GET_FRU_AREA_INFO] = { get_fru_area_info, 3 }, + [IPMI_CMD_READ_FRU_DATA] = { read_fru_data, 5 }, + [IPMI_CMD_WRITE_FRU_DATA] = { write_fru_data, 5 }, [IPMI_CMD_GET_SDR_REP_INFO] = { get_sdr_rep_info }, [IPMI_CMD_RESERVE_SDR_REP] = { reserve_sdr_rep }, [IPMI_CMD_GET_SDR] = { get_sdr, 8 }, @@ -1701,22 +1975,33 @@ static void ipmi_sdr_init(IPMIBmcSim *ibs) sdrs_size = sizeof(init_sdrs); sdrs = init_sdrs; + if (ibs->sdr_filename && + !g_file_get_contents(ibs->sdr_filename, (gchar **) &sdrs, &sdrs_size, + NULL)) { + error_report("failed to load sdr file '%s'", ibs->sdr_filename); + sdrs_size = sizeof(init_sdrs); + sdrs = init_sdrs; + } for (i = 0; i < sdrs_size; i += len) { struct ipmi_sdr_header *sdrh; if (i + IPMI_SDR_HEADER_SIZE > sdrs_size) { error_report("Problem with recid 0x%4.4x", i); - return; + break; } sdrh = (struct ipmi_sdr_header *) &sdrs[i]; len = ipmi_sdr_length(sdrh); if (i + len > sdrs_size) { error_report("Problem with recid 0x%4.4x", i); - return; + break; } sdr_add_entry(ibs, sdrh, len, NULL); } + + if (sdrs != init_sdrs) { + g_free(sdrs); + } } static const VMStateDescription vmstate_ipmi_sim = { @@ -1747,6 +2032,31 @@ static const VMStateDescription vmstate_ipmi_sim = { } }; +static void ipmi_fru_init(IPMIFru *fru) +{ + int fsize; + int size = 0; + + fsize = get_image_size(fru->filename); + if (fsize > 0) { + size = QEMU_ALIGN_UP(fsize, fru->areasize); + fru->data = g_malloc0(size); + if (load_image_size(fru->filename, fru->data, fsize) != fsize) { + error_report("Could not load file '%s'", fru->filename); + g_free(fru->data); + fru->data = NULL; + } + } + + if (!fru->data) { + /* give one default FRU */ + size = fru->areasize; + fru->data = g_malloc0(size); + } + + fru->nentries = size / fru->areasize; +} + static void ipmi_sim_realize(DeviceState *dev, Error **errp) { IPMIBmc *b = IPMI_BMC(dev); @@ -1769,6 +2079,8 @@ static void ipmi_sim_realize(DeviceState *dev, Error **errp) ipmi_sdr_init(ibs); + ipmi_fru_init(&ibs->fru); + ibs->acpi_power_state[0] = 0; ibs->acpi_power_state[1] = 0; @@ -1786,12 +2098,20 @@ static void ipmi_sim_realize(DeviceState *dev, Error **errp) vmstate_register(NULL, 0, &vmstate_ipmi_sim, ibs); } +static Property ipmi_sim_properties[] = { + DEFINE_PROP_UINT16("fruareasize", IPMIBmcSim, fru.areasize, 1024), + DEFINE_PROP_STRING("frudatafile", IPMIBmcSim, fru.filename), + DEFINE_PROP_STRING("sdrfile", IPMIBmcSim, sdr_filename), + DEFINE_PROP_END_OF_LIST(), +}; + static void ipmi_sim_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); IPMIBmcClass *bk = IPMI_BMC_CLASS(oc); dc->realize = ipmi_sim_realize; + dc->props = ipmi_sim_properties; bk->handle_command = ipmi_sim_handle_command; } diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c index 7b582e96aca1d..9a11559f57ea7 100644 --- a/hw/pci-bridge/pci_bridge_dev.c +++ b/hw/pci-bridge/pci_bridge_dev.c @@ -57,6 +57,8 @@ static int pci_bridge_dev_initfn(PCIDevice *dev) pci_bridge_initfn(dev, TYPE_PCI_BUS); if (bridge_dev->flags & (1 << PCI_BRIDGE_DEV_F_SHPC_REQ)) { + /* SHCP gets upset if we try to use slot 0 */ + br->sec_bus.devfn_min = PCI_FUNC_MAX; dev->config[PCI_INTERRUPT_PIN] = 0x1; memory_region_init(&bridge_dev->bar, OBJECT(dev), "shpc-bar", shpc_bar_size(dev)); diff --git a/hw/pci-host/Makefile.objs b/hw/pci-host/Makefile.objs index 45f1f0ebabd64..961fe5b6e25e7 100644 --- a/hw/pci-host/Makefile.objs +++ b/hw/pci-host/Makefile.objs @@ -16,3 +16,5 @@ common-obj-$(CONFIG_FULONG) += bonito.o common-obj-$(CONFIG_PCI_PIIX) += piix.o common-obj-$(CONFIG_PCI_Q35) += q35.o common-obj-$(CONFIG_PCI_GENERIC) += gpex.o + +obj-$(CONFIG_POWERNV) += pnv_phb3.o pnv_phb3_pbcq.o pnv_phb3_rc.o pnv_phb3_msi.o diff --git a/hw/pci-host/pnv_phb3.c b/hw/pci-host/pnv_phb3.c new file mode 100644 index 0000000000000..0dc31ec421f18 --- /dev/null +++ b/hw/pci-host/pnv_phb3.c @@ -0,0 +1,1085 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Copyright IBM Corp. 2014 + */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/pci-host/pnv_phb3.h" +#include "hw/pci/pci_bridge.h" +#include "hw/pci/pci_bus.h" +#include "hw/ppc/xics.h" + +#define DBG_ERR(p, fmt, ...) do { \ + if (1) fprintf(stderr, "PHB3(%s): " fmt "\n", __func__, ## __VA_ARGS__); \ + } while(0) + +#define DBG_DMA(p, fmt, ...) do { \ + if (0) fprintf(stderr, "PHB3(%s): " fmt "\n", __func__, ## __VA_ARGS__); \ + } while(0) + +#define DBG_MAP(p, fmt, ...) do { \ + if (0) fprintf(stderr, "PHB3(%s): " fmt "\n", __func__, ## __VA_ARGS__); \ + } while(0) + +//#define DISPLAY_UNIMPLENTED_REG + +static PCIDevice *pnb_phb3_find_cfg_dev(PnvPhb3State *phb) +{ + PCIHostState *pci = PCI_HOST_BRIDGE(phb); + uint64_t addr = phb->regs[PHB_CONFIG_ADDRESS >> 3]; + uint8_t bus, devfn; + + if (!(addr >> 63)) { + return NULL; + } + bus = (addr >> 52) & 0xff; + devfn = (addr >> 44) & 0xff; + + return pci_find_device(pci->bus, bus, devfn); +} + +static void pnv_phb3_config_write(PnvPhb3State *phb, unsigned off, + unsigned size, uint64_t val) +{ + uint32_t cfg_addr, limit; + PCIDevice *pdev; + + pdev = pnb_phb3_find_cfg_dev(phb); + if (!pdev) { + return; + } + cfg_addr = (phb->regs[PHB_CONFIG_ADDRESS >> 3] >> 32) & 0xfff; + cfg_addr |= off; + limit = pci_config_size(pdev); + if (limit <= cfg_addr) { + /* conventional pci device can be behind pcie-to-pci bridge. + 256 <= addr < 4K has no effects. */ + return; + } + switch (size) { + case 1: + break; + case 2: + val = bswap16(val); + break; + case 4: + val = bswap32(val); + break; + default: + DBG_ERR(phb, "Unsupported config access size %d !", size); + return; + } + pci_host_config_write_common(pdev, cfg_addr, limit, val, size); +} + +static uint64_t pnv_phb3_config_read(PnvPhb3State *phb, unsigned off, + unsigned size) +{ + uint32_t cfg_addr, limit; + PCIDevice *pdev; + uint64_t val; + + pdev = pnb_phb3_find_cfg_dev(phb); + if (!pdev) { + return ~0ull; + } + cfg_addr = (phb->regs[PHB_CONFIG_ADDRESS >> 3] >> 32) & 0xffc; + cfg_addr |= off; + limit = pci_config_size(pdev); + if (limit <= cfg_addr) { + /* conventional pci device can be behind pcie-to-pci bridge. + 256 <= addr < 4K has no effects. */ + return ~0ull; + } + val = pci_host_config_read_common(pdev, cfg_addr, limit, size); + switch (size) { + case 1: + return val; + case 2: + return bswap16(val); + case 4: + return bswap32(val); + default: + DBG_ERR(phb, "Unsupported config access size %d !", size); + return ~0ull; + } +} + +static void pnv_phb3_check_m32(PnvPhb3State *phb) +{ + uint64_t base, start, size; + MemoryRegion *parent; + + if (phb->m32_mapped) { + // Should we destroy it in RCU friendly way... ? + memory_region_del_subregion(phb->mr_m32.container, &phb->mr_m32); + phb->m32_mapped = false; + } + + /* Disabled ? move on with life ... */ + if (!(phb->regs[PHB_PHB3_CONFIG >> 3] & PHB_PHB3C_M32_EN)) { + return; + } + + /* Grab geometry from registers */ + base = phb->regs[PHB_M32_BASE_ADDR >> 3]; + start = phb->regs[PHB_M32_START_ADDR >> 3]; + size = ~(phb->regs[PHB_M32_BASE_MASK >> 3] | 0xfffc000000000000ull) + 1; + + DBG_MAP(phb, "PHB3: M32 enabled, base=%016llx/%016llx", + (unsigned long long)base, (unsigned long long)size); + DBG_MAP(phb, "PHB3: MMIO0 =%016llx/%016llx [%d]", + (unsigned long long)phb->pbcq->mmio0_base, + (unsigned long long)phb->pbcq->mmio0_size, phb->pbcq->mmio0_mapped); + DBG_MAP(phb, "PHB3: MMIO1 =%016llx/%016llx [%d]", + (unsigned long long)phb->pbcq->mmio1_base, + (unsigned long long)phb->pbcq->mmio1_size, phb->pbcq->mmio1_mapped); + /* Check if it matches an enabled MMIO region in the PBCQ */ + if (phb->pbcq->mmio0_mapped && base >= phb->pbcq->mmio0_base && + (base + size) <= (phb->pbcq->mmio0_base + phb->pbcq->mmio0_size)) { + parent = &phb->pbcq->mmbar0; + base -= phb->pbcq->mmio0_base; + DBG_MAP(phb, "M32: Mapping under MMIO0"); + } else if (phb->pbcq->mmio1_mapped && base >= phb->pbcq->mmio1_base && + (base + size) <= (phb->pbcq->mmio1_base + phb->pbcq->mmio1_size)) { + parent = &phb->pbcq->mmbar1; + base -= phb->pbcq->mmio1_base; + DBG_MAP(phb, "M32: Mapping under MMIO1"); + } else { + DBG_MAP(phb, "M32 not matching either PBCQ MMIO region !"); + return; + } + + /* Create alias */ + memory_region_init_alias(&phb->mr_m32, OBJECT(phb), "phb3-m32", + &phb->pci_mmio, start, size); + memory_region_add_subregion(parent, base, &phb->mr_m32); + phb->m32_mapped = true; +} + +static void pnv_phb3_check_m64(PnvPhb3State *phb, uint32_t index) +{ + uint64_t base, start, size, m64; + MemoryRegion *parent; + + if (phb->m64_mapped[index]) { + // Should we destroy it in RCU friendly way... ? + memory_region_del_subregion(phb->mr_m64[index].container, + &phb->mr_m64[index]); + phb->m64_mapped[index] = false; + } + + /* Get table entry */ + m64 = phb->ioda_M64BT[index]; + + /* Disabled ? move on with life ... */ + if (!(m64 & IODA2_M64BT_ENABLE)) { + return; + } + + /* Grab geometry from registers */ + base = GETFIELD(IODA2_M64BT_BASE, m64) << 20; + if (m64 & IODA2_M64BT_SINGLE_PE) { + base &= ~0x1ffffffull; + } + size = GETFIELD(IODA2_M64BT_MASK, m64) << 20; + size |= 0xfffc000000000000ull; + size = ~size + 1; + start = base | (phb->regs[PHB_M64_UPPER_BITS >> 3]); + + DBG_MAP(phb, "PHB3: M64[%d] enabled, base=%016llx/%016llx", index, + (unsigned long long)base, (unsigned long long)size); + DBG_MAP(phb, "PHB3: MMIO0 =%016llx/%016llx [%d]", + (unsigned long long)phb->pbcq->mmio0_base, + (unsigned long long)phb->pbcq->mmio0_size, phb->pbcq->mmio0_mapped); + DBG_MAP(phb, "PHB3: MMIO1 =%016llx/%016llx [%d]", + (unsigned long long)phb->pbcq->mmio1_base, + (unsigned long long)phb->pbcq->mmio1_size, phb->pbcq->mmio1_mapped); + /* Check if it matches an enabled MMIO region in the PBCQ */ + if (phb->pbcq->mmio0_mapped && base >= phb->pbcq->mmio0_base && + (base + size) <= (phb->pbcq->mmio0_base + phb->pbcq->mmio0_size)) { + parent = &phb->pbcq->mmbar0; + base -= phb->pbcq->mmio0_base; + DBG_MAP(phb, "M64: Mapping under MMIO0"); + } else if (phb->pbcq->mmio1_mapped && base >= phb->pbcq->mmio1_base && + (base + size) <= (phb->pbcq->mmio1_base + phb->pbcq->mmio1_size)) { + parent = &phb->pbcq->mmbar1; + base -= phb->pbcq->mmio1_base; + DBG_MAP(phb, "M64: Mapping under MMIO1"); + } else { + DBG_MAP(phb, "M64 not matching either PBCQ MMIO region !"); + return; + } + + /* Create alias */ + memory_region_init_alias(&phb->mr_m64[index], OBJECT(phb), "phb3-m64", + &phb->pci_mmio, start, size); + memory_region_add_subregion(parent, base, &phb->mr_m64[index]); + phb->m64_mapped[index] = true; +} + +static void pnv_phb3_check_all_m64s(PnvPhb3State *phb) +{ + uint64_t i; + + for (i=0; iioda_LXIVT[idx] = val & (IODA2_LXIVT_SERVER_MASK | + IODA2_LXIVT_PRIORITY_MASK | + IODA2_LXIVT_NODE_ID_MASK); + server = GETFIELD(IODA2_LXIVT_SERVER, val); + prio = GETFIELD(IODA2_LXIVT_PRIORITY, val); + + ics_simple_write_xive(phb->lsi_ics, idx, server, prio, prio); +} + +static uint64_t *pnv_phb3_ioda_access(PnvPhb3State *phb, + unsigned *out_table, unsigned *out_idx) +{ + uint64_t adreg = phb->regs[PHB_IODA_ADDR >> 3]; + unsigned int index = GETFIELD(PHB_IODA_AD_TADR, adreg); + unsigned int table = GETFIELD(PHB_IODA_AD_TSEL, adreg); + unsigned int mask; + uint64_t *tptr = NULL; + + switch(table) { + case IODA2_TBL_LIST: + tptr = phb->ioda_LIST; + mask = 7; + break; + case IODA2_TBL_LXIVT: + tptr = phb->ioda_LXIVT; + mask = 7; + break; + case IODA2_TBL_IVC_CAM: + case IODA2_TBL_RBA: + mask = 31; + break; + case IODA2_TBL_RCAM: + mask = 63; + break; + case IODA2_TBL_MRT: + mask = 7; + break; + case IODA2_TBL_PESTA: + case IODA2_TBL_PESTB: + mask = 255; + break; + case IODA2_TBL_TVT: + tptr = phb->ioda_TVT; + mask = 255; + break; + case IODA2_TBL_TCAM: + case IODA2_TBL_TDR: + mask = 63; + break; + case IODA2_TBL_M64BT: + tptr = phb->ioda_M64BT; + mask = 15; + break; + case IODA2_TBL_M32DT: + tptr = phb->ioda_MDT; + mask = 255; + break; + case IODA2_TBL_PEEV: + tptr = phb->ioda_PEEV; + mask = 3; + break; + default: + DBG_ERR(phb, "Unknown IODA table idx %d", table); + return NULL; + } + index &= mask; + if (out_idx) { + *out_idx = index; + } + if (out_table) { + *out_table = table; + } + if (adreg & PHB_IODA_AD_AUTOINC) { + index = (index + 1) & mask; + adreg = SETFIELD(PHB_IODA_AD_TADR, adreg, index); + } + if (tptr) { + tptr += index; + } + phb->regs[PHB_IODA_ADDR >> 3] = adreg; + return tptr; +} + +static uint64_t pnv_phb3_ioda_read(PnvPhb3State *phb) +{ + unsigned table; + uint64_t *tptr; + + tptr = pnv_phb3_ioda_access(phb, &table, NULL); + if (!tptr) { + /* Return 0 on unsupported tables, not ff's */ + return 0; + } + return *tptr; +} + +static void pnv_phb3_ioda_write(PnvPhb3State *phb, uint64_t val) +{ + unsigned table, idx; + uint64_t *tptr; + + tptr = pnv_phb3_ioda_access(phb, &table, &idx); + if (!tptr) { + return; + } + + /* Handle side effects */ + switch(table) { + case IODA2_TBL_LXIVT: + pnv_phb3_lxivt_write(phb, idx, val); + break; + case IODA2_TBL_M64BT: + *tptr = val; + pnv_phb3_check_m64(phb, idx); + break; + default: + *tptr = val; + } +} + +/* This is called whenever the PHB LSI, MSI source ID register or + * the PBCQ irq filters are written. + */ +void pnv_phb3_remap_irqs(PnvPhb3State *phb) +{ + uint32_t local, global, count, mask, comp; + uint64_t baren; + + /* First check if we are enabled. Unlike real HW we don't separate TX and RX + * so we enable if both are set + */ + baren = phb->pbcq->nest_regs[PBCQ_NEST_BAR_EN]; + if (!(baren & PBCQ_NEST_BAR_EN_IRSN_RX) || + !(baren & PBCQ_NEST_BAR_EN_IRSN_TX)) { + phb->lsi_ics->offset = 0; + return; + } + + /* Grab local LSI source ID */ + local = GETFIELD(PHB_LSI_SRC_ID, phb->regs[PHB_LSI_SOURCE_ID >> 3]) << 3; + + /* Grab global one and compare */ + global = GETFIELD(PBCQ_NEST_LSI_SRC, + phb->pbcq->nest_regs[PBCQ_NEST_LSI_SRC_ID]) << 3; + if (global != local) { + /* This happens during initialization, let's come back when we + * are properly configured + */ + phb->lsi_ics->offset = 0; + return; + } + + /* Get the base on the powerbus */ + comp = GETFIELD(PBCQ_NEST_IRSN_COMP, + phb->pbcq->nest_regs[PBCQ_NEST_IRSN_COMPARE]); + mask = GETFIELD(PBCQ_NEST_IRSN_COMP, + phb->pbcq->nest_regs[PBCQ_NEST_IRSN_MASK]); + count = ((~mask) + 1) & 0x7ffff; + phb->total_irq = count; + + /* Sanity checks */ + if ((global + 8) > count) { + DBG_MAP(phb, "LSIs out of reach: LSI base=%d total irq=%d", + global, count); + } + if (count > 2048) { + DBG_MAP(phb, "More interrupts than supported: %d", count); + } + if ((comp & mask) != comp) { + DBG_MAP(phb, "IRQ compare bits not in mask: comp=0x%x mask=0x%x", + comp, mask); + comp &= mask; + } + /* Setup LSI offset */ + phb->lsi_ics->offset = comp + global; + + /* Setup MSI offset */ + pnv_phb3_msi_update_config(phb->msis, comp, count); + + DBG_MAP(phb, "Initialized for %d interrupts @0x%x, LSI off=%d", + count, comp, global); +} + +static void pnv_phb3_lsi_src_id_write(PnvPhb3State *phb, uint64_t val) +{ + /* Sanitize content */ + val &= PHB_LSI_SRC_ID_MASK; + phb->regs[PHB_LSI_SOURCE_ID >> 3] = val; + pnv_phb3_remap_irqs(phb); +} + +static void pnv_phb3_rtc_invalidate(PnvPhb3State *phb, uint64_t val) +{ + PnvPhb3DMASpace *ds; + + /* Always invalidate all for now ... */ + QLIST_FOREACH(ds, &phb->dma_spaces, list) { + ds->pe_num = PHB_INVALID_PE; + } +} + + +static void pnv_phb3_update_msi_regions(PnvPhb3DMASpace *ds) +{ + uint64_t cfg = ds->phb->regs[PHB_PHB3_CONFIG >> 3]; + + if (cfg & PHB_PHB3C_32BIT_MSI_EN) { + if (!ds->msi32_mapped) { + memory_region_add_subregion(&ds->dma_mr, 0xffff0000, &ds->msi32_mr); + ds->msi32_mapped = true; + } + } else { + if (ds->msi32_mapped) { + memory_region_del_subregion(&ds->dma_mr, &ds->msi32_mr); + ds->msi32_mapped = false; + } + } + + if (cfg & PHB_PHB3C_64BIT_MSI_EN) { + if (!ds->msi64_mapped) { + memory_region_add_subregion(&ds->dma_mr, + (1ull << 60), &ds->msi64_mr); + ds->msi64_mapped = true; + } + } else { + if (ds->msi64_mapped) { + memory_region_del_subregion(&ds->dma_mr, &ds->msi64_mr); + ds->msi64_mapped = false; + } + } +} + +static void pnv_phb3_update_all_msi_regions(PnvPhb3State *phb) +{ + PnvPhb3DMASpace *ds; + + QLIST_FOREACH(ds, &phb->dma_spaces, list) { + pnv_phb3_update_msi_regions(ds); + } +} + +void pnv_phb3_reg_write(void *opaque, hwaddr off, uint64_t val, unsigned size) +{ + PnvPhb3State *phb = opaque; + bool changed; + + /* Special case configuration data */ + if ((off & 0xfffc) == PHB_CONFIG_DATA) { + pnv_phb3_config_write(phb, off & 0x3, size, val); + return; + } + + /* Other registers are 64-bit only */ + if (size != 8 || off & 0x7) { + DBG_ERR(phb, "Invalid register access, offset: 0x%x size: %d", + (unsigned int)off, size); + return; + } + + /* Handle masking */ + switch(off) { + case PHB_M64_UPPER_BITS: + val &= 0xfffc000000000000ull; + break; + } + + /* Record whether it changed */ + changed = phb->regs[off >> 3] != val; + + /* Store in register cache first */ + phb->regs[off >> 3] = val; + + /* Handle side effects */ + switch(off) { + case PHB_PHB3_CONFIG: + if (changed) { + pnv_phb3_update_all_msi_regions(phb); + } + /* fall through */ + case PHB_M32_BASE_ADDR: + case PHB_M32_BASE_MASK: + case PHB_M32_START_ADDR: + if (changed) { + pnv_phb3_check_m32(phb); + } + break; + case PHB_M64_UPPER_BITS: + if (changed) { + pnv_phb3_check_all_m64s(phb); + } + break; + case PHB_LSI_SOURCE_ID: + if (changed) { + pnv_phb3_lsi_src_id_write(phb, val); + } + break; + + /* IODA table accesses */ + case PHB_IODA_DATA0: + pnv_phb3_ioda_write(phb, val); + break; + + /* RTC invalidation */ + case PHB_RTC_INVALIDATE: + pnv_phb3_rtc_invalidate(phb, val); + break; + + /* FFI request */ + case PHB_FFI_REQUEST: + pnv_phb3_msi_ffi(phb->msis, val); + break; + + /* Silent simple writes */ + case PHB_CONFIG_ADDRESS: + case PHB_IODA_ADDR: + case PHB_TCE_KILL: + case PHB_TCE_SPEC_CTL: + case PHB_PEST_BAR: + case PHB_PELTV_BAR: + case PHB_RTT_BAR: + case PHB_RBA_BAR: + case PHB_IVT_BAR: + case PHB_FFI_LOCK: + break; + +#ifdef DISPLAY_UNIMPLENTED_REG + /* Noise on anything else */ + default: + DBG_ERR(phb, "reg_write 0x%x=%016llx", + (unsigned int)off, (unsigned long long)val); +#endif + } +} + +uint64_t pnv_phb3_reg_read(void *opaque, hwaddr off, unsigned size) +{ + PnvPhb3State *phb = opaque; + uint64_t val; + + if ((off & 0xfffc) == PHB_CONFIG_DATA) { + return pnv_phb3_config_read(phb, off & 0x3, size); + } + + /* Other registers are 64-bit only */ + if (size != 8 || off & 0x7) { + DBG_ERR(phb, "Invalid register access, offset: 0x%x size: %d", + (unsigned int)off, size); + return ~0ull; + } + + /* Default read from cache */ + val = phb->regs[off >> 3]; + + switch(off) { + /* Simulate venice DD2.0 */ + case PHB_VERSION: + return 0x000000a300000005ull; + + /* IODA table accesses */ + case PHB_IODA_DATA0: + return pnv_phb3_ioda_read(phb); + + /* Link training always appears trained */ + case PHB_PCIE_DLP_TRAIN_CTL: + return PHB_PCIE_DLP_INBAND_PRESENCE | PHB_PCIE_DLP_TC_DL_LINKACT; + + /* FFI Lock */ + case PHB_FFI_LOCK: + /* Set lock and return previous value */ + phb->regs[off >> 3] |= PHB_FFI_LOCK_STATE; + return val; + + /* Silent simple reads */ + case PHB_PHB3_CONFIG: + case PHB_M32_BASE_ADDR: + case PHB_M32_BASE_MASK: + case PHB_M32_START_ADDR: + case PHB_CONFIG_ADDRESS: + case PHB_IODA_ADDR: + case PHB_RTC_INVALIDATE: + case PHB_TCE_KILL: + case PHB_TCE_SPEC_CTL: + case PHB_PEST_BAR: + case PHB_PELTV_BAR: + case PHB_RTT_BAR: + case PHB_RBA_BAR: + case PHB_IVT_BAR: + case PHB_M64_UPPER_BITS: + break; + +#ifdef DISPLAY_UNIMPLENTED_REG + /* Noise on anything else */ + default: + DBG_ERR(phb, "reg_read 0x%x=%016llx", + (unsigned int)off, (unsigned long long)val); +#endif + } + return val; +} + +static const MemoryRegionOps pnv_phb3_reg_ops = { + .read = pnv_phb3_reg_read, + .write = pnv_phb3_reg_write, + .valid.min_access_size = 1, + .valid.max_access_size = 8, + .impl.min_access_size = 1, + .impl.max_access_size = 8, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static int pnv_phb3_map_irq(PCIDevice *pci_dev, int irq_num) +{ + /* Check that out properly ... */ + return irq_num & 3; +} + +static void pnv_phb3_set_irq(void *opaque, int irq_num, int level) +{ + PnvPhb3State *phb = opaque; + + /* LSI only ... */ + if (irq_num > 3) { + DBG_ERR(phb, "Unknown IRQ to set %d", irq_num); + } + qemu_set_irq(phb->lsi_ics->qirqs[irq_num], level); +} + +static bool pnv_phb3_resolve_pe(PnvPhb3DMASpace *ds) +{ + uint64_t rtt, addr; + uint16_t rte; + int bus_num; + + /* Already resolved ? */ + if (ds->pe_num != PHB_INVALID_PE) { + return true; + } + + /* We need to lookup the RTT */ + rtt = ds->phb->regs[PHB_RTT_BAR >> 3]; + if (!(rtt & PHB_RBA_BAR_ENABLE)) { + DBG_ERR(ds->phb, "DMA with RTT BAR disabled !"); + // Set error bits ? fence ? ... + return false; + } + + /* Read RTE */ + bus_num = pci_bus_num(ds->bus); + addr = rtt & PHB_RTT_BASE_ADDRESS_MASK; + addr += 2 * ((bus_num << 8) | ds->devfn); + if (dma_memory_read(&address_space_memory, addr, &rte, sizeof(rte))) { + DBG_ERR(ds->phb, "Failed to read RTT entry at %016llx", + (unsigned long long)addr); + // Set error bits ? fence ? ... + return false; + } + rte = be16_to_cpu(rte); + + /* Fail upon reading of invalid PE# */ + if (rte >= PHB_NUM_PE) { + DBG_ERR(ds->phb, "RTE for RID 0x%x invalid (%04x)", ds->devfn, rte); + // Set error bits ? fence ? ... + return false; + } + ds->pe_num = rte; + return true; +} + +static void pnv_phb3_translate_tve(PnvPhb3DMASpace *ds, hwaddr addr, + bool is_write, uint64_t tve, + IOMMUTLBEntry *tlb) +{ + uint64_t tta = GETFIELD(IODA2_TVT_TABLE_ADDR, tve); + int32_t lev = GETFIELD(IODA2_TVT_NUM_LEVELS, tve); + uint32_t tts = GETFIELD(IODA2_TVT_TCE_TABLE_SIZE, tve); + uint32_t tps = GETFIELD(IODA2_TVT_IO_PSIZE, tve); + + DBG_DMA(ds->phb, "xlate %016llx:%c TVE=%016llx", + (unsigned long long)addr, is_write ? 'W' : 'R', + (unsigned long long)tve); + + DBG_DMA(ds->phb, " tta=%016llx lev=%d tts=%d tps=%d", + (unsigned long long)tta, lev, tts, tps); + + /* Invalid levels */ + if (lev > 4) { + DBG_ERR(ds->phb, "Invalid #levels in TVE %d", lev); + return; + } + + /* IO Page Size of 0 means untranslated, else use TCEs */ + if (tps == 0) { + /* We only support non-translate in top window + * XXX FIX THAT, Venice/Murano support it on bottom window + * above 4G and Naples suports it on everything + */ + if (!(tve & PPC_BIT(51))) { + DBG_ERR(ds->phb, "xlate for invalid non-translate TVE"); + return; + } + // XXX Handle boundaries */ + + /* XXX Use 4k pages like q35 ... for now */ + DBG_DMA(ds->phb, " non-translate ok"); + tlb->iova = addr & 0xfffffffffffff000ull; + tlb->translated_addr = addr & 0x0003fffffffff000ull; + tlb->addr_mask = 0xfffull; + tlb->perm = IOMMU_RW; + } else { + uint32_t tce_shift, tbl_shift, sh; + uint64_t base, taddr, tce, tce_mask; + + /* TVE disabled ? */ + if (tts == 0) { + DBG_ERR(ds->phb, "xlate for invalid translated TVE"); + return; + } + + /* Address bits per bottom level TCE entry */ + tce_shift = tps + 11; + + /* Address bits per table level */ + tbl_shift = tts + 8; + + /* Top level table base address */ + base = tta << 12; + + /* Total shift to first level */ + sh = tbl_shift * lev + tce_shift; + + DBG_DMA(ds->phb, " tce_shift %d tbl_shift %d", tce_shift, tbl_shift); + + // XXX Multi-level untested */ + while ((lev--) >= 0) { + /* Grab the TCE address */ + taddr = base | (((addr >> sh) & ((1ul << tbl_shift) - 1)) << 3); + if (dma_memory_read(&address_space_memory, taddr, &tce, sizeof(tce))) { + DBG_ERR(ds->phb, "Failed to read TCE at 0x%016llx", + (unsigned long long)taddr); + return; + } + tce = be64_to_cpu(tce); + DBG_DMA(ds->phb, " lev %d taddr 0x%016llx tce 0x%016llx", lev + 1, + (unsigned long long)taddr, (unsigned long long)tce); + + /* Check permission for indirect TCE */ + if ((lev >= 0) && !(tce & 3)) { + DBG_ERR(ds->phb, "Invalid indirect TCE at 0x%016llx", + (unsigned long long)taddr); + DBG_ERR(ds->phb, " xlate %016llx:%c TVE=%016llx", + (unsigned long long)addr, is_write ? 'W' : 'R', + (unsigned long long)tve); + DBG_ERR(ds->phb, " tta=%016llx lev=%d tts=%d tps=%d", + (unsigned long long)tta, lev, tts, tps); + return; + } + sh -= tbl_shift; + base = tce & ~0xfffull; + } + + /* We exit the loop with TCE being the final TCE */ + tce_mask = ~((1ull << tce_shift) - 1); + tlb->iova = addr & tce_mask; + tlb->translated_addr = tce & tce_mask; + tlb->addr_mask = ~tce_mask; + tlb->perm = tce & 3; + if ((is_write & !(tce & 2)) || ((!is_write) && !(tce & 1))) { + DBG_ERR(ds->phb, "TCE access fault at 0x%016llx", + (unsigned long long)taddr); + DBG_ERR(ds->phb, " xlate %016llx:%c TVE=%016llx", + (unsigned long long)addr, is_write ? 'W' : 'R', + (unsigned long long)tve); + DBG_ERR(ds->phb, " tta=%016llx lev=%d tts=%d tps=%d", + (unsigned long long)tta, lev, tts, tps); + } + } +} + +static IOMMUTLBEntry pnv_phb3_translate_iommu(MemoryRegion *iommu, hwaddr addr, + bool is_write) +{ + PnvPhb3DMASpace *ds = container_of(iommu, PnvPhb3DMASpace, dma_mr); + int tve_sel; + uint64_t tve, cfg; + IOMMUTLBEntry ret = { + .target_as = &address_space_memory, + .iova = addr, + .translated_addr = 0, + .addr_mask = ~(hwaddr)0, + .perm = IOMMU_NONE, + }; + + /* Resolve PE# */ + if (!pnv_phb3_resolve_pe(ds)) { + DBG_ERR(ds->phb, "Failed to resolve PE# for bus @%p (%d) devfn 0x%x", + ds->bus, pci_bus_num(ds->bus), ds->devfn); + return ret; + } + + DBG_DMA(ds->phb, "xlate bus @%p (%d) devfn 0x%x PE %d ds @%p", + ds->bus, pci_bus_num(ds->bus),ds->devfn, ds->pe_num, ds); + + /* Check top bits */ + switch (addr >> 60) { + case 00: + /* DMA or 32-bit MSI ? */ + cfg = ds->phb->regs[PHB_PHB3_CONFIG >> 3]; + if ((cfg & PHB_PHB3C_32BIT_MSI_EN) && + ((addr & 0xffffffffffff0000ull) == 0xffff0000ull)) { + DBG_ERR(ds->phb, "xlate on 32-bit MSI region"); + return ret; + } + /* Choose TVE XXX Use PHB3 Control Register */ + tve_sel = (addr >> 59) & 1; + tve = ds->phb->ioda_TVT[ds->pe_num * 2 + tve_sel]; + DBG_DMA(ds->phb, " TVE_SEL=%d -> %d", + tve_sel, ds->pe_num * 2 + tve_sel); + pnv_phb3_translate_tve(ds, addr, is_write, tve, &ret); + break; + case 01: + DBG_ERR(ds->phb, "xlate on 64-bit MSI region"); + break; + default: + DBG_ERR(ds->phb, "xlate on unsupported address 0x%016llx", + (unsigned long long)addr); + } + return ret; +} + +static const MemoryRegionIOMMUOps pnv_phb3_iommu_ops = { + .translate = pnv_phb3_translate_iommu, +}; + +/* + * MSI/MSIX memory region implementation. + * The handler handles both MSI and MSIX. + */ +static void pnv_phb3_msi_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + PnvPhb3DMASpace *ds = opaque; + + /* Resolve PE# */ + if (!pnv_phb3_resolve_pe(ds)) { + DBG_ERR(ds->phb, "Failed to resolve PE# for bus @%p (%d) devfn 0x%x", + ds->bus, pci_bus_num(ds->bus), ds->devfn); + return; + } + + pnv_phb3_msi_send(ds->phb->msis, addr, data, ds->pe_num); +} + +static const MemoryRegionOps pnv_phb3_msi_ops = { + /* There is no .read as the read result is undefined by PCI spec */ + .read = NULL, + .write = pnv_phb3_msi_write, + .endianness = DEVICE_LITTLE_ENDIAN +}; + +static AddressSpace *pnv_phb3_dma_iommu(PCIBus *bus, void *opaque, int devfn) +{ + PnvPhb3State *phb = opaque; + PnvPhb3DMASpace *ds; + + DBG_DMA(phb, "get IOMMU for bus @%p devfn 0x%x", bus, devfn); + + QLIST_FOREACH(ds, &phb->dma_spaces, list) { + if (ds->bus == bus && ds->devfn == devfn) { + DBG_DMA(phb, " found @%p", ds); + break; + } + } + if (ds == NULL) { + ds = g_malloc0(sizeof(PnvPhb3DMASpace)); + ds->bus = bus; + ds->devfn = devfn; + ds->pe_num = PHB_INVALID_PE; + ds->phb = phb; + memory_region_init_iommu(&ds->dma_mr, OBJECT(phb), + &pnv_phb3_iommu_ops, "phb3_iommu", UINT64_MAX); + address_space_init(&ds->dma_as, &ds->dma_mr, "phb3_iommu"); + memory_region_init_io(&ds->msi32_mr, OBJECT(phb), &pnv_phb3_msi_ops, + ds, "msi32", 0x10000); + memory_region_init_io(&ds->msi64_mr, OBJECT(phb), &pnv_phb3_msi_ops, + ds, "msi64", 0x100000); + pnv_phb3_update_msi_regions(ds); + + QLIST_INSERT_HEAD(&phb->dma_spaces, ds, list); + DBG_DMA(phb, " created @%p", ds); + } + return &ds->dma_as; +} + +static void pnv_phb3_root_bus_class_init(ObjectClass *klass, void *data) +{ + BusClass *k = BUS_CLASS(klass); + + k->max_dev = 1; +} + +#define TYPE_PNV_PHB3_ROOT_BUS "pnv-phb3-root-bus" + +static const TypeInfo pnv_phb3_root_bus_info = { + .name = TYPE_PNV_PHB3_ROOT_BUS, + .parent = TYPE_PCIE_BUS, + .class_init = pnv_phb3_root_bus_class_init, +}; + +static void pnv_phb3_initfn(Object *obj) +{ + PnvPhb3State *phb = PNV_PHB3(obj); + + phb->lsi_ics = ICS(object_new(TYPE_ICS_SIMPLE)); + object_property_add_child(OBJECT(phb), "ics", OBJECT(phb->lsi_ics), NULL); + /* Default init ... will be fixed by HW inits */ + phb->lsi_ics->offset = 0; + phb->lsi_ics->nr_irqs = PHB_NUM_LSI; + QLIST_INIT(&phb->dma_spaces); +} + +static void pnv_phb3_realize(DeviceState *dev, Error **errp) +{ + PnvPhb3State *phb = PNV_PHB3(dev); + PCIHostState *pci = PCI_HOST_BRIDGE(dev); + Error *error = NULL; + int i; + + memory_region_init(&phb->pci_mmio, OBJECT(phb), "pci-mmio", + PCI_MMIO_TOTAL_SIZE); + + /* PHB3 doesn't support IO space. However, qemu gets very upset if + * we don't have an IO region to anchor IO BARs onto so we just + * initialize one which we never hook up to anything + */ + memory_region_init(&phb->pci_io, OBJECT(phb), "pci-io", 0x10000); + + memory_region_init_io(&phb->mr_regs, OBJECT(phb), &pnv_phb3_reg_ops, phb, + "phb3-regs", 0x1000); + + /* Realize LSI ICS */ + xics_add_ics(phb->xics, phb->lsi_ics); + object_property_set_bool(OBJECT(phb->lsi_ics), true, "realized", &error); + if (error) { + error_propagate(errp, error); + return; + } + for (i = 0; i < PHB_NUM_LSI; i++) + ics_simple_set_irq_type(phb->lsi_ics, i, true); + + pci->bus = pci_register_bus(dev, "phb3-root-bus", + pnv_phb3_set_irq, pnv_phb3_map_irq, phb, + &phb->pci_mmio, &phb->pci_io, + 0, 4, TYPE_PNV_PHB3_ROOT_BUS); + pci->bus->devfn_max = 1; + pci_setup_iommu(pci->bus, pnv_phb3_dma_iommu, phb); +} + +void pnv_phb3_update_regions(PnvPhb3State *phb) +{ + /* Unmap first always */ + if (phb->regs_mapped) { + memory_region_del_subregion(&phb->pbcq->phbbar, &phb->mr_regs); + phb->regs_mapped = false; + } + + /* Map registers if enabled */ + if (phb->pbcq->phb_mapped) { + /* XXX We should use the PHB BAR 2 register but we don't ... */ + memory_region_add_subregion(&phb->pbcq->phbbar, 0, &phb->mr_regs); + phb->regs_mapped = true; + } + + /* Check/update m32 */ + if (phb->m32_mapped) { + pnv_phb3_check_m32(phb); + } +} + +static void pnv_phb3_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = pnv_phb3_realize; +} + +static const TypeInfo pnv_phb3_type_info = { + .name = TYPE_PNV_PHB3, + .parent = TYPE_PCI_HOST_BRIDGE, + .instance_size = sizeof(PnvPhb3State), + .class_init = pnv_phb3_class_init, + .instance_init = pnv_phb3_initfn, +}; + +static void pnv_phb3_register_types(void) +{ + type_register_static(&pnv_phb3_type_info); + type_register_static(&pnv_phb3_root_bus_info); +} + +type_init(pnv_phb3_register_types) + +void pnv_phb3_create(PnvChip *chip, XICSState *xics, uint32_t idx) +{ + struct DeviceState *dev; + PnvPhb3State *phb; + PnvPBCQState *pbcq; + PCIHostState *pcih; + PCIDevice *pdev; + PCIBridge *bdev; + uint8_t chassis; + + chassis = chip->chip_id * 4 + idx; + + /* Create PBCQ */ + dev = qdev_create(&chip->xscom->bus, TYPE_PNV_PBCQ); + qdev_prop_set_uint32(dev, "phb_id", idx); + qdev_init_nofail(dev); + pbcq = PNV_PBCQ(dev); + + /* Create PHB3 */ + dev = qdev_create(NULL, TYPE_PNV_PHB3); + phb = PNV_PHB3(dev); + phb->pbcq = pbcq; + pbcq->phb = phb; + phb->xics = xics; + qdev_init_nofail(dev); + pcih = PCI_HOST_BRIDGE(phb); + + /* Create MSI source */ + phb->msis = pnv_phb3_msi_create(phb); + + /* Add root complex */ + pdev = pci_create_multifunction(pcih->bus, 0, false, TYPE_PNV_PHB3_RC); + qdev_prop_set_uint8(&pdev->qdev, "chassis", chassis); + qdev_prop_set_uint16(&pdev->qdev, "slot", 1); + qdev_init_nofail(&pdev->qdev); + bdev = PCI_BRIDGE(pdev); + + /* Setup bus for that chip */ + chip->phb[idx] = pci_bridge_get_sec_bus(bdev); +} diff --git a/hw/pci-host/pnv_phb3_msi.c b/hw/pci-host/pnv_phb3_msi.c new file mode 100644 index 0000000000000..1cc289922109f --- /dev/null +++ b/hw/pci-host/pnv_phb3_msi.c @@ -0,0 +1,354 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Copyright IBM Corp. 2014 + */ +#include "qemu/osdep.h" +#include "hw/pci-host/pnv_phb3.h" +#include "hw/pci/pci_bridge.h" +#include "hw/pci/pci_bus.h" +#include "hw/pci/msi.h" +#include "hw/ppc/xics.h" + +#define PHB3_MAX_MSI 2048 + +//#define DEBUG_MSI 1 +#define DEBUG_MSI 0 + +typedef struct Phb3MsiState { + struct ICSState ics; + PnvPhb3State *phb; + uint64_t rba[PHB3_MAX_MSI/64]; + uint32_t rba_sum; +} Phb3MsiState; + +#define TYPE_PHB3_MSI "phb3-msi" +#define PHB3_MSI(obj) OBJECT_CHECK(Phb3MsiState, (obj), TYPE_PHB3_MSI) + +#define DBG_ERR(p, fmt, ...) do { \ + if (1) fprintf(stderr, "PHB3(%s): " fmt "\n", __func__, ## __VA_ARGS__); \ + } while(0) + +#define DBG_MSI(p, fmt, ...) do { \ + if (DEBUG_MSI) fprintf(stderr, "PHB3(%s): " fmt "\n", __func__, ## __VA_ARGS__); \ + } while(0) + + +static void phb3_msi_initfn(Object *obj) +{ + Phb3MsiState *msis = PHB3_MSI(obj); + + /* Will be overriden later */ + msis->ics.offset = 0; + + /* Hard wire 2048, we ignore the fact that 8 of them can be + * taken over by LSIs at this point + */ + msis->ics.nr_irqs = PHB3_MAX_MSI; +} + +static uint64_t phb3_msi_ive_addr(Phb3MsiState *msis, int srcno) +{ + uint64_t ivtbar = msis->phb->regs[PHB_IVT_BAR >> 3]; + uint64_t phbctl = msis->phb->regs[PHB_CONTROL >> 3]; + + if (!(ivtbar & PHB_IVT_BAR_ENABLE)) { + DBG_ERR(msis->phb, "Failed access to disable IVT BAR !"); + return 0; + } + + if (srcno >= (ivtbar & PHB_IVT_LENGTH_MASK)) { + DBG_ERR(msis->phb, "MSI out of bounds (%d vs %ld)", + srcno, (long)(ivtbar & PHB_IVT_LENGTH_MASK)); + return 0; + } + + ivtbar &= PHB_IVT_BASE_ADDRESS_MASK; + + if (phbctl & PHB_CTRL_IVE_128_BYTES) { + return ivtbar + 128 * srcno; + } else { + return ivtbar + 16 * srcno; + } +} + +static bool phb3_msi_read_ive(Phb3MsiState *msis, int srcno, uint64_t *out_ive) +{ + uint64_t ive_addr, ive; + + ive_addr = phb3_msi_ive_addr(msis, srcno); + if (!ive_addr) { + return false; + } + if (dma_memory_read(&address_space_memory, ive_addr, &ive, sizeof(ive))) { + DBG_ERR(msis->phb, "Failed to read IVE at 0x%016llx", + (unsigned long long)ive_addr); + return false; + } + *out_ive = be64_to_cpu(ive); + + return true; +} + +static void phb3_msi_set_p(Phb3MsiState *msis, int srcno, uint8_t gen) +{ + uint64_t ive_addr; + uint8_t p = 0x01 | (gen << 1); + + ive_addr = phb3_msi_ive_addr(msis, srcno); + if (!ive_addr) { + return; + } + DBG_MSI(msis->phb, "MSI %d: setting P", srcno); + if (dma_memory_write(&address_space_memory, ive_addr + 4, &p, 1)) { + DBG_ERR(msis->phb, "Failed to write IVE (set P) at 0x%016llx", + (unsigned long long)ive_addr); + } +#ifdef DEBUG_MSI + { + uint64_t ive; + + if (dma_memory_read(&address_space_memory, ive_addr, &ive, 8)) { + DBG_ERR(ds->phb, "Failed to read IVE (set P) at 0x%016llx", + (unsigned long long)ive_addr); + } + DBG_MSI(msis->phb, " IVE readback: %016llx", + (unsigned long long)be64_to_cpu(ive)); + } +#endif +} + +static void phb3_msi_set_q(Phb3MsiState *msis, int srcno) +{ + uint64_t ive_addr; + uint8_t q = 0x01; + + ive_addr = phb3_msi_ive_addr(msis, srcno); + if (!ive_addr) { + return; + } + DBG_MSI(msis->phb, "MSI %d: setting Q", srcno); + if (dma_memory_write(&address_space_memory, ive_addr + 5, &q, 1)) { + DBG_ERR(ds->phb, "Failed to write IVE (set Q) at 0x%016llx", + (unsigned long long)ive_addr); + } +#ifdef DEBUG_MSI + { + uint64_t ive; + + if (dma_memory_read(&address_space_memory, ive_addr, &ive, 8)) { + DBG_ERR(ds->phb, "Failed to read IVE (set P) at 0x%016llx", + (unsigned long long)ive_addr); + } + DBG_MSI(msis->phb, " IVE readback: %016llx", + (unsigned long long)be64_to_cpu(ive)); + } +#endif +} + +static void phb3_msi_try_send(Phb3MsiState *msis, int srcno, bool ignore_p) +{ + uint64_t ive; + uint64_t server, prio, pq, gen; + + if (!phb3_msi_read_ive(msis, srcno, &ive)) { + return; + } + + server = GETFIELD(IODA2_IVT_SERVER, ive); + prio = GETFIELD(IODA2_IVT_PRIORITY, ive); + pq = GETFIELD(IODA2_IVT_Q, ive); + if (!ignore_p) { + pq |= GETFIELD(IODA2_IVT_P, ive) << 1; + } + gen = GETFIELD(IODA2_IVT_GEN, ive); + + DBG_MSI(msis->phb, "MSI %d: try_send, ive=0x%016llx eff pq=%d", srcno, + (unsigned long long)ive, (int)pq); + + switch(pq) { + case 0: /* 00 */ + if (prio == 0xff) { + /* Masked, set Q */ + phb3_msi_set_q(msis, srcno); + } else { + /* Enabled, set P and send */ + phb3_msi_set_p(msis, srcno, gen); + icp_irq(&msis->ics, server, srcno + msis->ics.offset, prio); + } + break; + case 2: /* 10 */ + /* Already pending, set Q */ + phb3_msi_set_q(msis, srcno); + break; + case 1: /* 01 */ + case 3: /* 11 */ + default: + /* Just drop stuff if Q already set */ + break; + } +} + +static void phb3_msi_set_irq(void *opaque, int srcno, int val) +{ + Phb3MsiState *msis = opaque; + + if (val) { + phb3_msi_try_send(msis, srcno, false); + } +} + + +void pnv_phb3_msi_send(Phb3MsiState *msis, uint64_t addr, uint16_t data, + int32_t dev_pe) +{ + uint64_t ive; + uint16_t pe; + uint32_t src = ((addr >> 4) & 0xffff) | (data & 0x1f); + + if (src >= msis->ics.nr_irqs) { + DBG_ERR(msis->phb, "MSI %d out of bounds", src); + return; + } + if (dev_pe >= 0) { + if (!phb3_msi_read_ive(msis, src, &ive)) { + return; + } + pe = GETFIELD(IODA2_IVT_PE, ive); + if (pe != dev_pe) { + DBG_ERR(msis->phb, "MSI %d send by PE#%d but assigned to PE#%d", + src, dev_pe, pe); + return; + } + } + qemu_irq_pulse(msis->ics.qirqs[src]); + +} + +void pnv_phb3_msi_ffi(Phb3MsiState *msis, uint64_t val) +{ + /* Emit interrupt */ + pnv_phb3_msi_send(msis, val, 0, -1); + + /* Clear FFI lock */ + msis->phb->regs[PHB_FFI_LOCK >> 3] = 0; +} + +static void phb3_msi_reject(ICSState *ics, uint32_t nr) +{ + Phb3MsiState *msis = PHB3_MSI(ics); + unsigned int srcno = nr - ics->offset; + unsigned int idx = srcno >> 6; + unsigned int bit = 1ull << (srcno & 0x3f); + + assert(srcno < PHB3_MAX_MSI); + + DBG_MSI(msis->phb, "MSI %d rejected", srcno); + + msis->rba[idx] |= bit; + msis->rba_sum |= (1u << idx); +} + +static void phb3_msi_resend(ICSState *ics) +{ + Phb3MsiState *msis = PHB3_MSI(ics); + unsigned int i,j; + + if (msis->rba_sum == 0) { + return; + } + + DBG_MSI(msis->phb, "MSI resend..."); + + for (i = 0; i < 32; i++) { + if ((msis->rba_sum & (1u << i)) == 0) { + continue; + } + msis->rba_sum &= ~(1u << i); + for (j = 0; j < 64; j++) { + if ((msis->rba[i] & (1ull << j)) == 0) { + continue; + } + msis->rba[i] &= ~(1u << j); + phb3_msi_try_send(msis, i * 64 + j, true); + } + } +} + +static void phb3_msi_reset(DeviceState *dev) +{ + Phb3MsiState *msis = PHB3_MSI(dev); + + memset(msis->rba, 0, sizeof(msis->rba)); + msis->rba_sum = 0; +} + +void pnv_phb3_msi_update_config(Phb3MsiState *msis, uint32_t base, + uint32_t count) +{ + if (count > PHB3_MAX_MSI) { + count = PHB3_MAX_MSI; + } + msis->ics.nr_irqs = count; + msis->ics.offset = base; +} + +static void phb3_msi_realize(DeviceState *dev, Error **errp) +{ + Phb3MsiState *msis = PHB3_MSI(dev); + + msis->ics.irqs = NULL; + msis->ics.qirqs = qemu_allocate_irqs(phb3_msi_set_irq, msis, PHB3_MAX_MSI); +} + +static void phb3_msi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ICSStateClass *isc = ICS_CLASS(klass); + + dc->realize = phb3_msi_realize; + dc->reset = phb3_msi_reset; + isc->reject = phb3_msi_reject; + isc->resend = phb3_msi_resend; +} + +Phb3MsiState *pnv_phb3_msi_create(PnvPhb3State *phb) +{ + Phb3MsiState *msis; + DeviceState *dev; + + dev = qdev_create(NULL, TYPE_PHB3_MSI); + msis = PHB3_MSI(dev); + msis->phb = phb; + phb->msis = msis; + xics_add_ics(phb->xics, &msis->ics); + qdev_init_nofail(dev); + + return msis; +} + +static const TypeInfo phb3_msi_info = { + .name = TYPE_PHB3_MSI, + .parent = TYPE_ICS, + .instance_size = sizeof(Phb3MsiState), + .class_init = phb3_msi_class_init, + .class_size = sizeof(ICSStateClass), + .instance_init = phb3_msi_initfn, +}; + +static void pnv_phb3_msi_register_types(void) +{ + type_register_static(&phb3_msi_info); +} + +type_init(pnv_phb3_msi_register_types) diff --git a/hw/pci-host/pnv_phb3_pbcq.c b/hw/pci-host/pnv_phb3_pbcq.c new file mode 100644 index 0000000000000..591aa96ad2f5f --- /dev/null +++ b/hw/pci-host/pnv_phb3_pbcq.c @@ -0,0 +1,315 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Copyright IBM Corp. 2014 + */ +#include "qemu/osdep.h" +#include "hw/pci-host/pnv_phb3.h" +#include "hw/pci/pci_bridge.h" +#include "hw/pci/pci_bus.h" + +#include + +static bool pnv_pbcq_xscom_read(XScomDevice *dev, uint32_t range, + uint32_t offset, uint64_t *out_val) +{ + PnvPBCQState *pbcq = PNV_PBCQ(dev); + + switch(range) { + case 0: + *out_val = pbcq->nest_regs[offset]; + break; + case 1: + *out_val = pbcq->pci_regs[offset]; + break; + case 2: + if (offset == PBCQ_SPCI_ASB_DATA) { + if (!pbcq->phb) { + *out_val = ~0ull; + break; + } + *out_val = pnv_phb3_reg_read(pbcq->phb, + pbcq->spci_regs[PBCQ_SPCI_ASB_ADDR], 8); + break; + } + *out_val = pbcq->spci_regs[offset]; + break; + default: + return false; + } + return true; +} + +static void pnv_pbcq_update_map(PnvPBCQState *pbcq) +{ + uint64_t bar_en = pbcq->nest_regs[PBCQ_NEST_BAR_EN]; + uint64_t bar, mask, size; + + /* + * NOTE: This will really not work well if those are remapped + * after the PHB has created its sub regions. We could do better + * if we had a way to resize regions but we don't really care + * that much in practice as the stuff below really only happens + * once early during boot + */ + + /* Handle unmaps */ + if (pbcq->mmio0_mapped && !(bar_en & PBCQ_NEST_BAR_EN_MMIO0)) { + memory_region_del_subregion(get_system_memory(), &pbcq->mmbar0); + pbcq->mmio0_mapped = false; + } + if (pbcq->mmio1_mapped && !(bar_en & PBCQ_NEST_BAR_EN_MMIO1)) { + memory_region_del_subregion(get_system_memory(), &pbcq->mmbar1); + pbcq->mmio1_mapped = false; + } + if (pbcq->phb_mapped && !(bar_en & PBCQ_NEST_BAR_EN_PHB)) { + memory_region_del_subregion(get_system_memory(), &pbcq->phbbar); + pbcq->phb_mapped = false; + } + + /* Update PHB if it exists */ + if (pbcq->phb) { + pnv_phb3_update_regions(pbcq->phb); + } + + /* Handle maps */ + if (!pbcq->mmio0_mapped && (bar_en & PBCQ_NEST_BAR_EN_MMIO0)) { + bar = pbcq->nest_regs[PBCQ_NEST_MMIO_BAR0] >> 14; + mask = pbcq->nest_regs[PBCQ_NEST_MMIO_MASK0]; + size = ((~mask) >> 14) + 1; + memory_region_init(&pbcq->mmbar0, OBJECT(pbcq), "pbcq-mmio0", size); + memory_region_add_subregion(get_system_memory(), bar, &pbcq->mmbar0); + pbcq->mmio0_mapped = true; + pbcq->mmio0_base = bar; + pbcq->mmio0_size = size; + } + if (!pbcq->mmio1_mapped && (bar_en & PBCQ_NEST_BAR_EN_MMIO1)) { + bar = pbcq->nest_regs[PBCQ_NEST_MMIO_BAR1] >> 14; + mask = pbcq->nest_regs[PBCQ_NEST_MMIO_MASK1]; + size = ((~mask) >> 14) + 1; + memory_region_init(&pbcq->mmbar1, OBJECT(pbcq), "pbcq-mmio1", size); + memory_region_add_subregion(get_system_memory(), bar, &pbcq->mmbar1); + pbcq->mmio1_mapped = true; + pbcq->mmio1_base = bar; + pbcq->mmio1_size = size; + } + if (!pbcq->phb_mapped && (bar_en & PBCQ_NEST_BAR_EN_PHB)) { + bar = pbcq->nest_regs[PBCQ_NEST_PHB_BAR] >> 14; + size = 0x1000; + memory_region_init(&pbcq->phbbar, OBJECT(pbcq), "pbcq-phb", size); + memory_region_add_subregion(get_system_memory(), bar, &pbcq->phbbar); + pbcq->phb_mapped = true; + } + + /* Update PHB if it exists */ + if (pbcq->phb) { + pnv_phb3_update_regions(pbcq->phb); + } +} + +static bool pnv_pbcq_xnest_write(PnvPBCQState *pbcq, uint32_t reg, uint64_t val) +{ + switch(reg) { + case PBCQ_NEST_MMIO_BAR0: + case PBCQ_NEST_MMIO_BAR1: + case PBCQ_NEST_MMIO_MASK0: + case PBCQ_NEST_MMIO_MASK1: + if (pbcq->nest_regs[PBCQ_NEST_BAR_EN] & + (PBCQ_NEST_BAR_EN_MMIO0 | + PBCQ_NEST_BAR_EN_MMIO1)) { + printf("WARNING: PH3: Changing enabled BAR unsupported\n"); + } + pbcq->nest_regs[reg] = val & 0xffffffffc0000000ull; + return true; + case PBCQ_NEST_PHB_BAR: + if (pbcq->nest_regs[PBCQ_NEST_BAR_EN] & PBCQ_NEST_BAR_EN_PHB) { + printf("WARNING: PH3: Changing enabled BAR unsupported\n"); + } + pbcq->nest_regs[reg] = val & 0xfffffffffc000000ull; + return true; + case PBCQ_NEST_BAR_EN: + pbcq->nest_regs[reg] = val & 0xf800000000000000ull; + pnv_pbcq_update_map(pbcq); + pnv_phb3_remap_irqs(pbcq->phb); + return true; + case PBCQ_NEST_IRSN_COMPARE: + case PBCQ_NEST_IRSN_MASK: + pbcq->nest_regs[reg] = val & PBCQ_NEST_IRSN_COMP_MASK; + pnv_phb3_remap_irqs(pbcq->phb); + return true; + case PBCQ_NEST_LSI_SRC_ID: + pbcq->nest_regs[reg] = val & PBCQ_NEST_LSI_SRC_MASK; + pnv_phb3_remap_irqs(pbcq->phb); + return true; + } + + /* XXX Don't error out on other regs for now ... */ + return true; +} + +static bool pnv_pbcq_xpci_write(PnvPBCQState *pbcq, uint32_t reg, uint64_t val) +{ + switch(reg) { + case PBCQ_PCI_BAR2: + pbcq->pci_regs[reg] = val & 0xfffffffffc000000ull; + pnv_pbcq_update_map(pbcq); + break; + } + + /* XXX Don't error out on other regs for now ... */ + return true; +} + +static bool pnv_pbcq_xspci_write(PnvPBCQState *pbcq, uint32_t reg, uint64_t val) +{ + switch(reg) { + case PBCQ_SPCI_ASB_ADDR: + pbcq->spci_regs[reg] = val & 0xfff; + return true; + case PBCQ_SPCI_ASB_STATUS: + pbcq->spci_regs[reg] &= ~val; + return true; + case PBCQ_SPCI_ASB_DATA: + if (!pbcq->phb) { + return true; + } + pnv_phb3_reg_write(pbcq->phb, pbcq->spci_regs[PBCQ_SPCI_ASB_ADDR], val, 8); + return true; + // case PBCQ_SPCI_AIB_CAPP_EN: + // case PBCQ_SPCI_CAPP_SEC_TMR: + } + + /* XXX Don't error out on other regs for now ... */ + return true; +} + +static bool pnv_pbcq_xscom_write(XScomDevice *dev, uint32_t range, + uint32_t offset, uint64_t val) +{ + PnvPBCQState *pbcq = PNV_PBCQ(dev); + + switch(range) { + case 0: + return pnv_pbcq_xnest_write(pbcq, offset, val); + case 1: + return pnv_pbcq_xpci_write(pbcq, offset, val); + case 2: + return pnv_pbcq_xspci_write(pbcq, offset, val); + default: + return false; + } +} + +static void pnv_pbcq_default_bars(PnvPBCQState *pbcq) +{ + uint64_t mm0, mm1, reg; + + mm0 = 0x3d00000000000ull + + 0x4000000000ull * pbcq->chip_id + + 0x1000000000ull * pbcq->phb_id; + mm1 = 0x3ff8000000000ull + + 0x0200000000ull * pbcq->chip_id + + 0x0080000000ull * pbcq->phb_id; + reg = 0x3fffe40000000ull + + 0x0000400000ull * pbcq->chip_id + + 0x0000100000ull * pbcq->phb_id; + + pbcq->nest_regs[PBCQ_NEST_MMIO_BAR0] = mm0 << 14; + pbcq->nest_regs[PBCQ_NEST_MMIO_BAR1] = mm1 << 14; + pbcq->nest_regs[PBCQ_NEST_PHB_BAR] = reg << 14; + pbcq->nest_regs[PBCQ_NEST_MMIO_MASK0] = 0x3fff000000000ull << 14; + pbcq->nest_regs[PBCQ_NEST_MMIO_MASK1] = 0x3ffff80000000ull << 14; + pbcq->pci_regs[PBCQ_PCI_BAR2] = reg << 14; +} + +static void pnv_pbcq_realize(DeviceState *dev, Error **errp) +{ + PnvPBCQState *pbcq = PNV_PBCQ(dev); + XScomBus *xb = XSCOM_BUS(dev->parent_bus); + XScomDevice *xd = XSCOM_DEVICE(dev); + + assert(pbcq->phb_id < 4); + + /* Copy chip ID over for ease of access */ + pbcq->chip_id = xb->chip_id; + + /* Calculate XSCOM bases */ + pbcq->nest_xbase = 0x02012000 + 0x400 * pbcq->phb_id; + pbcq->pci_xbase = 0x09012000 + 0x400 * pbcq->phb_id; + pbcq->spci_xbase = 0x09013c00 + 0x040 * pbcq->phb_id; + xd->ranges[0].addr = pbcq->nest_xbase; + xd->ranges[0].size = PBCQ_NEST_REGS_COUNT; + xd->ranges[1].addr = pbcq->pci_xbase; + xd->ranges[1].size = PBCQ_PCI_REGS_COUNT; + xd->ranges[2].addr = pbcq->spci_xbase; + xd->ranges[2].size = PBCQ_SPCI_REGS_COUNT; + + /* XXX Fix OPAL to do that: establish default BAR values */ + pnv_pbcq_default_bars(pbcq); +} + +#define _FDT(exp) \ + do { \ + int ret = (exp); \ + if (ret < 0) { \ + fprintf(stderr, "qemu: error creating device tree: %s: %s\n", \ + #exp, fdt_strerror(ret)); \ + exit(1); \ + } \ + } while (0) + + +static int pnv_pbcq_devnode(XScomDevice *dev, void *fdt) +{ + PnvPBCQState *pbcq = PNV_PBCQ(dev); + + _FDT((fdt_property_cell(fdt, "ibm,phb-index", pbcq->phb_id))); + + return 0; +} + +static Property pnv_pbcq_properties[] = { + DEFINE_PROP_UINT32("phb_id", PnvPBCQState, phb_id, 0), + DEFINE_PROP_END_OF_LIST(), + +}; + +static void pnv_pbcq_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + XScomDeviceClass *k = XSCOM_DEVICE_CLASS(klass); + static const char *compat[] = { "ibm,power8-pbcq", NULL }; + + k->devnode = pnv_pbcq_devnode; + k->read = pnv_pbcq_xscom_read; + k->write = pnv_pbcq_xscom_write; + k->dt_name = "pbcq"; + k->dt_compatible = compat; + + dc->realize = pnv_pbcq_realize; + dc->props = pnv_pbcq_properties; +} + +static const TypeInfo pnv_pbcq_type_info = { + .name = TYPE_PNV_PBCQ, + .parent = TYPE_XSCOM_DEVICE, + .instance_size = sizeof(PnvPBCQState), + .class_init = pnv_pbcq_class_init, +}; + +static void pnv_pbcq_register_types(void) +{ + type_register_static(&pnv_pbcq_type_info); +} + +type_init(pnv_pbcq_register_types) diff --git a/hw/pci-host/pnv_phb3_rc.c b/hw/pci-host/pnv_phb3_rc.c new file mode 100644 index 0000000000000..76641166889c6 --- /dev/null +++ b/hw/pci-host/pnv_phb3_rc.c @@ -0,0 +1,130 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Copyright IBM Corp. 2014 + */ +#include "qemu/osdep.h" +#include "hw/pci-host/pnv_phb3.h" +#include "hw/pci/pcie_port.h" + +static void pnv_phb3_rc_write_config(PCIDevice *d, + uint32_t address, uint32_t val, int len) +{ + uint32_t root_cmd = + pci_get_long(d->config + d->exp.aer_cap + PCI_ERR_ROOT_COMMAND); + + pci_bridge_write_config(d, address, val, len); + pcie_cap_slot_write_config(d, address, val, len); + pcie_aer_write_config(d, address, val, len); + pcie_aer_root_write_config(d, address, val, len, root_cmd); +} + +static void pnv_phb3_rc_reset(DeviceState *qdev) +{ + PCIDevice *d = PCI_DEVICE(qdev); + + pcie_cap_root_reset(d); + pcie_cap_deverr_reset(d); + pcie_cap_slot_reset(d); + pcie_cap_arifwd_reset(d); + pcie_aer_root_reset(d); + pci_bridge_reset(qdev); + pci_bridge_disable_base_limit(d); +} + +static int pnv_phb3_rc_initfn(PCIDevice *d) +{ + PCIEPort *p = PCIE_PORT(d); + PCIESlot *s = PCIE_SLOT(d); + int rc; + + DEVICE(d)->id = "pcie"; + pci_bridge_initfn(d, TYPE_PCIE_BUS); + + /* XXX Make that a property ? Allow for only one device (8 functions) */ + pci_bridge_get_sec_bus(PCI_BRIDGE(d))->devfn_max = 8; + + pcie_port_init_reg(d); + + rc = pcie_cap_init(d, 0x48, PCI_EXP_TYPE_ROOT_PORT, p->port); + if (rc < 0) { + printf("phb3-rc: pcie_cap_init() error %d !\n", rc); + goto err_bridge; + } + pcie_cap_arifwd_init(d); + pcie_cap_deverr_init(d); + pcie_cap_slot_init(d, s->slot); + pcie_chassis_create(s->chassis); + rc = pcie_chassis_add_slot(s); + if (rc < 0) { + printf("phb3-rc: pcie_chassis_add_slot() error %d !\n", rc); + goto err_pcie_cap; + } + pcie_cap_root_init(d); + rc = pcie_aer_init(d, 0x100, PCI_ERR_SIZEOF); + if (rc < 0) { + printf("phb3-rc: pcie_aer_init() error %d !\n", rc); + goto err_slot; + } + pcie_aer_root_init(d); + return 0; + +err_slot: + pcie_chassis_del_slot(s); +err_pcie_cap: + pcie_cap_exit(d); +err_bridge: + pci_bridge_exitfn(d); + return rc; +} + +static void pnv_phb3_rc_exitfn(PCIDevice *d) +{ + PCIESlot *s = PCIE_SLOT(d); + + pcie_aer_exit(d); + pcie_chassis_del_slot(s); + pcie_cap_exit(d); + pci_bridge_exitfn(d); +} + +static void pnv_phb3_rc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->is_express = 1; + k->is_bridge = 1; + k->init = pnv_phb3_rc_initfn; + k->exit = pnv_phb3_rc_exitfn; + k->config_write = pnv_phb3_rc_write_config; + k->vendor_id = PCI_VENDOR_ID_IBM; + k->device_id = 0x03dc; + k->revision = 0; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); + dc->desc = "IBM PHB3 PCIE Root Port"; + dc->reset = pnv_phb3_rc_reset; +} + +static const TypeInfo pnv_phb3_rc_info = { + .name = TYPE_PNV_PHB3_RC, + .parent = TYPE_PCIE_SLOT, + .class_init = pnv_phb3_rc_class_init, +}; + +static void pnv_phb3_rc_register_types(void) +{ + type_register_static(&pnv_phb3_rc_info); +} + +type_init(pnv_phb3_rc_register_types) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index bb605efae0236..8e7033798d534 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -110,6 +110,27 @@ static uint16_t pcibus_numa_node(PCIBus *bus) return NUMA_NODE_UNASSIGNED; } +static bool pci_can_add_device(BusState *bus, QemuOpts *opts) +{ + unsigned int devfn, max; + PCIBus *pbus = PCI_BUS(bus); + + /* If address is specified, say yes and let it fail if that doesn't work */ + if (qemu_opt_get(opts, "addr") != NULL) { + return true; + } + max = ARRAY_SIZE(pbus->devices); + if (pbus->devfn_max && pbus->devfn_max < max) { + max = pbus->devfn_max; + } + for (devfn = pbus->devfn_min ; devfn < max; devfn += PCI_FUNC_MAX) { + if (!pbus->devices[devfn]) { + break; + } + } + return devfn < max; +} + static void pci_bus_class_init(ObjectClass *klass, void *data) { BusClass *k = BUS_CLASS(klass); @@ -121,6 +142,7 @@ static void pci_bus_class_init(ObjectClass *klass, void *data) k->realize = pci_bus_realize; k->unrealize = pci_bus_unrealize; k->reset = pcibus_reset; + k->can_add_device = pci_can_add_device; pbc->is_root = pcibus_is_root; pbc->bus_num = pcibus_num; @@ -1282,7 +1304,9 @@ qemu_irq pci_allocate_irq(PCIDevice *pci_dev) void pci_set_irq(PCIDevice *pci_dev, int level) { int intx = pci_intx(pci_dev); - pci_irq_handler(pci_dev, intx, level); + if (intx >= 0) { + pci_irq_handler(pci_dev, intx, level); + } } /* Special hooks used by device assignment */ diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index c1ffc7771b5d1..a795b1cb03138 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -4,6 +4,8 @@ obj-y += ppc.o ppc_booke.o obj-$(CONFIG_PSERIES) += spapr.o spapr_vio.o spapr_events.o obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o +# IBM PowerNV +obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_lpc.o pnv_psi.o pnv_occ.o ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy) obj-y += spapr_pci_vfio.o endif diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c new file mode 100644 index 0000000000000..7d7c4ed4eed0e --- /dev/null +++ b/hw/ppc/pnv.c @@ -0,0 +1,1149 @@ +/* + * QEMU PowerPC PowerNV model + * + * Copyright (c) 2004-2007 Fabrice Bellard + * Copyright (c) 2007 Jocelyn Mayer + * Copyright (c) 2010 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 "sysemu/sysemu.h" +#include "hw/hw.h" +#include "hw/fw-path-provider.h" +#include "elf.h" +#include "net/net.h" +#include "sysemu/block-backend.h" +#include "sysemu/cpus.h" +#include "sysemu/kvm.h" +#include "sysemu/numa.h" +#include "sysemu/device_tree.h" +#include "kvm_ppc.h" +#include "mmu-hash64.h" +#include "qom/cpu.h" + +#include "hw/boards.h" +#include "hw/ppc/ppc.h" +#include "hw/ppc/pnv.h" +#include "hw/loader.h" +#include "hw/ppc/xics.h" +#include "hw/ppc/pnv_xscom.h" + +#include "hw/pci/pci.h" +#include "hw/pci/pci_bus.h" +#include "hw/pci/pci_bridge.h" +#include "hw/pci/msi.h" +#include "hw/isa/isa.h" +#include "hw/char/serial.h" +#include "hw/timer/mc146818rtc.h" +#include "hw/pci-host/pnv_phb3.h" +#include "hw/usb.h" +#include "hw/ide/pci.h" +#include "hw/ide/ahci.h" + +#include "exec/address-spaces.h" +#include "qemu/config-file.h" +#include "qapi/error.h" +#include "trace.h" +#include "hw/nmi.h" + +#include "hw/compat.h" +#include "hw/ipmi/ipmi.h" + +#include + +#define FDT_ADDR 0x01000000 +#define FDT_MAX_SIZE 0x00100000 +#define FW_MAX_SIZE 0x00400000 +#define FW_FILE_NAME "skiboot.lid" +#define KERNEL_FILE_NAME "skiroot.lid" +#define KERNEL_LOAD_ADDR 0x20000000 + +#define TIMEBASE_FREQ 512000000ULL + +#define MAX_CPUS 255 + +#define PHANDLE_XICP 0x00001111 + +typedef struct sPowerNVMachineState sPowerNVMachineState; + +#define TYPE_POWERNV_MACHINE "powernv-machine" +#define POWERNV_MACHINE(obj) \ + OBJECT_CHECK(sPowerNVMachineState, (obj), TYPE_POWERNV_MACHINE) + +/** + * sPowerNVMachineState: + */ + +#define MAX_SATA_PORTS 6 + +struct sPowerNVMachineState { + /*< private >*/ + MachineState parent_obj; + PnvSystem sys; + hwaddr fdt_addr; + void *fdt_skel; + Notifier powerdown_notifier; +}; + +static XICSState *try_create_xics(const char *type, int nr_servers, + int nr_irqs, Error **errp) +{ + Error *err = NULL; + DeviceState *dev; + + dev = qdev_create(NULL, type); + qdev_prop_set_uint32(dev, "nr_servers", nr_servers); + object_property_set_bool(OBJECT(dev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + object_unparent(OBJECT(dev)); + return NULL; + } + + return XICS_COMMON(dev); +} + +static XICSState *xics_system_init(int nr_servers, int nr_irqs) +{ + XICSState *xics = NULL; + +#if 0 /* Some fixing needed to handle native ICS in KVM mode */ + 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, + &error_abort); + } + + if (irqchip_required && !icp) { + perror("Failed to create in-kernel XICS\n"); + abort(); + } + } +#endif + + if (!xics) { + xics = try_create_xics(TYPE_XICS_NATIVE, nr_servers, nr_irqs, + &error_abort); + } + + if (!xics) { + perror("Failed to create XICS\n"); + abort(); + } + return xics; +} + +static size_t create_page_sizes_prop(CPUPPCState *env, uint32_t *prop, + size_t maxsize) +{ + size_t maxcells = maxsize / sizeof(uint32_t); + int i, j, count; + uint32_t *p = prop; + + for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { + struct ppc_one_seg_page_size *sps = &env->sps.sps[i]; + + if (!sps->page_shift) { + break; + } + for (count = 0; count < PPC_PAGE_SIZES_MAX_SZ; count++) { + if (sps->enc[count].page_shift == 0) { + break; + } + } + if ((p - prop) >= (maxcells - 3 - count * 2)) { + break; + } + *(p++) = cpu_to_be32(sps->page_shift); + *(p++) = cpu_to_be32(sps->slb_enc); + *(p++) = cpu_to_be32(count); + for (j = 0; j < count; j++) { + *(p++) = cpu_to_be32(sps->enc[j].page_shift); + *(p++) = cpu_to_be32(sps->enc[j].pte_enc); + } + } + + return (p - prop) * sizeof(uint32_t); +} + +#define _FDT(exp) \ + do { \ + int ret = (exp); \ + if (ret < 0) { \ + fprintf(stderr, "qemu: error creating device tree: %s: %s\n", \ + #exp, fdt_strerror(ret)); \ + exit(1); \ + } \ + } while (0) + +static void powernv_populate_memory_node(void *fdt, int nodeid, hwaddr start, + hwaddr size) +{ + /* Probablly bogus, need to match with what's going on in CPU nodes */ + uint32_t chip_id[] = { + cpu_to_be32(0x0), cpu_to_be32(nodeid) + }; + char *mem_name; + uint64_t mem_reg_property[2]; + + mem_reg_property[0] = cpu_to_be64(start); + mem_reg_property[1] = cpu_to_be64(size); + + mem_name = g_strdup_printf("memory@"TARGET_FMT_lx, start); + _FDT((fdt_begin_node(fdt, mem_name))); + g_free(mem_name); + _FDT((fdt_property_string(fdt, "device_type", "memory"))); + _FDT((fdt_property(fdt, "reg", mem_reg_property, + sizeof(mem_reg_property)))); + _FDT((fdt_property(fdt, "ibm,chip-id", chip_id, sizeof(chip_id)))); + _FDT((fdt_end_node(fdt))); +} + +static int powernv_populate_memory(void *fdt) +{ + hwaddr mem_start, node_size; + int i, nb_nodes = nb_numa_nodes; + NodeInfo *nodes = numa_info; + NodeInfo ramnode; + + /* No NUMA nodes, assume there is just one node with whole RAM */ + if (!nb_numa_nodes) { + nb_nodes = 1; + ramnode.node_mem = ram_size; + nodes = &ramnode; + } + + for (i = 0, mem_start = 0; i < nb_nodes; ++i) { + if (!nodes[i].node_mem) { + continue; + } + if (mem_start >= ram_size) { + node_size = 0; + } else { + node_size = nodes[i].node_mem; + if (node_size > ram_size - mem_start) { + node_size = ram_size - mem_start; + } + } + for ( ; node_size; ) { + hwaddr sizetmp = pow2floor(node_size); + + /* mem_start != 0 here */ + if (ctzl(mem_start) < ctzl(sizetmp)) { + sizetmp = 1ULL << ctzl(mem_start); + } + + powernv_populate_memory_node(fdt, i, mem_start, sizetmp); + node_size -= sizetmp; + mem_start += sizetmp; + } + } + + return 0; +} + +static void powernv_create_cpu_node(void *fdt, CPUState *cs, int smt_threads) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + DeviceClass *dc = DEVICE_GET_CLASS(cs); + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs); + uint32_t servers_prop[smt_threads]; + uint32_t gservers_prop[smt_threads * 2]; + int i, index = ppc_get_vcpu_dt_id(cpu); + uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40), + 0xffffffff, 0xffffffff}; + uint32_t tbfreq = kvm_enabled() ? kvmppc_get_tbfreq() : TIMEBASE_FREQ; + uint32_t cpufreq = kvm_enabled() ? kvmppc_get_clockfreq() : 1000000000; + uint32_t page_sizes_prop[64]; + size_t page_sizes_prop_size; + char *nodename; + const uint8_t pa_features[] = { 24, 0, + 0xf6, 0x3f, 0xc7, 0xc0, 0x80, 0xf0, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00 }; + + if ((index % smt_threads) != 0) { + return; + } + + nodename = g_strdup_printf("%s@%x", dc->fw_name, index); + + _FDT((fdt_begin_node(fdt, nodename))); + + g_free(nodename); + + _FDT((fdt_property_cell(fdt, "reg", index))); + _FDT((fdt_property_string(fdt, "device_type", "cpu"))); + + _FDT((fdt_property_cell(fdt, "cpu-version", env->spr[SPR_PVR]))); + _FDT((fdt_property_cell(fdt, "d-cache-block-size", + env->dcache_line_size))); + _FDT((fdt_property_cell(fdt, "d-cache-line-size", + env->dcache_line_size))); + _FDT((fdt_property_cell(fdt, "i-cache-block-size", + env->icache_line_size))); + _FDT((fdt_property_cell(fdt, "i-cache-line-size", + env->icache_line_size))); + + if (pcc->l1_dcache_size) { + _FDT((fdt_property_cell(fdt, "d-cache-size", pcc->l1_dcache_size))); + } else { + fprintf(stderr, "Warning: Unknown L1 dcache size for cpu\n"); + } + if (pcc->l1_icache_size) { + _FDT((fdt_property_cell(fdt, "i-cache-size", pcc->l1_icache_size))); + } else { + fprintf(stderr, "Warning: Unknown L1 icache size for cpu\n"); + } + + _FDT((fdt_property_cell(fdt, "timebase-frequency", tbfreq))); + _FDT((fdt_property_cell(fdt, "clock-frequency", cpufreq))); + _FDT((fdt_property_cell(fdt, "ibm,slb-size", env->slb_nr))); + _FDT((fdt_property_string(fdt, "status", "okay"))); + _FDT((fdt_property(fdt, "64-bit", NULL, 0))); + + 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)))); + } + + /* Advertise VMX/VSX (vector extensions) if available + * 0 / no property == no vector extensions + * 1 == VMX / Altivec available + * 2 == VSX available */ + if (env->insns_flags & PPC_ALTIVEC) { + uint32_t vmx = (env->insns_flags2 & PPC2_VSX) ? 2 : 1; + + _FDT((fdt_property_cell(fdt, "ibm,vmx", vmx))); + } + + /* Advertise DFP (Decimal Floating Point) if available + * 0 / no property == no DFP + * 1 == DFP available */ + if (env->insns_flags2 & PPC2_DFP) { + _FDT((fdt_property_cell(fdt, "ibm,dfp", 1))); + } + + page_sizes_prop_size = create_page_sizes_prop(env, page_sizes_prop, + sizeof(page_sizes_prop)); + if (page_sizes_prop_size) { + _FDT((fdt_property(fdt, "ibm,segment-page-sizes", + page_sizes_prop, page_sizes_prop_size))); + } + + _FDT((fdt_property(fdt, "ibm,pa-features", + pa_features, sizeof(pa_features)))); + + /* XXX Just a hack for now */ + _FDT((fdt_property_cell(fdt, "ibm,chip-id", 0))); + + if (cpu->cpu_version) { + _FDT((fdt_property_cell(fdt, "cpu-version", cpu->cpu_version))); + } + + /* Build interrupt servers and gservers properties */ + for (i = 0; i < smt_threads; i++) { + servers_prop[i] = cpu_to_be32(index + i); + /* Hack, direct the group queues back to cpu 0 */ + gservers_prop[i*2] = cpu_to_be32(index + i); + gservers_prop[i*2 + 1] = 0; + } + _FDT((fdt_property(fdt, "ibm,ppc-interrupt-server#s", + servers_prop, sizeof(servers_prop)))); + _FDT((fdt_property(fdt, "ibm,ppc-interrupt-gserver#s", + gservers_prop, sizeof(gservers_prop)))); + + _FDT((fdt_end_node(fdt))); +} + +static void *powernv_create_fdt(PnvSystem *sys, const char *kernel_cmdline, uint32_t initrd_base, uint32_t initrd_size) +{ + void *fdt; + CPUState *cs; + int smt = kvmppc_smt_threads(); + uint32_t start_prop = cpu_to_be32(initrd_base); + uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size); + char *buf; + const char plat_compat[] = "qemu,powernv\0ibm,powernv"; + unsigned int i; + + fdt = g_malloc0(FDT_MAX_SIZE); + _FDT((fdt_create(fdt, FDT_MAX_SIZE))); + _FDT((fdt_finish_reservemap(fdt))); + + /* Root node */ + _FDT((fdt_begin_node(fdt, ""))); + _FDT((fdt_property_string(fdt, "model", "IBM PowerNV (emulated by qemu)"))); + _FDT((fdt_property(fdt, "compatible", plat_compat, sizeof(plat_compat)))); + + /* + * Add info to guest to indentify which host is it being run on + * and what is the uuid of the guest + */ + if (kvmppc_get_host_model(&buf)) { + _FDT((fdt_property_string(fdt, "host-model", buf))); + g_free(buf); + } + if (kvmppc_get_host_serial(&buf)) { + _FDT((fdt_property_string(fdt, "host-serial", buf))); + g_free(buf); + } + + buf = g_strdup_printf(UUID_FMT, qemu_uuid[0], qemu_uuid[1], + qemu_uuid[2], qemu_uuid[3], qemu_uuid[4], + qemu_uuid[5], qemu_uuid[6], qemu_uuid[7], + qemu_uuid[8], qemu_uuid[9], qemu_uuid[10], + qemu_uuid[11], qemu_uuid[12], qemu_uuid[13], + qemu_uuid[14], qemu_uuid[15]); + + _FDT((fdt_property_string(fdt, "vm,uuid", buf))); + g_free(buf); + + _FDT((fdt_begin_node(fdt, "chosen"))); + if (kernel_cmdline) { + _FDT((fdt_property_string(fdt, "bootargs", kernel_cmdline))); + } + _FDT((fdt_property(fdt, "linux,initrd-start", + &start_prop, sizeof(start_prop)))); + _FDT((fdt_property(fdt, "linux,initrd-end", + &end_prop, sizeof(end_prop)))); + _FDT((fdt_end_node(fdt))); + + _FDT((fdt_property_cell(fdt, "#address-cells", 0x2))); + _FDT((fdt_property_cell(fdt, "#size-cells", 0x2))); + + /* cpus */ + _FDT((fdt_begin_node(fdt, "cpus"))); + _FDT((fdt_property_cell(fdt, "#address-cells", 0x1))); + _FDT((fdt_property_cell(fdt, "#size-cells", 0x0))); + + CPU_FOREACH(cs) { + powernv_create_cpu_node(fdt, cs, smt); + } + + _FDT((fdt_end_node(fdt))); + + /* ICPs */ + CPU_FOREACH(cs) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + uint32_t base_server = ppc_get_vcpu_dt_id(cpu); + xics_create_native_icp_node(sys->xics, fdt, base_server, smt); + } + + /* Memory */ + _FDT((powernv_populate_memory(fdt))); + + /* For each chip */ + for (i = 0; i < sys->num_chips; i++) { + /* Populate XSCOM */ + _FDT((xscom_populate_fdt(sys->chips[i].xscom, fdt))); + } + + /* /hypervisor node */ + if (kvm_enabled()) { + uint8_t hypercall[16]; + + /* indicate KVM hypercall interface */ + _FDT((fdt_begin_node(fdt, "hypervisor"))); + _FDT((fdt_property_string(fdt, "compatible", "linux,kvm"))); + if (kvmppc_has_cap_fixup_hcalls()) { + /* + * Older KVM versions with older guest kernels were broken with the + * magic page, don't allow the guest to map it. + */ + kvmppc_get_hypercall(first_cpu->env_ptr, hypercall, + sizeof(hypercall)); + _FDT((fdt_property(fdt, "hcall-instructions", hypercall, + sizeof(hypercall)))); + } + _FDT((fdt_end_node(fdt))); + } + + _FDT((fdt_end_node(fdt))); /* close root node */ + _FDT((fdt_finish(fdt))); + + return fdt; +} + +static void powernv_cpu_reset(void *opaque) +{ + PowerPCCPU *cpu = opaque; + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + + cpu_reset(cs); + + env->spr[SPR_PIR] = ppc_get_vcpu_dt_id(cpu); + env->spr[SPR_HIOR] = 0; + env->gpr[3] = FDT_ADDR; + env->nip = 0x10; + env->msr |= MSR_HVB; +} + +static const VMStateDescription vmstate_powernv = { + .name = "powernv", + .version_id = 1, + .minimum_version_id = 1, +}; + +/* Returns whether we want to use VGA or not */ +static int pnv_vga_init(PCIBus *pci_bus) +{ + switch (vga_interface_type) { + case VGA_NONE: + return false; + case VGA_DEVICE: + return true; + case VGA_STD: + case VGA_VIRTIO: + return pci_vga_init(pci_bus) != NULL; + default: + fprintf(stderr, "This vga model is not supported," + "currently it only supports -vga std\n"); + exit(0); + } +} + +static void pnv_nic_init(PCIBus *pci_bus) +{ + int i; + + for (i = 0; i < nb_nics; i++) { + NICInfo *nd = &nd_table[i]; + DeviceState *dev; + PCIDevice *pdev; + Error *err = NULL; + + pdev = pci_create(pci_bus, -1, "e1000"); + dev = &pdev->qdev; + qdev_set_nic_properties(dev, nd); + object_property_set_bool(OBJECT(dev), true, "realized", &err); + if (err) { + error_report_err(err); + object_unparent(OBJECT(dev)); + exit(1); + } + } +} + +static void pnv_storage_init(PCIBus *pci_bus) +{ + DriveInfo *hd[MAX_SATA_PORTS]; + PCIDevice *ahci; + + /* Add an AHCI device. We use an ICH9 since that's all we have + * at hand for PCI AHCI but it shouldn't really matter + */ + ahci = pci_create_simple(pci_bus, -1, "ich9-ahci"); + g_assert(MAX_SATA_PORTS == ICH_AHCI(ahci)->ahci.ports); + ide_drive_get(hd, ICH_AHCI(ahci)->ahci.ports); + ahci_ide_create_devs(ahci, hd); +} + +static PCIBus *pnv_create_pci_legacy_bridge(PCIBus *parent, uint8_t chassis_nr) +{ + PCIDevice *dev; + + dev = pci_create_multifunction(parent, 0, false, "pci-bridge"); + qdev_prop_set_uint8(&dev->qdev, "chassis_nr", chassis_nr); + dev->qdev.id = "pci"; + qdev_init_nofail(&dev->qdev); + return pci_bridge_get_sec_bus(PCI_BRIDGE(dev)); +} + +static void pnv_lpc_irq_handler_cpld(void *opaque, int n, int level) +{ +#define MAX_ISA_IRQ 16 + static uint32_t irqstate; + uint32_t old_state = irqstate; + PnvPsiController *psi = opaque; + + if (n >= MAX_ISA_IRQ) { + return; + } + if (level) { + irqstate |= 1u << n; + } else { + irqstate &= ~(1u << n); + } + if (irqstate != old_state) { + pnv_psi_irq_set(psi, PSIHB_IRQ_EXTERNAL, irqstate != 0); + } +} + +static void pnv_create_chip(PnvSystem *sys, unsigned int chip_no, + bool has_lpc, bool has_lpc_irq, + unsigned int num_phbs) +{ + PnvChip *chip = &sys->chips[chip_no]; + unsigned int i; + + if (chip_no >= PNV_MAX_CHIPS) { + return; + } + + /* XXX Improve chip numbering to better match HW */ + chip->chip_id = chip_no; + + /* Set up XSCOM bus */ + xscom_create(chip); + + /* Create PSI */ + pnv_psi_create(chip, sys->xics); + + /* Create LPC controller */ + if (has_lpc) { + pnv_lpc_create(chip, has_lpc_irq); + + /* If we don't use the built-in LPC interrupt deserializer, we need + * to provide a set of qirqs for the ISA bus or things will go bad. + * + * Most machines using pre-Naples chips (without said deserializer) + * have a CPLD that will collect the SerIRQ and shoot them as a + * single level interrupt to the P8 chip. So let's setup a hook + * for doing just that. + */ + if (!has_lpc_irq) { + isa_bus_irqs(chip->lpc_bus, + qemu_allocate_irqs(pnv_lpc_irq_handler_cpld, + chip->psi, 16)); + } + } + + /* Create the simplified OCC model */ + pnv_occ_create(chip); + + /* Create a PCI, for now do one chip with 2 PHBs */ + for (i = 0; i < num_phbs; i++) { + pnv_phb3_create(chip, sys->xics, i); + } +} + +static int powernv_populate_rtc(ISADevice *d, void *fdt, int lpc_off) +{ + uint32_t io_base = d->ioport_id; + uint32_t io_regs[] = { + cpu_to_be32(1), + cpu_to_be32(io_base), + cpu_to_be32(2) + }; + char *name; + int node; + int ret; + + name = g_strdup_printf("%s@i%x", qdev_fw_name(DEVICE(d)), io_base); + node = fdt_add_subnode(fdt, lpc_off, name); + g_free(name); + if (node <= 0) { + return node; + } + ret = fdt_setprop(fdt, node, "reg", io_regs, sizeof(io_regs)); + ret |= fdt_setprop_string(fdt, node, "compatible", "pnpPNP,b00"); + return ret; +} + +static int powernv_populate_serial(ISADevice *d, void *fdt, int lpc_off) +{ + const char compatible[] = "ns16550\0pnpPNP,501"; + uint32_t io_base = d->ioport_id; + uint32_t io_regs[] = { + cpu_to_be32(1), + cpu_to_be32(io_base), + cpu_to_be32(8) + }; + char *name; + int node; + int ret; + + name = g_strdup_printf("%s@i%x", qdev_fw_name(DEVICE(d)), io_base); + node = fdt_add_subnode(fdt, lpc_off, name); + g_free(name); + if (node <= 0) { + return node; + } + ret = fdt_setprop(fdt, node, "reg", io_regs, sizeof(io_regs)); + ret |= fdt_setprop(fdt, node, "compatible", compatible, sizeof(compatible)); + + ret |= fdt_setprop_cell(fdt, node, "clock-frequency", 1843200); + ret |= fdt_setprop_cell(fdt, node, "current-speed", 115200); + ret |= fdt_setprop_cell(fdt, node, "interrupts", d->isairq[0]); + ret |= fdt_setprop_cell(fdt, node, "interrupt-parent", + fdt_get_phandle(fdt, lpc_off)); + + /* This is needed by Linux */ + ret |= fdt_setprop_string(fdt, node, "device_type", "serial"); + return ret; +} + +static int powernv_populate_ipmi_sensor(Object *objbmc, void *fdt) +{ + int node; + int ret; + int i; + const struct ipmi_sdr_compact *sdr; + + node = qemu_fdt_add_subnode(fdt, "/bmc"); + if (node <= 0) { + return -1; + } + + ret = fdt_setprop_string(fdt, node, "name", "bmc"); + ret |= fdt_setprop_cell(fdt, node, "#address-cells", 0x1); + ret |= fdt_setprop_cell(fdt, node, "#size-cells", 0x0); + + node = fdt_add_subnode(fdt, node, "sensors"); + if (node <= 0) { + return -1; + } + ret |= fdt_setprop_cell(fdt, node, "#address-cells", 0x1); + ret |= fdt_setprop_cell(fdt, node, "#size-cells", 0x0); + + for (i = 0; !ipmi_bmc_sdr_find(IPMI_BMC(objbmc), i, &sdr, NULL); i++) { + int snode; + char sensor_name[32]; + + sprintf(sensor_name, "sensor@%x", sdr->sensor_owner_number); + snode = fdt_add_subnode(fdt, node, sensor_name); + if (snode <= 0) { + return -1; + } + + ret |= fdt_setprop_cell(fdt, snode, "reg", sdr->sensor_owner_number); + ret |= fdt_setprop_string(fdt, snode, "name", "sensor"); + ret |= fdt_setprop_string(fdt, snode, "compatible", "ibm,ipmi-sensor"); + ret |= fdt_setprop_cell(fdt, snode, "ipmi-sensor-reading-type", + sdr->reading_type); + ret |= fdt_setprop_cell(fdt, snode, "ipmi-entity-id", + sdr->entity_id); + ret |= fdt_setprop_cell(fdt, snode, "ipmi-entity-instance", + sdr->entity_instance); + ret |= fdt_setprop_cell(fdt, snode, "ipmi-sensor-type", + sdr->sensor_type); + } + + return ret; +} + +static int powernv_populate_ipmi_bt(ISADevice *d, void *fdt, int lpc_off) +{ + const char compatible[] = "bt\0ipmi-bt"; + uint32_t io_base = 0x0; + uint32_t io_regs[] = { + cpu_to_be32(1), + cpu_to_be32(io_base), + cpu_to_be32(3) + }; + uint32_t irq; + char *name; + int node; + int ret; + Error *err = NULL; + Object *obj; + + io_base = object_property_get_int(OBJECT(d), "ioport", &err); + if (err) { + return -1; + } + io_regs[1] = cpu_to_be32(io_base); + + irq = object_property_get_int(OBJECT(d), "irq", &err); + if (err) { + return -1; + } + + name = g_strdup_printf("%s@i%x", qdev_fw_name(DEVICE(d)), io_base); + node = fdt_add_subnode(fdt, lpc_off, name); + g_free(name); + if (node <= 0) { + return node; + } + ret = fdt_setprop(fdt, node, "reg", io_regs, sizeof(io_regs)); + ret |= fdt_setprop(fdt, node, "compatible", compatible, sizeof(compatible)); + + /* Mark it as reserved to avoid Linux trying to claim it */ + ret |= fdt_setprop_string(fdt, node, "status", "reserved"); + ret |= fdt_setprop_cell(fdt, node, "interrupts", irq); + ret |= fdt_setprop_cell(fdt, node, "interrupt-parent", + fdt_get_phandle(fdt, lpc_off)); + + /* a ipmi bt device necessarily comes with a bmc : + * -device ipmi-bmc-sim,id=bmc0 + */ + obj = object_resolve_path_type("", "ipmi-bmc-sim", NULL); + if (obj) { + ret = powernv_populate_ipmi_sensor(obj, fdt); + } else { + fprintf(stderr, "bmc simulator is not running !?"); + } + + return ret; +} +#if 0 +static DeviceState *ipmi_bt_create(BusState *bus, const char *bmcname, + Error **errp) +{ + Error *err = NULL; + DeviceState *dev; + + dev = qdev_create(bus, "isa-ipmi-bt"); + qdev_prop_set_int32(dev, "irq", 10); + qdev_prop_set_string(dev, "bmc", bmcname); + object_property_set_bool(OBJECT(dev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + object_unparent(OBJECT(dev)); + return NULL; + } + + return dev; +} +#endif +static int walk_isa_device(DeviceState *dev, void *fdt) +{ + ISADevice *d = ISA_DEVICE(dev); + Object *obj = OBJECT(dev); + int lpc_off; + + lpc_off = fdt_node_offset_by_compatible(fdt, -1, "ibm,power8-lpc"); + if (lpc_off < 0) { + return lpc_off; + } + + if (object_dynamic_cast(obj, TYPE_MC146818_RTC)) { + powernv_populate_rtc(d, fdt, lpc_off); + } else if (object_dynamic_cast(obj, TYPE_ISA_SERIAL)) { + powernv_populate_serial(d, fdt, lpc_off); + } else if (object_dynamic_cast(obj, "isa-ipmi-bt")) { + powernv_populate_ipmi_bt(d, fdt, lpc_off); + } else { + fprintf(stderr, "unknown isa device %s@i%x\n", qdev_fw_name(dev), + d->ioport_id); + } + + return 0; +} + +/* + * OEM SEL Event data packet sent by BMC in response of a Read Event + * Message Buffer command + */ +struct oem_sel { + /* SEL header */ + uint8_t id[2]; + uint8_t type; + uint8_t timestamp[4]; + uint8_t manuf_id[3]; + /* OEM SEL data (6 bytes) follows */ + uint8_t netfun; + uint8_t cmd; + uint8_t data[4]; +}; + +#define SOFT_OFF 0x00 +#define SOFT_REBOOT 0x01 + +static void pnv_gen_oem_sel(uint8_t reboot) +{ + Object *obj; + uint8_t evt[16]; + struct oem_sel sel = { + .id = { 0x55 , 0x55 }, + .type = 0xC0, /* OEM */ + .manuf_id = { 0x0, 0x0, 0x0 }, + .timestamp = { 0x0, 0x0, 0x0, 0x0 }, + .netfun = 0x3a, /* IBM */ + .cmd = 0x04, /* AMI OEM SEL Power Notification */ + .data = { reboot, 0xFF, 0xFF, 0xFF }, + }; + + obj = object_resolve_path_type("", "ipmi-bmc-sim", NULL); + if (!obj) { + fprintf(stderr, "bmc simulator is not running\n"); + return; + } + + memcpy(evt, &sel, 16); + ipmi_bmc_gen_event(IPMI_BMC(obj), evt, 0 /* do not log the event */); +} + +static void pnv_powerdown_notify(Notifier *n, void *opaque) +{ + pnv_gen_oem_sel(SOFT_OFF); +} + + +static void ppc_powernv_reset(void) +{ + sPowerNVMachineState *pnv = POWERNV_MACHINE(qdev_get_machine()); + void *fdt; + Object *obj; + + qemu_devices_reset(); + + fdt = g_malloc(FDT_MAX_SIZE); + + _FDT((fdt_open_into(pnv->fdt_skel, fdt, FDT_MAX_SIZE))); + + obj = object_resolve_path_type("", TYPE_ISA_BUS, NULL); + if (!obj) { + fprintf(stderr, "no isa bus ?!\n"); + return; + } + + qbus_walk_children(BUS(obj), walk_isa_device, NULL, NULL, NULL, fdt); + + cpu_physical_memory_write(pnv->fdt_addr, fdt, fdt_totalsize(fdt)); + + g_free(fdt); +} + +static void ppc_powernv_init(MachineState *machine) +{ + ram_addr_t ram_size = machine->ram_size; + const char *cpu_model = machine->cpu_model; + const char *kernel_filename = machine->kernel_filename; + const char *initrd_filename = machine->initrd_filename; + uint32_t initrd_base = 0; + long initrd_size = 0; + PowerPCCPU *cpu; + CPUPPCState *env; + MemoryRegion *sysmem = get_system_memory(); + MemoryRegion *ram = g_new(MemoryRegion, 1); + sPowerNVMachineState *pnv_machine = POWERNV_MACHINE(machine); + PnvSystem *sys = &pnv_machine->sys; + XICSState *xics; + PCIBus *pbus; + ISABus *isa_bus; + bool has_gfx = false; + long fw_size; + char *filename; + void *fdt; + int i; + + /* MSIs are supported on this platform */ + msi_nonbroken = true; + + /* Set up Interrupt Controller before we create the VCPUs */ + xics = xics_system_init(smp_cpus * kvmppc_smt_threads() / smp_threads, + XICS_IRQS_POWERNV); + sys->xics = xics; + + /* init CPUs */ + if (cpu_model == NULL) { + cpu_model = kvm_enabled() ? "host" : "POWER8"; + } + + for (i = 0; i < smp_cpus; i++) { + cpu = cpu_ppc_init(cpu_model); + if (cpu == NULL) { + fprintf(stderr, "Unable to find PowerPC CPU definition\n"); + exit(1); + } + env = &cpu->env; + + /* Set time-base frequency to 512 MHz */ + cpu_ppc_tb_init(env, TIMEBASE_FREQ); + + /* MSR[IP] doesn't exist nowadays */ + env->msr_mask &= ~(1 << 6); + + xics_cpu_setup(xics, cpu); + + qemu_register_reset(powernv_cpu_reset, cpu); + } + + /* allocate RAM */ + memory_region_allocate_system_memory(ram, NULL, "ppc_powernv.ram", ram_size); + memory_region_add_subregion(sysmem, 0, ram); + + /* XXX We should decide how many chips to create based on #cores and + * Venice vs. Murano vs. Naples chip type etc..., for now, just create + * one chip. Also creation of the CPUs should be done per-chip + */ + sys->num_chips = 1; + + /* Create only one chip for now with an LPC bus and one PHB + */ + pnv_create_chip(sys, 0, true, false, 1); + + /* Grab chip 0's ISA bus */ + isa_bus = sys->chips[0].lpc_bus; + + /* Create serial port */ + serial_hds_isa_init(isa_bus, MAX_SERIAL_PORTS); + + /* Create an RTC ISA device too */ + rtc_init(isa_bus, 2000, NULL); + + /* Add a PCI switch */ + pbus = pnv_create_pci_legacy_bridge(sys->chips[0].phb[0], 128); + + /* Graphics */ + if (pnv_vga_init(pbus)) { + has_gfx = true; + machine->usb |= defaults_enabled() && !machine->usb_disabled; + } + if (machine->usb) { + pci_create_simple(pbus, -1, "nec-usb-xhci"); + if (has_gfx) { + USBBus *usb_bus = usb_bus_find(-1); + + usb_create_simple(usb_bus, "usb-kbd"); + usb_create_simple(usb_bus, "usb-mouse"); + } + } + + /* Add NIC */ + pnv_nic_init(pbus); + + /* Add storage */ + pnv_storage_init(pbus); + + if (bios_name == NULL) { + bios_name = FW_FILE_NAME; + } + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + fw_size = load_image_targphys(filename, 0, FW_MAX_SIZE); + if (fw_size < 0) { + hw_error("qemu: could not load OPAL '%s'\n", filename); + exit(1); + } + g_free(filename); + + + if (kernel_filename == NULL) { + kernel_filename = KERNEL_FILE_NAME; + } + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, kernel_filename); + fw_size = load_image_targphys(filename, 0x20000000, 0x2000000); + if (fw_size < 0) { + hw_error("qemu: could not load kernel'%s'\n", filename); + exit(1); + } + g_free(filename); + + /* load initrd */ + if (initrd_filename) { + initrd_base = 0x40000000; + initrd_size = load_image_targphys(initrd_filename, initrd_base, + 0x80000000); // 2GB max + if (initrd_size < 0) { + fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", + initrd_filename); + exit(1); + } + } else { + initrd_base = 0; + initrd_size = 0; + } + fdt = powernv_create_fdt(sys, machine->kernel_cmdline, + initrd_base, initrd_size); + pnv_machine->fdt_skel = fdt; + pnv_machine->fdt_addr = FDT_ADDR; + + pnv_machine->powerdown_notifier.notify = pnv_powerdown_notify; + qemu_register_powerdown_notifier(&pnv_machine->powerdown_notifier); +} + +static int powernv_kvm_type(const char *vm_type) +{ + /* Always force PR KVM */ + return 2; +} + +static void ppc_cpu_do_nmi_on_cpu(void *arg) +{ + CPUState *cs = arg; + + cpu_synchronize_state(cs); + ppc_cpu_do_system_reset(cs); +} + +static void powernv_nmi(NMIState *n, int cpu_index, Error **errp) +{ + CPUState *cs; + + CPU_FOREACH(cs) { + async_run_on_cpu(cs, ppc_cpu_do_nmi_on_cpu, cs); + } +} + +static void powernv_machine_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + NMIClass *nc = NMI_CLASS(oc); + + mc->init = ppc_powernv_init; + mc->reset = ppc_powernv_reset; + mc->block_default_type = IF_IDE; + mc->max_cpus = MAX_CPUS; + mc->no_parallel = 1; + mc->default_boot_order = NULL; + mc->kvm_type = powernv_kvm_type; + + nc->nmi_monitor_handler = powernv_nmi; +} + +static const TypeInfo powernv_machine_info = { + .name = TYPE_POWERNV_MACHINE, + .parent = TYPE_MACHINE, + .abstract = true, + .instance_size = sizeof(sPowerNVMachineState), + .class_init = powernv_machine_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_NMI }, + { } + }, +}; + +static void powernv_machine_2_5_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->name = "powernv-2.5"; + mc->desc = "PowerNV v2.5"; + mc->alias = "powernv"; +} + +static const TypeInfo powernv_machine_2_5_info = { + .name = MACHINE_TYPE_NAME("powernv-2.5"), + .parent = TYPE_POWERNV_MACHINE, + .class_init = powernv_machine_2_5_class_init, +}; + +static void powernv_machine_register_types(void) +{ + type_register_static(&powernv_machine_info); + type_register_static(&powernv_machine_2_5_info); +} + +type_init(powernv_machine_register_types) diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c new file mode 100644 index 0000000000000..ea2241e97a30d --- /dev/null +++ b/hw/ppc/pnv_lpc.c @@ -0,0 +1,527 @@ + +/* + * QEMU PowerNV LPC bus definitions + * + * Copyright (c) 2010 David Gibson, IBM Corporation + * Based on the s390 virtio bus code: + * Copyright (c) 2009 Alexander Graf + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "sysemu/sysemu.h" +#include "hw/boards.h" +#include "monitor/monitor.h" +#include "hw/loader.h" +#include "elf.h" +#include "hw/sysbus.h" +#include "sysemu/kvm.h" +#include "sysemu/device_tree.h" +#include "kvm_ppc.h" + +#include "hw/isa/isa.h" +#include "hw/ppc/pnv_xscom.h" +#include "hw/ppc/pnv.h" + +#include + +enum { + ECCB_CTL = 0, + ECCB_RESET = 1, + ECCB_STAT = 2, + ECCB_DATA = 3, +}; + +#define LPCDBG(fmt...) do { } while(0) +//#define LPCDBG(fmt...) do { printf(fmt); } while(0) +#define OPBDBG(fmt...) do { } while(0) +//#define OPBDBG(fmt...) do { printf(fmt); } while(0) + +typedef struct PnvLpcController { + XScomDevice xd; + uint64_t eccb_stat_reg; + uint32_t eccb_data_reg; + bool has_serirq; + + /* OPB bus */ + MemoryRegion opb_mr; + AddressSpace opb_as; + /* ISA IO and Memory space */ + MemoryRegion isa_io; + MemoryRegion isa_mem; + ISABus *isa_bus; + /* Windows from OPB to ISA (aliases) */ + MemoryRegion opb_isa_io; + MemoryRegion opb_isa_mem; + MemoryRegion opb_isa_fw; + /* Registers */ + MemoryRegion lpc_hc_regs; + MemoryRegion opb_master_regs; + + /* OPB Master LS registers */ +#define OPB_MASTER_LS_IRQ_STAT 0x50 +#define OPB_MASTER_IRQ_LPC 0x00000800 + uint32_t opb_irq_stat; +#define OPB_MASTER_LS_IRQ_MASK 0x54 + uint32_t opb_irq_mask; +#define OPB_MASTER_LS_IRQ_POL 0x58 + uint32_t opb_irq_pol; + + /* LPC HC registers */ +#define LPC_HC_FW_SEG_IDSEL 0x24 + uint32_t lpc_hc_fw_seg_idsel; +#define LPC_HC_FW_RD_ACC_SIZE 0x28 +#define LPC_HC_FW_RD_1B 0x00000000 +#define LPC_HC_FW_RD_2B 0x01000000 +#define LPC_HC_FW_RD_4B 0x02000000 +#define LPC_HC_FW_RD_16B 0x04000000 +#define LPC_HC_FW_RD_128B 0x07000000 + uint32_t lpc_hc_fw_rd_acc_size; +#define LPC_HC_IRQSER_CTRL 0x30 +#define LPC_HC_IRQSER_EN 0x80000000 +#define LPC_HC_IRQSER_QMODE 0x40000000 +#define LPC_HC_IRQSER_START_MASK 0x03000000 +#define LPC_HC_IRQSER_START_4CLK 0x00000000 +#define LPC_HC_IRQSER_START_6CLK 0x01000000 +#define LPC_HC_IRQSER_START_8CLK 0x02000000 + uint32_t lpc_hc_irqser_ctrl; +#define LPC_HC_IRQMASK 0x34 /* same bit defs as LPC_HC_IRQSTAT */ + uint32_t lpc_hc_irqmask; +#define LPC_HC_IRQSTAT 0x38 +#define LPC_HC_IRQ_SERIRQ0 0x80000000 /* all bits down to ... */ +#define LPC_HC_IRQ_SERIRQ16 0x00008000 /* IRQ16=IOCHK#, IRQ2=SMI# */ +#define LPC_HC_IRQ_SERIRQ_ALL 0xffff8000 +#define LPC_HC_IRQ_LRESET 0x00000400 +#define LPC_HC_IRQ_SYNC_ABNORM_ERR 0x00000080 +#define LPC_HC_IRQ_SYNC_NORESP_ERR 0x00000040 +#define LPC_HC_IRQ_SYNC_NORM_ERR 0x00000020 +#define LPC_HC_IRQ_SYNC_TIMEOUT_ERR 0x00000010 +#define LPC_HC_IRQ_SYNC_TARG_TAR_ERR 0x00000008 +#define LPC_HC_IRQ_SYNC_BM_TAR_ERR 0x00000004 +#define LPC_HC_IRQ_SYNC_BM0_REQ 0x00000002 +#define LPC_HC_IRQ_SYNC_BM1_REQ 0x00000001 + uint32_t lpc_hc_irqstat; +#define LPC_HC_ERROR_ADDRESS 0x40 + uint32_t lpc_hc_error_addr; + +} PnvLpcController; + +#define ISA_IO_SIZE 0x00010000 +#define ISA_MEM_SIZE 0x10000000 +#define LPC_IO_OPB_ADDR 0xd0010000 +#define LPC_IO_OPB_SIZE 0x00010000 +#define LPC_MEM_OPB_ADDR 0xe0010000 +#define LPC_MEM_OPB_SIZE 0x10000000 +#define LPC_FW_OPB_ADDR 0xf0000000 +#define LPC_FW_OPB_SIZE 0x10000000 + +#define LPC_OPB_REGS_OPB_ADDR 0xc0010000 +#define LPC_OPB_REGS_OPB_SIZE 0x00002000 +#define LPC_HC_REGS_OPB_ADDR 0xc0012000 +#define LPC_HC_REGS_OPB_SIZE 0x00001000 + +#define TYPE_PNV_LPC_CONTROLLER "pnv-lpc" +#define PNV_LPC_CONTROLLER(obj) \ + OBJECT_CHECK(PnvLpcController, (obj), TYPE_PNV_LPC_CONTROLLER) + +#define _FDT(exp) \ + do { \ + int ret = (exp); \ + if (ret < 0) { \ + fprintf(stderr, "qemu: error creating device tree: %s: %s\n", \ + #exp, fdt_strerror(ret)); \ + exit(1); \ + } \ + } while (0) + +static int pnv_lpc_devnode(XScomDevice *dev, void *fdt) +{ + _FDT((fdt_property_cell(fdt, "#address-cells", 2))); + _FDT((fdt_property_cell(fdt, "#size-cells", 1))); + _FDT((fdt_property(fdt, "primary", NULL, 0))); + return 0; +} + +static bool opb_read(PnvLpcController *lpc, uint32_t addr, uint8_t *data, int sz) +{ + bool success; + + /* XXX Handle access size limits and FW read caching here */ + success = !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED, + data, sz, false); + + LPCDBG("OPB read @0x%08x, sz=%d data=%02x %02x %02x %02x ok=%d\n", + addr, sz, data[0], data[1], data[2], data[3], success); + + return success; +} + +static bool opb_write(PnvLpcController *lpc, uint32_t addr, uint8_t *data, int sz) +{ + bool success; + + /* XXX Handle access size limits here */ + success = !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED, + data, sz, true); + + LPCDBG("OPB write @0x%08x, sz=%d data=%02x %02x %02x %02x ok=%d\n", + addr, sz, data[0], data[1], data[2], data[3], success); + + return success; +} + +#define ECCB_CTL_READ (1ull << (63-15)) +#define ECCB_CTL_SZ_LSH (63-7) +#define ECCB_CTL_SZ_MASK (0xfull << ECCB_CTL_SZ_LSH) +#define ECCB_CTL_ADDR_MASK 0xffffffffu; + +#define ECCB_STAT_OP_DONE (1ull << (63-52)) +#define ECCB_STAT_OP_ERR (1ull << (63-52)) +#define ECCB_STAT_RD_DATA_LSH (63-37) +#define ECCB_STAT_RD_DATA_MASK (0xffffffff << ECCB_STAT_RD_DATA_LSH) + +static void pnv_lpc_do_eccb(PnvLpcController *lpc, uint64_t cmd) +{ + /* XXX Check for magic bits at the top, addr size etc... */ + unsigned int sz = (cmd & ECCB_CTL_SZ_MASK) >> ECCB_CTL_SZ_LSH; + uint32_t opb_addr = cmd & ECCB_CTL_ADDR_MASK; + uint8_t data[4]; + bool success; + + LPCDBG("ECCB cmd: %016llx data: %08x\n", + (unsigned long long)cmd, lpc->eccb_data_reg); + + if (cmd & ECCB_CTL_READ) { + success = opb_read(lpc, opb_addr, data, sz); + if (success) { + lpc->eccb_stat_reg = ECCB_STAT_OP_DONE | + (((uint64_t)data[0]) << 24 | + ((uint64_t)data[1]) << 16 | + ((uint64_t)data[2]) << 8 | + ((uint64_t)data[3])) << ECCB_STAT_RD_DATA_LSH; + } else { + lpc->eccb_stat_reg = ECCB_STAT_OP_DONE | + (0xffffffffull << ECCB_STAT_RD_DATA_LSH); + } + } else { + data[0] = lpc->eccb_data_reg >> 24; + data[1] = lpc->eccb_data_reg >> 16; + data[2] = lpc->eccb_data_reg >> 8; + data[3] = lpc->eccb_data_reg; + + LPCDBG("OPB write @0x%08x, sz=%d data=%02x %02x %02x %02x\n", + opb_addr, sz, data[0], data[1], data[2], data[3]); + + success = opb_write(lpc, opb_addr, data, sz); + lpc->eccb_stat_reg = ECCB_STAT_OP_DONE; + } + /* XXX Which error bit (if any) to signal OPB error ? */ +} + +static bool pnv_lpc_xscom_read(XScomDevice *dev, uint32_t range, + uint32_t offset, uint64_t *out_val) +{ + PnvLpcController *lpc = PNV_LPC_CONTROLLER(dev); + + switch(offset & 3) { + case ECCB_CTL: + case ECCB_RESET: + *out_val = 0; + break; + case ECCB_STAT: + *out_val = lpc->eccb_stat_reg; + lpc->eccb_stat_reg = 0; + break; + case ECCB_DATA: + *out_val = ((uint64_t)lpc->eccb_data_reg) << 32; + break; + } + return true; +} + +static bool pnv_lpc_xscom_write(XScomDevice *dev, uint32_t range, + uint32_t offset, uint64_t val) +{ + PnvLpcController *lpc = PNV_LPC_CONTROLLER(dev); + + switch(offset & 3) { + case ECCB_CTL: + pnv_lpc_do_eccb(lpc, val); + break; + case ECCB_RESET: + /* XXXX */ + break; + case ECCB_STAT: + break; + case ECCB_DATA: + lpc->eccb_data_reg = val >> 32; + break; + } + return true; +} + +static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level) +{ + /* XXX TODO */ +} + +static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size) +{ + PnvLpcController *lpc = opaque; + + if (size != 4) { + fprintf(stderr, "lpc_hc_read: Invalid size %d\n", size); + return 0xfffffffffffffffful; + } + + OPBDBG("LPC HC read @0x%08x\n", (unsigned int)addr); + + switch(addr) { + case LPC_HC_FW_SEG_IDSEL: + return lpc->lpc_hc_fw_seg_idsel; + case LPC_HC_FW_RD_ACC_SIZE: + return lpc->lpc_hc_fw_rd_acc_size; + case LPC_HC_IRQSER_CTRL: + return lpc->lpc_hc_irqser_ctrl; + case LPC_HC_IRQMASK: + return lpc->lpc_hc_irqmask; + case LPC_HC_IRQSTAT: + return lpc->lpc_hc_irqstat; + case LPC_HC_ERROR_ADDRESS: + return lpc->lpc_hc_error_addr; + default: + OPBDBG("LPC HC Unimplemented register !\n"); + return 0xfffffffffffffffful; + } +} + +static void lpc_hc_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + PnvLpcController *lpc = opaque; + + if (size != 4) { + fprintf(stderr, "lpc_hc_write: Invalid size %d\n", size); + return; + } + + OPBDBG("LPC HC write @0x%08x\n", (unsigned int)addr); + + /* XXX Filter out reserved bits */ + + switch(addr) { + case LPC_HC_FW_SEG_IDSEL: + /* XXX Actually figure out how that works as this impact + * memory regions/aliases\ + */ + lpc->lpc_hc_fw_seg_idsel = val; + case LPC_HC_FW_RD_ACC_SIZE: + lpc->lpc_hc_fw_rd_acc_size = val; + case LPC_HC_IRQSER_CTRL: + lpc->lpc_hc_irqser_ctrl = val; + case LPC_HC_IRQMASK: + lpc->lpc_hc_irqmask = val; + case LPC_HC_IRQSTAT: + lpc->lpc_hc_irqstat &= ~val; + case LPC_HC_ERROR_ADDRESS: + break; + default: + OPBDBG("LPC HC Unimplemented register !\n"); + } +} + +static const MemoryRegionOps lpc_hc_ops = { + .read = lpc_hc_read, + .write = lpc_hc_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static uint64_t opb_master_read(void *opaque, hwaddr addr, unsigned size) +{ + PnvLpcController *lpc = opaque; + + if (size != 4) { + fprintf(stderr, "opb_master_read: Invalid size %d\n", size); + return 0xfffffffffffffffful; + } + + OPBDBG("OPB MASTER read @0x%08x\n", (unsigned int)addr); + + switch(addr) { + case OPB_MASTER_LS_IRQ_STAT: + return lpc->opb_irq_stat; + case OPB_MASTER_LS_IRQ_MASK: + return lpc->opb_irq_mask; + case OPB_MASTER_LS_IRQ_POL: + return lpc->opb_irq_pol; + default: + OPBDBG("OPB MASTER Unimplemented register !\n"); + return 0xfffffffffffffffful; + } +} + +static void opb_master_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvLpcController *lpc = opaque; + + if (size != 4) { + fprintf(stderr, "opb_master_write: Invalid size %d\n", size); + return; + } + + OPBDBG("OPB MASTER write @0x%08x\n", (unsigned int)addr); + + switch(addr) { + case OPB_MASTER_LS_IRQ_STAT: + lpc->opb_irq_stat &= ~val; + break; + case OPB_MASTER_LS_IRQ_MASK: + /* XXX Filter out reserved bits */ + lpc->opb_irq_mask = val; + break; + case OPB_MASTER_LS_IRQ_POL: + /* XXX Filter out reserved bits */ + lpc->opb_irq_pol = val; + break; + default: + OPBDBG("OPB MASTER Unimplemented register !\n"); + } +} + +static const MemoryRegionOps opb_master_ops = { + .read = opb_master_read, + .write = opb_master_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void pnv_lpc_realize(DeviceState *dev, Error **errp) +{ + PnvLpcController *lpc = PNV_LPC_CONTROLLER(dev); + + /* LPC XSCOM address is fixed */ + lpc->xd.ranges[0].addr = 0xb0020; + lpc->xd.ranges[0].size = 4; + + /* Reg inits */ + lpc->lpc_hc_fw_rd_acc_size = LPC_HC_FW_RD_4B; + + /* Create address space and backing MR for the OPB bus */ + memory_region_init(&lpc->opb_mr, OBJECT(dev), "lpc-opb", 0x100000000ull); + address_space_init(&lpc->opb_as, &lpc->opb_mr, "lpc-opb"); + + /* Create ISA IO and Mem space regions which are the root of + * the ISA bus (ie, ISA address spaces). We don't create a + * separate one for FW which we alias to memory. + */ + memory_region_init(&lpc->isa_io, OBJECT(dev), "isa-io", ISA_IO_SIZE); + memory_region_init(&lpc->isa_mem, OBJECT(dev), "isa-mem", ISA_MEM_SIZE); + + /* Create windows from the OPB space to the ISA space */ + memory_region_init_alias(&lpc->opb_isa_io, OBJECT(dev), "lpc-isa-io", + &lpc->isa_io, 0, LPC_IO_OPB_SIZE); + memory_region_add_subregion(&lpc->opb_mr, LPC_IO_OPB_ADDR, + &lpc->opb_isa_io); + memory_region_init_alias(&lpc->opb_isa_mem, OBJECT(dev), "lpc-isa-mem", + &lpc->isa_mem, 0, LPC_MEM_OPB_SIZE); + memory_region_add_subregion(&lpc->opb_mr, LPC_MEM_OPB_ADDR, + &lpc->opb_isa_mem); + memory_region_init_alias(&lpc->opb_isa_fw, OBJECT(dev), "lpc-isa-fw", + &lpc->isa_mem, 0, LPC_FW_OPB_SIZE); + memory_region_add_subregion(&lpc->opb_mr, LPC_FW_OPB_ADDR, + &lpc->opb_isa_fw); + + + /* Create MMIO regions for LPC HC and OPB registers */ + memory_region_init_io(&lpc->opb_master_regs, OBJECT(dev), &opb_master_ops, + lpc, "lpc-opb-master", LPC_OPB_REGS_OPB_SIZE); + memory_region_add_subregion(&lpc->opb_mr, LPC_OPB_REGS_OPB_ADDR, + &lpc->opb_master_regs); + memory_region_init_io(&lpc->lpc_hc_regs, OBJECT(dev), &lpc_hc_ops, lpc, + "lpc-hc", LPC_HC_REGS_OPB_SIZE); + memory_region_add_subregion(&lpc->opb_mr, LPC_HC_REGS_OPB_ADDR, + &lpc->lpc_hc_regs); + + /* Instanciate ISA bus */ + lpc->isa_bus = isa_bus_new(dev, &lpc->isa_mem, &lpc->isa_io, errp); + + /* Not all variants have a working serial irq decoder. If not, + * handling of LPC interrupts becomes a platform issue (some + * platforms have a CPLD to do it). + */ + if (lpc->has_serirq) { + isa_bus_irqs(lpc->isa_bus, + qemu_allocate_irqs(pnv_lpc_isa_irq_handler, lpc, 16)); + } +} + +void pnv_lpc_create(PnvChip *chip, bool has_serirq) +{ + struct DeviceState *dev; + PnvLpcController *lpc; + + dev = qdev_create(&chip->xscom->bus, TYPE_PNV_LPC_CONTROLLER); + lpc = PNV_LPC_CONTROLLER(dev); + lpc->has_serirq = has_serirq; + qdev_init_nofail(dev); + chip->lpc = lpc; + chip->lpc_bus = lpc->isa_bus; +} + +static void pnv_lpc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + XScomDeviceClass *k = XSCOM_DEVICE_CLASS(klass); + static const char *compat[] = { "ibm,power8-lpc", NULL }; + + k->devnode = pnv_lpc_devnode; + k->read = pnv_lpc_xscom_read; + k->write = pnv_lpc_xscom_write; + k->dt_name = "isa"; + k->dt_compatible = compat; + + dc->realize = pnv_lpc_realize; +} + +static const TypeInfo pnv_lpc_info = { + .name = TYPE_PNV_LPC_CONTROLLER, + .parent = TYPE_XSCOM_DEVICE, + .instance_size = sizeof(PnvLpcController), + .class_init = pnv_lpc_class_init, +}; + +static void pnv_lpc_register_types(void) +{ + type_register_static(&pnv_lpc_info); +} + +type_init(pnv_lpc_register_types) diff --git a/hw/ppc/pnv_occ.c b/hw/ppc/pnv_occ.c new file mode 100644 index 0000000000000..d3cbb5fc181bc --- /dev/null +++ b/hw/ppc/pnv_occ.c @@ -0,0 +1,126 @@ +/* + * Emulation of a few OCC related registers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Copyright IBM Corp. 2014 + */ +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_xscom.h" + +struct PnvOCCState { + XScomDevice xd; + PnvPsiController *psi; + + /* OCC Misc interrupt */ + uint64_t occmisc; +}; + +#define TYPE_PNV_OCC "pnv-occ" +#define PNV_OCC(obj) OBJECT_CHECK(PnvOCCState, (obj), TYPE_PNV_OCC) + +static void pnv_occ_set_misc(PnvOCCState *occ, uint64_t val) +{ + bool irq_state; + + val &= 0xffff000000000000ull; + + occ->occmisc = val; + irq_state = !!(val >> 63); + pnv_psi_irq_set(occ->psi, PSIHB_IRQ_OCC, irq_state); +} + +static bool pnv_occ_xscom_read(XScomDevice *dev, uint32_t range, + uint32_t offset, uint64_t *out_val) +{ + PnvOCCState *occ = PNV_OCC(dev); + uint32_t pcb_addr = dev->ranges[range].addr + offset; + + switch(pcb_addr) { + case 0x6a020: + *out_val = occ->occmisc; + return true; + } + return false; +} + +static bool pnv_occ_xscom_write(XScomDevice *dev, uint32_t range, + uint32_t offset, uint64_t val) +{ + PnvOCCState *occ = PNV_OCC(dev); + uint32_t pcb_addr = dev->ranges[range].addr + offset; + + switch(pcb_addr) { + default: + case 0x6a020: + pnv_occ_set_misc(occ, val); + return true; + case 0x6a021: + pnv_occ_set_misc(occ, occ->occmisc & val); + return true; + case 0x6a022: + pnv_occ_set_misc(occ, occ->occmisc | val); + return true; + } + return false; +} + +static void pnv_occ_realize(DeviceState *dev, Error **errp) +{ + PnvOCCState *occ = PNV_OCC(dev); + XScomDevice *xd = XSCOM_DEVICE(dev); + + xd->ranges[0].addr = 0x66000; + xd->ranges[0].size = 0x6000; + + occ->occmisc = 0; +} + +static void pnv_occ_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + XScomDeviceClass *k = XSCOM_DEVICE_CLASS(klass); + + k->read = pnv_occ_xscom_read; + k->write = pnv_occ_xscom_write; + + dc->realize = pnv_occ_realize; +} + +static const TypeInfo pnv_occ_type_info = { + .name = TYPE_PNV_OCC, + .parent = TYPE_XSCOM_DEVICE, + .instance_size = sizeof(PnvOCCState), + .class_init = pnv_occ_class_init, +}; + +static void pnv_occ_register_types(void) +{ + type_register_static(&pnv_occ_type_info); +} + +type_init(pnv_occ_register_types) + +void pnv_occ_create(PnvChip *chip) +{ + struct DeviceState *dev; + PnvOCCState *occ; + + dev = qdev_create(&chip->xscom->bus, TYPE_PNV_OCC); + occ = PNV_OCC(dev); + occ->psi = chip->psi; + qdev_init_nofail(dev); + chip->occ = occ; +} diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c new file mode 100644 index 0000000000000..f656ee2a0f2b2 --- /dev/null +++ b/hw/ppc/pnv_psi.c @@ -0,0 +1,595 @@ + +/* + * QEMU PowerNV Limited PSI interface + * + * Copyright 2015 IBM Corporation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "sysemu/sysemu.h" +#include "hw/boards.h" +#include "monitor/monitor.h" +#include "hw/loader.h" +#include "elf.h" +#include "hw/sysbus.h" +#include "sysemu/kvm.h" +#include "sysemu/device_tree.h" +#include "kvm_ppc.h" +#include "exec/address-spaces.h" +#include "qapi/error.h" + +#include "hw/ppc/pnv_xscom.h" +#include "hw/ppc/xics.h" +#include "hw/ppc/pnv.h" + +#include + +//#define PSIDBG(fmt...) printf("PSI "fmt) +#define PSIDBG(fmt...) do { } while(0) + +#define PSIHB_BAR_SIZE 0x100000ull + +#define PSIHB_XSCOM_FIR_RW 0x00 +#define PSIHB_XSCOM_FIR_AND 0x01 +#define PSIHB_XSCOM_FIR_OR 0x02 +#define PSIHB_XSCOM_FIRMASK_RW 0x03 +#define PSIHB_XSCOM_FIRMASK_AND 0x04 +#define PSIHB_XSCOM_FIRMASK_OR 0x05 +#define PSIHB_XSCOM_FIRACT0 0x06 +#define PSIHB_XSCOM_FIRACT1 0x07 +#define PSIHB_XSCOM_BAR 0x0a +#define PSIHB_BAR_EN 0x0000000000000001ull +#define PSIHB_XSCOM_FSPBAR 0x0b +#define PSIHB_XSCOM_CR 0x0e +#define PSIHB_CR_FSP_CMD_ENABLE 0x8000000000000000ull +#define PSIHB_CR_FSP_MMIO_ENABLE 0x4000000000000000ull +#define PSIHB_CR_FSP_IRQ_ENABLE 0x1000000000000000ull +#define PSIHB_CR_FSP_ERR_RSP_ENABLE 0x0800000000000000ull +#define PSIHB_CR_PSI_LINK_ENABLE 0x0400000000000000ull +#define PSIHB_CR_FSP_RESET 0x0200000000000000ull +#define PSIHB_CR_PSIHB_RESET 0x0100000000000000ull +#define PSIHB_CR_PSI_IRQ 0x0000800000000000ull +#define PSIHB_CR_FSP_IRQ 0x0000400000000000ull +#define PSIHB_CR_FSP_LINK_ACTIVE 0x0000200000000000ull + /* and more ... */ +#define PSIHB_XSCOM_SEMR 0x0f +#define PSIHB_XSCOM_XIVR_PSI 0x10 +#define PSIHB_XIVR_SERVER_SH 40 +#define PSIHB_XIVR_SERVER_MSK (0xffffull << PSIHB_XIVR_SERVER_SH) +#define PSIHB_XIVR_PRIO_SH 32 +#define PSIHB_XIVR_PRIO_MSK (0xffull << PSIHB_XIVR_PRIO_SH) +#define PSIHB_XIVR_SRC_SH 29 +#define PSIHB_XIVR_SRC_MSK (0x7ull << PSIHB_XIVR_SRC_SH) +#define PSIHB_XIVR_PENDING 0x01000000ull +#define PSIHB_XSCOM_SCR 0x12 +#define PSIHB_XSCOM_CCR 0x13 +#define PSIHB_XSCOM_DMA_UPADD 0x14 +#define PSIHB_XSCOM_IRQ_STAT 0x15 +#define PSIHB_IRQ_STAT_OCC 0x0000001000000000ull +#define PSIHB_IRQ_STAT_FSI 0x0000000800000000ull +#define PSIHB_IRQ_STAT_LPCI2C 0x0000000400000000ull +#define PSIHB_IRQ_STAT_LOCERR 0x0000000200000000ull +#define PSIHB_IRQ_STAT_EXT 0x0000000100000000ull +#define PSIHB_XSCOM_XIVR_OCC 0x16 +#define PSIHB_XSCOM_XIVR_FSI 0x17 +#define PSIHB_XSCOM_XIVR_LPCI2C 0x18 +#define PSIHB_XSCOM_XIVR_LOCERR 0x19 +#define PSIHB_XSCOM_XIVR_EXT 0x1a +#define PSIHB_XSCOM_IRSN 0x1b +#define PSIHB_IRSN_COMP_SH 45 +#define PSIHB_IRSN_COMP_MSK (0x7ffffull << PSIHB_IRSN_COMP_SH) +#define PSIHB_IRSN_IRQ_MUX 0x0000000800000000ull +#define PSIHB_IRSN_IRQ_RESET 0x0000000400000000ull +#define PSIHB_IRSN_DOWNSTREAM_EN 0x0000000200000000ull +#define PSIHB_IRSN_UPSTREAM_EN 0x0000000100000000ull +#define PSIHB_IRSN_COMPMASK_SH 13 +#define PSIHB_IRSN_COMPMASK_MSK (0x7ffffull << PSIHB_IRSN_COMPMASK_SH) +#define PSIHB_XSCOM_MAX 0x20 + +#define PSIHB_MMIO_BAR 0x00 +#define PSIHB_MMIO_FSPBAR 0x08 +#define PSIHB_MMIO_CR 0x20 +#define PSIHB_MMIO_SEMR 0x28 +#define PSIHB_MMIO_XIVR_PSI 0x30 +#define PSIHB_MMIO_SCR 0x40 +#define PSIHB_MMIO_CCR 0x48 +#define PSIHB_MMIO_DMA_UPADD 0x50 +#define PSIHB_MMIO_IRQ_STAT 0x58 +#define PSIHB_MMIO_XIVR_OCC 0x60 +#define PSIHB_MMIO_XIVR_FSI 0x68 +#define PSIHB_MMIO_XIVR_LPCI2C 0x70 +#define PSIHB_MMIO_XIVR_LOCERR 0x78 +#define PSIHB_MMIO_XIVR_EXT 0x80 +#define PSIHB_MMIO_IRSN 0x88 +#define PSIHB_MMIO_MAX 0x100 + +struct PnvPsiController { + XScomDevice xd; + MemoryRegion regs_mr; + + /* FSP region not supported */ + /* MemoryRegion fsp_mr; */ + + /* Interrupt generation */ + XICSState *xics; + ICSState *ics; + + /* Registers */ + uint64_t regs[PSIHB_XSCOM_MAX]; +}; + +#define TYPE_PNV_PSI_CONTROLLER "pnv-psi" +#define PNV_PSI_CONTROLLER(obj) \ + OBJECT_CHECK(PnvPsiController, (obj), TYPE_PNV_PSI_CONTROLLER) + +static const uint32_t psi_mmio_to_xscom[PSIHB_MMIO_MAX/8] = { + [PSIHB_MMIO_BAR/8] = PSIHB_XSCOM_BAR, + [PSIHB_MMIO_FSPBAR/8] = PSIHB_XSCOM_FSPBAR, + [PSIHB_MMIO_CR/8] = PSIHB_XSCOM_CR, + [PSIHB_MMIO_SCR/8] = PSIHB_XSCOM_SCR, + [PSIHB_MMIO_CCR/8] = PSIHB_XSCOM_CCR, + [PSIHB_MMIO_SEMR/8] = PSIHB_XSCOM_SEMR, + [PSIHB_MMIO_XIVR_PSI/8] = PSIHB_XSCOM_XIVR_PSI, + [PSIHB_MMIO_XIVR_OCC/8] = PSIHB_XSCOM_XIVR_OCC, + [PSIHB_MMIO_XIVR_FSI/8] = PSIHB_XSCOM_XIVR_FSI, + [PSIHB_MMIO_XIVR_LPCI2C/8] = PSIHB_XSCOM_XIVR_LPCI2C, + [PSIHB_MMIO_XIVR_LOCERR/8] = PSIHB_XSCOM_XIVR_LOCERR, + [PSIHB_MMIO_XIVR_EXT/8] = PSIHB_XSCOM_XIVR_EXT, + [PSIHB_MMIO_IRQ_STAT/8] = PSIHB_XSCOM_IRQ_STAT, + [PSIHB_MMIO_DMA_UPADD/8] = PSIHB_XSCOM_DMA_UPADD, + [PSIHB_MMIO_IRSN/8] = PSIHB_XSCOM_IRSN, +}; + +static void pnv_psi_set_bar(PnvPsiController *psi, uint64_t bar) +{ + MemoryRegion *sysmem = get_system_memory(); + uint64_t old = psi->regs[PSIHB_XSCOM_BAR]; + + psi->regs[PSIHB_XSCOM_BAR] = bar & 0x0003fffffff00001; + + /* Update MR, always remove it first */ + if (old & PSIHB_BAR_EN) { + memory_region_del_subregion(sysmem, &psi->regs_mr); + } + /* Then add it back if needed */ + if (bar & PSIHB_BAR_EN) { + uint64_t addr = bar & 0x0003fffffff00000; + memory_region_add_subregion(sysmem, addr, &psi->regs_mr); + } +} + +static void pnv_psi_update_fsp_mr(PnvPsiController *psi) +{ + /* XXX Update FSP MR if/when we support FSP BAR */ +} + +static void pnv_psi_set_cr(PnvPsiController *psi, uint64_t cr) +{ + uint64_t old = psi->regs[PSIHB_XSCOM_CR]; + + psi->regs[PSIHB_XSCOM_CR] = cr & 0x0003ffff00000000; + + /* Check some bit changes */ + if ((old ^ psi->regs[PSIHB_XSCOM_CR]) & PSIHB_CR_FSP_MMIO_ENABLE) { + pnv_psi_update_fsp_mr(psi); + } +} + +static void pnv_psi_set_irsn(PnvPsiController *psi, uint64_t val) +{ + uint32_t offset; + + /* In this model we ignore the up/down enable bits for now + * as SW doesn't use them (other than setting them at boot). + * We ignore IRQ_MUX, its meaning isn't clear and we don't use + * it and finally we ignore reset (XXX fix that ?) + */ + psi->regs[PSIHB_XSCOM_IRSN] = val & (PSIHB_IRSN_COMP_MSK | + PSIHB_IRSN_IRQ_MUX | + PSIHB_IRSN_DOWNSTREAM_EN | + PSIHB_IRSN_DOWNSTREAM_EN | + PSIHB_IRSN_DOWNSTREAM_EN); + + /* We ignore the compare mask as well, our ICS emulation is too + * simplistic to make any use if it, and we extract the offset + * from the compare value + */ + offset = (val & PSIHB_IRSN_COMP_MSK) >> PSIHB_IRSN_COMP_SH; + psi->ics->offset = offset; + PSIDBG("Interrupt offset=0x%x\n", offset); +} + +static bool pnv_psi_irq_bits(PnvPsiController *psi, PnvPsiIrq irq, + uint32_t *out_xivr_reg, + uint32_t *out_stat_reg, + uint64_t *out_stat_bit) +{ + switch(irq) { + case PSIHB_IRQ_PSI: + *out_xivr_reg = PSIHB_XSCOM_XIVR_PSI; + *out_stat_reg = PSIHB_XSCOM_CR; + *out_stat_bit = PSIHB_CR_PSI_IRQ; + break; + case PSIHB_IRQ_FSP: + *out_xivr_reg = PSIHB_XSCOM_XIVR_PSI; + *out_stat_reg = PSIHB_XSCOM_CR; + *out_stat_bit = PSIHB_CR_FSP_IRQ; + break; + case PSIHB_IRQ_OCC: + *out_xivr_reg = PSIHB_XSCOM_XIVR_OCC; + *out_stat_reg = PSIHB_XSCOM_IRQ_STAT; + *out_stat_bit = PSIHB_IRQ_STAT_OCC; + break; + case PSIHB_IRQ_FSI: + *out_xivr_reg = PSIHB_XSCOM_XIVR_FSI; + *out_stat_reg = PSIHB_XSCOM_IRQ_STAT; + *out_stat_bit = PSIHB_IRQ_STAT_FSI; + break; + case PSIHB_IRQ_LPC_I2C: + *out_xivr_reg = PSIHB_XSCOM_XIVR_LPCI2C; + *out_stat_reg = PSIHB_XSCOM_IRQ_STAT; + *out_stat_bit = PSIHB_IRQ_STAT_LPCI2C; + break; + case PSIHB_IRQ_LOCAL_ERR: + *out_xivr_reg = PSIHB_XSCOM_XIVR_LOCERR; + *out_stat_reg = PSIHB_XSCOM_IRQ_STAT; + *out_stat_bit = PSIHB_IRQ_STAT_LOCERR; + break; + case PSIHB_IRQ_EXTERNAL: + *out_xivr_reg = PSIHB_XSCOM_XIVR_EXT; + *out_stat_reg = PSIHB_XSCOM_IRQ_STAT; + *out_stat_bit = PSIHB_IRQ_STAT_EXT; + break; + default: + return false; + } + return true; +} + +void pnv_psi_irq_set(PnvPsiController *psi, PnvPsiIrq irq, bool state) +{ + uint32_t xivr_reg; + uint32_t stat_reg; + uint64_t stat_bit; + uint32_t src; + bool masked; + + if (!pnv_psi_irq_bits(psi, irq, &xivr_reg, &stat_reg, &stat_bit)) { + /* XXX Generate an error ? */ + fprintf(stderr, "PSI: Unsupported irq %d\n", irq); + return; + } + src = (psi->regs[xivr_reg] & PSIHB_XIVR_SRC_MSK) >> PSIHB_XIVR_SRC_SH; + masked = (psi->regs[xivr_reg] & PSIHB_XIVR_PRIO_MSK) == PSIHB_XIVR_PRIO_MSK; + if (state) { + psi->regs[stat_reg] |= stat_bit; + /* XXX optimization: check mask here. That means re-evaluating + * when unmasking, thus TODO + */ + qemu_irq_raise(psi->ics->qirqs[src]); + } else { + psi->regs[stat_reg] &= ~stat_bit; + + /* FSP and PSI are muxed so don't lower if either still set */ + if (stat_reg != PSIHB_XSCOM_CR || + !(psi->regs[stat_reg] & (PSIHB_CR_PSI_IRQ | PSIHB_CR_FSP_IRQ))) { + qemu_irq_lower(psi->ics->qirqs[src]); + } else { + state = true; + } + } + + /* XXX Note about the emulation of the pending bit: This isn't + * entirely correct. The pending bit should be cleared when the + * EOI has been received. However, we don't have callbacks on + * EOI (especially not under KVM) so no way to emulate that + * properly, so instead we just set that bit as the logical + * "output" of the XIVR (ie pending & !masked) + * XXX TODO: Also update it on set_xivr + */ + if (state && !masked) { + psi->regs[xivr_reg] |= PSIHB_XIVR_PENDING; + } else { + psi->regs[xivr_reg] &= ~PSIHB_XIVR_PENDING; + } +} + +static void pnv_psi_set_xivr(PnvPsiController *psi, uint32_t reg, uint64_t val) +{ + uint16_t server; + uint8_t prio; + uint8_t src; + + psi->regs[reg] = (psi->regs[reg] & PSIHB_XIVR_PENDING) | + (val & (PSIHB_XIVR_SERVER_MSK | + PSIHB_XIVR_PRIO_MSK | + PSIHB_XIVR_SRC_MSK)); + val = psi->regs[reg]; + server = (val & PSIHB_XIVR_SERVER_MSK) >> PSIHB_XIVR_SERVER_SH; + prio = (val & PSIHB_XIVR_PRIO_MSK) >> PSIHB_XIVR_PRIO_SH; + src = (val & PSIHB_XIVR_SRC_MSK) >> PSIHB_XIVR_SRC_SH; + if (src > PSIHB_IRQ_EXTERNAL) { + /* XXX Generate error ? */ + return; + } + /* Now because of source remapping, weird things can happen + * if you change the source number dynamically, our simple ICS + * doesn't deal with remapping. So we just poke a different + * ICS entry based on what source number was written. This will + * do for now but a more accurate implementation would instead + * use a fixed server/prio and a remapper of the generated irq. + */ + PSIDBG("IRQ %d server 0x%x prio %x\n", src, server, prio); + ics_simple_write_xive(psi->ics, src, server, prio, prio); +} + +static bool pnv_psi_reg_read(PnvPsiController *psi, uint32_t offset, + uint64_t *out_val, bool mmio) +{ + switch(offset) { + case PSIHB_XSCOM_FIR_RW: + case PSIHB_XSCOM_FIRACT0: + case PSIHB_XSCOM_FIRACT1: + case PSIHB_XSCOM_BAR: + case PSIHB_XSCOM_FSPBAR: + case PSIHB_XSCOM_CR: + case PSIHB_XSCOM_XIVR_PSI: + case PSIHB_XSCOM_XIVR_OCC: + case PSIHB_XSCOM_XIVR_FSI: + case PSIHB_XSCOM_XIVR_LPCI2C: + case PSIHB_XSCOM_XIVR_LOCERR: + case PSIHB_XSCOM_XIVR_EXT: + case PSIHB_XSCOM_IRQ_STAT: + case PSIHB_XSCOM_SEMR: + case PSIHB_XSCOM_DMA_UPADD: + case PSIHB_XSCOM_IRSN: + *out_val = psi->regs[offset]; + return true; + } + return false; +} + +static bool pnv_psi_reg_write(PnvPsiController *psi, uint32_t offset, + uint64_t val, bool mmio) +{ + switch(offset) { + case PSIHB_XSCOM_FIR_RW: + case PSIHB_XSCOM_FIRACT0: + case PSIHB_XSCOM_FIRACT1: + case PSIHB_XSCOM_SEMR: + case PSIHB_XSCOM_DMA_UPADD: + psi->regs[offset] = val; + return true; + case PSIHB_XSCOM_FIR_OR: + psi->regs[PSIHB_XSCOM_FIR_RW] |= val; + return true; + case PSIHB_XSCOM_FIR_AND: + psi->regs[PSIHB_XSCOM_FIR_RW] &= val; + return true; + case PSIHB_XSCOM_BAR: + /* Only XSCOM can write this one */ + if (!mmio) { + pnv_psi_set_bar(psi, val); + } + return true; + case PSIHB_XSCOM_FSPBAR: + psi->regs[PSIHB_XSCOM_BAR] = val & 0x0003ffff00000000; + pnv_psi_update_fsp_mr(psi); + return true; + case PSIHB_XSCOM_CR: + pnv_psi_set_cr(psi, val); + return true; + case PSIHB_XSCOM_SCR: + pnv_psi_set_cr(psi, psi->regs[PSIHB_XSCOM_CR] | val); + return true; + case PSIHB_XSCOM_CCR: + pnv_psi_set_cr(psi, psi->regs[PSIHB_XSCOM_CR] & ~val); + return true; + case PSIHB_XSCOM_XIVR_PSI: + case PSIHB_XSCOM_XIVR_OCC: + case PSIHB_XSCOM_XIVR_FSI: + case PSIHB_XSCOM_XIVR_LPCI2C: + case PSIHB_XSCOM_XIVR_LOCERR: + case PSIHB_XSCOM_XIVR_EXT: + pnv_psi_set_xivr(psi, offset, val); + return true; + case PSIHB_XSCOM_IRQ_STAT: + /* Read only, should we generate an error ? */ + return true; + case PSIHB_XSCOM_IRSN: + pnv_psi_set_irsn(psi, val); + return true; + } + return false; +} + +static uint64_t pnv_psi_mmio_read(void *opaque, hwaddr addr, unsigned size) +{ + PnvPsiController *psi = opaque; + uint32_t xscom_off; + uint64_t val; + + if (size != 8) { + goto fail; + } + + addr &= (PSIHB_BAR_SIZE - 1); + + PSIDBG("MMIO read 0x%x\n", (unsigned int)addr); + + if (addr >= PSIHB_MMIO_MAX) { + goto fail; + } + xscom_off = psi_mmio_to_xscom[addr/8]; + if (xscom_off == 0) { + goto fail; + } + if (pnv_psi_reg_read(psi, xscom_off, &val, true)) { + return val; + } + fail: + return 0xffffffffffffffffull; +} + +static void pnv_psi_mmio_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvPsiController *psi = opaque; + uint32_t xscom_off; + + if (size != 8) { + return; + } + + addr &= (PSIHB_BAR_SIZE - 1); + + PSIDBG("MMIO write 0x%x val 0x%016llx\n", + (unsigned int)addr, (unsigned long long)val); + + if (addr >= PSIHB_MMIO_MAX) { + return; + } + xscom_off = psi_mmio_to_xscom[addr/8]; + if (xscom_off == 0) { + return; + } + pnv_psi_reg_write(psi, xscom_off, val, true); +} + +static const MemoryRegionOps psi_mmio_ops = { + .read = pnv_psi_mmio_read, + .write = pnv_psi_mmio_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 8, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 8, + .max_access_size = 8, + }, +}; + +static bool pnv_psi_xscom_read(XScomDevice *dev, uint32_t range, + uint32_t offset, uint64_t *out_val) +{ + PnvPsiController *psi = PNV_PSI_CONTROLLER(dev); + + PSIDBG("XSCOM read 0x%x\n", offset); + + return pnv_psi_reg_read(psi, offset, out_val, false); +} + +static bool pnv_psi_xscom_write(XScomDevice *dev, uint32_t range, + uint32_t offset, uint64_t val) +{ + PnvPsiController *psi = PNV_PSI_CONTROLLER(dev); + + PSIDBG("XSCOM write 0x%x val 0x%016llx\n", + offset, (unsigned long long)val); + + return pnv_psi_reg_write(psi, offset, val, false); +} + +static void pnv_psi_realize(DeviceState *dev, Error **errp) +{ + PnvPsiController *psi = PNV_PSI_CONTROLLER(dev); + Error *error = NULL; + unsigned int i; + + /* PSI XSCOM address is fixed */ + psi->xd.ranges[0].addr = 0x02010900; + psi->xd.ranges[0].size = 0x20; + + /* Initialize MMIO region */ + memory_region_init_io(&psi->regs_mr, OBJECT(dev), &psi_mmio_ops, psi, + "psihb", PSIHB_BAR_SIZE); + + /* Default BAR. Use object properties ? */ + pnv_psi_set_bar(psi, 0x0003fffe80000001); + + /* Default sources in XIVR */ + psi->regs[PSIHB_XSCOM_XIVR_PSI] = PSIHB_XIVR_PRIO_MSK | + (0ull << PSIHB_XIVR_SRC_SH); + psi->regs[PSIHB_XSCOM_XIVR_OCC] = PSIHB_XIVR_PRIO_MSK | + (1ull << PSIHB_XIVR_SRC_SH); + psi->regs[PSIHB_XSCOM_XIVR_FSI] = PSIHB_XIVR_PRIO_MSK | + (2ull << PSIHB_XIVR_SRC_SH); + psi->regs[PSIHB_XSCOM_XIVR_LPCI2C] = PSIHB_XIVR_PRIO_MSK | + (3ull << PSIHB_XIVR_SRC_SH); + psi->regs[PSIHB_XSCOM_XIVR_LOCERR] = PSIHB_XIVR_PRIO_MSK | + (4ull << PSIHB_XIVR_SRC_SH); + psi->regs[PSIHB_XSCOM_XIVR_EXT] = PSIHB_XIVR_PRIO_MSK | + (5ull << PSIHB_XIVR_SRC_SH); + + /* Create ICS object */ + psi->ics = ICS(object_new(TYPE_ICS_SIMPLE)); + object_property_add_child(OBJECT(psi), "ics", OBJECT(psi->ics), NULL); + psi->ics->offset = 0; +#define PSI_NUM_INTERRUPTS 6 + psi->ics->nr_irqs = PSI_NUM_INTERRUPTS; + xics_add_ics(psi->xics, psi->ics); + object_property_set_bool(OBJECT(psi->ics), true, "realized", &error); + if (error) { + error_propagate(errp, error); + return; + } + for (i = 0; i < PSI_NUM_INTERRUPTS; i++) + ics_simple_set_irq_type(psi->ics, i, true); + +} + +void pnv_psi_create(PnvChip *chip, XICSState *xics) +{ + struct DeviceState *dev; + PnvPsiController *psi; + + dev = qdev_create(&chip->xscom->bus, TYPE_PNV_PSI_CONTROLLER); + psi = PNV_PSI_CONTROLLER(dev); + psi->xics = xics; + qdev_init_nofail(dev); + chip->psi = psi; +} + +static void pnv_psi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + XScomDeviceClass *k = XSCOM_DEVICE_CLASS(klass); + static const char *compat[] = { "ibm,power8-psihb-x", + "ibm,psihb-x", NULL }; + + k->read = pnv_psi_xscom_read; + k->write = pnv_psi_xscom_write; + k->dt_name = "psihb"; + k->dt_compatible = compat; + + dc->realize = pnv_psi_realize; +} + +static const TypeInfo pnv_psi_info = { + .name = TYPE_PNV_PSI_CONTROLLER, + .parent = TYPE_XSCOM_DEVICE, + .instance_size = sizeof(PnvPsiController), + .class_init = pnv_psi_class_init, +}; + +static void pnv_psi_register_types(void) +{ + type_register_static(&pnv_psi_info); +} + +type_init(pnv_psi_register_types) diff --git a/hw/ppc/pnv_xscom.c b/hw/ppc/pnv_xscom.c new file mode 100644 index 0000000000000..49a5d18064452 --- /dev/null +++ b/hw/ppc/pnv_xscom.c @@ -0,0 +1,420 @@ + +/* + * QEMU PowerNV XSCOM bus definitions + * + * Copyright (c) 2010 David Gibson, IBM Corporation + * Based on the s390 virtio bus code: + * Copyright (c) 2009 Alexander Graf + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +/* TODO: Add some infrastructure for "random stuff" and FIRs that + * various units might want to deal with without creating actual + * XSCOM devices. + * + * For example, HB LPC XSCOM in the PIBAM + */ +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "sysemu/sysemu.h" +#include "hw/boards.h" +#include "monitor/monitor.h" +#include "hw/loader.h" +#include "elf.h" +#include "hw/sysbus.h" +#include "sysemu/kvm.h" +#include "sysemu/device_tree.h" +#include "kvm_ppc.h" + +#include "hw/ppc/pnv_xscom.h" + +#include + +#define TYPE_XSCOM "xscom" +#define XSCOM(obj) OBJECT_CHECK(XScomState, (obj), TYPE_XSCOM) + +#define XSCOM_SIZE 0x800000000ull +#define XSCOM_BASE(chip) (0x3fc0000000000ull + ((uint64_t)(chip)) * XSCOM_SIZE) + +//#define TRACE_SCOMS + +typedef struct XScomState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + MemoryRegion mem; + int32_t chip_id; + XScomBus *bus; +} XScomState; + +static uint32_t xscom_to_pcb_addr(uint64_t addr) +{ + addr &= (XSCOM_SIZE - 1); + return ((addr >> 4) & ~0xfull) | ((addr >> 3) & 0xf); +} + +static void xscom_complete(uint64_t hmer_bits) +{ + CPUState *cs = current_cpu; + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + + cpu_synchronize_state(cs); + env->spr[SPR_HMER] |= hmer_bits; + + /* XXX Need a CPU helper to set HMER, also handle gneeration + * of HMIs + */ +} + +static XScomDevice *xscom_find_target(XScomState *s, uint32_t pcb_addr, uint32_t *range) +{ + BusChild *bc; + + QTAILQ_FOREACH(bc, &s->bus->bus.children, sibling) { + DeviceState *qd = bc->child; + XScomDevice *xd = XSCOM_DEVICE(qd); + unsigned int i; + + for (i = 0; i < MAX_XSCOM_RANGES; i++) { + if (xd->ranges[i].addr <= pcb_addr && + (xd->ranges[i].addr + xd->ranges[i].size) > pcb_addr) { + *range = i; + return xd; + } + } + } + return NULL; +} + +static bool xscom_dispatch_read(XScomState *s, uint32_t pcb_addr, uint64_t *out_val) +{ + uint32_t range, offset; + struct XScomDevice *xd = xscom_find_target(s, pcb_addr, &range); + XScomDeviceClass *xc; + + if (!xd) { + return false; + } + xc = XSCOM_DEVICE_GET_CLASS(xd); + if (!xc->read) { + return false; + } + offset = pcb_addr - xd->ranges[range].addr; + return xc->read(xd, range, offset, out_val); +} + +static bool xscom_dispatch_write(XScomState *s, uint32_t pcb_addr, uint64_t val) +{ + uint32_t range, offset; + struct XScomDevice *xd = xscom_find_target(s, pcb_addr, &range); + XScomDeviceClass *xc; + + if (!xd) { + return false; + } + xc = XSCOM_DEVICE_GET_CLASS(xd); + if (!xc->write) { + return false; + } + offset = pcb_addr - xd->ranges[range].addr; + return xc->write(xd, range, offset, val); +} + +static uint64_t xscom_read(void *opaque, hwaddr addr, unsigned width) +{ + XScomState *s = opaque; + uint32_t pcba = xscom_to_pcb_addr(addr); + uint64_t val; + + assert(width == 8); + +#ifdef TRACE_SCOMS + printf("XSCOM_READ(0x%x:0x%x)\n", s->chip_id, pcba); +#endif + + /* Handle some SCOMs here before dispatch */ + switch(pcba) { + case 0xf000f: + val = 0x221EF04980000000; + break; + case 0x1010c00: /* PIBAM FIR */ + case 0x1010c03: /* PIBAM FIR MASK */ + case 0x2020007: /* ADU stuff */ + case 0x2020009: /* ADU stuff */ + case 0x202000f: /* ADU stuff */ + val = 0; + break; + case 0x2013f00: /* PBA stuff */ + case 0x2013f01: /* PBA stuff */ + case 0x2013f02: /* PBA stuff */ + case 0x2013f03: /* PBA stuff */ + case 0x2013f04: /* PBA stuff */ + case 0x2013f05: /* PBA stuff */ + case 0x2013f06: /* PBA stuff */ + case 0x2013f07: /* PBA stuff */ + val = 0; + break; + default: + if (!xscom_dispatch_read(s, pcba, &val)) { + xscom_complete(HMER_XSCOM_FAIL | HMER_XSCOM_DONE); + return 0; + } + } + + xscom_complete(HMER_XSCOM_DONE); + return val; +} + +static void xscom_write(void *opaque, hwaddr addr, uint64_t val, + unsigned width) +{ + XScomState *s = opaque; + uint32_t pcba = xscom_to_pcb_addr(addr); + + assert(width == 8); + +#ifdef TRACE_SCOMS + printf("XSCOM_WRITE(0x%x:0x%x, 0x%016llx)\n", + s->chip_id, pcba, (unsigned long long)val); +#endif + /* Handle some SCOMs here before dispatch */ + switch(pcba) { + /* We ignore writes to these */ + case 0xf000f: /* chip id is RO */ + case 0x1010c00: /* PIBAM FIR */ + case 0x1010c01: /* PIBAM FIR */ + case 0x1010c02: /* PIBAM FIR */ + case 0x1010c03: /* PIBAM FIR MASK */ + case 0x1010c04: /* PIBAM FIR MASK */ + case 0x1010c05: /* PIBAM FIR MASK */ + case 0x2020007: /* ADU stuff */ + case 0x2020009: /* ADU stuff */ + case 0x202000f: /* ADU stuff */ + break; + case 0x2013028: /* CAPP stuff */ + case 0x201302a: /* CAPP stuff */ + case 0x2013801: /* CAPP stuff */ + case 0x2013802: /* CAPP stuff */ + break; + default: + if (!xscom_dispatch_write(s, pcba, val)) { + xscom_complete(HMER_XSCOM_FAIL | HMER_XSCOM_DONE); + return; + } + } + + xscom_complete(HMER_XSCOM_DONE); +} + +static const MemoryRegionOps xscom_ops = { + .read = xscom_read, + .write = xscom_write, + .valid.min_access_size = 8, + .valid.max_access_size = 8, + .impl.min_access_size = 8, + .impl.max_access_size = 8, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static int xscom_init(SysBusDevice *dev) +{ + XScomState *s = XSCOM(dev); + + s->chip_id = -1; + return 0; +} + +static void xscom_realize(DeviceState *dev, Error **errp) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + XScomState *s = XSCOM(dev); + char *name; + + assert(s->chip_id >= 0); + name = g_strdup_printf("xscom-%x", s->chip_id); + memory_region_init_io(&s->mem, OBJECT(s), &xscom_ops, s, name, XSCOM_SIZE); + sysbus_init_mmio(sbd, &s->mem); + sysbus_mmio_map(sbd, 0, XSCOM_BASE(s->chip_id)); +} + +static Property xscom_properties[] = { + DEFINE_PROP_INT32("chip_id", XScomState, chip_id, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void xscom_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->props = xscom_properties; + dc->realize = xscom_realize; + k->init = xscom_init; +} + +static const TypeInfo xscom_info = { + .name = TYPE_XSCOM, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XScomState), + .class_init = xscom_class_init, +}; + +static void xscom_bus_class_init(ObjectClass *klass, void *data) +{ +} + +static const TypeInfo xscom_bus_info = { + .name = TYPE_XSCOM_BUS, + .parent = TYPE_BUS, + .class_init = xscom_bus_class_init, + .instance_size = sizeof(XScomBus), +}; + +void xscom_create(PnvChip *chip) +{ + DeviceState *dev; + XScomState *xdev; + BusState *qbus; + XScomBus *xb; + + dev = qdev_create(NULL, TYPE_XSCOM); + qdev_prop_set_uint32(dev, "chip_id", chip->chip_id); + qdev_init_nofail(dev); + + /* Create bus on bridge device */ + qbus = qbus_create(TYPE_XSCOM_BUS, dev, "xscom"); + xb = DO_UPCAST(XScomBus, bus, qbus); + xb->chip_id = chip->chip_id; + xdev = XSCOM(dev); + xdev->bus = xb; + chip->xscom = xb; +} + +#define _FDT(exp) \ + do { \ + int ret = (exp); \ + if (ret < 0) { \ + fprintf(stderr, "qemu: error creating device tree: %s: %s\n", \ + #exp, fdt_strerror(ret)); \ + exit(1); \ + } \ + } while (0) + + +int xscom_populate_fdt(XScomBus *xb, void *fdt) +{ + BusChild *bc; + char *name; + const char compat[] = "ibm,power8-xscom\0ibm,xscom"; + uint64_t reg[] = { cpu_to_be64(XSCOM_BASE(xb->chip_id)), + cpu_to_be64(XSCOM_SIZE) }; + + name = g_strdup_printf("xscom@%llx", (unsigned long long)be64_to_cpu(reg[0])); + _FDT((fdt_begin_node(fdt, name))); + g_free(name); + _FDT((fdt_property_cell(fdt, "ibm,chip-id", xb->chip_id))); + _FDT((fdt_property_cell(fdt, "#address-cells", 1))); + _FDT((fdt_property_cell(fdt, "#size-cells", 1))); + _FDT((fdt_property(fdt, "reg", reg, sizeof(reg)))); + _FDT((fdt_property(fdt, "compatible", compat, sizeof(compat)))); + _FDT((fdt_property(fdt, "scom-controller", NULL, 0))); + + QTAILQ_FOREACH(bc, &xb->bus.children, sibling) { + DeviceState *qd = bc->child; + XScomDevice *xd = XSCOM_DEVICE(qd); + XScomDeviceClass *xc = XSCOM_DEVICE_GET_CLASS(xd); + uint32_t reg[MAX_XSCOM_RANGES * 2]; + unsigned int i, sz = 0; + void *cp, *p; + + /* Some XSCOM slaves may not be represented in the DT */ + if (!xc->dt_name) { + continue; + } + name = g_strdup_printf("%s@%x", xc->dt_name, xd->ranges[0].addr); + _FDT((fdt_begin_node(fdt, name))); + g_free(name); + for (i = 0; i < MAX_XSCOM_RANGES; i++) { + if (xd->ranges[i].size == 0) { + break; + } + reg[sz++] = cpu_to_be32(xd->ranges[i].addr); + reg[sz++] = cpu_to_be32(xd->ranges[i].size); + } + _FDT((fdt_property(fdt, "reg", reg, sz * 4))); + if (xc->devnode) { + _FDT((xc->devnode(xd, fdt))); + } +#define MAX_COMPATIBLE_PROP 1024 + cp = p = g_malloc0(MAX_COMPATIBLE_PROP); + i = 0; + while((p - cp) < MAX_COMPATIBLE_PROP) { + int l; + if (xc->dt_compatible[i] == NULL) { + break; + } + l = strlen(xc->dt_compatible[i]); + if (l >= (MAX_COMPATIBLE_PROP - i)) { + break; + } + strcpy(p, xc->dt_compatible[i++]); + p += l + 1; + } + _FDT((fdt_property(fdt, "compatible", cp, p - cp))); + _FDT((fdt_end_node(fdt))); + } + + _FDT((fdt_end_node(fdt))); + + return 0; +} + +static int xscom_qdev_init(DeviceState *qdev) +{ + XScomDevice *xdev = (XScomDevice *)qdev; + XScomDeviceClass *xc = XSCOM_DEVICE_GET_CLASS(xdev); + + if (xc->init) { + return xc->init(xdev); + } + return 0; +} + +static void xscom_device_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *k = DEVICE_CLASS(klass); + k->init = xscom_qdev_init; + k->bus_type = TYPE_XSCOM_BUS; +} + +static const TypeInfo xscom_dev_info = { + .name = TYPE_XSCOM_DEVICE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(XScomDevice), + .abstract = true, + .class_size = sizeof(XScomDeviceClass), + .class_init = xscom_device_class_init, +}; + +static void xscom_register_types(void) +{ + type_register_static(&xscom_info); + type_register_static(&xscom_bus_info); + type_register_static(&xscom_dev_info); +} + +type_init(xscom_register_types) diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index 38ff2e1596ca9..3212480d68183 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -27,6 +27,7 @@ #include "hw/hw.h" #include "hw/ppc/ppc.h" #include "hw/ppc/ppc_e500.h" +#include "hw/i386/pc.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" #include "sysemu/cpus.h" @@ -38,6 +39,10 @@ #include "kvm_ppc.h" #include "trace.h" +#if defined(TARGET_PPC64) +#include "hw/ppc/xics.h" +#endif + //#define PPC_DEBUG_IRQ //#define PPC_DEBUG_TB @@ -699,9 +704,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 +942,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 { @@ -1343,3 +1355,12 @@ PowerPCCPU *ppc_get_vcpu_by_dt_id(int cpu_dt_id) return NULL; } + +void ppc_hmp_info_pic(Monitor *mon, const QDict *qdict) +{ + /* Call in turn every PIC around. OpenPIC doesn't have one yet */ +#ifdef TARGET_PPC64 + xics_hmp_info_pic(mon, qdict); +#endif + hmp_info_pic(mon, qdict); +} diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index b69995e0dce1b..6f654e816c507 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -121,7 +121,7 @@ static XICSState *xics_system_init(MachineState *machine, Error *err = NULL; if (machine_kernel_irqchip_allowed(machine)) { - icp = try_create_xics(TYPE_KVM_XICS, nr_servers, nr_irqs, &err); + icp = try_create_xics(TYPE_XICS_SPAPR_KVM, nr_servers, nr_irqs, &err); } if (machine_kernel_irqchip_required(machine) && !icp) { error_reportf_err(err, @@ -132,7 +132,7 @@ static XICSState *xics_system_init(MachineState *machine, } if (!icp) { - icp = try_create_xics(TYPE_XICS, nr_servers, nr_irqs, errp); + icp = try_create_xics(TYPE_XICS_SPAPR, nr_servers, nr_irqs, errp); } return icp; @@ -1626,7 +1626,7 @@ static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, } } - xics_cpu_setup(spapr->icp, cpu); + xics_cpu_setup(spapr->xics, cpu); qemu_register_reset(spapr_cpu_reset, cpu); } @@ -1759,6 +1759,13 @@ static void ppc_spapr_init(MachineState *machine) spapr->vrma_adjust = 1; spapr->rma_size = MIN(spapr->rma_size, 0x10000000); } + + /* Actually we don't support unbounded RMA anymore since we + * added proper emulation of HV mode. The max we can get is + * 16G which also happens to be what we configure for PAPR + * mode so make sure we don't do anything bigger than that + */ + spapr->rma_size = MIN(spapr->rma_size, 0x400000000ull); } if (spapr->rma_size > node0_size) { @@ -1771,10 +1778,10 @@ 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, + spapr->xics = xics_system_init(machine, DIV_ROUND_UP(max_cpus * kvmppc_smt_threads(), smp_threads), - XICS_IRQS, &error_fatal); + XICS_IRQS_SPAPR, &error_fatal); if (smc->dr_lmb_enabled) { spapr_validate_node_memory(machine, &error_fatal); diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c index 049fb1b325869..b8f953f8f8b03 100644 --- a/hw/ppc/spapr_events.c +++ b/hw/ppc/spapr_events.c @@ -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) @@ -465,7 +465,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) @@ -548,7 +548,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; @@ -600,7 +600,7 @@ static void event_scan(PowerPCCPU *cpu, sPAPRMachineState *spapr, 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, false, &error_fatal); spapr->epow_notifier.notify = spapr_powerdown_req; qemu_register_powerdown_notifier(&spapr->epow_notifier); diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 8f40602a5efbe..2713087c1e5d0 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -183,6 +183,7 @@ static RemoveResult remove_hpte(PowerPCCPU *cpu, target_ulong ptex, static target_ulong h_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { + CPUPPCState *env = &cpu->env; target_ulong flags = args[0]; target_ulong pte_index = args[1]; target_ulong avpn = args[2]; @@ -193,6 +194,7 @@ static target_ulong h_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr, switch (ret) { case REMOVE_SUCCESS: + check_tlb_flush(env); return H_SUCCESS; case REMOVE_NOT_FOUND: @@ -229,7 +231,9 @@ static target_ulong h_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr, static target_ulong h_bulk_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { + CPUPPCState *env = &cpu->env; int i; + target_ulong rc = H_SUCCESS; for (i = 0; i < H_BULK_REMOVE_MAX_BATCH; i++) { target_ulong *tsh = &args[i*2]; @@ -262,14 +266,18 @@ static target_ulong h_bulk_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr, break; case REMOVE_PARM: - return H_PARAMETER; + rc = H_PARAMETER; + goto exit; case REMOVE_HW: - return H_HARDWARE; + rc = H_HARDWARE; + goto exit; } } + exit: + check_tlb_flush(env); - return H_SUCCESS; + return rc; } static target_ulong h_protect(PowerPCCPU *cpu, sPAPRMachineState *spapr, diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 573e635bfb1c9..f1dd87540686d 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -321,7 +321,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); } @@ -359,7 +359,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, req_num, false, ret_intr_type == RTAS_TYPE_MSI, &err); if (err) { error_reportf_err(err, "Can't allocate MSIs for device %x: ", @@ -370,7 +370,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); } @@ -732,7 +732,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 = { @@ -1444,7 +1444,7 @@ 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, 1, true, false, &local_err); if (local_err) { error_propagate(errp, local_err); error_prepend(errp, "can't allocate LSIs: "); @@ -1790,7 +1790,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 diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c index 8aa021fde99c2..5bf540df5b243 100644 --- a/hw/ppc/spapr_vio.c +++ b/hw/ppc/spapr_vio.c @@ -465,7 +465,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, dev->irq, false, &local_err); if (local_err) { error_propagate(errp, local_err); return; diff --git a/include/hw/ipmi/ipmi.h b/include/hw/ipmi/ipmi.h index 74a2b5af96131..178e72d72b400 100644 --- a/include/hw/ipmi/ipmi.h +++ b/include/hw/ipmi/ipmi.h @@ -255,4 +255,8 @@ struct ipmi_sdr_compact { typedef uint8_t ipmi_sdr_compact_buffer[sizeof(struct ipmi_sdr_compact)]; +int ipmi_bmc_sdr_find(IPMIBmc *b, uint16_t recid, + const struct ipmi_sdr_compact **sdr, uint16_t *nextrec); +void ipmi_bmc_gen_event(IPMIBmc *b, uint8_t *evt, bool log); + #endif diff --git a/include/hw/pci-host/pnv_phb3.h b/include/hw/pci-host/pnv_phb3.h new file mode 100644 index 0000000000000..d70bf8bd38c0b --- /dev/null +++ b/include/hw/pci-host/pnv_phb3.h @@ -0,0 +1,145 @@ +#ifndef _HW_PNV_PHB3_H +#define _HW_PNV_PHB3_H +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Copyright IBM Corp. 2014 + */ +#include "hw/hw.h" +#include "hw/ppc/pnv.h" +#include "hw/pci/pci_host.h" +#include "exec/address-spaces.h" +#include "hw/pci-host/pnv_phb3_regs.h" +#include "sysemu/cpus.h" +#include "qom/cpu.h" +#include "hw/ppc/pnv_xscom.h" +#include "hw/ppc/xics.h" + +#define PHB_NUM_M64 16 +#define PHB_NUM_REGS (0x1000 >> 3) +#define PHB_NUM_LSI 8 +#define PHB_NUM_PE 256 + +#define PCI_MMIO_TOTAL_SIZE (0x1ull << 60) + +#define IODA2_PCI_BUS_MAX 256 + +typedef struct PnvPBCQState PnvPBCQState; +typedef struct PnvPhb3State PnvPhb3State; +typedef struct PnvPhb3DMASpace PnvPhb3DMASpace; + +/* We don't want to include xics.h here */ +typedef struct XICSState XICSState; +typedef struct ICSState ICSState; + +/* Similarily with pnv_phb3_msi */ +typedef struct Phb3MsiState Phb3MsiState; + +/* We have one such address space wrapper per possible device + * under the PHB since they need to be assigned statically at + * qemu device creation time. The relationship to a PE is done + * later dynamically. This means we can potentially create a lot + * of these guys. Q35 stores them as some kind of radix tree but + * we never really need to do fast lookups so instead we simply + * keep a QLIST of them for now, we can add the radix if needed + * later on. + * + * We do cache the PE number to speed things up a bit though. + */ +struct PnvPhb3DMASpace { + PCIBus *bus; + uint8_t devfn; + int pe_num; /* Cached PE number */ +#define PHB_INVALID_PE (-1) + PnvPhb3State *phb; + AddressSpace dma_as; + MemoryRegion dma_mr; + MemoryRegion msi32_mr; + MemoryRegion msi64_mr; + bool msi32_mapped; + bool msi64_mapped; + QLIST_ENTRY(PnvPhb3DMASpace) list; +}; + +struct PnvPhb3State { + PCIHostState parent_obj; + MemoryRegion mr_m32; + MemoryRegion mr_m64[PHB_NUM_M64]; + MemoryRegion mr_regs; + bool regs_mapped; + bool m32_mapped; + bool m64_mapped[PHB_NUM_M64]; + MemoryRegion pci_mmio; + MemoryRegion pci_io; + uint64_t regs[PHB_NUM_REGS]; + PnvPBCQState *pbcq; + uint64_t ioda_LIST[8]; + uint64_t ioda_LXIVT[8]; + uint64_t ioda_TVT[512]; + uint64_t ioda_M64BT[16]; + uint64_t ioda_MDT[256]; + uint64_t ioda_PEEV[4]; + uint32_t total_irq; + XICSState *xics; + ICSState *lsi_ics; + Phb3MsiState *msis; + QLIST_HEAD(, PnvPhb3DMASpace) dma_spaces; +}; + +struct PnvPBCQState { + XScomDevice xd; + uint32_t chip_id; + uint32_t phb_id; + uint32_t nest_xbase; + uint32_t spci_xbase; + uint32_t pci_xbase; + uint64_t nest_regs[PBCQ_NEST_REGS_COUNT]; + uint64_t spci_regs[PBCQ_SPCI_REGS_COUNT]; + uint64_t pci_regs[PBCQ_PCI_REGS_COUNT]; + MemoryRegion mmbar0; + MemoryRegion mmbar1; + MemoryRegion phbbar; + bool mmio0_mapped; + bool mmio1_mapped; + bool phb_mapped; + uint64_t mmio0_base; + uint64_t mmio0_size; + uint64_t mmio1_base; + uint64_t mmio1_size; + PnvPhb3State *phb; +}; + +#define TYPE_PNV_PBCQ "pnv-pbcq" +#define PNV_PBCQ(obj) \ + OBJECT_CHECK(PnvPBCQState, (obj), TYPE_PNV_PBCQ) + + +#define TYPE_PNV_PHB3 "pnv-phb3" +#define PNV_PHB3(obj) \ + OBJECT_CHECK(PnvPhb3State, (obj), TYPE_PNV_PHB3) + +#define TYPE_PNV_PHB3_RC "pnv-phb3-rc" + +uint64_t pnv_phb3_reg_read(void *opaque, hwaddr off, unsigned size); +void pnv_phb3_reg_write(void *opaque, hwaddr off, uint64_t val, unsigned size); +void pnv_phb3_update_regions(PnvPhb3State *phb); +void pnv_phb3_remap_irqs(PnvPhb3State *phb); +void pnv_phb3_create(PnvChip *chip, XICSState *xics, uint32_t idx); +Phb3MsiState *pnv_phb3_msi_create(PnvPhb3State *phb); +void pnv_phb3_msi_update_config(Phb3MsiState *msis, uint32_t base, + uint32_t count); +void pnv_phb3_msi_send(Phb3MsiState *msis, uint64_t addr, uint16_t data, + int32_t dev_pe); +void pnv_phb3_msi_ffi(Phb3MsiState *msis, uint64_t val); + +#endif /* _HW_PNV_PHB3_H */ diff --git a/include/hw/pci-host/pnv_phb3_regs.h b/include/hw/pci-host/pnv_phb3_regs.h new file mode 100644 index 0000000000000..daad8fcc77d39 --- /dev/null +++ b/include/hw/pci-host/pnv_phb3_regs.h @@ -0,0 +1,505 @@ +/* Copyright 2013-2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __PHB3_REGS_H +#define __PHB3_REGS_H + +#define PPC_BIT(bit) (0x8000000000000000UL >> (bit)) +#define PPC_BIT32(bit) (0x80000000UL >> (bit)) +#define PPC_BIT8(bit) (0x80UL >> (bit)) +#define PPC_BITMASK(bs,be) ((PPC_BIT(bs) - PPC_BIT(be)) | PPC_BIT(bs)) +#define PPC_BITMASK32(bs,be) ((PPC_BIT32(bs) - PPC_BIT32(be))|PPC_BIT32(bs)) +#define PPC_BITLSHIFT(be) (63 - (be)) +#define PPC_BITLSHIFT32(be) (31 - (be)) + +/* Extract field fname from val */ +#define GETFIELD(fname, val) \ + (((val) & fname##_MASK) >> fname##_LSH) + +/* Set field fname of oval to fval + * NOTE: oval isn't modified, the combined result is returned + */ +#define SETFIELD(fname, oval, fval) \ + (((oval) & ~fname##_MASK) | \ + ((((typeof(oval))(fval)) << fname##_LSH) & fname##_MASK)) + +/* + * PBCQ XSCOM registers + */ + +#define PBCQ_NEST_IRSN_COMPARE 0x1a +#define PBCQ_NEST_IRSN_COMP_MASK PPC_BITMASK(0,18) +#define PBCQ_NEST_IRSN_COMP_LSH PPC_BITLSHIFT(18) +#define PBCQ_NEST_IRSN_MASK 0x1b +#define PBCQ_NEST_LSI_SRC_ID 0x1f +#define PBCQ_NEST_LSI_SRC_MASK PPC_BITMASK(0,7) +#define PBCQ_NEST_LSI_SRC_LSH PPC_BITLSHIFT(7) +#define PBCQ_NEST_REGS_COUNT 0x46 +#define PBCQ_NEST_MMIO_BAR0 0x40 +#define PBCQ_NEST_MMIO_BAR1 0x41 +#define PBCQ_NEST_PHB_BAR 0x42 +#define PBCQ_NEST_MMIO_MASK0 0x43 +#define PBCQ_NEST_MMIO_MASK1 0x44 +#define PBCQ_NEST_BAR_EN 0x45 +#define PBCQ_NEST_BAR_EN_MMIO0 PPC_BIT(0) +#define PBCQ_NEST_BAR_EN_MMIO1 PPC_BIT(1) +#define PBCQ_NEST_BAR_EN_PHB PPC_BIT(2) +#define PBCQ_NEST_BAR_EN_IRSN_RX PPC_BIT(3) +#define PBCQ_NEST_BAR_EN_IRSN_TX PPC_BIT(4) + +#define PBCQ_PCI_REGS_COUNT 0x15 +#define PBCQ_PCI_BAR2 0x0b + +#define PBCQ_SPCI_REGS_COUNT 0x5 +#define PBCQ_SPCI_ASB_ADDR 0x0 +#define PBCQ_SPCI_ASB_STATUS 0x1 +#define PBCQ_SPCI_ASB_DATA 0x2 +#define PBCQ_SPCI_AIB_CAPP_EN 0x3 +#define PBCQ_SPCI_CAPP_SEC_TMR 0x4 + +/* + * PHB MMIO registers + */ + +/* PHB Fundamental register set A */ +#define PHB_LSI_SOURCE_ID 0x100 +#define PHB_LSI_SRC_ID_MASK PPC_BITMASK(5,12) +#define PHB_LSI_SRC_ID_LSH PPC_BITLSHIFT(12) +#define PHB_DMA_CHAN_STATUS 0x110 +#define PHB_DMA_CHAN_ANY_ERR PPC_BIT(27) +#define PHB_DMA_CHAN_ANY_ERR1 PPC_BIT(28) +#define PHB_DMA_CHAN_ANY_FREEZE PPC_BIT(29) +#define PHB_CPU_LOADSTORE_STATUS 0x120 +#define PHB_CPU_LS_ANY_ERR PPC_BIT(27) +#define PHB_CPU_LS_ANY_ERR1 PPC_BIT(28) +#define PHB_CPU_LS_ANY_FREEZE PPC_BIT(29) +#define PHB_DMA_MSI_NODE_ID 0x128 +#define PHB_DMAMSI_NID_FIXED PPC_BIT(0) +#define PHB_DMAMSI_NID_MASK PPC_BITMASK(24,31) +#define PHB_DMAMSI_NID_LSH PPC_BITLSHIFT(31) +#define PHB_CONFIG_DATA 0x130 +#define PHB_LOCK0 0x138 +#define PHB_CONFIG_ADDRESS 0x140 +#define PHB_CA_ENABLE PPC_BIT(0) +#define PHB_CA_BUS_MASK PPC_BITMASK(4,11) +#define PHB_CA_BUS_LSH PPC_BITLSHIFT(11) +#define PHB_CA_DEV_MASK PPC_BITMASK(12,16) +#define PHB_CA_DEV_LSH PPC_BITLSHIFT(16) +#define PHB_CA_FUNC_MASK PPC_BITMASK(17,19) +#define PHB_CA_FUNC_LSH PPC_BITLSHIFT(19) +#define PHB_CA_REG_MASK PPC_BITMASK(20,31) +#define PHB_CA_REG_LSH PPC_BITLSHIFT(31) +#define PHB_CA_PE_MASK PPC_BITMASK(40,47) +#define PHB_CA_PE_LSH PPC_BITLSHIFT(47) +#define PHB_LOCK1 0x148 +#define PHB_IVT_BAR 0x150 +#define PHB_IVT_BAR_ENABLE PPC_BIT(0) +#define PHB_IVT_BASE_ADDRESS_MASK PPC_BITMASK(14,48) +#define PHB_IVT_BASE_ADDRESS_LSH PPC_BITLSHIFT(48) +#define PHB_IVT_LENGTH_MASK PPC_BITMASK(52,63) +#define PHB_IVT_LENGTH_ADDRESS_LSH PPC_BITLSHIFT(63) +#define PHB_RBA_BAR 0x158 +#define PHB_RBA_BAR_ENABLE PPC_BIT(0) +#define PHB_RBA_BASE_ADDRESS_MASK PPC_BITMASK(14,55) +#define PHB_RBA_BASE_ADDRESS_LSH PPC_BITLSHIFT(55) +#define PHB_PHB3_CONFIG 0x160 +#define PHB_PHB3C_64B_TCE_EN PPC_BIT(2) +#define PHB_PHB3C_32BIT_MSI_EN PPC_BIT(8) +#define PHB_PHB3C_64BIT_MSI_EN PPC_BIT(14) +#define PHB_PHB3C_M32_EN PPC_BIT(16) +#define PHB_RTT_BAR 0x168 +#define PHB_RTT_BAR_ENABLE PPC_BIT(0) +#define PHB_RTT_BASE_ADDRESS_MASK PPC_BITMASK(14,46) +#define PHB_RTT_BASE_ADDRESS_LSH PPC_BITLSHIFT(46) +#define PHB_PELTV_BAR 0x188 +#define PHB_PELTV_BAR_ENABLE PPC_BIT(0) +#define PHB_PELTV_BASE_ADDRESS_MASK PPC_BITMASK(14,50) +#define PHB_PELTV_BASE_ADDRESS_LSH PPC_BITLSHIFT(50) +#define PHB_M32_BASE_ADDR 0x190 +#define PHB_M32_BASE_MASK 0x198 +#define PHB_M32_START_ADDR 0x1a0 +#define PHB_PEST_BAR 0x1a8 +#define PHB_PEST_BAR_ENABLE PPC_BIT(0) +#define PHB_PEST_BASE_ADDRESS_MASK PPC_BITMASK(14,51) +#define PHB_PEST_BASE_ADDRESS_LSH PPC_BITLSHIFT(51) +#define PHB_M64_UPPER_BITS 0x1f0 +#define PHB_INTREP_TIMER 0x1f8 +#define PHB_DMARD_SYNC 0x200 +#define PHB_RTC_INVALIDATE 0x208 +#define PHB_RTC_INVALIDATE_ALL PPC_BIT(0) +#define PHB_RTC_INVALIDATE_RID_MASK PPC_BITMASK(16,31) +#define PHB_RTC_INVALIDATE_RID_LSH PPC_BITLSHIFT(31) +#define PHB_TCE_KILL 0x210 +#define PHB_TCE_KILL_ALL PPC_BIT(0) +#define PHB_TCE_SPEC_CTL 0x218 +#define PHB_IODA_ADDR 0x220 +#define PHB_IODA_AD_AUTOINC PPC_BIT(0) +#define PHB_IODA_AD_TSEL_MASK PPC_BITMASK(11,15) +#define PHB_IODA_AD_TSEL_LSH PPC_BITLSHIFT(15) +#define PHB_IODA_AD_TADR_MASK PPC_BITMASK(55,63) +#define PHB_IODA_AD_TADR_LSH PPC_BITLSHIFT(63) +#define PHB_IODA_DATA0 0x228 +#define PHB_FFI_REQUEST 0x238 +#define PHB_FFI_LOCK_CLEAR PPC_BIT(3) +#define PHB_FFI_REQUEST_ISN_MASK PPC_BITMASK(49,59) +#define PHB_FFI_REQUEST_ISN_LSH PPC_BITLSHIFT(59) +#define PHB_FFI_LOCK 0x240 +#define PHB_FFI_LOCK_STATE PPC_BIT(0) +#define PHB_XIVE_UPDATE 0x248 /* Broken in DD1 */ +#define PHB_PHB3_GEN_CAP 0x250 +#define PHB_PHB3_TCE_CAP 0x258 +#define PHB_PHB3_IRQ_CAP 0x260 +#define PHB_PHB3_EEH_CAP 0x268 +#define PHB_IVC_INVALIDATE 0x2a0 +#define PHB_IVC_INVALIDATE_ALL PPC_BIT(0) +#define PHB_IVC_INVALIDATE_SID_MASK PPC_BITMASK(16,31) +#define PHB_IVC_INVALIDATE_SID_LSH PPC_BITLSHIFT(31) +#define PHB_IVC_UPDATE 0x2a8 +#define PHB_IVC_UPDATE_ENABLE_P PPC_BIT(0) +#define PHB_IVC_UPDATE_ENABLE_Q PPC_BIT(1) +#define PHB_IVC_UPDATE_ENABLE_SERVER PPC_BIT(2) +#define PHB_IVC_UPDATE_ENABLE_PRI PPC_BIT(3) +#define PHB_IVC_UPDATE_ENABLE_GEN PPC_BIT(4) +#define PHB_IVC_UPDATE_ENABLE_CON PPC_BIT(5) +#define PHB_IVC_UPDATE_GEN_MATCH_MASK PPC_BITMASK(6, 7) +#define PHB_IVC_UPDATE_GEN_MATCH_LSH PPC_BITLSHIFT(7) +#define PHB_IVC_UPDATE_SERVER_MASK PPC_BITMASK(8, 23) +#define PHB_IVC_UPDATE_SERVER_LSH PPC_BITLSHIFT(23) +#define PHB_IVC_UPDATE_PRI_MASK PPC_BITMASK(24, 31) +#define PHB_IVC_UPDATE_PRI_LSH PPC_BITLSHIFT(31) +#define PHB_IVC_UPDATE_GEN_MASK PPC_BITMASK(32,33) +#define PHB_IVC_UPDATE_GEN_LSH PPC_BITLSHIFT(33) +#define PHB_IVC_UPDATE_P_MASK PPC_BITMASK(34,34) +#define PHB_IVC_UPDATE_P_LSH PPC_BITLSHIFT(34) +#define PHB_IVC_UPDATE_Q_MASK PPC_BITMASK(35,35) +#define PHB_IVC_UPDATE_Q_LSH PPC_BITLSHIFT(35) +#define PHB_IVC_UPDATE_SID_MASK PPC_BITMASK(48,63) +#define PHB_IVC_UPDATE_SID_LSH PPC_BITLSHIFT(63) +#define PHB_PAPR_ERR_INJ_CTL 0x2b0 +#define PHB_PAPR_ERR_INJ_CTL_INB PPC_BIT(0) +#define PHB_PAPR_ERR_INJ_CTL_OUTB PPC_BIT(1) +#define PHB_PAPR_ERR_INJ_CTL_STICKY PPC_BIT(2) +#define PHB_PAPR_ERR_INJ_CTL_CFG PPC_BIT(3) +#define PHB_PAPR_ERR_INJ_CTL_RD PPC_BIT(4) +#define PHB_PAPR_ERR_INJ_CTL_WR PPC_BIT(5) +#define PHB_PAPR_ERR_INJ_CTL_FREEZE PPC_BIT(6) +#define PHB_PAPR_ERR_INJ_ADDR 0x2b8 +#define PHB_PAPR_ERR_INJ_ADDR_MMIO_MASK PPC_BITMASK(16,63) +#define PHB_PAPR_ERR_INJ_ADDR_MMIO_LSH PPC_BITLSHIFT(63) +#define PHB_PAPR_ERR_INJ_MASK 0x2c0 +#define PHB_PAPR_ERR_INJ_MASK_CFG_MASK PPC_BITMASK(4,11) +#define PHB_PAPR_ERR_INJ_MASK_CFG_LSH PPC_BITLSHIFT(11) +#define PHB_PAPR_ERR_INJ_MASK_MMIO_MASK PPC_BITMASK(16,63) +#define PHB_PAPR_ERR_INJ_MASK_MMIO_LSH PPC_BITLSHIFT(63) +#define PHB_ETU_ERR_SUMMARY 0x2c8 + +/* UTL registers */ +#define UTL_SYS_BUS_CONTROL 0x400 +#define UTL_STATUS 0x408 +#define UTL_SYS_BUS_AGENT_STATUS 0x410 +#define UTL_SYS_BUS_AGENT_ERR_SEVERITY 0x418 +#define UTL_SYS_BUS_AGENT_IRQ_EN 0x420 +#define UTL_SYS_BUS_BURST_SZ_CONF 0x440 +#define UTL_REVISION_ID 0x448 +#define UTL_BCLK_DOMAIN_DBG1 0x460 +#define UTL_BCLK_DOMAIN_DBG2 0x468 +#define UTL_BCLK_DOMAIN_DBG3 0x470 +#define UTL_BCLK_DOMAIN_DBG4 0x478 +#define UTL_BCLK_DOMAIN_DBG5 0x480 +#define UTL_BCLK_DOMAIN_DBG6 0x488 +#define UTL_OUT_POST_HDR_BUF_ALLOC 0x4c0 +#define UTL_OUT_POST_DAT_BUF_ALLOC 0x4d0 +#define UTL_IN_POST_HDR_BUF_ALLOC 0x4e0 +#define UTL_IN_POST_DAT_BUF_ALLOC 0x4f0 +#define UTL_OUT_NP_BUF_ALLOC 0x500 +#define UTL_IN_NP_BUF_ALLOC 0x510 +#define UTL_PCIE_TAGS_ALLOC 0x520 +#define UTL_GBIF_READ_TAGS_ALLOC 0x530 +#define UTL_PCIE_PORT_CONTROL 0x540 +#define UTL_PCIE_PORT_STATUS 0x548 +#define UTL_PCIE_PORT_ERROR_SEV 0x550 +#define UTL_PCIE_PORT_IRQ_EN 0x558 +#define UTL_RC_STATUS 0x560 +#define UTL_RC_ERR_SEVERITY 0x568 +#define UTL_RC_IRQ_EN 0x570 +#define UTL_EP_STATUS 0x578 +#define UTL_EP_ERR_SEVERITY 0x580 +#define UTL_EP_ERR_IRQ_EN 0x588 +#define UTL_PCI_PM_CTRL1 0x590 +#define UTL_PCI_PM_CTRL2 0x598 +#define UTL_GP_CTL1 0x5a0 +#define UTL_GP_CTL2 0x5a8 +#define UTL_PCLK_DOMAIN_DBG1 0x5b0 +#define UTL_PCLK_DOMAIN_DBG2 0x5b8 +#define UTL_PCLK_DOMAIN_DBG3 0x5c0 +#define UTL_PCLK_DOMAIN_DBG4 0x5c8 + +/* PCI-E Stack registers */ +#define PHB_PCIE_SYSTEM_CONFIG 0x600 +#define PHB_PCIE_BUS_NUMBER 0x608 +#define PHB_PCIE_SYSTEM_TEST 0x618 +#define PHB_PCIE_LINK_MANAGEMENT 0x630 +#define PHB_PCIE_LM_LINK_ACTIVE PPC_BIT(8) +#define PHB_PCIE_DLP_TRAIN_CTL 0x640 +#define PHB_PCIE_DLP_TCTX_DISABLE PPC_BIT(1) +#define PHB_PCIE_DLP_TCRX_DISABLED PPC_BIT(16) +#define PHB_PCIE_DLP_INBAND_PRESENCE PPC_BIT(19) +#define PHB_PCIE_DLP_TC_DL_LINKUP PPC_BIT(21) +#define PHB_PCIE_DLP_TC_DL_PGRESET PPC_BIT(22) +#define PHB_PCIE_DLP_TC_DL_LINKACT PPC_BIT(23) +#define PHB_PCIE_SLOP_LOOPBACK_STATUS 0x648 +#define PHB_PCIE_SYS_LINK_INIT 0x668 +#define PHB_PCIE_UTL_CONFIG 0x670 +#define PHB_PCIE_DLP_CONTROL 0x678 +#define PHB_PCIE_UTL_ERRLOG1 0x680 +#define PHB_PCIE_UTL_ERRLOG2 0x688 +#define PHB_PCIE_UTL_ERRLOG3 0x690 +#define PHB_PCIE_UTL_ERRLOG4 0x698 +#define PHB_PCIE_DLP_ERRLOG1 0x6a0 +#define PHB_PCIE_DLP_ERRLOG2 0x6a8 +#define PHB_PCIE_DLP_ERR_STATUS 0x6b0 +#define PHB_PCIE_DLP_ERR_COUNTERS 0x6b8 +#define PHB_PCIE_UTL_ERR_INJECT 0x6c0 +#define PHB_PCIE_TLDLP_ERR_INJECT 0x6c8 +#define PHB_PCIE_LANE_EQ_CNTL0 0x6d0 +#define PHB_PCIE_LANE_EQ_CNTL1 0x6d8 +#define PHB_PCIE_LANE_EQ_CNTL2 0x6e0 +#define PHB_PCIE_LANE_EQ_CNTL3 0x6e8 +#define PHB_PCIE_STRAPPING 0x700 + +/* Fundamental register set B */ +#define PHB_VERSION 0x800 +#define PHB_RESET 0x808 +#define PHB_CONTROL 0x810 +#define PHB_CTRL_IVE_128_BYTES PPC_BIT(24) +#define PHB_AIB_RX_CRED_INIT_TIMER 0x818 +#define PHB_AIB_RX_CMD_CRED 0x820 +#define PHB_AIB_RX_DATA_CRED 0x828 +#define PHB_AIB_TX_CMD_CRED 0x830 +#define PHB_AIB_TX_DATA_CRED 0x838 +#define PHB_AIB_TX_CHAN_MAPPING 0x840 +#define PHB_AIB_TAG_ENABLE 0x858 +#define PHB_AIB_FENCE_CTRL 0x860 +#define PHB_TCE_TAG_ENABLE 0x868 +#define PHB_TCE_WATERMARK 0x870 +#define PHB_TIMEOUT_CTRL1 0x878 +#define PHB_TIMEOUT_CTRL2 0x880 +#define PHB_QUIESCE_DMA_G 0x888 +#define PHB_AIB_TAG_STATUS 0x900 +#define PHB_TCE_TAG_STATUS 0x908 + +/* FIR & Error registers */ +#define PHB_LEM_FIR_ACCUM 0xc00 +#define PHB_LEM_FIR_AND_MASK 0xc08 +#define PHB_LEM_FIR_OR_MASK 0xc10 +#define PHB_LEM_ERROR_MASK 0xc18 +#define PHB_LEM_ERROR_AND_MASK 0xc20 +#define PHB_LEM_ERROR_OR_MASK 0xc28 +#define PHB_LEM_ACTION0 0xc30 +#define PHB_LEM_ACTION1 0xc38 +#define PHB_LEM_WOF 0xc40 +#define PHB_ERR_STATUS 0xc80 +#define PHB_ERR1_STATUS 0xc88 +#define PHB_ERR_INJECT 0xc90 +#define PHB_ERR_LEM_ENABLE 0xc98 +#define PHB_ERR_IRQ_ENABLE 0xca0 +#define PHB_ERR_FREEZE_ENABLE 0xca8 +#define PHB_ERR_AIB_FENCE_ENABLE 0xcb0 +#define PHB_ERR_LOG_0 0xcc0 +#define PHB_ERR_LOG_1 0xcc8 +#define PHB_ERR_STATUS_MASK 0xcd0 +#define PHB_ERR1_STATUS_MASK 0xcd8 + +#define PHB_OUT_ERR_STATUS 0xd00 +#define PHB_OUT_ERR1_STATUS 0xd08 +#define PHB_OUT_ERR_INJECT 0xd10 +#define PHB_OUT_ERR_LEM_ENABLE 0xd18 +#define PHB_OUT_ERR_IRQ_ENABLE 0xd20 +#define PHB_OUT_ERR_FREEZE_ENABLE 0xd28 +#define PHB_OUT_ERR_AIB_FENCE_ENABLE 0xd30 +#define PHB_OUT_ERR_LOG_0 0xd40 +#define PHB_OUT_ERR_LOG_1 0xd48 +#define PHB_OUT_ERR_STATUS_MASK 0xd50 +#define PHB_OUT_ERR1_STATUS_MASK 0xd58 + +#define PHB_INA_ERR_STATUS 0xd80 +#define PHB_INA_ERR1_STATUS 0xd88 +#define PHB_INA_ERR_INJECT 0xd90 +#define PHB_INA_ERR_LEM_ENABLE 0xd98 +#define PHB_INA_ERR_IRQ_ENABLE 0xda0 +#define PHB_INA_ERR_FREEZE_ENABLE 0xda8 +#define PHB_INA_ERR_AIB_FENCE_ENABLE 0xdb0 +#define PHB_INA_ERR_LOG_0 0xdc0 +#define PHB_INA_ERR_LOG_1 0xdc8 +#define PHB_INA_ERR_STATUS_MASK 0xdd0 +#define PHB_INA_ERR1_STATUS_MASK 0xdd8 + +#define PHB_INB_ERR_STATUS 0xe00 +#define PHB_INB_ERR1_STATUS 0xe08 +#define PHB_INB_ERR_INJECT 0xe10 +#define PHB_INB_ERR_LEM_ENABLE 0xe18 +#define PHB_INB_ERR_IRQ_ENABLE 0xe20 +#define PHB_INB_ERR_FREEZE_ENABLE 0xe28 +#define PHB_INB_ERR_AIB_FENCE_ENABLE 0xe30 +#define PHB_INB_ERR_LOG_0 0xe40 +#define PHB_INB_ERR_LOG_1 0xe48 +#define PHB_INB_ERR_STATUS_MASK 0xe50 +#define PHB_INB_ERR1_STATUS_MASK 0xe58 + +/* Performance monitor & Debug registers */ +#define PHB_TRACE_CONTROL 0xf80 +#define PHB_PERFMON_CONFIG 0xf88 +#define PHB_PERFMON_CTR0 0xf90 +#define PHB_PERFMON_CTR1 0xf98 +#define PHB_PERFMON_CTR2 0xfa0 +#define PHB_PERFMON_CTR3 0xfa8 +#define PHB_HOTPLUG_OVERRIDE 0xfb0 +#define PHB_HPOVR_FORCE_RESAMPLE PPC_BIT(9) +#define PHB_HPOVR_PRESENCE_A PPC_BIT(10) +#define PHB_HPOVR_PRESENCE_B PPC_BIT(11) +#define PHB_HPOVR_LINK_ACTIVE PPC_BIT(12) +#define PHB_HPOVR_LINK_BIFURCATED PPC_BIT(13) +#define PHB_HPOVR_LINK_LANE_SWAPPED PPC_BIT(14) + +/* + * IODA2 on-chip tables + */ + +#define IODA2_TBL_LIST 1 +#define IODA2_TBL_LXIVT 2 +#define IODA2_TBL_IVC_CAM 3 +#define IODA2_TBL_RBA 4 +#define IODA2_TBL_RCAM 5 +#define IODA2_TBL_MRT 6 +#define IODA2_TBL_PESTA 7 +#define IODA2_TBL_PESTB 8 +#define IODA2_TBL_TVT 9 +#define IODA2_TBL_TCAM 10 +#define IODA2_TBL_TDR 11 +#define IODA2_TBL_M64BT 16 +#define IODA2_TBL_M32DT 17 +#define IODA2_TBL_PEEV 20 + +/* LXIVT */ +#define IODA2_LXIVT_SERVER_MASK PPC_BITMASK(8,23) +#define IODA2_LXIVT_SERVER_LSH PPC_BITLSHIFT(23) +#define IODA2_LXIVT_PRIORITY_MASK PPC_BITMASK(24,31) +#define IODA2_LXIVT_PRIORITY_LSH PPC_BITLSHIFT(31) +#define IODA2_LXIVT_NODE_ID_MASK PPC_BITMASK(56,63) +#define IODA2_LXIVT_NODE_ID_LSH PPC_BITLSHIFT(63) + +/* IVT */ +#define IODA2_IVT_SERVER_MASK PPC_BITMASK(0,23) +#define IODA2_IVT_SERVER_LSH PPC_BITLSHIFT(23) +#define IODA2_IVT_PRIORITY_MASK PPC_BITMASK(24,31) +#define IODA2_IVT_PRIORITY_LSH PPC_BITLSHIFT(31) +#define IODA2_IVT_GEN_MASK PPC_BITMASK(37,38) +#define IODA2_IVT_GEN_LSH PPC_BITLSHIFT(38) +#define IODA2_IVT_P_MASK PPC_BITMASK(39,39) +#define IODA2_IVT_P_LSH PPC_BITLSHIFT(39) +#define IODA2_IVT_Q_MASK PPC_BITMASK(47,47) +#define IODA2_IVT_Q_LSH PPC_BITLSHIFT(47) +#define IODA2_IVT_PE_MASK PPC_BITMASK(48,63) +#define IODA2_IVT_PE_LSH PPC_BITLSHIFT(63) + +/* TVT */ +#define IODA2_TVT_TABLE_ADDR_MASK PPC_BITMASK(0,47) +#define IODA2_TVT_TABLE_ADDR_LSH PPC_BITLSHIFT(47) +#define IODA2_TVT_NUM_LEVELS_MASK PPC_BITMASK(48,50) +#define IODA2_TVT_NUM_LEVELS_LSH PPC_BITLSHIFT(50) +#define IODA2_TVE_1_LEVEL 0 +#define IODA2_TVE_2_LEVELS 1 +#define IODA2_TVE_3_LEVELS 2 +#define IODA2_TVE_4_LEVELS 3 +#define IODA2_TVE_5_LEVELS 4 +#define IODA2_TVT_TCE_TABLE_SIZE_MASK PPC_BITMASK(51,55) +#define IODA2_TVT_TCE_TABLE_SIZE_LSH PPC_BITLSHIFT(55) +#define IODA2_TVT_IO_PSIZE_MASK PPC_BITMASK(59,63) +#define IODA2_TVT_IO_PSIZE_LSH PPC_BITLSHIFT(63) + +/* PESTA */ +#define IODA2_PESTA_MMIO_FROZEN PPC_BIT(0) + +/* PESTB */ +#define IODA2_PESTB_DMA_STOPPED PPC_BIT(0) + +/* M32DT */ +#define IODA2_M32DT_PE_MASK PPC_BITMASK(8,15) +#define IODA2_M32DT_PE_LSH PPC_BITLSHIFT(15) + +/* M64BT */ +#define IODA2_M64BT_ENABLE PPC_BIT(0) +#define IODA2_M64BT_SINGLE_PE PPC_BIT(1) +#define IODA2_M64BT_BASE_MASK PPC_BITMASK(2,31) +#define IODA2_M64BT_BASE_LSH PPC_BITLSHIFT(31) +#define IODA2_M64BT_MASK_MASK PPC_BITMASK(34,63) +#define IODA2_M64BT_MASK_LSH PPC_BITLSHIFT(63) +#define IODA2_M64BT_SINGLE_BASE_MASK PPC_BITMASK(2,26) +#define IODA2_M64BT_SINGLE_BASE_LSH PPC_BITLSHIFT(26) +#define IODA2_M64BT_PE_HI_MASK PPC_BITMASK(27,31) +#define IODA2_M64BT_PE_HI_LSH PPC_BITLSHIFT(31) +#define IODA2_M64BT_SINGLE_MASK_MASK PPC_BITMASK(34,58) +#define IODA2_M64BT_SINGLE_MASK_LSH PPC_BITLSHIFT(58) +#define IODA2_M64BT_PE_LOW_MASK PPC_BITMASK(59,63) +#define IODA2_M64BT_PE_LOW_LSH PPC_BITLSHIFT(63) + +/* + * IODA2 in-memory tables + */ + +/* PEST + * + * 2x8 bytes entries, PEST0 and PEST1 + */ + +#define IODA2_PEST0_MMIO_CAUSE PPC_BIT(2) +#define IODA2_PEST0_CFG_READ PPC_BIT(3) +#define IODA2_PEST0_CFG_WRITE PPC_BIT(4) +#define IODA2_PEST0_TTYPE_MASK PPC_BITMASK(5,7) +#define IODA2_PEST0_TTYPE_LSH PPC_BITLSHIFT(7) +#define PEST_TTYPE_DMA_WRITE 0 +#define PEST_TTYPE_MSI 1 +#define PEST_TTYPE_DMA_READ 2 +#define PEST_TTYPE_DMA_READ_RESP 3 +#define PEST_TTYPE_MMIO_LOAD 4 +#define PEST_TTYPE_MMIO_STORE 5 +#define PEST_TTYPE_OTHER 7 +#define IODA2_PEST0_CA_RETURN PPC_BIT(8) +#define IODA2_PEST0_UTL_RTOS_TIMEOUT PPC_BIT(8) /* Same bit as CA return */ +#define IODA2_PEST0_UR_RETURN PPC_BIT(9) +#define IODA2_PEST0_UTL_NONFATAL PPC_BIT(10) +#define IODA2_PEST0_UTL_FATAL PPC_BIT(11) +#define IODA2_PEST0_PARITY_UE PPC_BIT(13) +#define IODA2_PEST0_UTL_CORRECTABLE PPC_BIT(14) +#define IODA2_PEST0_UTL_INTERRUPT PPC_BIT(15) +#define IODA2_PEST0_MMIO_XLATE PPC_BIT(16) +#define IODA2_PEST0_IODA2_ERROR PPC_BIT(16) /* Same bit as MMIO xlate */ +#define IODA2_PEST0_TCE_PAGE_FAULT PPC_BIT(18) +#define IODA2_PEST0_TCE_ACCESS_FAULT PPC_BIT(19) +#define IODA2_PEST0_DMA_RESP_TIMEOUT PPC_BIT(20) +#define IODA2_PEST0_AIB_SIZE_INVALID PPC_BIT(21) +#define IODA2_PEST0_LEM_BIT_MASK PPC_BITMASK(26,31) +#define IODA2_PEST0_LEM_BIT_LSH PPC_BITLSHIFT(31) +#define IODA2_PEST0_RID_MASK PPC_BITMASK(32,47) +#define IODA2_PEST0_RID_LSH PPC_BITLSHIFT(47) +#define IODA2_PEST0_MSI_DATA_MASK PPC_BITMASK(48,63) +#define IODA2_PEST0_MSI_DATA_LSH PPC_BITLSHIFT(63) + +#define IODA2_PEST1_FAIL_ADDR_MASK PPC_BITMASK(3,63) +#define IODA2_PEST1_FAIL_ADDR_LSH PPC_BITLSHIFT(63) + + +#endif /* __PHB3_REGS_H */ diff --git a/include/hw/pci-host/spapr.h b/include/hw/pci-host/spapr.h index 03ee006406f6d..a2b3f0b7f8bfe 100644 --- a/include/hw/pci-host/spapr.h +++ b/include/hw/pci-host/spapr.h @@ -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); diff --git a/include/hw/pci/pci_bus.h b/include/hw/pci/pci_bus.h index 403fec6e58c0e..02055d4716308 100644 --- a/include/hw/pci/pci_bus.h +++ b/include/hw/pci/pci_bus.h @@ -23,6 +23,7 @@ struct PCIBus { PCIIOMMUFunc iommu_fn; void *iommu_opaque; uint8_t devfn_min; + uint8_t devfn_max; pci_set_irq_fn set_irq; pci_map_irq_fn map_irq; pci_route_irq_fn route_intx_to_irq; diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h new file mode 100644 index 0000000000000..98d75b0661510 --- /dev/null +++ b/include/hw/ppc/pnv.h @@ -0,0 +1,67 @@ +#ifndef _HW_LPC_H +#define _HW_LPC_H +/* + * QEMU PowerNV various definitions + * + * Copyright (c) 2014 BenH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "hw/hw.h" +typedef struct XScomBus XScomBus; +typedef struct ISABus ISABus; +typedef struct PnvLpcController PnvLpcController; +typedef struct PnvPsiController PnvPsiController; +typedef struct XICSState XICSState; +typedef struct PnvOCCState PnvOCCState; +typedef struct PCIBus PCIBus; + +/* Should we turn that into a QOjb of some sort ? */ +typedef struct PnvChip { + uint32_t chip_id; + XScomBus *xscom; + PnvLpcController *lpc; + ISABus *lpc_bus; + PnvPsiController *psi; + PnvOCCState *occ; +#define PNV_MAX_CHIP_PHB 4 + PCIBus *phb[PNV_MAX_CHIP_PHB]; +} PnvChip; + +typedef struct PnvSystem { + XICSState *xics; + uint32_t num_chips; +#define PNV_MAX_CHIPS 1 + PnvChip chips[PNV_MAX_CHIPS]; +} PnvSystem; + +extern void pnv_lpc_create(PnvChip *chip, bool has_serirq); +extern void pnv_psi_create(PnvChip *chip, XICSState *xics); +extern void pnv_occ_create(PnvChip *chip); + +typedef enum PnvPsiIrq { + PSIHB_IRQ_PSI, /* internal use only */ + PSIHB_IRQ_FSP, /* internal use only */ + PSIHB_IRQ_OCC, + PSIHB_IRQ_FSI, + PSIHB_IRQ_LPC_I2C, + PSIHB_IRQ_LOCAL_ERR, + PSIHB_IRQ_EXTERNAL, +} PnvPsiIrq; + +extern void pnv_psi_irq_set(PnvPsiController *psi, PnvPsiIrq irq, bool state); + +#endif /* _HW_PNV_LPC_H */ + diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h new file mode 100644 index 0000000000000..99de078c88946 --- /dev/null +++ b/include/hw/ppc/pnv_xscom.h @@ -0,0 +1,73 @@ +#ifndef _HW_XSCOM_H +#define _HW_XSCOM_H +/* + * QEMU PowerNV XSCOM bus definitions + * + * Copyright (c) 2010 David Gibson, IBM Corporation + * Based on the s390 virtio bus definitions: + * Copyright (c) 2009 Alexander Graf + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include + +#define TYPE_XSCOM_DEVICE "xscom-device" +#define XSCOM_DEVICE(obj) \ + OBJECT_CHECK(XScomDevice, (obj), TYPE_XSCOM_DEVICE) +#define XSCOM_DEVICE_CLASS(klass) \ + OBJECT_CLASS_CHECK(XScomDeviceClass, (klass), TYPE_XSCOM_DEVICE) +#define XSCOM_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(XScomDeviceClass, (obj), TYPE_XSCOM_DEVICE) + +#define TYPE_XSCOM_BUS "xscom-bus" +#define XSCOM_BUS(obj) OBJECT_CHECK(XScomBus, (obj), TYPE_XSCOM_BUS) + +typedef struct XScomDevice XScomDevice; +typedef struct XScomBus XScomBus; + +typedef struct XScomDeviceClass { + DeviceClass parent_class; + + const char *dt_name; + const char **dt_compatible; + int (*init)(XScomDevice *dev); + int (*devnode)(XScomDevice *dev, void *fdt); + + /* Actual XScom accesses */ + bool (*read)(XScomDevice *dev, uint32_t range, uint32_t offset, uint64_t *out_val); + bool (*write)(XScomDevice *dev, uint32_t range, uint32_t offset, uint64_t val); +} XScomDeviceClass; + +typedef struct XScomRange { + uint32_t addr; + uint32_t size; +} XScomRange; + +struct XScomDevice { + DeviceState qdev; +#define MAX_XSCOM_RANGES 4 + struct XScomRange ranges[MAX_XSCOM_RANGES]; +}; + +struct XScomBus { + BusState bus; + uint32_t chip_id; +}; + +extern void xscom_create(PnvChip *chip); +extern int xscom_populate_fdt(XScomBus *xscom, void *fdt); + + +#endif /* _HW_XSCOM_H */ diff --git a/include/hw/ppc/ppc.h b/include/hw/ppc/ppc.h index 14efd0ca31780..d5c648d322ac8 100644 --- a/include/hw/ppc/ppc.h +++ b/include/hw/ppc/ppc.h @@ -1,6 +1,8 @@ #ifndef HW_PPC_H #define HW_PPC_H 1 +void ppc_hmp_info_pic(Monitor *mon, const QDict *qdict); + void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level); /* PowerPC hardware exceptions management helpers */ diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 815d5eec45d1f..4fd022870ac10 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -49,7 +49,7 @@ struct sPAPRMachineState { struct VIOsPAPRBus *vio_bus; QLIST_HEAD(, sPAPRPHBState) phbs; struct sPAPRNVRAM *nvram; - XICSState *icp; + XICSState *xics; DeviceState *rtc; void *htab; diff --git a/include/hw/ppc/spapr_vio.h b/include/hw/ppc/spapr_vio.h index c9733e7552638..714fe76ddb1eb 100644 --- a/include/hw/ppc/spapr_vio.h +++ b/include/hw/ppc/spapr_vio.h @@ -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, diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index f60b06ae829e0..e5d98f591064b 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -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) +#define TYPE_XICS_SPAPR "xics-spapr" +#define XICS(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 KVM_XICS(obj) OBJECT_CHECK(KVMXICSState, (obj), TYPE_XICS_SPAPR_KVM) + +#define TYPE_XICS_NATIVE "xics-native" +#define XICS_NATIVE(obj) OBJECT_CHECK(XICSState, (obj), TYPE_XICS_NATIVE) #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_NATIVE_CLASS(klass) \ + OBJECT_CLASS_CHECK(XICSStateClass, (klass), TYPE_XICS_NATIVE) #define XICS_IPI 0x2 #define XICS_BUID 0x1 @@ -79,7 +84,8 @@ struct XICSState { uint32_t nr_servers; uint32_t nr_irqs; ICPState *ss; - ICSState *ics; + QLIST_HEAD(, ICSState) ics; + MemoryRegion icp_mmio; }; #define TYPE_ICP "icp" @@ -105,16 +111,25 @@ struct ICPState { DeviceState parent_obj; /*< public >*/ CPUState *cs; + ICSState *xirr_owner; uint32_t xirr; uint8_t pending_priority; uint8_t mfrr; qemu_irq output; bool cap_irq_xics_enabled; + uint32_t links[3]; }; +/* This should be an XSCOM BAR ... the size is arbitrary as well */ +#define ICP_MM_BASE 0x0003FFFF80000000 +#define ICP_MM_SIZE 0x0000000010000000 + #define TYPE_ICS "ics" #define ICS(obj) OBJECT_CHECK(ICSState, (obj), TYPE_ICS) +#define TYPE_ICS_SIMPLE "ics-simple" +#define ICS_SIMPLE(obj) OBJECT_CHECK(ICSState, (obj), TYPE_ICS_SIMPLE) + #define TYPE_KVM_ICS "icskvm" #define KVM_ICS(obj) OBJECT_CHECK(ICSState, (obj), TYPE_KVM_ICS) @@ -128,6 +143,9 @@ struct ICSStateClass { void (*pre_save)(ICSState *s); int (*post_load)(ICSState *s, int version_id); + void (*reject)(ICSState *s, uint32_t irq); + void (*resend)(ICSState *s); + void (*eoi)(ICSState *s, uint32_t irq); }; struct ICSState { @@ -138,9 +156,16 @@ struct ICSState { uint32_t offset; qemu_irq *qirqs; ICSIRQState *irqs; - XICSState *icp; + XICSState *xics; + QLIST_ENTRY(ICSState) list; }; +static inline bool ics_valid_irq(ICSState *ics, uint32_t nr) +{ + return (ics->offset != 0) && (nr >= ics->offset) + && (nr < (ics->offset + ics->nr_irqs)); +} + struct ICSIRQState { uint32_t server; uint8_t priority; @@ -157,15 +182,41 @@ struct ICSIRQState { uint8_t flags; }; -#define XICS_IRQS 1024 +#define XICS_IRQS_SPAPR 1024 +#define XICS_IRQS_POWERNV (1 << 19) + qemu_irq xics_get_qirq(XICSState *icp, int irq); -void xics_set_irq_type(XICSState *icp, int irq, bool lsi); -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 irq_hint, bool lsi, Error **errp); +int xics_spapr_alloc_block(XICSState *icp, int num, bool lsi, bool align, Error **errp); -void xics_free(XICSState *icp, int irq, int num); +void xics_spapr_free(XICSState *icp, int irq, int num); void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu); +void xics_create_native_icp_node(XICSState *s, void *fdt, + uint32_t base, uint32_t count); + +/* Internal XICS interfaces */ +int 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 icp_irq(ICSState *ics, int server, int nr, uint8_t priority); + +void ics_simple_write_xive(ICSState *ics, int nr, int server, + uint8_t priority, uint8_t saved_priority); + +void ics_simple_set_irq_type(ICSState *ics, int srcno, bool lsi); + +void xics_set_nr_servers(XICSState *icp, uint32_t nr_servers, Error **errp); +ICSState *xics_find_source(XICSState *icp, int irq); +void xics_add_ics(XICSState *xics, ICSState *ics); + +void xics_hmp_info_pic(Monitor *mon, const QDict *qdict); + #endif /* __XICS_H__ */ diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 1ce02b20daf0e..c08d67f9069e8 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -200,6 +200,7 @@ struct BusClass { */ char *(*get_fw_dev_path)(DeviceState *dev); void (*reset)(BusState *bus); + bool (*can_add_device)(BusState *bus, QemuOpts *opts); BusRealize realize; BusUnrealize unrealize; diff --git a/linux-user/main.c b/linux-user/main.c index 5f3ec9747acd7..02cb8ff23ef97 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -1688,6 +1688,7 @@ void cpu_loop(CPUPPCState *env) queue_signal(env, info.si_signo, &info); break; case POWERPC_EXCP_PROGRAM: /* Program exception */ + case POWERPC_EXCP_HV_EMU: /* HV emulation */ /* XXX: check this */ switch (env->error_code & ~0xF) { case POWERPC_EXCP_FP: diff --git a/monitor.c b/monitor.c index d1c193013e365..7f039a1524566 100644 --- a/monitor.c +++ b/monitor.c @@ -90,6 +90,10 @@ #include "hw/s390x/storage-keys.h" #endif +#if defined(TARGET_PPC) +#include "hw/ppc/ppc.h" +#endif + /* * Supported types: * diff --git a/qdev-monitor.c b/qdev-monitor.c index e19617fa8b33c..119eb78500aee 100644 --- a/qdev-monitor.c +++ b/qdev-monitor.c @@ -413,7 +413,7 @@ static inline bool qbus_is_full(BusState *bus) * Return the bus if found, else %NULL. */ static BusState *qbus_find_recursive(BusState *bus, const char *name, - const char *bus_typename) + const char *bus_typename, QemuOpts *opts) { BusChild *kid; BusState *pick, *child, *ret; @@ -427,7 +427,10 @@ static BusState *qbus_find_recursive(BusState *bus, const char *name, } if (match && !qbus_is_full(bus)) { - return bus; /* root matches and isn't full */ + BusClass *bc = BUS_GET_CLASS(bus); + if (!bc->can_add_device || bc->can_add_device(bus, opts)) { + return bus; /* root matches and isn't full */ + } } pick = match ? bus : NULL; @@ -435,7 +438,7 @@ static BusState *qbus_find_recursive(BusState *bus, const char *name, QTAILQ_FOREACH(kid, &bus->children, sibling) { DeviceState *dev = kid->child; QLIST_FOREACH(child, &dev->child_bus, sibling) { - ret = qbus_find_recursive(child, name, bus_typename); + ret = qbus_find_recursive(child, name, bus_typename, opts); if (ret && !qbus_is_full(ret)) { return ret; /* a descendant matches and isn't full */ } @@ -465,7 +468,7 @@ static BusState *qbus_find(const char *path, Error **errp) assert(!path[0]); elem[0] = len = 0; } - bus = qbus_find_recursive(sysbus_get_default(), elem, NULL); + bus = qbus_find_recursive(sysbus_get_default(), elem, NULL, NULL); if (!bus) { error_setg(errp, "Bus '%s' not found", elem); return NULL; @@ -571,7 +574,7 @@ DeviceState *qdev_device_add(QemuOpts *opts, Error **errp) return NULL; } } else if (dc->bus_type != NULL) { - bus = qbus_find_recursive(sysbus_get_default(), NULL, dc->bus_type); + bus = qbus_find_recursive(sysbus_get_default(), NULL, dc->bus_type, opts); if (!bus || qbus_is_full(bus)) { error_setg(errp, "No '%s' bus found for device '%s'", dc->bus_type, driver); diff --git a/qemu-options.hx b/qemu-options.hx index 6106520c56e01..de6f301d23710 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -388,7 +388,7 @@ possible drivers and properties, use @code{-device help} and @code{-device @var{driver},help}. Some drivers are: -@item -device ipmi-bmc-sim,id=@var{id}[,slave_addr=@var{val}] +@item -device ipmi-bmc-sim,id=@var{id}[,slave_addr=@var{val}][,sdrfile=@var{file}][,furareasize=@var{val}][,furdatafile=@var{file}] Add an IPMI BMC. This is a simulation of a hardware management interface processor that normally sits on a system. It provides @@ -400,6 +400,19 @@ This address is the BMC's address on the I2C network of management controllers. If you don't know what this means, it is safe to ignore it. +@table @option +@item bmc=@var{id} +The BMC to connect to, one of ipmi-bmc-sim or ipmi-bmc-extern above. +@item slave_addr=@var{val} +Define slave address to use for the BMC. The default is 0x20. +@item sdrfile=@var{file} +file containing raw Sensor Data Records (SDR) data. The default is none. +@item fruareasize=@var{val} +size of a Field Replaceable Unit (FRU) area. The default is 1024. +@item frudatafile=@var{file} +file containing raw Field Replaceable Unit (FRU) inventory data. The default is none. +@end table + @item -device ipmi-bmc-extern,id=@var{id},chardev=@var{id}[,slave_addr=@var{val}] Add a connection to an external IPMI BMC simulator. Instead of diff --git a/target-ppc/cpu-qom.h b/target-ppc/cpu-qom.h index 7d5e2b36a997c..735981309c5b1 100644 --- a/target-ppc/cpu-qom.h +++ b/target-ppc/cpu-qom.h @@ -68,6 +68,7 @@ typedef struct PowerPCCPUClass { uint32_t flags; int bfd_mach; uint32_t l1_dcache_size, l1_icache_size; + uint32_t threads_per_core; #if defined(TARGET_PPC64) const struct ppc_segment_page_sizes *sps; #endif diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 5282533b38587..13b78654c2b82 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -114,18 +114,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 @@ -205,6 +208,9 @@ enum { POWERPC_EXCP_HYPPRIV = 41, /* Embedded hypervisor priv instruction */ /* Vectors 42 to 63 are reserved */ /* Exceptions defined in the PowerPC server specification */ + /* Server doorbell variants */ +#define POWERPC_EXCP_SDOOR POWERPC_EXCP_GDOORI +#define POWERPC_EXCP_SDOOR_HV POWERPC_EXCP_DOORI POWERPC_EXCP_RESET = 64, /* System reset exception */ POWERPC_EXCP_DSEG = 65, /* Data segment exception */ POWERPC_EXCP_ISEG = 66, /* Instruction segment exception */ @@ -247,8 +253,12 @@ enum { /* VSX Unavailable (Power ISA 2.06 and later) */ POWERPC_EXCP_VSXU = 94, /* VSX Unavailable */ POWERPC_EXCP_FU = 95, /* Facility Unavailable */ + /* Additional ISA 2.06 and later server exceptions */ + POWERPC_EXCP_HV_EMU = 96, /* HV emulation assistance */ + POWERPC_EXCP_HV_MAINT = 97, /* HMI */ + POWERPC_EXCP_HV_FU = 98, /* Hypervisor Facility unavailable */ /* EOL */ - POWERPC_EXCP_NB = 96, + POWERPC_EXCP_NB = 99, /* QEMU exceptions: used internally during code translation */ POWERPC_EXCP_STOP = 0x200, /* stop translation */ POWERPC_EXCP_BRANCH = 0x201, /* branch instruction */ @@ -297,6 +307,15 @@ enum { POWERPC_EXCP_TRAP = 0x40, }; +/*****************************************************************************/ +/* PM instructions */ +typedef enum { + PPC_PM_DOZE, + PPC_PM_NAP, + PPC_PM_SLEEP, + PPC_PM_RVWINKLE, +} powerpc_pm_insn_t; + /*****************************************************************************/ /* Input pins model */ typedef enum powerpc_input_t powerpc_input_t; @@ -470,6 +489,8 @@ struct ppc_slb_t { #define MSR_EP 6 /* Exception prefix on 601 */ #define MSR_IR 5 /* Instruction relocate */ #define MSR_DR 4 /* Data relocate */ +#define MSR_IS 5 /* Instruction address space (BookE) */ +#define MSR_DS 4 /* Data address space (BookE) */ #define MSR_PE 3 /* Protection enable on 403 */ #define MSR_PX 2 /* Protection exclusive on 403 x */ #define MSR_PMM 2 /* Performance monitor mark on POWER x */ @@ -481,12 +502,30 @@ 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)) +#define LPCR_P8_PECE0 (1ull << (63-47)) +#define LPCR_P8_PECE1 (1ull << (63-48)) +#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) @@ -521,6 +560,8 @@ struct ppc_slb_t { #define msr_ep ((env->msr >> MSR_EP) & 1) #define msr_ir ((env->msr >> MSR_IR) & 1) #define msr_dr ((env->msr >> MSR_DR) & 1) +#define msr_is ((env->msr >> MSR_IS) & 1) +#define msr_ds ((env->msr >> MSR_DS) & 1) #define msr_pe ((env->msr >> MSR_PE) & 1) #define msr_px ((env->msr >> MSR_PX) & 1) #define msr_pmm ((env->msr >> MSR_PMM) & 1) @@ -1000,7 +1041,7 @@ struct ppc_segment_page_sizes { /*****************************************************************************/ /* The whole PowerPC CPU context */ -#define NB_MMU_MODES 3 +#define NB_MMU_MODES 8 #define PPC_CPU_OPCODES_LEN 0x40 #define PPC_CPU_INDIRECT_OPCODES_LEN 0x20 @@ -1065,6 +1106,8 @@ struct CPUPPCState { /* PowerPC 64 SLB area */ ppc_slb_t slb[MAX_SLB_ENTRIES]; int32_t slb_nr; + /* tcg TLB needs flush (deferred slb inval instruction typically) */ + uint32_t tlb_need_flush; #endif /* segment registers */ hwaddr htab_base; @@ -1155,6 +1198,15 @@ struct CPUPPCState { hwaddr mpic_iack; /* true when the external proxy facility mode is enabled */ bool mpic_proxy; + /* set when the processor has an HV mode, thus HV priv + * instructions and SPRs are diallowed if MSR:HV is 0 + */ + bool has_hv_mode; + /* On P7/P8, set when in PM state, we need to handle resume + * in a special way (such as routing some resume causes to + * 0x100), so flag this here. + */ + bool in_pm_state; #endif /* Those resources are used only during code translation */ @@ -1164,7 +1216,8 @@ struct CPUPPCState { /* Those resources are used only in QEMU core */ target_ulong hflags; /* hflags is a MSR & HFLAGS_MASK */ target_ulong hflags_nmsr; /* specific hflags, not coming from MSR */ - int mmu_idx; /* precomputed MMU index to speed up mem accesses */ + int immu_idx; /* precomputed MMU index to speed up insn access */ + int dmmu_idx; /* precomputed MMU index to speed up data accesses */ /* Power management */ int (*check_pow)(CPUPPCState *env); @@ -1303,13 +1356,10 @@ int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val); #define cpu_list ppc_cpu_list /* MMU modes definitions */ -#define MMU_MODE0_SUFFIX _user -#define MMU_MODE1_SUFFIX _kernel -#define MMU_MODE2_SUFFIX _hypv #define MMU_USER_IDX 0 static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch) { - return env->mmu_idx; + return ifetch ? env->immu_idx : env->dmmu_idx; } #include "exec/cpu-all.h" @@ -1395,7 +1445,10 @@ static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch) #define SPR_MPC_ICTRL (0x09E) #define SPR_MPC_BAR (0x09F) #define SPR_PSPB (0x09F) +#define SPR_DHDES (0x0B1) +#define SPR_DPDES (0x0B0) #define SPR_DAWR (0x0B4) +#define SPR_MPPR (0x0B8) #define SPR_RPR (0x0BA) #define SPR_CIABR (0x0BB) #define SPR_DAWRX (0x0BC) @@ -1958,6 +2011,8 @@ enum { PPC_POPCNTB = 0x0000000000001000ULL, /* string load / store */ PPC_STRING = 0x0000000000002000ULL, + /* real mode cache inhibited load / store */ + PPC_CILDST = 0x0000000000004000ULL, /* Floating-point unit extensions */ /* Optional floating point instructions */ @@ -2072,7 +2127,7 @@ enum { | PPC_MFAPIDI | PPC_TLBIVA | PPC_TLBIVAX \ | PPC_4xx_COMMON | PPC_40x_ICBT | PPC_RFMCI \ | PPC_RFDI | PPC_DCR | PPC_DCRX | PPC_DCRUX \ - | PPC_POPCNTWD) + | PPC_POPCNTWD | PPC_CILDST) /* extended type values */ @@ -2112,6 +2167,8 @@ enum { PPC2_FP_CVT_S64 = 0x0000000000010000ULL, /* Transactional Memory (ISA 2.07, Book II) */ PPC2_TM = 0x0000000000020000ULL, + /* Server PM instructgions (ISA 2.06, Book III) */ + PPC2_PM_ISA206 = 0x0000000000040000ULL, #define PPC_TCG_INSNS2 (PPC2_BOOKE206 | PPC2_VSX | PPC2_PRCNTL | PPC2_DBRX | \ PPC2_ISA205 | PPC2_VSX207 | PPC2_PERM_ISA206 | \ @@ -2119,7 +2176,7 @@ enum { PPC2_FP_CVT_ISA206 | PPC2_FP_TST_ISA206 | \ PPC2_BCTAR_ISA207 | PPC2_LSQ_ISA207 | \ PPC2_ALTIVEC_207 | PPC2_ISA207S | PPC2_DFP | \ - PPC2_FP_CVT_S64 | PPC2_TM) + PPC2_FP_CVT_S64 | PPC2_TM | PPC2_PM_ISA206) }; /*****************************************************************************/ @@ -2249,6 +2306,8 @@ enum { PPC_INTERRUPT_CDOORBELL, /* Critical doorbell interrupt */ PPC_INTERRUPT_DOORBELL, /* Doorbell interrupt */ PPC_INTERRUPT_PERFM, /* Performance monitor interrupt */ + PPC_INTERRUPT_HMI, /* Hypervisor Maintainance interrupt */ + PPC_INTERRUPT_HDOORBELL, /* Hypervisor Doorbell interrupt */ }; /* Processor Compatibility mask (PCR) */ diff --git a/target-ppc/excp_helper.c b/target-ppc/excp_helper.c index ca4ffe8ad660b..1f43b56c39562 100644 --- a/target-ppc/excp_helper.c +++ b/target-ppc/excp_helper.c @@ -76,18 +76,8 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; target_ulong msr, new_msr, vector; - int srr0, srr1, asrr0, asrr1; - int lpes0, lpes1, lev, ail; - - if (0) { - /* XXX: find a suitable condition to enable the hypervisor mode */ - lpes0 = (env->spr[SPR_LPCR] >> 1) & 1; - lpes1 = (env->spr[SPR_LPCR] >> 2) & 1; - } else { - /* Those values ensure we won't enter the hypervisor mode */ - lpes0 = 0; - lpes1 = 1; - } + int srr0, srr1, asrr0, asrr1, lev, ail; + bool lpes0; qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx " => %08x (%02x)\n", env->nip, excp, env->error_code); @@ -99,8 +89,10 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) msr = env->msr & ~0x783f0000ULL; } - /* new interrupt handler msr */ - new_msr = env->msr & ((target_ulong)1 << MSR_ME); + /* new interrupt handler msr preserves existing HV and ME unless + * explicitly overriden + */ + new_msr = env->msr & (((target_ulong)1 << MSR_ME) | MSR_HVB); /* target registers */ srr0 = SPR_SRR0; @@ -108,7 +100,51 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) asrr0 = -1; asrr1 = -1; + /* check for special resume at 0x100 from doze/nap/sleep/winkle on P7/P8 */ + if (env->in_pm_state) { + env->in_pm_state = false; + + /* Pretend to be returning from doze always as we don't lose state */ + msr |= (0x1ull << (63 - 47)); + + /* Non-machine check are routed to 0x100 with a wakeup cause + * encoded in SRR1 + */ + if (excp != POWERPC_EXCP_MCHECK) { + switch(excp) { + case POWERPC_EXCP_RESET: + msr |= 0x4ull << (63-45); + break; + case POWERPC_EXCP_EXTERNAL: + msr |= 0x8ull << (63-45); + break; + case POWERPC_EXCP_DECR: + msr |= 0x6ull << (63-45); + break; + case POWERPC_EXCP_SDOOR: + msr |= 0x5ull << (63-45); + break; + case POWERPC_EXCP_SDOOR_HV: + msr |= 0x3ull << (63-45); + break; + case POWERPC_EXCP_HV_MAINT: + msr |= 0xaull << (63-45); + break; + default: + cpu_abort(cs, "Unsupported exception %d in Power Save mode\n", + excp); + } + excp = POWERPC_EXCP_RESET; + } + } + /* Exception targetting modifiers + * + * LPES0 is supported on POWER7/8 + * LPES1 is not supported (old iSeries mode) + * + * On anything else, we behave as if LPES0 is 1 + * (externals don't alter MSR:HV) * * AIL is initialized here but can be cleared by * selected exceptions @@ -116,6 +152,7 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) #if defined(TARGET_PPC64) if (excp_model == POWERPC_EXCP_POWER7 || excp_model == POWERPC_EXCP_POWER8) { + lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0); if (excp_model == POWERPC_EXCP_POWER8) { ail = (env->spr[SPR_LPCR] & LPCR_AIL) >> LPCR_AIL_SHIFT; } else { @@ -124,9 +161,23 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) } else #endif /* defined(TARGET_PPC64) */ { + lpes0 = true; ail = 0; } + /* Hypervisor emulation assistance interrupt only exists on server + * arch 2.05 server or later. We also don't want to generate it if + * we don't have HVB in msr_mask (PAPR mode). + */ + if (excp == POWERPC_EXCP_HV_EMU +#if defined(TARGET_PPC64) + && !((env->mmu_model & POWERPC_MMU_64) && (env->msr_mask & MSR_HVB)) +#endif /* defined(TARGET_PPC64) */ + + ) { + excp = POWERPC_EXCP_PROGRAM; + } + switch (excp) { case POWERPC_EXCP_NONE: /* Should never happen */ @@ -161,10 +212,7 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) cs->halted = 1; cs->interrupt_request |= CPU_INTERRUPT_EXITTB; } - if (0) { - /* XXX: find a suitable condition to enable the hypervisor mode */ - new_msr |= (target_ulong)MSR_HVB; - } + new_msr |= (target_ulong)MSR_HVB; ail = 0; /* machine check exceptions don't have ME set */ @@ -190,23 +238,20 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) case POWERPC_EXCP_DSI: /* Data storage exception */ LOG_EXCP("DSI exception: DSISR=" TARGET_FMT_lx" DAR=" TARGET_FMT_lx "\n", env->spr[SPR_DSISR], env->spr[SPR_DAR]); - if (lpes1 == 0) { - new_msr |= (target_ulong)MSR_HVB; - } goto store_next; case POWERPC_EXCP_ISI: /* Instruction storage exception */ LOG_EXCP("ISI exception: msr=" TARGET_FMT_lx ", nip=" TARGET_FMT_lx "\n", msr, env->nip); - if (lpes1 == 0) { - new_msr |= (target_ulong)MSR_HVB; - } msr |= env->error_code; goto store_next; case POWERPC_EXCP_EXTERNAL: /* External input */ cs = CPU(cpu); - if (lpes0 == 1) { + if (!lpes0) { new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); + srr0 = SPR_HSRR0; + srr1 = SPR_HSRR1; } if (env->mpic_proxy) { /* IACK the IRQ on delivery */ @@ -214,9 +259,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) } goto store_next; case POWERPC_EXCP_ALIGN: /* Alignment exception */ - if (lpes1 == 0) { - new_msr |= (target_ulong)MSR_HVB; - } /* XXX: this is false */ /* Get rS/rD and rA from faulting opcode */ env->spr[SPR_DSISR] |= (cpu_ldl_code(env, (env->nip - 4)) @@ -231,9 +273,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) env->error_code = 0; return; } - if (lpes1 == 0) { - new_msr |= (target_ulong)MSR_HVB; - } msr |= 0x00100000; if (msr_fe0 == msr_fe1) { goto store_next; @@ -242,23 +281,14 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) break; case POWERPC_EXCP_INVAL: LOG_EXCP("Invalid instruction at " TARGET_FMT_lx "\n", env->nip); - if (lpes1 == 0) { - new_msr |= (target_ulong)MSR_HVB; - } msr |= 0x00080000; env->spr[SPR_BOOKE_ESR] = ESR_PIL; break; case POWERPC_EXCP_PRIV: - if (lpes1 == 0) { - new_msr |= (target_ulong)MSR_HVB; - } msr |= 0x00040000; env->spr[SPR_BOOKE_ESR] = ESR_PPR; break; case POWERPC_EXCP_TRAP: - if (lpes1 == 0) { - new_msr |= (target_ulong)MSR_HVB; - } msr |= 0x00020000; env->spr[SPR_BOOKE_ESR] = ESR_PTR; break; @@ -269,28 +299,30 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) break; } goto store_current; + case POWERPC_EXCP_HV_EMU: + srr0 = SPR_HSRR0; + srr1 = SPR_HSRR1; + new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); + goto store_current; case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ - if (lpes1 == 0) { - new_msr |= (target_ulong)MSR_HVB; - } goto store_current; case POWERPC_EXCP_SYSCALL: /* System call exception */ dump_syscall(env); lev = env->error_code; + + /* "PAPR mode" built-in hypercall emulation */ if ((lev == 1) && cpu_ppc_hypercall) { cpu_ppc_hypercall(cpu); return; } - if (lev == 1 || (lpes0 == 0 && lpes1 == 0)) { + if (lev == 1) { new_msr |= (target_ulong)MSR_HVB; } goto store_next; case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */ goto store_current; case POWERPC_EXCP_DECR: /* Decrementer exception */ - if (lpes1 == 0) { - new_msr |= (target_ulong)MSR_HVB; - } goto store_next; case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */ /* FIT on 4xx */ @@ -360,21 +392,13 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) new_msr &= ~((target_ulong)1 << MSR_ME); } - if (0) { - /* XXX: find a suitable condition to enable the hypervisor mode */ - new_msr |= (target_ulong)MSR_HVB; - } + new_msr |= (target_ulong)MSR_HVB; ail = 0; goto store_next; case POWERPC_EXCP_DSEG: /* Data segment exception */ - if (lpes1 == 0) { - new_msr |= (target_ulong)MSR_HVB; - } + new_msr |= (target_ulong)MSR_HVB; goto store_next; case POWERPC_EXCP_ISEG: /* Instruction segment exception */ - if (lpes1 == 0) { - new_msr |= (target_ulong)MSR_HVB; - } goto store_next; case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */ srr0 = SPR_HSRR0; @@ -383,9 +407,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) new_msr |= env->msr & ((target_ulong)1 << MSR_RI); goto store_next; case POWERPC_EXCP_TRACE: /* Trace exception */ - if (lpes1 == 0) { - new_msr |= (target_ulong)MSR_HVB; - } goto store_next; case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */ srr0 = SPR_HSRR0; @@ -412,19 +433,10 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) new_msr |= env->msr & ((target_ulong)1 << MSR_RI); goto store_next; case POWERPC_EXCP_VPU: /* Vector unavailable exception */ - if (lpes1 == 0) { - new_msr |= (target_ulong)MSR_HVB; - } goto store_current; case POWERPC_EXCP_VSXU: /* VSX unavailable exception */ - if (lpes1 == 0) { - new_msr |= (target_ulong)MSR_HVB; - } goto store_current; case POWERPC_EXCP_FU: /* Facility unavailable exception */ - if (lpes1 == 0) { - new_msr |= (target_ulong)MSR_HVB; - } goto store_current; case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */ LOG_EXCP("PIT exception\n"); @@ -443,9 +455,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) "is not implemented yet !\n"); goto store_next; case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */ - if (lpes1 == 0) { /* XXX: check this */ - new_msr |= (target_ulong)MSR_HVB; - } switch (excp_model) { case POWERPC_EXCP_602: case POWERPC_EXCP_603: @@ -462,9 +471,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) } break; case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ - if (lpes1 == 0) { /* XXX: check this */ - new_msr |= (target_ulong)MSR_HVB; - } switch (excp_model) { case POWERPC_EXCP_602: case POWERPC_EXCP_603: @@ -481,9 +487,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) } break; case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ - if (lpes1 == 0) { /* XXX: check this */ - new_msr |= (target_ulong)MSR_HVB; - } switch (excp_model) { case POWERPC_EXCP_602: case POWERPC_EXCP_603: @@ -589,9 +592,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) "is not implemented yet !\n"); goto store_next; case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */ - if (lpes1 == 0) { - new_msr |= (target_ulong)MSR_HVB; - } /* XXX: TODO */ cpu_abort(cs, "Performance counter exception is not implemented yet !\n"); @@ -635,6 +635,12 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) } /* Save MSR */ env->spr[srr1] = msr; + + /* Sanity check */ + if (!(env->msr_mask & MSR_HVB) && (srr0 == SPR_HSRR0)) { + cpu_abort(cs, "Trying to deliver HV exception %d with no HV support\n", excp); + } + /* If any alternate SRR register are defined, duplicate saved values */ if (asrr0 != -1) { env->spr[asrr0] = env->spr[srr0]; @@ -643,17 +649,20 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) env->spr[asrr1] = env->spr[srr1]; } - if (env->spr[SPR_LPCR] & LPCR_AIL) { - new_msr |= (1 << MSR_IR) | (1 << MSR_DR); - } else if (msr & ((1 << MSR_IR) | (1 << MSR_DR))) { - /* If we disactivated any translation, flush TLBs */ - tlb_flush(cs, 1); - } - + /* Sort out endianness of interrupt, this differs depending on the + * CPU, the HV mode, etc... + */ #ifdef TARGET_PPC64 - if (excp_model == POWERPC_EXCP_POWER7 || - excp_model == POWERPC_EXCP_POWER8) { - if (env->spr[SPR_LPCR] & LPCR_ILE) { + if (excp_model == POWERPC_EXCP_POWER7) { + if (!(new_msr & MSR_HVB) && (env->spr[SPR_LPCR] & LPCR_ILE)) { + new_msr |= (target_ulong)1 << MSR_LE; + } + } else if (excp_model == POWERPC_EXCP_POWER8) { + if (new_msr & MSR_HVB) { + if (env->spr[SPR_HID0] & HID0_HILE) { + new_msr |= (target_ulong)1 << MSR_LE; + } + } else if (env->spr[SPR_LPCR] & LPCR_ILE) { new_msr |= (target_ulong)1 << MSR_LE; } } else if (msr_ile) { @@ -676,7 +685,8 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) /* AIL only works if there is no HV transition and we are running with * translations enabled */ - if (!((msr >> MSR_IR) & 1) || !((msr >> MSR_DR) & 1)) { + if (!((msr >> MSR_IR) & 1) || !((msr >> MSR_DR) & 1) || + ((new_msr & MSR_HVB) && !(msr & MSR_HVB))) { ail = 0; } /* Handle AIL */ @@ -711,8 +721,12 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) } } #endif - /* XXX: we don't use hreg_store_msr here as already have treated - * any special case that could occur. Just store MSR and update hflags + /* We don't use hreg_store_msr here as already have treated + * any special case that could occur. Just store MSR and update hflags + * + * Note: We *MUST* not use hreg_store_msr() as-is anyway because it + * will prevent setting of the HV bit which some exceptions might need + * to do. */ env->msr = new_msr & env->msr_mask; hreg_compute_hflags(env); @@ -721,13 +735,10 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) cs->exception_index = POWERPC_EXCP_NONE; env->error_code = 0; - if ((env->mmu_model == POWERPC_MMU_BOOKE) || - (env->mmu_model == POWERPC_MMU_BOOKE206)) { - /* XXX: The BookE changes address space when switching modes, - we should probably implement that as different MMU indexes, - but for the moment we do it the slow way and flush all. */ - tlb_flush(cs, 1); - } + /* Any interrupt is context synchronizing, check if TCG TLB + * needs a delayed flush on ppc64 + */ + check_tlb_flush(env); } void ppc_cpu_do_interrupt(CPUState *cs) @@ -741,7 +752,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); @@ -749,6 +759,7 @@ static void ppc_hw_interrupt(CPUPPCState *env) __func__, env, env->pending_interrupts, cs->interrupt_request, (int)msr_me, (int)msr_ee); #endif + /* External reset */ if (env->pending_interrupts & (1 << PPC_INTERRUPT_RESET)) { env->pending_interrupts &= ~(1 << PPC_INTERRUPT_RESET); @@ -769,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)) { @@ -827,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); @@ -923,25 +929,46 @@ void helper_store_msr(CPUPPCState *env, target_ulong val) } } -static inline void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr, - target_ulong msrm, int keep_msrh) +#if defined(TARGET_PPC64) +void helper_pminsn(CPUPPCState *env, powerpc_pm_insn_t insn) +{ + CPUState *cs; + + cs = CPU(ppc_env_get_cpu(env)); + 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 + * whack EE in. It will be cleared by the 0x100 at wakeup + * anyway. It will still be observable by the guest in SRR1 + * but this doesn't seem to be a problem. + */ + env->msr |= (1ull << MSR_EE); + helper_raise_exception(env, EXCP_HLT); +} +#endif /* defined(TARGET_PPC64) */ + +static inline void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr) { CPUState *cs = CPU(ppc_env_get_cpu(env)); + /* MSR:POW cannot be set by any form of rfi */ + msr &= ~(1ULL << MSR_POW); + #if defined(TARGET_PPC64) - if (msr_is_64bit(env, msr)) { - nip = (uint64_t)nip; - msr &= (uint64_t)msrm; - } else { + /* Switching to 32-bit ? Crop the nip */ + if (!msr_is_64bit(env, msr)) { nip = (uint32_t)nip; - msr = (uint32_t)(msr & msrm); - if (keep_msrh) { - msr |= env->msr & ~((uint64_t)0xFFFFFFFF); - } } #else nip = (uint32_t)nip; - msr &= (uint32_t)msrm; #endif /* XXX: beware: this is false if VLE is supported */ env->nip = nip & ~((target_ulong)0x00000003); @@ -953,30 +980,31 @@ static inline void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr, * as rfi is always the last insn of a TB */ cs->interrupt_request |= CPU_INTERRUPT_EXITTB; + + /* Context synchronizing: check if TCG TLB needs flush */ + check_tlb_flush(env); } void helper_rfi(CPUPPCState *env) { - if (env->excp_model == POWERPC_EXCP_BOOKE) { - do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1], - ~((target_ulong)0), 0); - } else { - do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1], - ~((target_ulong)0x783F0000), 1); - } + do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1] & 0xfffffffful); } +#define MSR_BOOK3S_MASK #if defined(TARGET_PPC64) void helper_rfid(CPUPPCState *env) { - do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1], - ~((target_ulong)0x783F0000), 0); + /* The architeture defines a number of rules for which bits + * can change but in practice, we handle this in hreg_store_msr() + * which will be called by do_rfi(), so there is no need to filter + * here + */ + do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1]); } void helper_hrfid(CPUPPCState *env) { - do_rfi(env, env->spr[SPR_HSRR0], env->spr[SPR_HSRR1], - ~((target_ulong)0x783F0000), 0); + do_rfi(env, env->spr[SPR_HSRR0], env->spr[SPR_HSRR1]); } #endif @@ -984,28 +1012,24 @@ void helper_hrfid(CPUPPCState *env) /* Embedded PowerPC specific helpers */ void helper_40x_rfci(CPUPPCState *env) { - do_rfi(env, env->spr[SPR_40x_SRR2], env->spr[SPR_40x_SRR3], - ~((target_ulong)0xFFFF0000), 0); + do_rfi(env, env->spr[SPR_40x_SRR2], env->spr[SPR_40x_SRR3]); } void helper_rfci(CPUPPCState *env) { - do_rfi(env, env->spr[SPR_BOOKE_CSRR0], env->spr[SPR_BOOKE_CSRR1], - ~((target_ulong)0), 0); + do_rfi(env, env->spr[SPR_BOOKE_CSRR0], env->spr[SPR_BOOKE_CSRR1]); } void helper_rfdi(CPUPPCState *env) { /* FIXME: choose CSRR1 or DSRR1 based on cpu type */ - do_rfi(env, env->spr[SPR_BOOKE_DSRR0], env->spr[SPR_BOOKE_DSRR1], - ~((target_ulong)0), 0); + do_rfi(env, env->spr[SPR_BOOKE_DSRR0], env->spr[SPR_BOOKE_DSRR1]); } void helper_rfmci(CPUPPCState *env) { /* FIXME: choose CSRR1 or MCSRR1 based on cpu type */ - do_rfi(env, env->spr[SPR_BOOKE_MCSRR0], env->spr[SPR_BOOKE_MCSRR1], - ~((target_ulong)0), 0); + do_rfi(env, env->spr[SPR_BOOKE_MCSRR0], env->spr[SPR_BOOKE_MCSRR1]); } #endif @@ -1043,7 +1067,7 @@ void helper_td(CPUPPCState *env, target_ulong arg1, target_ulong arg2, void helper_rfsvc(CPUPPCState *env) { - do_rfi(env, env->lr, env->ctr, 0x0000FFFF, 0); + do_rfi(env, env->lr, env->ctr & 0x0000FFFF); } /* Embedded.Processor Control */ diff --git a/target-ppc/helper.h b/target-ppc/helper.h index e5a8f7b9b5399..bad6edd0234d1 100644 --- a/target-ppc/helper.h +++ b/target-ppc/helper.h @@ -13,9 +13,12 @@ DEF_HELPER_1(rfci, void, env) DEF_HELPER_1(rfdi, void, env) DEF_HELPER_1(rfmci, void, env) #if defined(TARGET_PPC64) +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 DEF_HELPER_3(lmw, void, env, tl, i32) @@ -549,6 +552,7 @@ DEF_HELPER_FLAGS_2(tlbiva, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_3(store_slb, TCG_CALL_NO_RWG, void, env, tl, tl) DEF_HELPER_2(load_slb_esid, tl, env, tl) DEF_HELPER_2(load_slb_vsid, tl, env, tl) +DEF_HELPER_2(find_slb_esid, tl, env, tl) DEF_HELPER_FLAGS_1(slbia, TCG_CALL_NO_RWG, void, env) DEF_HELPER_FLAGS_2(slbie, TCG_CALL_NO_RWG, void, env, tl) #endif @@ -596,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) diff --git a/target-ppc/helper_regs.h b/target-ppc/helper_regs.h index 271fddf17f0aa..09bc450374a51 100644 --- a/target-ppc/helper_regs.h +++ b/target-ppc/helper_regs.h @@ -41,11 +41,50 @@ static inline void hreg_swap_gpr_tgpr(CPUPPCState *env) static inline void hreg_compute_mem_idx(CPUPPCState *env) { - /* Precompute MMU index */ - if (msr_pr == 0 && msr_hv != 0) { - env->mmu_idx = 2; + /* This is our encoding for server processors + * + * 0 = Guest User space virtual mode + * 1 = Guest Kernel space virtual mode + * 2 = Guest Kernel space real mode + * 3 = HV User space virtual mode + * 4 = HV Kernel space virtual mode + * 5 = HV Kernel space real mode + * + * The combination PR=1 IR&DR=0 is invalid, we will treat + * it as IR=DR=1 + * + * For BookE, we need 8 MMU modes as follow: + * + * 0 = AS 0 HV User space + * 1 = AS 0 HV Kernel space + * 2 = AS 1 HV User space + * 3 = AS 1 HV Kernel space + * 4 = AS 0 Guest User space + * 5 = AS 0 Guest Kernel space + * 6 = AS 1 Guest User space + * 7 = AS 1 Guest Kernel space + */ + if (env->mmu_model & POWERPC_MMU_BOOKE) { + env->immu_idx = env->dmmu_idx = msr_pr ? 0 : 1; + env->immu_idx += msr_is ? 2 : 0; + env->dmmu_idx += msr_ds ? 2 : 0; + env->immu_idx += msr_gs ? 4 : 0; + env->dmmu_idx += msr_gs ? 4 : 0; } else { - env->mmu_idx = 1 - msr_pr; + /* First calucalte a base value independent of HV */ + if (msr_pr != 0) { + /* User space, ignore IR and DR */ + env->immu_idx = env->dmmu_idx = 0; + } else { + /* Kernel, setup a base I/D value */ + env->immu_idx = msr_ir ? 1 : 2; + env->dmmu_idx = msr_dr ? 1 : 2; + } + /* Then offset it for HV */ + if (msr_hv) { + env->immu_idx += 3; + env->dmmu_idx += 3; + } } } @@ -75,16 +114,17 @@ static inline int hreg_store_msr(CPUPPCState *env, target_ulong value, excp = 0; value &= env->msr_mask; #if !defined(CONFIG_USER_ONLY) - if (!alter_hv) { - /* mtmsr cannot alter the hypervisor state */ + /* Neither mtmsr nor guest state can alter HV */ + if (!alter_hv || !(env->msr & MSR_HVB)) { value &= ~MSR_HVB; value |= env->msr & MSR_HVB; } if (((value >> MSR_IR) & 1) != msr_ir || ((value >> MSR_DR) & 1) != msr_dr) { - /* Flush all tlb when changing translation mode */ - tlb_flush(cs, 1); - excp = POWERPC_EXCP_NONE; + cs->interrupt_request |= CPU_INTERRUPT_EXITTB; + } + if ((env->mmu_model & POWERPC_MMU_BOOKE) && + ((value >> MSR_GS) & 1) != msr_gs) { cs->interrupt_request |= CPU_INTERRUPT_EXITTB; } if (unlikely((env->flags & POWERPC_FLAG_TGPR) && @@ -96,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); @@ -111,4 +155,17 @@ static inline int hreg_store_msr(CPUPPCState *env, target_ulong value, return excp; } +#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) +static inline void check_tlb_flush(CPUPPCState *env) +{ + CPUState *cs = CPU(ppc_env_get_cpu(env)); + if (env->tlb_need_flush) { + env->tlb_need_flush = 0; + tlb_flush(cs, 1); + } +} +#else +static inline void check_tlb_flush(CPUPPCState *env) { } +#endif + #endif /* !defined(__HELPER_REGS_H__) */ diff --git a/target-ppc/machine.c b/target-ppc/machine.c index 46684fb933722..cca2a57e54afa 100644 --- a/target-ppc/machine.c +++ b/target-ppc/machine.c @@ -92,9 +92,12 @@ static int cpu_load_old(QEMUFile *f, void *opaque, int version_id) qemu_get_betls(f, &env->nip); qemu_get_betls(f, &env->hflags); qemu_get_betls(f, &env->hflags_nmsr); - qemu_get_sbe32s(f, &env->mmu_idx); + qemu_get_sbe32(f); /* Discard unused mmu_idx */ qemu_get_sbe32(f); /* Discard unused power_mode */ + /* Recompute mmu indices */ + hreg_compute_mem_idx(env); + return 0; } diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index 39abb2fd39c49..4b4ad2c9d5fc5 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -36,8 +36,8 @@ struct mmu_ctx_hash32 { hwaddr raddr; /* Real address */ - int prot; /* Protection bits */ - int key; /* Access key */ + int prot; /* Protection bits */ + int key; /* Access key */ }; static int ppc_hash32_pp_prot(int key, int pp, int nx) diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index 72c4ab5d751c6..99ac6084f3382 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -98,10 +98,8 @@ void dump_slb(FILE *f, fprintf_function cpu_fprintf, PowerPCCPU *cpu) void helper_slbia(CPUPPCState *env) { - PowerPCCPU *cpu = ppc_env_get_cpu(env); - int n, do_invalidate; + int n; - do_invalidate = 0; /* XXX: Warning: slbia never invalidates the first segment */ for (n = 1; n < env->slb_nr; n++) { ppc_slb_t *slb = &env->slb[n]; @@ -112,12 +110,9 @@ void helper_slbia(CPUPPCState *env) * and we still don't have a tlb_flush_mask(env, n, mask) * in QEMU, we just invalidate all TLBs */ - do_invalidate = 1; + env->tlb_need_flush = true; } } - if (do_invalidate) { - tlb_flush(CPU(cpu), 1); - } } void helper_slbie(CPUPPCState *env, target_ulong addr) @@ -137,7 +132,7 @@ void helper_slbie(CPUPPCState *env, target_ulong addr) * and we still don't have a tlb_flush_mask(env, n, mask) * in QEMU, we just invalidate all TLBs */ - tlb_flush(CPU(cpu), 1); + env->tlb_need_flush = true; } } @@ -223,6 +218,20 @@ static int ppc_load_slb_vsid(PowerPCCPU *cpu, target_ulong rb, return 0; } +static int ppc_find_slb_esid(CPUPPCState *env, target_ulong rb, + target_ulong *rt) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + ppc_slb_t *slb = slb_lookup(cpu, rb); + + if (!slb) { + return -1; + } + + *rt = slb->vsid; + return 0; +} + void helper_store_slb(CPUPPCState *env, target_ulong rb, target_ulong rs) { PowerPCCPU *cpu = ppc_env_get_cpu(env); @@ -257,6 +266,17 @@ target_ulong helper_load_slb_vsid(CPUPPCState *env, target_ulong rb) return rt; } +target_ulong helper_find_slb_esid(CPUPPCState *env, target_ulong rb) +{ + target_ulong rt = 0; + + if (ppc_find_slb_esid(env, rb, &rt) < 0) { + helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_INVAL); + } + return rt; +} + /* * 64-bit hash table MMU handling */ @@ -426,9 +446,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; @@ -448,6 +490,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); @@ -501,7 +550,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 */ @@ -511,7 +561,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; @@ -589,27 +640,135 @@ unsigned ppc_hash64_hpte_page_shift_noslb(PowerPCCPU *cpu, return 0; } +static void ppc_hash64_set_isi(CPUState *cs, CPUPPCState *env, uint64_t error_code) +{ + bool vpm; + + if (msr_ir) { + vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM1); + } else { + vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM0); + } + if (vpm && !msr_hv) { + cs->exception_index = POWERPC_EXCP_HISI; + } else { + cs->exception_index = POWERPC_EXCP_ISI; + } + env->error_code = error_code; +} + +static void ppc_hash64_set_dsi(CPUState *cs, CPUPPCState *env, uint64_t dar, uint64_t dsisr) +{ + bool vpm; + + if (msr_dr) { + vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM1); + } else { + vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM0); + } + if (vpm && !msr_hv) { + cs->exception_index = POWERPC_EXCP_HDSI; + env->spr[SPR_HDAR] = dar; + env->spr[SPR_HDSISR] = dsisr; + } else { + cs->exception_index = POWERPC_EXCP_DSI; + env->spr[SPR_DAR] = dar; + env->spr[SPR_DSISR] = dsisr; + } + env->error_code = 0; +} + +static int64_t ppc_hash64_get_rmls(CPUPPCState *env) +{ + uint64_t lpcr = env->spr[SPR_LPCR]; + + /* + * This is the full 4 bits encoding of POWER8. Previous + * CPUs only support a subset of these but the filtering + * is done when writing LPCR + */ + switch((lpcr & LPCR_RMLS) >> LPCR_RMLS_SHIFT) { + case 0x8: /* 32MB */ + return 0x2000000ull; + case 0x3: /* 64MB */ + return 0x4000000ull; + case 0x7: /* 128MB */ + return 0x8000000ull; + case 0x4: /* 256MB */ + return 0x10000000ull; + case 0x2: /* 1GB */ + return 0x40000000ull; + case 0x1: /* 16GB */ + return 0x400000000ull; + default: + /* What to do here ??? */ + return 0; + } +} + int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, int rwx, int mmu_idx) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; - ppc_slb_t *slb; + ppc_slb_t *slb_ptr; + ppc_slb_t slb; unsigned apshift; hwaddr pte_offset; ppc_hash_pte64_t pte; int pp_prot, amr_prot, prot; - uint64_t new_pte1; + uint64_t new_pte1, dsisr; const int need_prot[] = {PAGE_READ, PAGE_WRITE, PAGE_EXEC}; hwaddr raddr; assert((rwx == 0) || (rwx == 1) || (rwx == 2)); + /* Note on LPCR usage: 970 uses HID4, but our special variant + * of store_spr copies relevant fields into env->spr[SPR_LPCR]. + * Similarily we filter unimplemented bits when storing into + * LPCR depending on the MMU version. This code can thus just + * use the LPCR "as-is". + */ + /* 1. Handle real mode accesses */ if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) { - /* Translation is off */ - /* In real mode the top 4 effective address bits are ignored */ + /* Translation is supposedly "off" */ + /* In real mode the top 4 effective address bits are (mostly) ignored */ raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL; + + /* In HV mode, add HRMOR if top EA bit is clear */ + if (msr_hv) { + if (!(eaddr >> 63)) { + raddr |= env->spr[SPR_HRMOR]; + } + } else { + /* Otherwise, check VPM for RMA vs VRMA */ + if (env->spr[SPR_LPCR] & LPCR_VPM0) { + uint32_t vrmasd; + /* VRMA, we make up an SLB entry */ + slb.vsid = SLB_VSID_VRMA; + vrmasd = (env->spr[SPR_LPCR] & LPCR_VRMASD) >> LPCR_VRMASD_SHIFT; + slb.vsid |= (vrmasd << 4) & (SLB_VSID_L | SLB_VSID_LP); + slb.esid = SLB_ESID_V; + goto skip_slb; + } + /* RMA. Check bounds in RMLS */ + if (raddr < ppc_hash64_get_rmls(env)) { + raddr |= env->spr[SPR_RMOR]; + } else { + /* The access failed, generate the approriate interrupt */ + if (rwx == 2) { + ppc_hash64_set_isi(cs, env, 0x08000000); + } else { + dsisr = 0x08000000; + if (rwx == 1) { + dsisr |= 0x02000000; + } + ppc_hash64_set_dsi(cs, env, eaddr, dsisr); + } + return 1; + } + } tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx, TARGET_PAGE_SIZE); @@ -617,9 +776,8 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, } /* 2. Translation is on, so look up the SLB */ - slb = slb_lookup(cpu, eaddr); - - if (!slb) { + slb_ptr = slb_lookup(cpu, eaddr); + if (!slb_ptr) { if (rwx == 2) { cs->exception_index = POWERPC_EXCP_ISEG; env->error_code = 0; @@ -631,28 +789,38 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, return 1; } + /* We grab a local copy because we can modify it (or get a + * pre-cooked one from the VRMA code + */ + slb = *slb_ptr; + + /* 2.5 Clamp L||LP in ISL mode */ + if (env->spr[SPR_LPCR] & LPCR_ISL) { + slb.vsid &= ~SLB_VSID_LLP_MASK; + } + /* 3. Check for segment level no-execute violation */ - if ((rwx == 2) && (slb->vsid & SLB_VSID_N)) { - cs->exception_index = POWERPC_EXCP_ISI; - env->error_code = 0x10000000; + if ((rwx == 2) && (slb.vsid & SLB_VSID_N)) { + ppc_hash64_set_isi(cs, env, 0x10000000); return 1; } + /* We go straight here for VRMA translations as none of the + * above applies in that case + */ + skip_slb: + /* 4. Locate the PTE in the hash table */ - pte_offset = ppc_hash64_htab_lookup(cpu, slb, eaddr, &pte); + pte_offset = ppc_hash64_htab_lookup(cpu, &slb, eaddr, &pte); if (pte_offset == -1) { + dsisr = 0x40000000; if (rwx == 2) { - cs->exception_index = POWERPC_EXCP_ISI; - env->error_code = 0x40000000; + ppc_hash64_set_isi(cs, env, dsisr); } else { - cs->exception_index = POWERPC_EXCP_DSI; - env->error_code = 0; - env->spr[SPR_DAR] = eaddr; if (rwx == 1) { - env->spr[SPR_DSISR] = 0x42000000; - } else { - env->spr[SPR_DSISR] = 0x40000000; + dsisr |= 0x02000000; } + ppc_hash64_set_dsi(cs, env, eaddr, dsisr); } return 1; } @@ -660,7 +828,7 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, "found PTE at offset %08" HWADDR_PRIx "\n", pte_offset); /* Validate page size encoding */ - apshift = hpte_page_shift(slb->sps, pte.pte0, pte.pte1); + apshift = hpte_page_shift(slb.sps, pte.pte0, pte.pte1); if (!apshift) { error_report("Bad page size encoding in HPTE 0x%"PRIx64" - 0x%"PRIx64 " @ 0x%"HWADDR_PRIx, pte.pte0, pte.pte1, pte_offset); @@ -673,7 +841,7 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, /* 5. Check access permissions */ - pp_prot = ppc_hash64_pte_prot(cpu, slb, pte); + pp_prot = ppc_hash64_pte_prot(cpu, &slb, pte); amr_prot = ppc_hash64_amr_prot(cpu, pte); prot = pp_prot & amr_prot; @@ -681,14 +849,9 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, /* Access right violation */ qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); if (rwx == 2) { - cs->exception_index = POWERPC_EXCP_ISI; - env->error_code = 0x08000000; + ppc_hash64_set_isi(cs, env, 0x08000000); } else { - target_ulong dsisr = 0; - - cs->exception_index = POWERPC_EXCP_DSI; - env->error_code = 0; - env->spr[SPR_DAR] = eaddr; + dsisr = 0; if (need_prot[rwx] & ~pp_prot) { dsisr |= 0x08000000; } @@ -698,7 +861,7 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, if (need_prot[rwx] & ~amr_prot) { dsisr |= 0x00200000; } - env->spr[SPR_DSISR] = dsisr; + ppc_hash64_set_dsi(cs, env, eaddr, dsisr); } return 1; } @@ -734,27 +897,51 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, hwaddr ppc_hash64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong addr) { CPUPPCState *env = &cpu->env; - ppc_slb_t *slb; - hwaddr pte_offset; + ppc_slb_t slb; + ppc_slb_t *slb_ptr; + hwaddr pte_offset, raddr; ppc_hash_pte64_t pte; unsigned apshift; + /* Handle real mode */ if (msr_dr == 0) { - /* In real mode the top 4 effective address bits are ignored */ - return addr & 0x0FFFFFFFFFFFFFFFULL; - } + raddr = addr & 0x0FFFFFFFFFFFFFFFULL; - slb = slb_lookup(cpu, addr); - if (!slb) { + /* In HV mode, add HRMOR if top EA bit is clear */ + if (msr_hv & !(addr >> 63)) { + return raddr | env->spr[SPR_HRMOR]; + } + + /* Otherwise, check VPM for RMA vs VRMA */ + if (env->spr[SPR_LPCR] & LPCR_VPM0) { + uint32_t vrmasd; + + /* VRMA, we make up an SLB entry */ + slb.vsid = SLB_VSID_VRMA; + vrmasd = (env->spr[SPR_LPCR] & LPCR_VRMASD) >> LPCR_VRMASD_SHIFT; + slb.vsid |= (vrmasd << 4) & (SLB_VSID_L | SLB_VSID_LP); + slb.esid = SLB_ESID_V; + goto skip_slb; + } + /* RMA. Check bounds in RMLS */ + if (raddr < ppc_hash64_get_rmls(env)) { + return raddr | env->spr[SPR_RMOR]; + } return -1; } - pte_offset = ppc_hash64_htab_lookup(cpu, slb, addr, &pte); + slb_ptr = slb_lookup(cpu, addr); + if (!slb_ptr) { + return -1; + } + slb = *slb_ptr; + skip_slb: + pte_offset = ppc_hash64_htab_lookup(cpu, &slb, addr, &pte); if (pte_offset == -1) { return -1; } - apshift = hpte_page_shift(slb->sps, pte.pte0, pte.pte1); + apshift = hpte_page_shift(slb.sps, pte.pte0, pte.pte1); if (!apshift) { return -1; } @@ -796,3 +983,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; +} diff --git a/target-ppc/mmu-hash64.h b/target-ppc/mmu-hash64.h index 9bf8b9b2679ae..17255651ce7d0 100644 --- a/target-ppc/mmu-hash64.h +++ b/target-ppc/mmu-hash64.h @@ -37,6 +37,7 @@ unsigned ppc_hash64_hpte_page_shift_noslb(PowerPCCPU *cpu, #define SLB_VSID_B_256M 0x0000000000000000ULL #define SLB_VSID_B_1T 0x4000000000000000ULL #define SLB_VSID_VSID 0x3FFFFFFFFFFFF000ULL +#define SLB_VSID_VRMA (0x0001FFFFFF000000ULL | SLB_VSID_B_1T) #define SLB_VSID_PTEM (SLB_VSID_B | SLB_VSID_VSID) #define SLB_VSID_KS 0x0000000000000800ULL #define SLB_VSID_KP 0x0000000000000400ULL diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c index ff217941b5a77..8b46f493adfda 100644 --- a/target-ppc/mmu_helper.c +++ b/target-ppc/mmu_helper.c @@ -26,6 +26,7 @@ #include "mmu-hash32.h" #include "exec/cpu_ldst.h" #include "exec/log.h" +#include "helper_regs.h" //#define DEBUG_MMU //#define DEBUG_BATS @@ -59,11 +60,11 @@ typedef struct mmu_ctx_t mmu_ctx_t; struct mmu_ctx_t { hwaddr raddr; /* Real address */ hwaddr eaddr; /* Effective address */ - int prot; /* Protection bits */ + int prot; /* Protection bits */ hwaddr hash[2]; /* Pagetable hash values */ - target_ulong ptem; /* Virtual segment ID | API */ - int key; /* Access key */ - int nx; /* Non-execute area */ + target_ulong ptem; /* Virtual segment ID | API */ + int key; /* Access key */ + int nx; /* Non-execute area */ }; /* Common routines used by software and hardware TLBs emulation */ @@ -1923,6 +1924,7 @@ void ppc_tlb_invalidate_all(CPUPPCState *env) case POWERPC_MMU_2_06a: case POWERPC_MMU_2_07: case POWERPC_MMU_2_07a: + env->tlb_need_flush = 0; #endif /* defined(TARGET_PPC64) */ tlb_flush(CPU(cpu), 1); break; @@ -1985,7 +1987,7 @@ void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr) * and we still don't have a tlb_flush_mask(env, n, mask) in QEMU, * we just invalidate all TLBs */ - tlb_flush(CPU(cpu), 1); + env->tlb_need_flush = 1; break; #endif /* defined(TARGET_PPC64) */ default: @@ -2874,6 +2876,11 @@ void helper_booke206_tlbflush(CPUPPCState *env, target_ulong type) } +void helper_check_tlb_flush(CPUPPCState *env) +{ + check_tlb_flush(env); +} + /*****************************************************************************/ /* try to fill the TLB and return an exception if error. If retaddr is diff --git a/target-ppc/timebase_helper.c b/target-ppc/timebase_helper.c index 3b340d70d1934..aede3e64c85be 100644 --- a/target-ppc/timebase_helper.c +++ b/target-ppc/timebase_helper.c @@ -101,6 +101,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); diff --git a/target-ppc/translate.c b/target-ppc/translate.c index b3860ecdea9cd..20b0bd5fdb419 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -191,21 +191,20 @@ struct DisasContext { uint32_t opcode; uint32_t exception; /* Routine used to access memory */ - bool pr, hv; + bool pr, hv, dr, le_mode; int mem_idx; int access_type; /* Translation flags */ - int le_mode; TCGMemOp default_tcg_memop_mask; #if defined(TARGET_PPC64) - int sf_mode; - int has_cfar; -#endif - int fpu_enabled; - int altivec_enabled; - int vsx_enabled; - int spe_enabled; - int tm_enabled; + bool sf_mode; + bool has_cfar; +#endif + bool fpu_enabled; + bool altivec_enabled; + bool vsx_enabled; + bool spe_enabled; + bool tm_enabled; ppc_spr_t *spr_cb; /* Needed to check rights for mfspr/mtspr */ int singlestep_enabled; uint64_t insns_flags; @@ -282,7 +281,8 @@ void gen_update_current_nip(void *opaque) tcg_gen_movi_tl(cpu_nip, ctx->nip); } -static inline void gen_exception_err(DisasContext *ctx, uint32_t excp, uint32_t error) +static void __attribute__((noinline)) +gen_exception_err(DisasContext *ctx, uint32_t excp, uint32_t error) { TCGv_i32 t0, t1; if (ctx->exception == POWERPC_EXCP_NONE) { @@ -296,7 +296,8 @@ static inline void gen_exception_err(DisasContext *ctx, uint32_t excp, uint32_t ctx->exception = (excp); } -static inline void gen_exception(DisasContext *ctx, uint32_t excp) +static void __attribute__((noinline)) +gen_exception(DisasContext *ctx, uint32_t excp) { TCGv_i32 t0; if (ctx->exception == POWERPC_EXCP_NONE) { @@ -308,7 +309,8 @@ static inline void gen_exception(DisasContext *ctx, uint32_t excp) ctx->exception = (excp); } -static inline void gen_debug_exception(DisasContext *ctx) +static void __attribute__((noinline)) +gen_debug_exception(DisasContext *ctx) { TCGv_i32 t0; @@ -323,7 +325,19 @@ static inline void gen_debug_exception(DisasContext *ctx) static inline void gen_inval_exception(DisasContext *ctx, uint32_t error) { - gen_exception_err(ctx, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_INVAL | error); + /* Will be converted to program check if needed */ + gen_exception_err(ctx, POWERPC_EXCP_HV_EMU, POWERPC_EXCP_INVAL | error); +} + +static inline void gen_priv_exception(DisasContext *ctx, uint32_t error) +{ + gen_exception_err(ctx, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_PRIV | error); +} + +static inline void gen_hvpriv_exception(DisasContext *ctx, uint32_t error) +{ + /* Will be converted to program check if needed */ + gen_exception_err(ctx, POWERPC_EXCP_HV_EMU, POWERPC_EXCP_PRIV | error); } /* Stop translation */ @@ -364,6 +378,22 @@ typedef struct opcode_t { const char *oname; } opcode_t; +/* Helpers for priv. check */ +#define GEN_PRIV do { gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } while(0) + +#if defined(CONFIG_USER_ONLY) +#define CHK_HV GEN_PRIV +#define CHK_SV GEN_PRIV +#define CHK_HVRM GEN_PRIV +#else +#define CHK_HV do { if (unlikely(ctx->pr || !ctx->hv)) GEN_PRIV; } while(0) +#define CHK_SV do { if (unlikely(ctx->pr)) GEN_PRIV; } while(0) +#define CHK_HVRM do { if (unlikely(ctx->pr || !ctx->hv || ctx->dr)) GEN_PRIV; } while(0) +#endif + +#define CHK_NONE + + /*****************************************************************************/ /*** Instruction decoding ***/ #define EXTRACT_HELPER(name, shift, nb) \ @@ -1398,6 +1428,19 @@ GEN_LOGICAL2(nand, tcg_gen_nand_tl, 0x0E, PPC_INTEGER); /* nor & nor. */ GEN_LOGICAL2(nor, tcg_gen_nor_tl, 0x03, PPC_INTEGER); +#if defined(TARGET_PPC64) +static void gen_pause(DisasContext *ctx) +{ + TCGv_i32 t0 = tcg_const_i32(0); + tcg_gen_st_i32(t0, cpu_env, + -offsetof(PowerPCCPU, env) + offsetof(CPUState, halted)); + tcg_temp_free_i32(t0); + + /* Stop translation, this gives other CPUs a chance to run */ + gen_exception_err(ctx, EXCP_HLT, 1); +} +#endif /* defined(TARGET_PPC64) */ + /* or & or. */ static void gen_or(DisasContext *ctx) { @@ -1453,7 +1496,7 @@ static void gen_or(DisasContext *ctx) } break; case 7: - if (ctx->hv) { + if (ctx->hv && !ctx->pr) { /* Set process priority to very high */ prio = 7; } @@ -1470,6 +1513,10 @@ 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 + */ + gen_pause(ctx); } #endif } @@ -1495,8 +1542,6 @@ static void gen_ori(DisasContext *ctx) target_ulong uimm = UIMM(ctx->opcode); if (rS(ctx->opcode) == rA(ctx->opcode) && uimm == 0) { - /* NOP */ - /* XXX: should handle special NOPs for POWER series */ return; } tcg_gen_ori_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], uimm); @@ -2877,18 +2922,23 @@ static void glue(gen_, name##ux)(DisasContext *ctx) tcg_temp_free(EA); \ } -#define GEN_LDX_E(name, ldop, opc2, opc3, type, type2) \ +#define GEN_LDX_E(name, ldop, opc2, opc3, type, type2, chk) \ static void glue(gen_, name##x)(DisasContext *ctx) \ { \ TCGv EA; \ + chk; \ gen_set_access_type(ctx, ACCESS_INT); \ EA = tcg_temp_new(); \ gen_addr_reg_index(ctx, EA); \ gen_qemu_##ldop(ctx, cpu_gpr[rD(ctx->opcode)], EA); \ tcg_temp_free(EA); \ } + #define GEN_LDX(name, ldop, opc2, opc3, type) \ - GEN_LDX_E(name, ldop, opc2, opc3, type, PPC_NONE) + GEN_LDX_E(name, ldop, opc2, opc3, type, PPC_NONE, CHK_NONE) + +#define GEN_LDX_HVRM(name, ldop, opc2, opc3, type) \ + GEN_LDX_E(name, ldop, opc2, opc3, type, PPC_NONE, CHK_HVRM) #define GEN_LDS(name, ldop, op, type) \ GEN_LD(name, ldop, op | 0x20, type); \ @@ -2914,6 +2964,12 @@ GEN_LDUX(ld, ld64, 0x15, 0x01, PPC_64B); /* ldx */ GEN_LDX(ld, ld64, 0x15, 0x00, PPC_64B); +/* CI load/store variants */ +GEN_LDX_HVRM(ldcix, ld64, 0x15, 0x1b, PPC_CILDST) +GEN_LDX_HVRM(lwzcix, ld32u, 0x15, 0x15, PPC_CILDST) +GEN_LDX_HVRM(lhzcix, ld16u, 0x15, 0x19, PPC_CILDST) +GEN_LDX_HVRM(lbzcix, ld8u, 0x15, 0x1a, PPC_CILDST) + static void gen_ld(DisasContext *ctx) { TCGv EA; @@ -2950,7 +3006,7 @@ static void gen_lq(DisasContext *ctx) bool le_is_supported = (ctx->insns_flags2 & PPC2_LSQ_ISA207) != 0; if (!legal_in_user_mode && ctx->pr) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -3032,10 +3088,11 @@ static void glue(gen_, name##ux)(DisasContext *ctx) tcg_temp_free(EA); \ } -#define GEN_STX_E(name, stop, opc2, opc3, type, type2) \ +#define GEN_STX_E(name, stop, opc2, opc3, type, type2, chk) \ static void glue(gen_, name##x)(DisasContext *ctx) \ { \ TCGv EA; \ + chk; \ gen_set_access_type(ctx, ACCESS_INT); \ EA = tcg_temp_new(); \ gen_addr_reg_index(ctx, EA); \ @@ -3043,7 +3100,10 @@ static void glue(gen_, name##x)(DisasContext *ctx) \ tcg_temp_free(EA); \ } #define GEN_STX(name, stop, opc2, opc3, type) \ - GEN_STX_E(name, stop, opc2, opc3, type, PPC_NONE) + GEN_STX_E(name, stop, opc2, opc3, type, PPC_NONE, CHK_NONE) + +#define GEN_STX_HVRM(name, stop, opc2, opc3, type) \ + GEN_STX_E(name, stop, opc2, opc3, type, PPC_NONE, CHK_HVRM) #define GEN_STS(name, stop, op, type) \ GEN_ST(name, stop, op | 0x20, type); \ @@ -3060,6 +3120,10 @@ GEN_STS(stw, st32, 0x04, PPC_INTEGER); #if defined(TARGET_PPC64) GEN_STUX(std, st64, 0x15, 0x05, PPC_64B); GEN_STX(std, st64, 0x15, 0x04, PPC_64B); +GEN_STX_HVRM(stdcix, st64, 0x15, 0x1f, PPC_CILDST) +GEN_STX_HVRM(stwcix, st32, 0x15, 0x1c, PPC_CILDST) +GEN_STX_HVRM(sthcix, st16, 0x15, 0x1d, PPC_CILDST) +GEN_STX_HVRM(stbcix, st8, 0x15, 0x1e, PPC_CILDST) static void gen_std(DisasContext *ctx) { @@ -3073,7 +3137,7 @@ static void gen_std(DisasContext *ctx) bool le_is_supported = (ctx->insns_flags2 & PPC2_LSQ_ISA207) != 0; if (!legal_in_user_mode && ctx->pr) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -3145,7 +3209,7 @@ static inline void gen_qemu_ld64ur(DisasContext *ctx, TCGv arg1, TCGv arg2) TCGMemOp op = MO_Q | (ctx->default_tcg_memop_mask ^ MO_BSWAP); tcg_gen_qemu_ld_i64(arg1, arg2, ctx->mem_idx, op); } -GEN_LDX_E(ldbr, ld64ur, 0x14, 0x10, PPC_NONE, PPC2_DBRX); +GEN_LDX_E(ldbr, ld64ur, 0x14, 0x10, PPC_NONE, PPC2_DBRX, CHK_NONE); #endif /* TARGET_PPC64 */ /* sthbrx */ @@ -3171,7 +3235,7 @@ static inline void gen_qemu_st64r(DisasContext *ctx, TCGv arg1, TCGv arg2) TCGMemOp op = MO_Q | (ctx->default_tcg_memop_mask ^ MO_BSWAP); tcg_gen_qemu_st_i64(arg1, arg2, ctx->mem_idx, op); } -GEN_STX_E(stdbr, st64r, 0x14, 0x14, PPC_NONE, PPC2_DBRX); +GEN_STX_E(stdbr, st64r, 0x14, 0x14, PPC_NONE, PPC2_DBRX, CHK_NONE); #endif /* TARGET_PPC64 */ /*** Integer load and store multiple ***/ @@ -3312,9 +3376,32 @@ static void gen_eieio(DisasContext *ctx) { } +#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) +static inline void gen_check_tlb_flush(DisasContext *ctx) +{ + TCGv_i32 t = tcg_temp_new_i32(); + TCGLabel *l = gen_new_label(); + + tcg_gen_ld_i32(t, cpu_env, offsetof(CPUPPCState, tlb_need_flush)); + tcg_gen_brcondi_i32(TCG_COND_EQ, t, 0, l); + gen_helper_check_tlb_flush(cpu_env); + gen_set_label(l); + tcg_temp_free_i32(t); +} +#else +static inline void gen_check_tlb_flush(DisasContext *ctx) { } +#endif + /* isync */ static void gen_isync(DisasContext *ctx) { + /* + * We need to check for a pending TLB flush. This can only happen in + * kernel mode however so check MSR_PR + */ + if (!ctx->pr) { + gen_check_tlb_flush(ctx); + } gen_stop_exception(ctx); } @@ -3471,6 +3558,15 @@ STCX(stqcx_, 16); /* sync */ static void gen_sync(DisasContext *ctx) { + uint32_t l = (ctx->opcode >> 21) & 3; + + /* + * For l == 2, it's a ptesync, We need to check for a pending TLB flush. + * This can only happen in kernel mode however so check MSR_PR as well. + */ + if (l == 2 && !ctx->pr) { + gen_check_tlb_flush(ctx); + } } /* wait */ @@ -3484,6 +3580,78 @@ static void gen_wait(DisasContext *ctx) gen_exception_err(ctx, EXCP_HLT, 1); } +#if defined(TARGET_PPC64) +static void gen_doze(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + TCGv_i32 t; + + CHK_HV; + t = tcg_const_i32(PPC_PM_DOZE); + gen_helper_pminsn(cpu_env, t); + tcg_temp_free_i32(t); + gen_stop_exception(ctx); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +static void gen_nap(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + TCGv_i32 t; + + CHK_HV; + t = tcg_const_i32(PPC_PM_NAP); + gen_helper_pminsn(cpu_env, t); + tcg_temp_free_i32(t); + gen_stop_exception(ctx); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +static void gen_sleep(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + TCGv_i32 t; + + CHK_HV; + t = tcg_const_i32(PPC_PM_SLEEP); + gen_helper_pminsn(cpu_env, t); + tcg_temp_free_i32(t); + gen_stop_exception(ctx); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +static void gen_rvwinkle(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + TCGv_i32 t; + + CHK_HV; + t = tcg_const_i32(PPC_PM_RVWINKLE); + gen_helper_pminsn(cpu_env, t); + tcg_temp_free_i32(t); + gen_stop_exception(ctx); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +static void gen_logmpp(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + CHK_HV; + /* This doesn't do anything in emulation */ +#endif /* defined(CONFIG_USER_ONLY) */ +} +#endif /* #if defined(TARGET_PPC64) */ + /*** Floating-point load ***/ #define GEN_LDF(name, ldop, opc, type) \ static void glue(gen_, name)(DisasContext *ctx) \ @@ -3880,7 +4048,7 @@ static void gen_b(DisasContext *ctx) if (LK(ctx->opcode)) { gen_setlr(ctx, ctx->nip); } - gen_update_cfar(ctx, ctx->nip); + gen_update_cfar(ctx, ctx->nip - 4); gen_goto_tb(ctx, 0, target); } @@ -3945,7 +4113,7 @@ static inline void gen_bcond(DisasContext *ctx, int type) } tcg_temp_free_i32(temp); } - gen_update_cfar(ctx, ctx->nip); + gen_update_cfar(ctx, ctx->nip - 4); if (type == BCOND_IM) { target_ulong li = (target_long)((int16_t)(BD(ctx->opcode))); if (likely(AA(ctx->opcode) == 0)) { @@ -4052,14 +4220,18 @@ static void gen_mcrf(DisasContext *ctx) static void gen_rfi(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - /* Restore CPU state */ - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + /* This instruction doesn't exist anymore on 64-bit server + * processors compliant with arch 2.x + */ + if (ctx->insns_flags & PPC_SEGMENT_64B) { + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); return; } - gen_update_cfar(ctx, ctx->nip); + /* Restore CPU state */ + CHK_SV; + gen_update_cfar(ctx, ctx->nip - 4); gen_helper_rfi(cpu_env); gen_sync_exception(ctx); #endif @@ -4069,14 +4241,11 @@ static void gen_rfi(DisasContext *ctx) static void gen_rfid(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else /* Restore CPU state */ - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } - gen_update_cfar(ctx, ctx->nip); + CHK_SV; + gen_update_cfar(ctx, ctx->nip - 4); gen_helper_rfid(cpu_env); gen_sync_exception(ctx); #endif @@ -4085,13 +4254,10 @@ static void gen_rfid(DisasContext *ctx) static void gen_hrfid(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else /* Restore CPU state */ - if (unlikely(!ctx->hv)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_HV; gen_helper_hrfid(cpu_env); gen_sync_exception(ctx); #endif @@ -4254,15 +4420,8 @@ static void gen_mfcr(DisasContext *ctx) /* mfmsr */ static void gen_mfmsr(DisasContext *ctx) { -#if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); -#else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } + CHK_SV; tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_msr); -#endif } static void spr_noaccess(DisasContext *ctx, int gprn, int sprn) @@ -4299,8 +4458,11 @@ static inline void gen_op_mfspr(DisasContext *ctx) /* This is a hack to avoid warnings when running Linux: * this OS breaks the PowerPC virtualisation model, * allowing userland application to read the PVR + * + * Also avoid spamming if we're obviously running a HV + * that does full guest virtualization via PR. */ - if (sprn != SPR_PVR) { + if (sprn != SPR_PVR && !ctx->hv) { fprintf(stderr, "Trying to read privileged spr %d (0x%03x) at " TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); if (qemu_log_separate()) { @@ -4308,17 +4470,41 @@ static inline void gen_op_mfspr(DisasContext *ctx) TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); } } - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + gen_priv_exception(ctx, POWERPC_EXCP_PRIV_REG); } } else { - /* Not defined */ - fprintf(stderr, "Trying to read invalid spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); - if (qemu_log_separate()) { - qemu_log("Trying to read invalid spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + /* ISA 2.07 defines these as no-ops */ + if ((ctx->insns_flags2 & PPC2_ISA207S) && + (sprn >= 808 && sprn <= 811)) { + /* This is a nop */ + return; + } + + /* + * Not defined - don't spam if we're in PR and HV mode, + * which would indicate that a HV is running a guest. + */ + if (!ctx->hv || !ctx->pr) { + fprintf(stderr, "Trying to read invalid spr %d (0x%03x) at " + TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + if (qemu_log_separate()) { + qemu_log("Trying to read invalid spr %d (0x%03x) at " + TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + } + } + + /* The behaviour depends on MSR:PR and SPR# bit 0x10, + * it can generate a priv, a hv emu or a no-op + */ + if (sprn & 0x10) { + if (ctx->pr) { + gen_priv_exception(ctx, POWERPC_EXCP_INVAL_SPR); + } + } else { + if (ctx->pr || sprn == 0 || sprn == 4 || sprn == 5 || sprn == 6) { + gen_hvpriv_exception(ctx, POWERPC_EXCP_INVAL_SPR); + } } - gen_inval_exception(ctx, POWERPC_EXCP_INVAL_SPR); } } @@ -4365,18 +4551,14 @@ static void gen_mtcrf(DisasContext *ctx) #if defined(TARGET_PPC64) static void gen_mtmsrd(DisasContext *ctx) { -#if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); -#else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } + CHK_SV; + +#if !defined(CONFIG_USER_ONLY) if (ctx->opcode & 0x00010000) { /* Special form that does not need any synchronisation */ TCGv t0 = tcg_temp_new(); tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], (1 << MSR_RI) | (1 << MSR_EE)); - tcg_gen_andi_tl(cpu_msr, cpu_msr, ~((1 << MSR_RI) | (1 << MSR_EE))); + tcg_gen_andi_tl(cpu_msr, cpu_msr, ~(target_ulong)((1 << MSR_RI) | (1 << MSR_EE))); tcg_gen_or_tl(cpu_msr, cpu_msr, t0); tcg_temp_free(t0); } else { @@ -4390,24 +4572,20 @@ static void gen_mtmsrd(DisasContext *ctx) /* Note that mtmsr is not always defined as context-synchronizing */ gen_stop_exception(ctx); } -#endif +#endif /* !defined(CONFIG_USER_ONLY) */ } -#endif +#endif /* defined(TARGET_PPC64) */ static void gen_mtmsr(DisasContext *ctx) { -#if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); -#else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } - if (ctx->opcode & 0x00010000) { + CHK_SV; + +#if !defined(CONFIG_USER_ONLY) + if (ctx->opcode & 0x00010000) { /* Special form that does not need any synchronisation */ TCGv t0 = tcg_temp_new(); tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], (1 << MSR_RI) | (1 << MSR_EE)); - tcg_gen_andi_tl(cpu_msr, cpu_msr, ~((1 << MSR_RI) | (1 << MSR_EE))); + tcg_gen_andi_tl(cpu_msr, cpu_msr, ~(target_ulong)((1 << MSR_RI) | (1 << MSR_EE))); tcg_gen_or_tl(cpu_msr, cpu_msr, t0); tcg_temp_free(t0); } else { @@ -4453,24 +4631,53 @@ static void gen_mtspr(DisasContext *ctx) if (likely(write_cb != SPR_NOACCESS)) { (*write_cb)(ctx, sprn, rS(ctx->opcode)); } else { - /* Privilege exception */ - fprintf(stderr, "Trying to write privileged spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + /* + * Privilege exception - don't spam if we're in PR and HV mode, + * which would indicate that a HV is running a guest. + */ + if (!ctx->hv) { + fprintf(stderr, "Trying to write privileged spr %d (0x%03x) at " + TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + if (qemu_log_separate()) { + qemu_log("Trying to write privileged spr %d (0x%03x) at " + TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + } + } + gen_priv_exception(ctx, POWERPC_EXCP_PRIV_REG); + } + } else { + /* ISA 2.07 defines these as no-ops */ + if ((ctx->insns_flags2 & PPC2_ISA207S) && + (sprn >= 808 && sprn <= 811)) { + /* This is a nop */ + return; + } + + /* + * Not defined - don't spam if we're in PR and HV mode, + * which would indicate that a HV is running a guest. + */ + if (!ctx->hv || !ctx->pr) { if (qemu_log_separate()) { - qemu_log("Trying to write privileged spr %d (0x%03x) at " + qemu_log("Trying to write invalid spr %d (0x%03x) at " TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); } - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + fprintf(stderr, "Trying to write invalid spr %d (0x%03x) at " + TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); } - } else { - /* Not defined */ - if (qemu_log_separate()) { - qemu_log("Trying to write invalid spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + + /* The behaviour depends on MSR:PR and SPR# bit 0x10, + * it can generate a priv, a hv emu or a no-op + */ + if (sprn & 0x10) { + if (ctx->pr) { + gen_priv_exception(ctx, POWERPC_EXCP_INVAL_SPR); + } + } else { + if (ctx->pr || sprn == 0) { + gen_hvpriv_exception(ctx, POWERPC_EXCP_INVAL_SPR); + } } - fprintf(stderr, "Trying to write invalid spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); - gen_inval_exception(ctx, POWERPC_EXCP_INVAL_SPR); } } @@ -4492,13 +4699,11 @@ static void gen_dcbf(DisasContext *ctx) static void gen_dcbi(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else TCGv EA, val; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + + CHK_SV; EA = tcg_temp_new(); gen_set_access_type(ctx, ACCESS_CACHE); gen_addr_reg_index(ctx, EA); @@ -4508,7 +4713,7 @@ static void gen_dcbi(DisasContext *ctx) gen_qemu_st8(ctx, val, EA); tcg_temp_free(val); tcg_temp_free(EA); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* dcdst */ @@ -4629,72 +4834,64 @@ static void gen_dcba(DisasContext *ctx) static void gen_mfsr(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + GEN_PRIV; #else TCGv t0; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } + + CHK_SV; t0 = tcg_const_tl(SR(ctx->opcode)); gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); tcg_temp_free(t0); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* mfsrin */ static void gen_mfsrin(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + GEN_PRIV; #else TCGv t0; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } + + CHK_SV; t0 = tcg_temp_new(); tcg_gen_shri_tl(t0, cpu_gpr[rB(ctx->opcode)], 28); tcg_gen_andi_tl(t0, t0, 0xF); gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); tcg_temp_free(t0); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* mtsr */ static void gen_mtsr(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + GEN_PRIV; #else TCGv t0; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } + + CHK_SV; t0 = tcg_const_tl(SR(ctx->opcode)); gen_helper_store_sr(cpu_env, t0, cpu_gpr[rS(ctx->opcode)]); tcg_temp_free(t0); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* mtsrin */ static void gen_mtsrin(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + GEN_PRIV; #else TCGv t0; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } + CHK_SV; + t0 = tcg_temp_new(); tcg_gen_shri_tl(t0, cpu_gpr[rB(ctx->opcode)], 28); tcg_gen_andi_tl(t0, t0, 0xF); gen_helper_store_sr(cpu_env, t0, cpu_gpr[rD(ctx->opcode)]); tcg_temp_free(t0); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } #if defined(TARGET_PPC64) @@ -4704,115 +4901,125 @@ static void gen_mtsrin(DisasContext *ctx) static void gen_mfsr_64b(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + GEN_PRIV; #else TCGv t0; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } + + CHK_SV; t0 = tcg_const_tl(SR(ctx->opcode)); gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); tcg_temp_free(t0); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* mfsrin */ static void gen_mfsrin_64b(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + GEN_PRIV; #else TCGv t0; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } + + CHK_SV; t0 = tcg_temp_new(); tcg_gen_shri_tl(t0, cpu_gpr[rB(ctx->opcode)], 28); tcg_gen_andi_tl(t0, t0, 0xF); gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); tcg_temp_free(t0); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* mtsr */ static void gen_mtsr_64b(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + GEN_PRIV; #else TCGv t0; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } + + CHK_SV; t0 = tcg_const_tl(SR(ctx->opcode)); gen_helper_store_sr(cpu_env, t0, cpu_gpr[rS(ctx->opcode)]); tcg_temp_free(t0); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* mtsrin */ static void gen_mtsrin_64b(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + GEN_PRIV; #else TCGv t0; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } + + CHK_SV; t0 = tcg_temp_new(); tcg_gen_shri_tl(t0, cpu_gpr[rB(ctx->opcode)], 28); tcg_gen_andi_tl(t0, t0, 0xF); gen_helper_store_sr(cpu_env, t0, cpu_gpr[rS(ctx->opcode)]); tcg_temp_free(t0); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* slbmte */ static void gen_slbmte(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } + CHK_SV; + gen_helper_store_slb(cpu_env, cpu_gpr[rB(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } static void gen_slbmfee(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } + CHK_SV; + gen_helper_load_slb_esid(cpu_gpr[rS(ctx->opcode)], cpu_env, cpu_gpr[rB(ctx->opcode)]); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } static void gen_slbmfev(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } + CHK_SV; + gen_helper_load_slb_vsid(cpu_gpr[rS(ctx->opcode)], cpu_env, cpu_gpr[rB(ctx->opcode)]); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ +} + +static void gen_slbfee_(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + TCGLabel *l1; + TCGLabel *l2; + + CHK_SV; + + gen_helper_find_slb_esid(cpu_gpr[rS(ctx->opcode)], cpu_env, + cpu_gpr[rB(ctx->opcode)]); + l1 = gen_new_label(); + l2 = gen_new_label(); + tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_gpr[rS(ctx->opcode)], -1, l1); + tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 1 << CRF_EQ); + tcg_gen_br(l2); + gen_set_label(l1); + tcg_gen_movi_tl(cpu_gpr[rS(ctx->opcode)], 0); + gen_set_label(l2); +#endif /* defined(CONFIG_USER_ONLY) */ } #endif /* defined(TARGET_PPC64) */ @@ -4823,40 +5030,34 @@ static void gen_slbmfev(DisasContext *ctx) static void gen_tlbia(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_HV; + gen_helper_tlbia(cpu_env); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* tlbiel */ static void gen_tlbiel(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; + gen_helper_tlbie(cpu_env, cpu_gpr[rB(ctx->opcode)]); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* tlbie */ static void gen_tlbie(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_HV; + if (NARROW_MODE(ctx)) { TCGv t0 = tcg_temp_new(); tcg_gen_ext32u_tl(t0, cpu_gpr[rB(ctx->opcode)]); @@ -4865,55 +5066,52 @@ static void gen_tlbie(DisasContext *ctx) } else { gen_helper_tlbie(cpu_env, cpu_gpr[rB(ctx->opcode)]); } -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* tlbsync */ static void gen_tlbsync(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } - /* This has no effect: it should ensure that all previous - * tlbie have completed + CHK_HV; + + /* tlbsync is a nop for server, ptesync handles delayed tlb flush, + * embedded however needs to deal with tlbsync. We don't try to be + * fancy and swallow the overhead of checking for both. */ - gen_stop_exception(ctx); -#endif + gen_check_tlb_flush(ctx); +#endif /* defined(CONFIG_USER_ONLY) */ } #if defined(TARGET_PPC64) + /* slbia */ static void gen_slbia(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; + gen_helper_slbia(cpu_env); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* slbie */ static void gen_slbie(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; + gen_helper_slbie(cpu_env, cpu_gpr[rB(ctx->opcode)]); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } -#endif + +#endif /* defined(TARGET_PPC64) */ /*** External control ***/ /* Optional: */ @@ -5612,14 +5810,11 @@ static void gen_esa(DisasContext *ctx) static void gen_mfrom(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; gen_helper_602_mfrom(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* 602 - 603 - G2 TLB management */ @@ -5628,28 +5823,22 @@ static void gen_mfrom(DisasContext *ctx) static void gen_tlbld_6xx(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; gen_helper_6xx_tlbd(cpu_env, cpu_gpr[rB(ctx->opcode)]); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* tlbli */ static void gen_tlbli_6xx(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; gen_helper_6xx_tlbi(cpu_env, cpu_gpr[rB(ctx->opcode)]); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* 74xx TLB management */ @@ -5658,28 +5847,22 @@ static void gen_tlbli_6xx(DisasContext *ctx) static void gen_tlbld_74xx(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; gen_helper_74xx_tlbd(cpu_env, cpu_gpr[rB(ctx->opcode)]); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* tlbli */ static void gen_tlbli_74xx(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; gen_helper_74xx_tlbi(cpu_env, cpu_gpr[rB(ctx->opcode)]); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* POWER instructions not in PowerPC 601 */ @@ -5693,15 +5876,12 @@ static void gen_clf(DisasContext *ctx) /* cli */ static void gen_cli(DisasContext *ctx) { - /* Cache line invalidate: privileged and treated as no-op */ #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } -#endif + /* Cache line invalidate: privileged and treated as no-op */ + CHK_SV; +#endif /* defined(CONFIG_USER_ONLY) */ } /* dclst */ @@ -5713,15 +5893,13 @@ static void gen_dclst(DisasContext *ctx) static void gen_mfsri(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else int ra = rA(ctx->opcode); int rd = rD(ctx->opcode); TCGv t0; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + + CHK_SV; t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); tcg_gen_shri_tl(t0, t0, 28); @@ -5730,38 +5908,34 @@ static void gen_mfsri(DisasContext *ctx) tcg_temp_free(t0); if (ra != 0 && ra != rd) tcg_gen_mov_tl(cpu_gpr[ra], cpu_gpr[rd]); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } static void gen_rac(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else TCGv t0; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + + CHK_SV; t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); gen_helper_rac(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); tcg_temp_free(t0); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } static void gen_rfsvc(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; + gen_helper_rfsvc(cpu_env); gen_sync_exception(ctx); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* svc is not implemented for now */ @@ -5914,18 +6088,16 @@ static void gen_mfapidi(DisasContext *ctx) static void gen_tlbiva(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else TCGv t0; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + + CHK_SV; t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); gen_helper_tlbiva(cpu_env, cpu_gpr[rB(ctx->opcode)]); tcg_temp_free(t0); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* All 405 MAC instructions are translated here */ @@ -6147,38 +6319,34 @@ GEN_MAC_HANDLER(mullhwu, 0x08, 0x0C); static void gen_mfdcr(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + GEN_PRIV; #else TCGv dcrn; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } + + CHK_SV; /* NIP cannot be restored if the memory exception comes from an helper */ gen_update_nip(ctx, ctx->nip - 4); dcrn = tcg_const_tl(SPR(ctx->opcode)); gen_helper_load_dcr(cpu_gpr[rD(ctx->opcode)], cpu_env, dcrn); tcg_temp_free(dcrn); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* mtdcr */ static void gen_mtdcr(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + GEN_PRIV; #else TCGv dcrn; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } + + CHK_SV; /* NIP cannot be restored if the memory exception comes from an helper */ gen_update_nip(ctx, ctx->nip - 4); dcrn = tcg_const_tl(SPR(ctx->opcode)); gen_helper_store_dcr(cpu_env, dcrn, cpu_gpr[rS(ctx->opcode)]); tcg_temp_free(dcrn); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* mfdcrx */ @@ -6186,18 +6354,15 @@ static void gen_mtdcr(DisasContext *ctx) static void gen_mfdcrx(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } + CHK_SV; /* NIP cannot be restored if the memory exception comes from an helper */ gen_update_nip(ctx, ctx->nip - 4); gen_helper_load_dcr(cpu_gpr[rD(ctx->opcode)], cpu_env, cpu_gpr[rA(ctx->opcode)]); /* Note: Rc update flag set leads to undefined state of Rc0 */ -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* mtdcrx */ @@ -6205,18 +6370,15 @@ static void gen_mfdcrx(DisasContext *ctx) static void gen_mtdcrx(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } + CHK_SV; /* NIP cannot be restored if the memory exception comes from an helper */ gen_update_nip(ctx, ctx->nip - 4); gen_helper_store_dcr(cpu_env, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); /* Note: Rc update flag set leads to undefined state of Rc0 */ -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* mfdcrux (PPC 460) : user-mode access to DCR */ @@ -6242,28 +6404,19 @@ static void gen_mtdcrux(DisasContext *ctx) /* dccci */ static void gen_dccci(DisasContext *ctx) { -#if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); -#else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; /* interpreted as no-op */ -#endif } /* dcread */ static void gen_dcread(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else TCGv EA, val; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + + CHK_SV; gen_set_access_type(ctx, ACCESS_CACHE); EA = tcg_temp_new(); gen_addr_reg_index(ctx, EA); @@ -6272,7 +6425,7 @@ static void gen_dcread(DisasContext *ctx) tcg_temp_free(val); tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], EA); tcg_temp_free(EA); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* icbt */ @@ -6287,60 +6440,40 @@ static void gen_icbt_40x(DisasContext *ctx) /* iccci */ static void gen_iccci(DisasContext *ctx) { -#if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); -#else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; /* interpreted as no-op */ -#endif } /* icread */ static void gen_icread(DisasContext *ctx) { -#if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); -#else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; /* interpreted as no-op */ -#endif } /* rfci (supervisor only) */ static void gen_rfci_40x(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; /* Restore CPU state */ gen_helper_40x_rfci(cpu_env); gen_sync_exception(ctx); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } static void gen_rfci(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; /* Restore CPU state */ gen_helper_rfci(cpu_env); gen_sync_exception(ctx); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* BookE specific */ @@ -6349,32 +6482,26 @@ static void gen_rfci(DisasContext *ctx) static void gen_rfdi(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; /* Restore CPU state */ gen_helper_rfdi(cpu_env); gen_sync_exception(ctx); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* XXX: not implemented on 440 ? */ static void gen_rfmci(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; /* Restore CPU state */ gen_helper_rfmci(cpu_env); gen_sync_exception(ctx); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* TLB management - PowerPC 405 implementation */ @@ -6383,12 +6510,9 @@ static void gen_rfmci(DisasContext *ctx) static void gen_tlbre_40x(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; switch (rB(ctx->opcode)) { case 0: gen_helper_4xx_tlbre_hi(cpu_gpr[rD(ctx->opcode)], cpu_env, @@ -6402,20 +6526,18 @@ static void gen_tlbre_40x(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); break; } -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* tlbsx - tlbsx. */ static void gen_tlbsx_40x(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else TCGv t0; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + + CHK_SV; t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); gen_helper_4xx_tlbsx(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); @@ -6427,19 +6549,17 @@ static void gen_tlbsx_40x(DisasContext *ctx) tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 0x02); gen_set_label(l1); } -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* tlbwe */ static void gen_tlbwe_40x(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; + switch (rB(ctx->opcode)) { case 0: gen_helper_4xx_tlbwe_hi(cpu_env, cpu_gpr[rA(ctx->opcode)], @@ -6453,7 +6573,7 @@ static void gen_tlbwe_40x(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); break; } -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* TLB management - PowerPC 440 implementation */ @@ -6462,12 +6582,10 @@ static void gen_tlbwe_40x(DisasContext *ctx) static void gen_tlbre_440(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; + switch (rB(ctx->opcode)) { case 0: case 1: @@ -6483,20 +6601,18 @@ static void gen_tlbre_440(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); break; } -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* tlbsx - tlbsx. */ static void gen_tlbsx_440(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else TCGv t0; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + + CHK_SV; t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); gen_helper_440_tlbsx(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); @@ -6508,19 +6624,16 @@ static void gen_tlbsx_440(DisasContext *ctx) tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 0x02); gen_set_label(l1); } -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* tlbwe */ static void gen_tlbwe_440(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; switch (rB(ctx->opcode)) { case 0: case 1: @@ -6536,7 +6649,7 @@ static void gen_tlbwe_440(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); break; } -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* TLB management - PowerPC BookE 2.06 implementation */ @@ -6544,30 +6657,23 @@ static void gen_tlbwe_440(DisasContext *ctx) /* tlbre */ static void gen_tlbre_booke206(DisasContext *ctx) { -#if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + #if defined(CONFIG_USER_ONLY) + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } - + CHK_SV; gen_helper_booke206_tlbre(cpu_env); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* tlbsx - tlbsx. */ static void gen_tlbsx_booke206(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else TCGv t0; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; if (rA(ctx->opcode)) { t0 = tcg_temp_new(); tcg_gen_mov_tl(t0, cpu_gpr[rD(ctx->opcode)]); @@ -6578,54 +6684,44 @@ static void gen_tlbsx_booke206(DisasContext *ctx) tcg_gen_add_tl(t0, t0, cpu_gpr[rB(ctx->opcode)]); gen_helper_booke206_tlbsx(cpu_env, t0); tcg_temp_free(t0); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* tlbwe */ static void gen_tlbwe_booke206(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; gen_update_nip(ctx, ctx->nip - 4); gen_helper_booke206_tlbwe(cpu_env); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } static void gen_tlbivax_booke206(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else TCGv t0; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); - gen_helper_booke206_tlbivax(cpu_env, t0); tcg_temp_free(t0); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } static void gen_tlbilx_booke206(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else TCGv t0; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); @@ -6645,7 +6741,7 @@ static void gen_tlbilx_booke206(DisasContext *ctx) } tcg_temp_free(t0); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } @@ -6653,13 +6749,11 @@ static void gen_tlbilx_booke206(DisasContext *ctx) static void gen_wrtee(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else TCGv t0; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + + CHK_SV; t0 = tcg_temp_new(); tcg_gen_andi_tl(t0, cpu_gpr[rD(ctx->opcode)], (1 << MSR_EE)); tcg_gen_andi_tl(cpu_msr, cpu_msr, ~(1 << MSR_EE)); @@ -6669,19 +6763,16 @@ static void gen_wrtee(DisasContext *ctx) * if we just set msr_ee to 1 */ gen_stop_exception(ctx); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* wrteei */ static void gen_wrteei(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; if (ctx->opcode & 0x00008000) { tcg_gen_ori_tl(cpu_msr, cpu_msr, (1 << MSR_EE)); /* Stop translation to have a chance to raise an exception */ @@ -6689,7 +6780,7 @@ static void gen_wrteei(DisasContext *ctx) } else { tcg_gen_andi_tl(cpu_msr, cpu_msr, ~(1 << MSR_EE)); } -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* PowerPC 440 specific instructions */ @@ -6729,29 +6820,21 @@ static void gen_icbt_440(DisasContext *ctx) static void gen_msgclr(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } - + CHK_SV; gen_helper_msgclr(cpu_env, cpu_gpr[rB(ctx->opcode)]); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } static void gen_msgsnd(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } - + CHK_SV; gen_helper_msgsnd(cpu_gpr[rB(ctx->opcode)]); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /*** Altivec vector extension ***/ @@ -9753,7 +9836,7 @@ static void gen_tcheck(DisasContext *ctx) #define GEN_TM_PRIV_NOOP(name) \ static inline void gen_##name(DisasContext *ctx) \ { \ - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); \ + gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC); \ } #else @@ -9761,10 +9844,7 @@ static inline void gen_##name(DisasContext *ctx) \ #define GEN_TM_PRIV_NOOP(name) \ static inline void gen_##name(DisasContext *ctx) \ { \ - if (unlikely(ctx->pr)) { \ - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); \ - return; \ - } \ + CHK_SV; \ if (unlikely(!ctx->tm_enabled)) { \ gen_exception_err(ctx, POWERPC_EXCP_FU, FSCR_IC_TM); \ return; \ @@ -9892,7 +9972,19 @@ GEN_HANDLER(mcrf, 0x13, 0x00, 0xFF, 0x00000001, PPC_INTEGER), GEN_HANDLER(rfi, 0x13, 0x12, 0x01, 0x03FF8001, PPC_FLOW), #if defined(TARGET_PPC64) GEN_HANDLER(rfid, 0x13, 0x12, 0x00, 0x03FF8001, PPC_64B), +GEN_HANDLER_E(doze, 0x13, 0x12, 0x0c, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206), +GEN_HANDLER_E(nap, 0x13, 0x12, 0x0d, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206), +GEN_HANDLER_E(sleep, 0x13, 0x12, 0x0e, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206), +GEN_HANDLER_E(rvwinkle, 0x13, 0x12, 0x0f, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206), GEN_HANDLER(hrfid, 0x13, 0x12, 0x08, 0x03FF8001, PPC_64H), + +/* This should be P8 Book4, not ISA207S, but I don't want to add a bit for that + * one dummy instruction. Note also that there's a discrepancy between the + * P8 Book4 which documents it as using RA while KVM implementation uses RB, + * so for now mark both fields as valid + */ +//GEN_HANDLER_E(logmpp, 0x1f, 0x12, 0x1f, 0x03E0F800, PPC_NONE, PPC2_ISA207S), +GEN_HANDLER_E(logmpp, 0x1f, 0x12, 0x1f, 0x03E00000, PPC_NONE, PPC2_ISA207S), #endif GEN_HANDLER(sc, 0x11, 0xFF, 0xFF, 0x03FFF01D, PPC_FLOW), GEN_HANDLER(tw, 0x1F, 0x04, 0x00, 0x00000001, PPC_FLOW), @@ -9938,10 +10030,13 @@ GEN_HANDLER2(mtsrin_64b, "mtsrin", 0x1F, 0x12, 0x07, 0x001F0001, GEN_HANDLER2(slbmte, "slbmte", 0x1F, 0x12, 0x0C, 0x001F0001, PPC_SEGMENT_64B), GEN_HANDLER2(slbmfee, "slbmfee", 0x1F, 0x13, 0x1C, 0x001F0001, PPC_SEGMENT_64B), GEN_HANDLER2(slbmfev, "slbmfev", 0x1F, 0x13, 0x1A, 0x001F0001, PPC_SEGMENT_64B), +GEN_HANDLER2(slbfee_, "slbfee.", 0x1F, 0x13, 0x1E, 0x001F0000, PPC_SEGMENT_64B), #endif GEN_HANDLER(tlbia, 0x1F, 0x12, 0x0B, 0x03FFFC01, PPC_MEM_TLBIA), -GEN_HANDLER(tlbiel, 0x1F, 0x12, 0x08, 0x03FF0001, PPC_MEM_TLBIE), -GEN_HANDLER(tlbie, 0x1F, 0x12, 0x09, 0x03FF0001, PPC_MEM_TLBIE), +/* XXX Those instructions will need to be handled differently for + * different ISA versions */ +GEN_HANDLER(tlbiel, 0x1F, 0x12, 0x08, 0x001F0001, PPC_MEM_TLBIE), +GEN_HANDLER(tlbie, 0x1F, 0x12, 0x09, 0x001F0001, PPC_MEM_TLBIE), GEN_HANDLER(tlbsync, 0x1F, 0x16, 0x11, 0x03FFF801, PPC_MEM_TLBSYNC), #if defined(TARGET_PPC64) GEN_HANDLER(slbia, 0x1F, 0x12, 0x0F, 0x03FFFC01, PPC_SLBI), @@ -10241,7 +10336,7 @@ GEN_HANDLER(name, opc, 0xFF, 0xFF, 0x00000000, type), GEN_HANDLER(name##u, opc, 0xFF, 0xFF, 0x00000000, type), #define GEN_LDUX(name, ldop, opc2, opc3, type) \ GEN_HANDLER(name##ux, 0x1F, opc2, opc3, 0x00000001, type), -#define GEN_LDX_E(name, ldop, opc2, opc3, type, type2) \ +#define GEN_LDX_E(name, ldop, opc2, opc3, type, type2, chk) \ GEN_HANDLER_E(name##x, 0x1F, opc2, opc3, 0x00000001, type, type2), #define GEN_LDS(name, ldop, op, type) \ GEN_LD(name, ldop, op | 0x20, type) \ @@ -10258,7 +10353,13 @@ GEN_LDUX(lwa, ld32s, 0x15, 0x0B, PPC_64B) GEN_LDX(lwa, ld32s, 0x15, 0x0A, PPC_64B) GEN_LDUX(ld, ld64, 0x15, 0x01, PPC_64B) GEN_LDX(ld, ld64, 0x15, 0x00, PPC_64B) -GEN_LDX_E(ldbr, ld64ur, 0x14, 0x10, PPC_NONE, PPC2_DBRX) +GEN_LDX_E(ldbr, ld64ur, 0x14, 0x10, PPC_NONE, PPC2_DBRX, CHK_NONE) + +/* HV/P7 and later only */ +GEN_LDX_HVRM(ldcix, ld64, 0x15, 0x1b, PPC_CILDST) +GEN_LDX_HVRM(lwzcix, ld32u, 0x15, 0x18, PPC_CILDST) +GEN_LDX_HVRM(lhzcix, ld16u, 0x15, 0x19, PPC_CILDST) +GEN_LDX_HVRM(lbzcix, ld8u, 0x15, 0x1a, PPC_CILDST) #endif GEN_LDX(lhbr, ld16ur, 0x16, 0x18, PPC_INTEGER) GEN_LDX(lwbr, ld32ur, 0x16, 0x10, PPC_INTEGER) @@ -10274,7 +10375,7 @@ GEN_HANDLER(name, opc, 0xFF, 0xFF, 0x00000000, type), GEN_HANDLER(stop##u, opc, 0xFF, 0xFF, 0x00000000, type), #define GEN_STUX(name, stop, opc2, opc3, type) \ GEN_HANDLER(name##ux, 0x1F, opc2, opc3, 0x00000001, type), -#define GEN_STX_E(name, stop, opc2, opc3, type, type2) \ +#define GEN_STX_E(name, stop, opc2, opc3, type, type2, chk) \ GEN_HANDLER_E(name##x, 0x1F, opc2, opc3, 0x00000001, type, type2), #define GEN_STS(name, stop, op, type) \ GEN_ST(name, stop, op | 0x20, type) \ @@ -10288,7 +10389,11 @@ GEN_STS(stw, st32, 0x04, PPC_INTEGER) #if defined(TARGET_PPC64) GEN_STUX(std, st64, 0x15, 0x05, PPC_64B) GEN_STX(std, st64, 0x15, 0x04, PPC_64B) -GEN_STX_E(stdbr, st64r, 0x14, 0x14, PPC_NONE, PPC2_DBRX) +GEN_STX_E(stdbr, st64r, 0x14, 0x14, PPC_NONE, PPC2_DBRX, CHK_NONE) +GEN_STX_HVRM(stdcix, st64, 0x15, 0x1f, PPC_CILDST) +GEN_STX_HVRM(stwcix, st32, 0x15, 0x1c, PPC_CILDST) +GEN_STX_HVRM(sthcix, st16, 0x15, 0x1d, PPC_CILDST) +GEN_STX_HVRM(stbcix, st8, 0x15, 0x1e, PPC_CILDST) #endif GEN_STX(sthbr, st16r, 0x16, 0x1C, PPC_INTEGER) GEN_STX(stwbr, st32r, 0x16, 0x14, PPC_INTEGER) @@ -11247,8 +11352,9 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, env->nip, env->lr, env->ctr, cpu_read_xer(env), cs->cpu_index); cpu_fprintf(f, "MSR " TARGET_FMT_lx " HID0 " TARGET_FMT_lx " HF " - TARGET_FMT_lx " idx %d\n", env->msr, env->spr[SPR_HID0], - env->hflags, env->mmu_idx); + TARGET_FMT_lx " iidx %d didx %d\n", + env->msr, env->spr[SPR_HID0], + env->hflags, env->immu_idx, env->dmmu_idx); #if !defined(NO_TIMER_DUMP) cpu_fprintf(f, "TB %08" PRIu32 " %08" PRIu64 #if !defined(CONFIG_USER_ONLY) @@ -11308,6 +11414,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", @@ -11454,36 +11567,39 @@ void gen_intermediate_code(CPUPPCState *env, struct TranslationBlock *tb) ctx.exception = POWERPC_EXCP_NONE; ctx.spr_cb = env->spr_cb; ctx.pr = msr_pr; - ctx.hv = !msr_pr && msr_hv; - ctx.mem_idx = env->mmu_idx; + ctx.mem_idx = env->dmmu_idx; + ctx.dr = msr_dr; +#if !defined(CONFIG_USER_ONLY) + ctx.hv = msr_hv || !env->has_hv_mode; +#endif ctx.insns_flags = env->insns_flags; ctx.insns_flags2 = env->insns_flags2; ctx.access_type = -1; - ctx.le_mode = env->hflags & (1 << MSR_LE) ? 1 : 0; + ctx.le_mode = !!(env->hflags & (1 << MSR_LE)); ctx.default_tcg_memop_mask = ctx.le_mode ? MO_LE : MO_BE; #if defined(TARGET_PPC64) ctx.sf_mode = msr_is_64bit(env, env->msr); ctx.has_cfar = !!(env->flags & POWERPC_FLAG_CFAR); #endif - ctx.fpu_enabled = msr_fp; + ctx.fpu_enabled = !!msr_fp; if ((env->flags & POWERPC_FLAG_SPE) && msr_spe) - ctx.spe_enabled = msr_spe; + ctx.spe_enabled = !!msr_spe; else - ctx.spe_enabled = 0; + ctx.spe_enabled = false; if ((env->flags & POWERPC_FLAG_VRE) && msr_vr) - ctx.altivec_enabled = msr_vr; + ctx.altivec_enabled = !!msr_vr; else - ctx.altivec_enabled = 0; + ctx.altivec_enabled = false; if ((env->flags & POWERPC_FLAG_VSX) && msr_vsx) { - ctx.vsx_enabled = msr_vsx; + ctx.vsx_enabled = !!msr_vsx; } else { - ctx.vsx_enabled = 0; + ctx.vsx_enabled = false; } #if defined(TARGET_PPC64) if ((env->flags & POWERPC_FLAG_TM) && msr_tm) { - ctx.tm_enabled = msr_tm; + ctx.tm_enabled = !!msr_tm; } else { - ctx.tm_enabled = 0; + ctx.tm_enabled = false; } #endif if ((env->flags & POWERPC_FLAG_SE) && msr_se) diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index f51572552bc2e..7db311be10f34 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -293,6 +293,44 @@ static void spr_read_purr (DisasContext *ctx, int gprn, int sprn) { gen_helper_load_purr(cpu_gpr[gprn], cpu_env); } + +__attribute__ (( unused )) +static void spr_write_purr (DisasContext *ctx, int gprn, int sprn) +{ + // Temporary placeholder +} + +__attribute__ (( unused )) +static void spr_write_vtb (DisasContext *ctx, int gprn, int sprn) +{ + // Temporary placeholder +} + +/* 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 @@ -3187,18 +3225,30 @@ static void init_excp_POWER7 (CPUPPCState *env) env->excp_vectors[POWERPC_EXCP_HDECR] = 0x00000980; env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00; env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00; + env->excp_vectors[POWERPC_EXCP_HDSI] = 0x00000E00; + env->excp_vectors[POWERPC_EXCP_HISI] = 0x00000E20; + env->excp_vectors[POWERPC_EXCP_HV_EMU] = 0x00000E40; + env->excp_vectors[POWERPC_EXCP_HV_MAINT] = 0x00000E60; env->excp_vectors[POWERPC_EXCP_PERFM] = 0x00000F00; env->excp_vectors[POWERPC_EXCP_VPU] = 0x00000F20; env->excp_vectors[POWERPC_EXCP_VSXU] = 0x00000F40; - env->excp_vectors[POWERPC_EXCP_FU] = 0x00000F60; - env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001300; - env->excp_vectors[POWERPC_EXCP_MAINT] = 0x00001600; - env->excp_vectors[POWERPC_EXCP_VPUA] = 0x00001700; - env->excp_vectors[POWERPC_EXCP_THERM] = 0x00001800; /* Hardware reset vector */ env->hreset_vector = 0x0000000000000100ULL; #endif } + +static void init_excp_POWER8 (CPUPPCState *env) +{ + init_excp_POWER7(env); + +#if !defined(CONFIG_USER_ONLY) + env->excp_vectors[POWERPC_EXCP_SDOOR] = 0x00000A00; + env->excp_vectors[POWERPC_EXCP_FU] = 0x00000F60; + env->excp_vectors[POWERPC_EXCP_HV_FU] = 0x00000F80; + env->excp_vectors[POWERPC_EXCP_SDOOR_HV] = 0x00000E80; +#endif +} + #endif /*****************************************************************************/ @@ -7520,16 +7570,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", @@ -7782,21 +7822,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); } @@ -7813,14 +7987,16 @@ static void gen_spr_book3s_purr(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) /* PURR & SPURR: Hack - treat these as aliases for the TB for now */ - spr_register_kvm(env, SPR_PURR, "PURR", - &spr_read_purr, SPR_NOACCESS, - &spr_read_purr, SPR_NOACCESS, - KVM_REG_PPC_PURR, 0x00000000); - spr_register_kvm(env, SPR_SPURR, "SPURR", - &spr_read_purr, SPR_NOACCESS, - &spr_read_purr, SPR_NOACCESS, - KVM_REG_PPC_SPURR, 0x00000000); + spr_register_kvm_hv(env, SPR_PURR, "PURR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_purr, SPR_NOACCESS, + &spr_read_purr, &spr_write_purr, + KVM_REG_PPC_PURR, 0x00000000); + spr_register_kvm_hv(env, SPR_SPURR, "SPURR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_purr, SPR_NOACCESS, + &spr_read_purr, &spr_write_purr, + KVM_REG_PPC_SPURR, 0x00000000); #endif } @@ -7983,10 +8159,11 @@ static void gen_spr_power8_ebb(CPUPPCState *env) /* Virtual Time Base */ static void gen_spr_vtb(CPUPPCState *env) { - spr_register(env, SPR_VTB, "VTB", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_tbl, SPR_NOACCESS, - 0x00000000); + spr_register_hv(env, SPR_VTB, "VTB", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_tbl, SPR_NOACCESS, + &spr_read_tbl, spr_write_vtb, + 0x00000000); } static void gen_spr_power8_fscr(CPUPPCState *env) @@ -8010,6 +8187,33 @@ static void gen_spr_power8_pspb(CPUPPCState *env) KVM_REG_PPC_PSPB, 0); } +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 gen_spr_power8_dbell(CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + spr_register_hv(env, SPR_DPDES, "DPDES", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0); + spr_register_hv(env, SPR_DHDES, "DHDES", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0); +#endif +} + static void gen_spr_power8_ic(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) @@ -8025,6 +8229,11 @@ static void gen_spr_power8_book4(CPUPPCState *env) { /* Add a number of P8 book4 registers */ #if !defined(CONFIG_USER_ONLY) + spr_register_hv(env, SPR_MPPR, "MPPR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0); spr_register_kvm(env, SPR_ACOP, "ACOP", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, @@ -8094,6 +8303,8 @@ 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); + gen_spr_power8_dbell(env); } if (version < BOOK3S_CPU_POWER8) { gen_spr_book3s_dbg(env); @@ -8121,10 +8332,13 @@ static void init_proc_book3s_64(CPUPPCState *env, int version) ppc970_irq_init(env); break; case BOOK3S_CPU_POWER7: - case BOOK3S_CPU_POWER8: init_excp_POWER7(env); ppcPOWER7_irq_init(env); break; + case BOOK3S_CPU_POWER8: + init_excp_POWER8(env); + ppcPOWER7_irq_init(env); + break; default: g_assert_not_reached(); } @@ -8236,6 +8450,7 @@ POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void *data) POWERPC_FLAG_BUS_CLK; pcc->l1_dcache_size = 0x8000; pcc->l1_icache_size = 0x10000; + pcc->threads_per_core = 2; } static void powerpc_get_compat(Object *obj, Visitor *v, const char *name, @@ -8354,10 +8569,45 @@ static bool ppc_pvr_match_power7(PowerPCCPUClass *pcc, uint32_t pvr) return false; } +static bool cpu_has_work_POWER7(CPUState *cs) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + + if (cs->halted) { + if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) { + return false; + } + if ((env->pending_interrupts & (1u << PPC_INTERRUPT_EXT)) && + (env->spr[SPR_LPCR] & LPCR_P7_PECE0)) { + return true; + } + if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DECR)) && + (env->spr[SPR_LPCR] & LPCR_P7_PECE1)) { + return true; + } + if ((env->pending_interrupts & (1u << PPC_INTERRUPT_MCK)) && + (env->spr[SPR_LPCR] & LPCR_P7_PECE2)) { + return true; + } + if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HMI)) && + (env->spr[SPR_LPCR] & LPCR_P7_PECE2)) { + return true; + } + if (env->pending_interrupts & (1u << PPC_INTERRUPT_RESET)) { + return true; + } + return false; + } else { + return msr_ee && (cs->interrupt_request & CPU_INTERRUPT_HARD); + } +} + POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + CPUClass *cc = CPU_CLASS(oc); dc->fw_name = "PowerPC,POWER7"; dc->desc = "POWER7"; @@ -8366,6 +8616,7 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) pcc->pcr_mask = PCR_COMPAT_2_05 | PCR_COMPAT_2_06; pcc->init_proc = init_proc_POWER7; pcc->check_pow = check_pow_nocheck; + cc->has_work = cpu_has_work_POWER7; pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -8375,13 +8626,15 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | PPC_MEM_SYNC | PPC_MEM_EIEIO | PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | - PPC_64B | PPC_ALTIVEC | + PPC_64B | PPC_64H | PPC_ALTIVEC | PPC_SEGMENT_64B | PPC_SLBI | - PPC_POPCNTB | PPC_POPCNTWD; + PPC_POPCNTB | PPC_POPCNTWD | + PPC_CILDST; pcc->insns_flags2 = PPC2_VSX | PPC2_DFP | PPC2_DBRX | PPC2_ISA205 | PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 | PPC2_ATOMIC_ISA206 | PPC2_FP_CVT_ISA206 | - PPC2_FP_TST_ISA206 | PPC2_FP_CVT_S64; + PPC2_FP_TST_ISA206 | PPC2_FP_CVT_S64 | + PPC2_PM_ISA206; pcc->msr_mask = (1ull << MSR_SF) | (1ull << MSR_VR) | (1ull << MSR_VSX) | @@ -8413,6 +8666,7 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) pcc->l1_dcache_size = 0x8000; pcc->l1_icache_size = 0x8000; pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr; + pcc->threads_per_core = 4; } static void init_proc_POWER8(CPUPPCState *env) @@ -8434,10 +8688,53 @@ static bool ppc_pvr_match_power8(PowerPCCPUClass *pcc, uint32_t pvr) return false; } +static bool cpu_has_work_POWER8(CPUState *cs) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + + if (cs->halted) { + if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) { + return false; + } + if ((env->pending_interrupts & (1u << PPC_INTERRUPT_EXT)) && + (env->spr[SPR_LPCR] & LPCR_P8_PECE2)) { + return true; + } + if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DECR)) && + (env->spr[SPR_LPCR] & LPCR_P8_PECE3)) { + return true; + } + if ((env->pending_interrupts & (1u << PPC_INTERRUPT_MCK)) && + (env->spr[SPR_LPCR] & LPCR_P8_PECE4)) { + return true; + } + if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HMI)) && + (env->spr[SPR_LPCR] & LPCR_P8_PECE4)) { + return true; + } + if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DOORBELL)) && + (env->spr[SPR_LPCR] & LPCR_P8_PECE0)) { + return true; + } + if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HDOORBELL)) && + (env->spr[SPR_LPCR] & LPCR_P8_PECE1)) { + return true; + } + if (env->pending_interrupts & (1u << PPC_INTERRUPT_RESET)) { + return true; + } + return false; + } else { + return msr_ee && (cs->interrupt_request & CPU_INTERRUPT_HARD); + } +} + POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + CPUClass *cc = CPU_CLASS(oc); dc->fw_name = "PowerPC,POWER8"; dc->desc = "POWER8"; @@ -8446,6 +8743,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) pcc->pcr_mask = PCR_COMPAT_2_05 | PCR_COMPAT_2_06; pcc->init_proc = init_proc_POWER8; pcc->check_pow = check_pow_nocheck; + cc->has_work = cpu_has_work_POWER8; pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -8455,18 +8753,20 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | PPC_MEM_SYNC | PPC_MEM_EIEIO | PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | - PPC_64B | PPC_64BX | PPC_ALTIVEC | + PPC_64B | PPC_64H | PPC_64BX | PPC_ALTIVEC | PPC_SEGMENT_64B | PPC_SLBI | - PPC_POPCNTB | PPC_POPCNTWD; + PPC_POPCNTB | PPC_POPCNTWD | + PPC_CILDST; pcc->insns_flags2 = PPC2_VSX | PPC2_VSX207 | PPC2_DFP | PPC2_DBRX | PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 | PPC2_ATOMIC_ISA206 | PPC2_FP_CVT_ISA206 | PPC2_FP_TST_ISA206 | PPC2_BCTAR_ISA207 | PPC2_LSQ_ISA207 | PPC2_ALTIVEC_207 | PPC2_ISA205 | PPC2_ISA207S | PPC2_FP_CVT_S64 | - PPC2_TM; + PPC2_TM | PPC2_PM_ISA206; pcc->msr_mask = (1ull << MSR_SF) | - (1ull << MSR_TM) | + (1ull << MSR_SHV) | + (1ull << MSR_TM) | (1ull << MSR_VR) | (1ull << MSR_VSX) | (1ull << MSR_EE) | @@ -8497,6 +8797,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) pcc->l1_dcache_size = 0x8000; pcc->l1_icache_size = 0x8000; pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr; + pcc->threads_per_core = 8; } #if !defined(CONFIG_USER_ONLY) @@ -8504,6 +8805,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) void cpu_ppc_set_papr(PowerPCCPU *cpu) { CPUPPCState *env = &cpu->env; + ppc_spr_t *lpcr = &env->spr_cb[SPR_LPCR]; ppc_spr_t *amor = &env->spr_cb[SPR_AMOR]; /* PAPR always has exception vectors in RAM not ROM. To ensure this, @@ -8513,6 +8815,34 @@ void cpu_ppc_set_papr(PowerPCCPU *cpu) */ env->msr_mask &= ~((1ull << MSR_EP) | MSR_HVB); + /* Set emulated LPCR to not send interrupts to hypervisor. Note that + * under KVM, the actual HW LPCR will be set differently by KVM itself, + * the settings below ensure proper operations with TCG in absence of + * a real hypervisor. + * + * Clearing VPM0 will also cause us to use RMOR in mmu-hash64.c for + * real mode accesses, which thankfully defaults to 0 and isn't + * accessible in guest mode. + */ + lpcr->default_value &= ~(LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV); + lpcr->default_value |= LPCR_LPES0 | LPCR_LPES1; + + /* Set RMLS to the max (ie, 16G) */ + lpcr->default_value &= ~LPCR_RMLS; + lpcr->default_value |= 1ull << LPCR_RMLS_SHIFT; + + /* P7 and P8 has slightly different PECE bits, mostly because P8 adds + * bit 47 and 48 which are reserved on P7. Here we set them all, which + * will work as expected for both implementations + */ + lpcr->default_value |= LPCR_P8_PECE0 | LPCR_P8_PECE1 | LPCR_P8_PECE2 | + LPCR_P8_PECE3 | LPCR_P8_PECE4; + + /* We should be followed by a CPU reset but update the active value + * just in case... + */ + env->spr[SPR_LPCR] = lpcr->default_value; + /* Set a full AMOR so guest can use the AMR as it sees fit */ env->spr[SPR_AMOR] = amor->default_value = 0xffffffffffffffffull; @@ -9225,6 +9555,12 @@ static void ppc_cpu_realizefn(DeviceState *dev, Error **errp) #endif #if !defined(CONFIG_USER_ONLY) + if (pcc->threads_per_core == 0) { + pcc->threads_per_core = 1; + } + if (max_smt > pcc->threads_per_core) { + max_smt = pcc->threads_per_core; + } if (smp_threads > max_smt) { error_setg(errp, "Cannot support more than %d threads on PPC with %s", max_smt, kvm_enabled() ? "KVM" : "TCG"); @@ -9245,7 +9581,7 @@ static void ppc_cpu_realizefn(DeviceState *dev, Error **errp) } #if !defined(CONFIG_USER_ONLY) - cpu->cpu_dt_id = (cs->cpu_index / smp_threads) * max_smt + cpu->cpu_dt_id = (cs->cpu_index / smp_threads) * pcc->threads_per_core + (cs->cpu_index % smp_threads); #endif @@ -9862,10 +10198,7 @@ static void ppc_cpu_reset(CPUState *s) pcc->parent_reset(s); msr = (target_ulong)0; - if (0) { - /* XXX: find a suitable condition to enable the hypervisor mode */ - msr |= (target_ulong)MSR_HVB; - } + msr |= (target_ulong)MSR_HVB; msr |= (target_ulong)0 << MSR_AP; /* TO BE CHECKED */ msr |= (target_ulong)0 << MSR_SA; /* TO BE CHECKED */ msr |= (target_ulong)1 << MSR_EP; @@ -9966,12 +10299,25 @@ static void ppc_cpu_initfn(Object *obj) env->bfd_mach = pcc->bfd_mach; env->check_pow = pcc->check_pow; + /* Mark HV mode as supported if the CPU has an MSR_HV bit + * in the msr_mask. The mask can later be cleared by PAPR + * mode but the hv mode support will remain, thus enforcing + * that we cannot use priv. instructions in guest in PAPR + * mode. For 970 we currently simply don't set HV in msr_mask + * thus simulating an "Apple mode" 970. If we ever want to + * support 970 HV mode, we'll have to add a processor attribute + * of some sort. + */ +#if !defined(CONFIG_USER_ONLY) + env->has_hv_mode = !!(env->msr_mask & MSR_HVB); +#endif + #if defined(TARGET_PPC64) 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, @@ -9983,7 +10329,24 @@ 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; + env->ci_large_pages = env->mmu_model >= POWERPC_MMU_2_06; } #endif /* defined(TARGET_PPC64) */