qemu/hw/intc/xive.c
Cédric Le Goater 5e79b155a8 ppc/xive: introduce the XiveNotifier interface
The XiveNotifier offers a simple interface, between the XiveSource
object and the main interrupt controller of the machine. It will
forward event notifications to the XIVE Interrupt Virtualization
Routing Engine (IVRE).

Signed-off-by: Cédric Le Goater <clg@kaod.org>
[dwg: Adjust type name string for XiveNotifier]
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2018-12-21 09:24:23 +11:00

463 lines
12 KiB
C

/*
* QEMU PowerPC XIVE interrupt controller model
*
* Copyright (c) 2017-2018, IBM Corporation.
*
* This code is licensed under the GPL version 2 or later. See the
* COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "qapi/error.h"
#include "target/ppc/cpu.h"
#include "sysemu/cpus.h"
#include "sysemu/dma.h"
#include "hw/qdev-properties.h"
#include "monitor/monitor.h"
#include "hw/ppc/xive.h"
/*
* XIVE ESB helpers
*/
static uint8_t xive_esb_set(uint8_t *pq, uint8_t value)
{
uint8_t old_pq = *pq & 0x3;
*pq &= ~0x3;
*pq |= value & 0x3;
return old_pq;
}
static bool xive_esb_trigger(uint8_t *pq)
{
uint8_t old_pq = *pq & 0x3;
switch (old_pq) {
case XIVE_ESB_RESET:
xive_esb_set(pq, XIVE_ESB_PENDING);
return true;
case XIVE_ESB_PENDING:
case XIVE_ESB_QUEUED:
xive_esb_set(pq, XIVE_ESB_QUEUED);
return false;
case XIVE_ESB_OFF:
xive_esb_set(pq, XIVE_ESB_OFF);
return false;
default:
g_assert_not_reached();
}
}
static bool xive_esb_eoi(uint8_t *pq)
{
uint8_t old_pq = *pq & 0x3;
switch (old_pq) {
case XIVE_ESB_RESET:
case XIVE_ESB_PENDING:
xive_esb_set(pq, XIVE_ESB_RESET);
return false;
case XIVE_ESB_QUEUED:
xive_esb_set(pq, XIVE_ESB_PENDING);
return true;
case XIVE_ESB_OFF:
xive_esb_set(pq, XIVE_ESB_OFF);
return false;
default:
g_assert_not_reached();
}
}
/*
* XIVE Interrupt Source (or IVSE)
*/
uint8_t xive_source_esb_get(XiveSource *xsrc, uint32_t srcno)
{
assert(srcno < xsrc->nr_irqs);
return xsrc->status[srcno] & 0x3;
}
uint8_t xive_source_esb_set(XiveSource *xsrc, uint32_t srcno, uint8_t pq)
{
assert(srcno < xsrc->nr_irqs);
return xive_esb_set(&xsrc->status[srcno], pq);
}
/*
* Returns whether the event notification should be forwarded.
*/
static bool xive_source_lsi_trigger(XiveSource *xsrc, uint32_t srcno)
{
uint8_t old_pq = xive_source_esb_get(xsrc, srcno);
xsrc->status[srcno] |= XIVE_STATUS_ASSERTED;
switch (old_pq) {
case XIVE_ESB_RESET:
xive_source_esb_set(xsrc, srcno, XIVE_ESB_PENDING);
return true;
default:
return false;
}
}
/*
* Returns whether the event notification should be forwarded.
*/
static bool xive_source_esb_trigger(XiveSource *xsrc, uint32_t srcno)
{
bool ret;
assert(srcno < xsrc->nr_irqs);
ret = xive_esb_trigger(&xsrc->status[srcno]);
if (xive_source_irq_is_lsi(xsrc, srcno) &&
xive_source_esb_get(xsrc, srcno) == XIVE_ESB_QUEUED) {
qemu_log_mask(LOG_GUEST_ERROR,
"XIVE: queued an event on LSI IRQ %d\n", srcno);
}
return ret;
}
/*
* Returns whether the event notification should be forwarded.
*/
static bool xive_source_esb_eoi(XiveSource *xsrc, uint32_t srcno)
{
bool ret;
assert(srcno < xsrc->nr_irqs);
ret = xive_esb_eoi(&xsrc->status[srcno]);
/*
* LSI sources do not set the Q bit but they can still be
* asserted, in which case we should forward a new event
* notification
*/
if (xive_source_irq_is_lsi(xsrc, srcno) &&
xsrc->status[srcno] & XIVE_STATUS_ASSERTED) {
ret = xive_source_lsi_trigger(xsrc, srcno);
}
return ret;
}
/*
* Forward the source event notification to the Router
*/
static void xive_source_notify(XiveSource *xsrc, int srcno)
{
XiveNotifierClass *xnc = XIVE_NOTIFIER_GET_CLASS(xsrc->xive);
if (xnc->notify) {
xnc->notify(xsrc->xive, srcno);
}
}
/*
* In a two pages ESB MMIO setting, even page is the trigger page, odd
* page is for management
*/
static inline bool addr_is_even(hwaddr addr, uint32_t shift)
{
return !((addr >> shift) & 1);
}
static inline bool xive_source_is_trigger_page(XiveSource *xsrc, hwaddr addr)
{
return xive_source_esb_has_2page(xsrc) &&
addr_is_even(addr, xsrc->esb_shift - 1);
}
/*
* ESB MMIO loads
* Trigger page Management/EOI page
*
* ESB MMIO setting 2 pages 1 or 2 pages
*
* 0x000 .. 0x3FF -1 EOI and return 0|1
* 0x400 .. 0x7FF -1 EOI and return 0|1
* 0x800 .. 0xBFF -1 return PQ
* 0xC00 .. 0xCFF -1 return PQ and atomically PQ=00
* 0xD00 .. 0xDFF -1 return PQ and atomically PQ=01
* 0xE00 .. 0xDFF -1 return PQ and atomically PQ=10
* 0xF00 .. 0xDFF -1 return PQ and atomically PQ=11
*/
static uint64_t xive_source_esb_read(void *opaque, hwaddr addr, unsigned size)
{
XiveSource *xsrc = XIVE_SOURCE(opaque);
uint32_t offset = addr & 0xFFF;
uint32_t srcno = addr >> xsrc->esb_shift;
uint64_t ret = -1;
/* In a two pages ESB MMIO setting, trigger page should not be read */
if (xive_source_is_trigger_page(xsrc, addr)) {
qemu_log_mask(LOG_GUEST_ERROR,
"XIVE: invalid load on IRQ %d trigger page at "
"0x%"HWADDR_PRIx"\n", srcno, addr);
return -1;
}
switch (offset) {
case XIVE_ESB_LOAD_EOI ... XIVE_ESB_LOAD_EOI + 0x7FF:
ret = xive_source_esb_eoi(xsrc, srcno);
/* Forward the source event notification for routing */
if (ret) {
xive_source_notify(xsrc, srcno);
}
break;
case XIVE_ESB_GET ... XIVE_ESB_GET + 0x3FF:
ret = xive_source_esb_get(xsrc, srcno);
break;
case XIVE_ESB_SET_PQ_00 ... XIVE_ESB_SET_PQ_00 + 0x0FF:
case XIVE_ESB_SET_PQ_01 ... XIVE_ESB_SET_PQ_01 + 0x0FF:
case XIVE_ESB_SET_PQ_10 ... XIVE_ESB_SET_PQ_10 + 0x0FF:
case XIVE_ESB_SET_PQ_11 ... XIVE_ESB_SET_PQ_11 + 0x0FF:
ret = xive_source_esb_set(xsrc, srcno, (offset >> 8) & 0x3);
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid ESB load addr %x\n",
offset);
}
return ret;
}
/*
* ESB MMIO stores
* Trigger page Management/EOI page
*
* ESB MMIO setting 2 pages 1 or 2 pages
*
* 0x000 .. 0x3FF Trigger Trigger
* 0x400 .. 0x7FF Trigger EOI
* 0x800 .. 0xBFF Trigger undefined
* 0xC00 .. 0xCFF Trigger PQ=00
* 0xD00 .. 0xDFF Trigger PQ=01
* 0xE00 .. 0xDFF Trigger PQ=10
* 0xF00 .. 0xDFF Trigger PQ=11
*/
static void xive_source_esb_write(void *opaque, hwaddr addr,
uint64_t value, unsigned size)
{
XiveSource *xsrc = XIVE_SOURCE(opaque);
uint32_t offset = addr & 0xFFF;
uint32_t srcno = addr >> xsrc->esb_shift;
bool notify = false;
/* In a two pages ESB MMIO setting, trigger page only triggers */
if (xive_source_is_trigger_page(xsrc, addr)) {
notify = xive_source_esb_trigger(xsrc, srcno);
goto out;
}
switch (offset) {
case 0 ... 0x3FF:
notify = xive_source_esb_trigger(xsrc, srcno);
break;
case XIVE_ESB_STORE_EOI ... XIVE_ESB_STORE_EOI + 0x3FF:
if (!(xsrc->esb_flags & XIVE_SRC_STORE_EOI)) {
qemu_log_mask(LOG_GUEST_ERROR,
"XIVE: invalid Store EOI for IRQ %d\n", srcno);
return;
}
notify = xive_source_esb_eoi(xsrc, srcno);
break;
case XIVE_ESB_SET_PQ_00 ... XIVE_ESB_SET_PQ_00 + 0x0FF:
case XIVE_ESB_SET_PQ_01 ... XIVE_ESB_SET_PQ_01 + 0x0FF:
case XIVE_ESB_SET_PQ_10 ... XIVE_ESB_SET_PQ_10 + 0x0FF:
case XIVE_ESB_SET_PQ_11 ... XIVE_ESB_SET_PQ_11 + 0x0FF:
xive_source_esb_set(xsrc, srcno, (offset >> 8) & 0x3);
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid ESB write addr %x\n",
offset);
return;
}
out:
/* Forward the source event notification for routing */
if (notify) {
xive_source_notify(xsrc, srcno);
}
}
static const MemoryRegionOps xive_source_esb_ops = {
.read = xive_source_esb_read,
.write = xive_source_esb_write,
.endianness = DEVICE_BIG_ENDIAN,
.valid = {
.min_access_size = 8,
.max_access_size = 8,
},
.impl = {
.min_access_size = 8,
.max_access_size = 8,
},
};
static void xive_source_set_irq(void *opaque, int srcno, int val)
{
XiveSource *xsrc = XIVE_SOURCE(opaque);
bool notify = false;
if (xive_source_irq_is_lsi(xsrc, srcno)) {
if (val) {
notify = xive_source_lsi_trigger(xsrc, srcno);
} else {
xsrc->status[srcno] &= ~XIVE_STATUS_ASSERTED;
}
} else {
if (val) {
notify = xive_source_esb_trigger(xsrc, srcno);
}
}
/* Forward the source event notification for routing */
if (notify) {
xive_source_notify(xsrc, srcno);
}
}
void xive_source_pic_print_info(XiveSource *xsrc, uint32_t offset, Monitor *mon)
{
int i;
for (i = 0; i < xsrc->nr_irqs; i++) {
uint8_t pq = xive_source_esb_get(xsrc, i);
if (pq == XIVE_ESB_OFF) {
continue;
}
monitor_printf(mon, " %08x %s %c%c%c\n", i + offset,
xive_source_irq_is_lsi(xsrc, i) ? "LSI" : "MSI",
pq & XIVE_ESB_VAL_P ? 'P' : '-',
pq & XIVE_ESB_VAL_Q ? 'Q' : '-',
xsrc->status[i] & XIVE_STATUS_ASSERTED ? 'A' : ' ');
}
}
static void xive_source_reset(void *dev)
{
XiveSource *xsrc = XIVE_SOURCE(dev);
/* Do not clear the LSI bitmap */
/* PQs are initialized to 0b01 (Q=1) which corresponds to "ints off" */
memset(xsrc->status, XIVE_ESB_OFF, xsrc->nr_irqs);
}
static void xive_source_realize(DeviceState *dev, Error **errp)
{
XiveSource *xsrc = XIVE_SOURCE(dev);
Object *obj;
Error *local_err = NULL;
obj = object_property_get_link(OBJECT(dev), "xive", &local_err);
if (!obj) {
error_propagate(errp, local_err);
error_prepend(errp, "required link 'xive' not found: ");
return;
}
xsrc->xive = XIVE_NOTIFIER(obj);
if (!xsrc->nr_irqs) {
error_setg(errp, "Number of interrupt needs to be greater than 0");
return;
}
if (xsrc->esb_shift != XIVE_ESB_4K &&
xsrc->esb_shift != XIVE_ESB_4K_2PAGE &&
xsrc->esb_shift != XIVE_ESB_64K &&
xsrc->esb_shift != XIVE_ESB_64K_2PAGE) {
error_setg(errp, "Invalid ESB shift setting");
return;
}
xsrc->status = g_malloc0(xsrc->nr_irqs);
xsrc->lsi_map = bitmap_new(xsrc->nr_irqs);
memory_region_init_io(&xsrc->esb_mmio, OBJECT(xsrc),
&xive_source_esb_ops, xsrc, "xive.esb",
(1ull << xsrc->esb_shift) * xsrc->nr_irqs);
xsrc->qirqs = qemu_allocate_irqs(xive_source_set_irq, xsrc,
xsrc->nr_irqs);
qemu_register_reset(xive_source_reset, dev);
}
static const VMStateDescription vmstate_xive_source = {
.name = TYPE_XIVE_SOURCE,
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32_EQUAL(nr_irqs, XiveSource, NULL),
VMSTATE_VBUFFER_UINT32(status, XiveSource, 1, NULL, nr_irqs),
VMSTATE_END_OF_LIST()
},
};
/*
* The default XIVE interrupt source setting for the ESB MMIOs is two
* 64k pages without Store EOI, to be in sync with KVM.
*/
static Property xive_source_properties[] = {
DEFINE_PROP_UINT64("flags", XiveSource, esb_flags, 0),
DEFINE_PROP_UINT32("nr-irqs", XiveSource, nr_irqs, 0),
DEFINE_PROP_UINT32("shift", XiveSource, esb_shift, XIVE_ESB_64K_2PAGE),
DEFINE_PROP_END_OF_LIST(),
};
static void xive_source_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->desc = "XIVE Interrupt Source";
dc->props = xive_source_properties;
dc->realize = xive_source_realize;
dc->vmsd = &vmstate_xive_source;
}
static const TypeInfo xive_source_info = {
.name = TYPE_XIVE_SOURCE,
.parent = TYPE_DEVICE,
.instance_size = sizeof(XiveSource),
.class_init = xive_source_class_init,
};
/*
* XIVE Fabric
*/
static const TypeInfo xive_fabric_info = {
.name = TYPE_XIVE_NOTIFIER,
.parent = TYPE_INTERFACE,
.class_size = sizeof(XiveNotifierClass),
};
static void xive_register_types(void)
{
type_register_static(&xive_source_info);
type_register_static(&xive_fabric_info);
}
type_init(xive_register_types)