2018-12-06 02:22:15 +03:00
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
|
2018-12-06 02:22:16 +03:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-06 02:22:15 +03:00
|
|
|
/*
|
|
|
|
* Returns whether the event notification should be forwarded.
|
|
|
|
*/
|
|
|
|
static bool xive_source_esb_trigger(XiveSource *xsrc, uint32_t srcno)
|
|
|
|
{
|
2018-12-06 02:22:16 +03:00
|
|
|
bool ret;
|
|
|
|
|
2018-12-06 02:22:15 +03:00
|
|
|
assert(srcno < xsrc->nr_irqs);
|
|
|
|
|
2018-12-06 02:22:16 +03:00
|
|
|
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;
|
2018-12-06 02:22:15 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns whether the event notification should be forwarded.
|
|
|
|
*/
|
|
|
|
static bool xive_source_esb_eoi(XiveSource *xsrc, uint32_t srcno)
|
|
|
|
{
|
2018-12-06 02:22:16 +03:00
|
|
|
bool ret;
|
|
|
|
|
2018-12-06 02:22:15 +03:00
|
|
|
assert(srcno < xsrc->nr_irqs);
|
|
|
|
|
2018-12-06 02:22:16 +03:00
|
|
|
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;
|
2018-12-06 02:22:15 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Forward the source event notification to the Router
|
|
|
|
*/
|
|
|
|
static void xive_source_notify(XiveSource *xsrc, int srcno)
|
|
|
|
{
|
2018-12-06 02:22:17 +03:00
|
|
|
XiveNotifierClass *xnc = XIVE_NOTIFIER_GET_CLASS(xsrc->xive);
|
2018-12-06 02:22:15 +03:00
|
|
|
|
2018-12-06 02:22:17 +03:00
|
|
|
if (xnc->notify) {
|
|
|
|
xnc->notify(xsrc->xive, srcno);
|
|
|
|
}
|
2018-12-06 02:22:15 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
2018-12-06 02:22:16 +03:00
|
|
|
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);
|
|
|
|
}
|
2018-12-06 02:22:15 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2018-12-06 02:22:16 +03:00
|
|
|
monitor_printf(mon, " %08x %s %c%c%c\n", i + offset,
|
|
|
|
xive_source_irq_is_lsi(xsrc, i) ? "LSI" : "MSI",
|
2018-12-06 02:22:15 +03:00
|
|
|
pq & XIVE_ESB_VAL_P ? 'P' : '-',
|
2018-12-06 02:22:16 +03:00
|
|
|
pq & XIVE_ESB_VAL_Q ? 'Q' : '-',
|
|
|
|
xsrc->status[i] & XIVE_STATUS_ASSERTED ? 'A' : ' ');
|
2018-12-06 02:22:15 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void xive_source_reset(void *dev)
|
|
|
|
{
|
|
|
|
XiveSource *xsrc = XIVE_SOURCE(dev);
|
|
|
|
|
2018-12-06 02:22:16 +03:00
|
|
|
/* Do not clear the LSI bitmap */
|
|
|
|
|
2018-12-06 02:22:15 +03:00
|
|
|
/* 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);
|
2018-12-06 02:22:17 +03:00
|
|
|
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);
|
2018-12-06 02:22:15 +03:00
|
|
|
|
|
|
|
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);
|
2018-12-06 02:22:16 +03:00
|
|
|
xsrc->lsi_map = bitmap_new(xsrc->nr_irqs);
|
2018-12-06 02:22:15 +03:00
|
|
|
|
|
|
|
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,
|
|
|
|
};
|
|
|
|
|
2018-12-06 02:22:19 +03:00
|
|
|
/*
|
|
|
|
* XiveEND helpers
|
|
|
|
*/
|
|
|
|
|
|
|
|
void xive_end_queue_pic_print_info(XiveEND *end, uint32_t width, Monitor *mon)
|
|
|
|
{
|
|
|
|
uint64_t qaddr_base = (uint64_t) be32_to_cpu(end->w2 & 0x0fffffff) << 32
|
|
|
|
| be32_to_cpu(end->w3);
|
|
|
|
uint32_t qsize = xive_get_field32(END_W0_QSIZE, end->w0);
|
|
|
|
uint32_t qindex = xive_get_field32(END_W1_PAGE_OFF, end->w1);
|
|
|
|
uint32_t qentries = 1 << (qsize + 10);
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* print out the [ (qindex - (width - 1)) .. (qindex + 1)] window
|
|
|
|
*/
|
|
|
|
monitor_printf(mon, " [ ");
|
|
|
|
qindex = (qindex - (width - 1)) & (qentries - 1);
|
|
|
|
for (i = 0; i < width; i++) {
|
|
|
|
uint64_t qaddr = qaddr_base + (qindex << 2);
|
|
|
|
uint32_t qdata = -1;
|
|
|
|
|
|
|
|
if (dma_memory_read(&address_space_memory, qaddr, &qdata,
|
|
|
|
sizeof(qdata))) {
|
|
|
|
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: failed to read EQ @0x%"
|
|
|
|
HWADDR_PRIx "\n", qaddr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
monitor_printf(mon, "%s%08x ", i == width - 1 ? "^" : "",
|
|
|
|
be32_to_cpu(qdata));
|
|
|
|
qindex = (qindex + 1) & (qentries - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void xive_end_pic_print_info(XiveEND *end, uint32_t end_idx, Monitor *mon)
|
|
|
|
{
|
|
|
|
uint64_t qaddr_base = (uint64_t) be32_to_cpu(end->w2 & 0x0fffffff) << 32
|
|
|
|
| be32_to_cpu(end->w3);
|
|
|
|
uint32_t qindex = xive_get_field32(END_W1_PAGE_OFF, end->w1);
|
|
|
|
uint32_t qgen = xive_get_field32(END_W1_GENERATION, end->w1);
|
|
|
|
uint32_t qsize = xive_get_field32(END_W0_QSIZE, end->w0);
|
|
|
|
uint32_t qentries = 1 << (qsize + 10);
|
|
|
|
|
|
|
|
uint32_t nvt = xive_get_field32(END_W6_NVT_INDEX, end->w6);
|
|
|
|
uint8_t priority = xive_get_field32(END_W7_F0_PRIORITY, end->w7);
|
|
|
|
|
|
|
|
if (!xive_end_is_valid(end)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
monitor_printf(mon, " %08x %c%c%c%c%c prio:%d nvt:%04x eq:@%08"PRIx64
|
|
|
|
"% 6d/%5d ^%d", end_idx,
|
|
|
|
xive_end_is_valid(end) ? 'v' : '-',
|
|
|
|
xive_end_is_enqueue(end) ? 'q' : '-',
|
|
|
|
xive_end_is_notify(end) ? 'n' : '-',
|
|
|
|
xive_end_is_backlog(end) ? 'b' : '-',
|
|
|
|
xive_end_is_escalate(end) ? 'e' : '-',
|
|
|
|
priority, nvt, qaddr_base, qindex, qentries, qgen);
|
|
|
|
|
|
|
|
xive_end_queue_pic_print_info(end, 6, mon);
|
|
|
|
monitor_printf(mon, "]\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void xive_end_enqueue(XiveEND *end, uint32_t data)
|
|
|
|
{
|
|
|
|
uint64_t qaddr_base = (uint64_t) be32_to_cpu(end->w2 & 0x0fffffff) << 32
|
|
|
|
| be32_to_cpu(end->w3);
|
|
|
|
uint32_t qsize = xive_get_field32(END_W0_QSIZE, end->w0);
|
|
|
|
uint32_t qindex = xive_get_field32(END_W1_PAGE_OFF, end->w1);
|
|
|
|
uint32_t qgen = xive_get_field32(END_W1_GENERATION, end->w1);
|
|
|
|
|
|
|
|
uint64_t qaddr = qaddr_base + (qindex << 2);
|
|
|
|
uint32_t qdata = cpu_to_be32((qgen << 31) | (data & 0x7fffffff));
|
|
|
|
uint32_t qentries = 1 << (qsize + 10);
|
|
|
|
|
|
|
|
if (dma_memory_write(&address_space_memory, qaddr, &qdata, sizeof(qdata))) {
|
|
|
|
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: failed to write END data @0x%"
|
|
|
|
HWADDR_PRIx "\n", qaddr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
qindex = (qindex + 1) & (qentries - 1);
|
|
|
|
if (qindex == 0) {
|
|
|
|
qgen ^= 1;
|
|
|
|
end->w1 = xive_set_field32(END_W1_GENERATION, end->w1, qgen);
|
|
|
|
}
|
|
|
|
end->w1 = xive_set_field32(END_W1_PAGE_OFF, end->w1, qindex);
|
|
|
|
}
|
|
|
|
|
2018-12-06 02:22:18 +03:00
|
|
|
/*
|
|
|
|
* XIVE Router (aka. Virtualization Controller or IVRE)
|
|
|
|
*/
|
|
|
|
|
|
|
|
int xive_router_get_eas(XiveRouter *xrtr, uint8_t eas_blk, uint32_t eas_idx,
|
|
|
|
XiveEAS *eas)
|
|
|
|
{
|
|
|
|
XiveRouterClass *xrc = XIVE_ROUTER_GET_CLASS(xrtr);
|
|
|
|
|
|
|
|
return xrc->get_eas(xrtr, eas_blk, eas_idx, eas);
|
|
|
|
}
|
|
|
|
|
2018-12-06 02:22:19 +03:00
|
|
|
int xive_router_get_end(XiveRouter *xrtr, uint8_t end_blk, uint32_t end_idx,
|
|
|
|
XiveEND *end)
|
|
|
|
{
|
|
|
|
XiveRouterClass *xrc = XIVE_ROUTER_GET_CLASS(xrtr);
|
|
|
|
|
|
|
|
return xrc->get_end(xrtr, end_blk, end_idx, end);
|
|
|
|
}
|
|
|
|
|
|
|
|
int xive_router_write_end(XiveRouter *xrtr, uint8_t end_blk, uint32_t end_idx,
|
|
|
|
XiveEND *end, uint8_t word_number)
|
|
|
|
{
|
|
|
|
XiveRouterClass *xrc = XIVE_ROUTER_GET_CLASS(xrtr);
|
|
|
|
|
|
|
|
return xrc->write_end(xrtr, end_blk, end_idx, end, word_number);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* An END trigger can come from an event trigger (IPI or HW) or from
|
|
|
|
* another chip. We don't model the PowerBus but the END trigger
|
|
|
|
* message has the same parameters than in the function below.
|
|
|
|
*/
|
|
|
|
static void xive_router_end_notify(XiveRouter *xrtr, uint8_t end_blk,
|
|
|
|
uint32_t end_idx, uint32_t end_data)
|
|
|
|
{
|
|
|
|
XiveEND end;
|
|
|
|
uint8_t priority;
|
|
|
|
uint8_t format;
|
|
|
|
|
|
|
|
/* END cache lookup */
|
|
|
|
if (xive_router_get_end(xrtr, end_blk, end_idx, &end)) {
|
|
|
|
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No END %x/%x\n", end_blk,
|
|
|
|
end_idx);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!xive_end_is_valid(&end)) {
|
|
|
|
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: END %x/%x is invalid\n",
|
|
|
|
end_blk, end_idx);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (xive_end_is_enqueue(&end)) {
|
|
|
|
xive_end_enqueue(&end, end_data);
|
|
|
|
/* Enqueuing event data modifies the EQ toggle and index */
|
|
|
|
xive_router_write_end(xrtr, end_blk, end_idx, &end, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The W7 format depends on the F bit in W6. It defines the type
|
|
|
|
* of the notification :
|
|
|
|
*
|
|
|
|
* F=0 : single or multiple NVT notification
|
|
|
|
* F=1 : User level Event-Based Branch (EBB) notification, no
|
|
|
|
* priority
|
|
|
|
*/
|
|
|
|
format = xive_get_field32(END_W6_FORMAT_BIT, end.w6);
|
|
|
|
priority = xive_get_field32(END_W7_F0_PRIORITY, end.w7);
|
|
|
|
|
|
|
|
/* The END is masked */
|
|
|
|
if (format == 0 && priority == 0xff) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check the END ESn (Event State Buffer for notification) for
|
|
|
|
* even futher coalescing in the Router
|
|
|
|
*/
|
|
|
|
if (!xive_end_is_notify(&end)) {
|
|
|
|
qemu_log_mask(LOG_UNIMP, "XIVE: !UCOND_NOTIFY not implemented\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Follows IVPE notification
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
2018-12-06 02:22:18 +03:00
|
|
|
static void xive_router_notify(XiveNotifier *xn, uint32_t lisn)
|
|
|
|
{
|
|
|
|
XiveRouter *xrtr = XIVE_ROUTER(xn);
|
|
|
|
uint8_t eas_blk = XIVE_SRCNO_BLOCK(lisn);
|
|
|
|
uint32_t eas_idx = XIVE_SRCNO_INDEX(lisn);
|
|
|
|
XiveEAS eas;
|
|
|
|
|
|
|
|
/* EAS cache lookup */
|
|
|
|
if (xive_router_get_eas(xrtr, eas_blk, eas_idx, &eas)) {
|
|
|
|
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Unknown LISN %x\n", lisn);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The IVRE checks the State Bit Cache at this point. We skip the
|
|
|
|
* SBC lookup because the state bits of the sources are modeled
|
|
|
|
* internally in QEMU.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!xive_eas_is_valid(&eas)) {
|
|
|
|
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid LISN %x\n", lisn);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (xive_eas_is_masked(&eas)) {
|
|
|
|
/* Notification completed */
|
|
|
|
return;
|
|
|
|
}
|
2018-12-06 02:22:19 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The event trigger becomes an END trigger
|
|
|
|
*/
|
|
|
|
xive_router_end_notify(xrtr,
|
|
|
|
xive_get_field64(EAS_END_BLOCK, eas.w),
|
|
|
|
xive_get_field64(EAS_END_INDEX, eas.w),
|
|
|
|
xive_get_field64(EAS_END_DATA, eas.w));
|
2018-12-06 02:22:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void xive_router_class_init(ObjectClass *klass, void *data)
|
|
|
|
{
|
|
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
|
|
|
XiveNotifierClass *xnc = XIVE_NOTIFIER_CLASS(klass);
|
|
|
|
|
|
|
|
dc->desc = "XIVE Router Engine";
|
|
|
|
xnc->notify = xive_router_notify;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const TypeInfo xive_router_info = {
|
|
|
|
.name = TYPE_XIVE_ROUTER,
|
|
|
|
.parent = TYPE_SYS_BUS_DEVICE,
|
|
|
|
.abstract = true,
|
|
|
|
.class_size = sizeof(XiveRouterClass),
|
|
|
|
.class_init = xive_router_class_init,
|
|
|
|
.interfaces = (InterfaceInfo[]) {
|
|
|
|
{ TYPE_XIVE_NOTIFIER },
|
|
|
|
{ }
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void xive_eas_pic_print_info(XiveEAS *eas, uint32_t lisn, Monitor *mon)
|
|
|
|
{
|
|
|
|
if (!xive_eas_is_valid(eas)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
monitor_printf(mon, " %08x %s end:%02x/%04x data:%08x\n",
|
|
|
|
lisn, xive_eas_is_masked(eas) ? "M" : " ",
|
|
|
|
(uint8_t) xive_get_field64(EAS_END_BLOCK, eas->w),
|
|
|
|
(uint32_t) xive_get_field64(EAS_END_INDEX, eas->w),
|
|
|
|
(uint32_t) xive_get_field64(EAS_END_DATA, eas->w));
|
|
|
|
}
|
|
|
|
|
2018-12-06 02:22:17 +03:00
|
|
|
/*
|
|
|
|
* XIVE Fabric
|
|
|
|
*/
|
|
|
|
static const TypeInfo xive_fabric_info = {
|
|
|
|
.name = TYPE_XIVE_NOTIFIER,
|
|
|
|
.parent = TYPE_INTERFACE,
|
|
|
|
.class_size = sizeof(XiveNotifierClass),
|
|
|
|
};
|
|
|
|
|
2018-12-06 02:22:15 +03:00
|
|
|
static void xive_register_types(void)
|
|
|
|
{
|
|
|
|
type_register_static(&xive_source_info);
|
2018-12-06 02:22:17 +03:00
|
|
|
type_register_static(&xive_fabric_info);
|
2018-12-06 02:22:18 +03:00
|
|
|
type_register_static(&xive_router_info);
|
2018-12-06 02:22:15 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
type_init(xive_register_types)
|