ppc/xive: introduce a XIVE interrupt source model
The first sub-engine of the overall XIVE architecture is the Interrupt Virtualization Source Engine (IVSE). An IVSE can be integrated into another logic, like in a PCI PHB or in the main interrupt controller to manage IPIs. Each IVSE instance is associated with an Event State Buffer (ESB) that contains a two bit state entry for each possible event source. When an event is signaled to the IVSE, by MMIO or some other means, the associated interrupt state bits are fetched from the ESB and modified. Depending on the resulting ESB state, the event is forwarded to the IVRE sub-engine of the controller doing the routing. Each supported ESB entry is associated with either a single or a even/odd pair of pages which provides commands to manage the source: to EOI, to turn off the source for instance. On a sPAPR machine, the O/S will obtain the page address of the ESB entry associated with a source and its characteristic using the H_INT_GET_SOURCE_INFO hcall. On PowerNV, a similar OPAL call is used. The xive_source_notify() routine is in charge forwarding the source event notification to the routing engine. It will be filled later on. Signed-off-by: Cédric Le Goater <clg@kaod.org> Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
This commit is contained in:
parent
2104d4f5bc
commit
02e3ff548d
@ -16,6 +16,7 @@ CONFIG_VIRTIO_VGA=y
|
||||
CONFIG_XICS=$(CONFIG_PSERIES)
|
||||
CONFIG_XICS_SPAPR=$(CONFIG_PSERIES)
|
||||
CONFIG_XICS_KVM=$(call land,$(CONFIG_PSERIES),$(CONFIG_KVM))
|
||||
CONFIG_XIVE=$(CONFIG_PSERIES)
|
||||
CONFIG_MEM_DEVICE=y
|
||||
CONFIG_DIMM=y
|
||||
CONFIG_SPAPR_RNG=y
|
||||
|
@ -37,6 +37,7 @@ obj-$(CONFIG_SH4) += sh_intc.o
|
||||
obj-$(CONFIG_XICS) += xics.o
|
||||
obj-$(CONFIG_XICS_SPAPR) += xics_spapr.o
|
||||
obj-$(CONFIG_XICS_KVM) += xics_kvm.o
|
||||
obj-$(CONFIG_XIVE) += xive.o
|
||||
obj-$(CONFIG_POWERNV) += xics_pnv.o
|
||||
obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
|
||||
obj-$(CONFIG_S390_FLIC) += s390_flic.o
|
||||
|
382
hw/intc/xive.c
Normal file
382
hw/intc/xive.c
Normal file
@ -0,0 +1,382 @@
|
||||
/*
|
||||
* 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_esb_trigger(XiveSource *xsrc, uint32_t srcno)
|
||||
{
|
||||
assert(srcno < xsrc->nr_irqs);
|
||||
|
||||
return xive_esb_trigger(&xsrc->status[srcno]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns whether the event notification should be forwarded.
|
||||
*/
|
||||
static bool xive_source_esb_eoi(XiveSource *xsrc, uint32_t srcno)
|
||||
{
|
||||
assert(srcno < xsrc->nr_irqs);
|
||||
|
||||
return xive_esb_eoi(&xsrc->status[srcno]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Forward the source event notification to the Router
|
||||
*/
|
||||
static void xive_source_notify(XiveSource *xsrc, int 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 (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 %c%c\n", i + offset,
|
||||
pq & XIVE_ESB_VAL_P ? 'P' : '-',
|
||||
pq & XIVE_ESB_VAL_Q ? 'Q' : '-');
|
||||
}
|
||||
}
|
||||
|
||||
static void xive_source_reset(void *dev)
|
||||
{
|
||||
XiveSource *xsrc = XIVE_SOURCE(dev);
|
||||
|
||||
/* 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);
|
||||
|
||||
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);
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
static void xive_register_types(void)
|
||||
{
|
||||
type_register_static(&xive_source_info);
|
||||
}
|
||||
|
||||
type_init(xive_register_types)
|
260
include/hw/ppc/xive.h
Normal file
260
include/hw/ppc/xive.h
Normal file
@ -0,0 +1,260 @@
|
||||
/*
|
||||
* QEMU PowerPC XIVE interrupt controller model
|
||||
*
|
||||
*
|
||||
* The POWER9 processor comes with a new interrupt controller, called
|
||||
* XIVE as "eXternal Interrupt Virtualization Engine".
|
||||
*
|
||||
* = Overall architecture
|
||||
*
|
||||
*
|
||||
* XIVE Interrupt Controller
|
||||
* +------------------------------------+ IPIs
|
||||
* | +---------+ +---------+ +--------+ | +-------+
|
||||
* | |VC | |CQ | |PC |----> | CORES |
|
||||
* | | esb | | | | |----> | |
|
||||
* | | eas | | Bridge | | tctx |----> | |
|
||||
* | |SC end | | | | nvt | | | |
|
||||
* +------+ | +---------+ +----+----+ +--------+ | +-+-+-+-+
|
||||
* | RAM | +------------------|-----------------+ | | |
|
||||
* | | | | | |
|
||||
* | | | | | |
|
||||
* | | +--------------------v------------------------v-v-v--+ other
|
||||
* | <--+ Power Bus +--> chips
|
||||
* | esb | +---------+-----------------------+------------------+
|
||||
* | eas | | |
|
||||
* | end | +--|------+ |
|
||||
* | nvt | +----+----+ | +----+----+
|
||||
* +------+ |SC | | |SC |
|
||||
* | | | | |
|
||||
* | PQ-bits | | | PQ-bits |
|
||||
* | local |-+ | in VC |
|
||||
* +---------+ +---------+
|
||||
* PCIe NX,NPU,CAPI
|
||||
*
|
||||
* SC: Source Controller (aka. IVSE)
|
||||
* VC: Virtualization Controller (aka. IVRE)
|
||||
* PC: Presentation Controller (aka. IVPE)
|
||||
* CQ: Common Queue (Bridge)
|
||||
*
|
||||
* PQ-bits: 2 bits source state machine (P:pending Q:queued)
|
||||
* esb: Event State Buffer (Array of PQ bits in an IVSE)
|
||||
* eas: Event Assignment Structure
|
||||
* end: Event Notification Descriptor
|
||||
* nvt: Notification Virtual Target
|
||||
* tctx: Thread interrupt Context
|
||||
*
|
||||
*
|
||||
* The XIVE IC is composed of three sub-engines :
|
||||
*
|
||||
* - Interrupt Virtualization Source Engine (IVSE), or Source
|
||||
* Controller (SC). These are found in PCI PHBs, in the PSI host
|
||||
* bridge controller, but also inside the main controller for the
|
||||
* core IPIs and other sub-chips (NX, CAP, NPU) of the
|
||||
* chip/processor. They are configured to feed the IVRE with events.
|
||||
*
|
||||
* - Interrupt Virtualization Routing Engine (IVRE) or Virtualization
|
||||
* Controller (VC). Its job is to match an event source with an
|
||||
* Event Notification Descriptor (END).
|
||||
*
|
||||
* - Interrupt Virtualization Presentation Engine (IVPE) or
|
||||
* Presentation Controller (PC). It maintains the interrupt context
|
||||
* state of each thread and handles the delivery of the external
|
||||
* exception to the thread.
|
||||
*
|
||||
* In XIVE 1.0, the sub-engines used to be referred as:
|
||||
*
|
||||
* SC Source Controller
|
||||
* VC Virtualization Controller
|
||||
* PC Presentation Controller
|
||||
* CQ Common Queue (PowerBUS Bridge)
|
||||
*
|
||||
*
|
||||
* = XIVE internal tables
|
||||
*
|
||||
* Each of the sub-engines uses a set of tables to redirect exceptions
|
||||
* from event sources to CPU threads.
|
||||
*
|
||||
* +-------+
|
||||
* User or OS | EQ |
|
||||
* or +------>|entries|
|
||||
* Hypervisor | | .. |
|
||||
* Memory | +-------+
|
||||
* | ^
|
||||
* | |
|
||||
* +-------------------------------------------------+
|
||||
* | |
|
||||
* Hypervisor +------+ +---+--+ +---+--+ +------+
|
||||
* Memory | ESB | | EAT | | ENDT | | NVTT |
|
||||
* (skiboot) +----+-+ +----+-+ +----+-+ +------+
|
||||
* ^ | ^ | ^ | ^
|
||||
* | | | | | | |
|
||||
* +-------------------------------------------------+
|
||||
* | | | | | | |
|
||||
* | | | | | | |
|
||||
* +----|--|--------|--|--------|--|-+ +-|-----+ +------+
|
||||
* | | | | | | | | | | tctx| |Thread|
|
||||
* IPI or --> | + v + v + v |---| + .. |-----> |
|
||||
* HW events --> | | | | | |
|
||||
* IVSE | IVRE | | IVPE | +------+
|
||||
* +---------------------------------+ +-------+
|
||||
*
|
||||
*
|
||||
*
|
||||
* The IVSE have a 2-bits state machine, P for pending and Q for queued,
|
||||
* for each source that allows events to be triggered. They are stored in
|
||||
* an Event State Buffer (ESB) array and can be controlled by MMIOs.
|
||||
*
|
||||
* If the event is let through, the IVRE looks up in the Event Assignment
|
||||
* Structure (EAS) table for an Event Notification Descriptor (END)
|
||||
* configured for the source. Each Event Notification Descriptor defines
|
||||
* a notification path to a CPU and an in-memory Event Queue, in which
|
||||
* will be enqueued an EQ data for the OS to pull.
|
||||
*
|
||||
* The IVPE determines if a Notification Virtual Target (NVT) can
|
||||
* handle the event by scanning the thread contexts of the VCPUs
|
||||
* dispatched on the processor HW threads. It maintains the state of
|
||||
* the thread interrupt context (TCTX) of each thread in a NVT table.
|
||||
*
|
||||
* = Acronyms
|
||||
*
|
||||
* Description In XIVE 1.0, used to be referred as
|
||||
*
|
||||
* EAS Event Assignment Structure IVE Interrupt Virt. Entry
|
||||
* EAT Event Assignment Table IVT Interrupt Virt. Table
|
||||
* ENDT Event Notif. Descriptor Table EQDT Event Queue Desc. Table
|
||||
* EQ Event Queue same
|
||||
* ESB Event State Buffer SBE State Bit Entry
|
||||
* NVT Notif. Virtual Target VPD Virtual Processor Desc.
|
||||
* NVTT Notif. Virtual Target Table VPDT Virtual Processor Desc. Table
|
||||
* TCTX Thread interrupt Context
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PPC_XIVE_H
|
||||
#define PPC_XIVE_H
|
||||
|
||||
#include "hw/qdev-core.h"
|
||||
|
||||
/*
|
||||
* XIVE Interrupt Source
|
||||
*/
|
||||
|
||||
#define TYPE_XIVE_SOURCE "xive-source"
|
||||
#define XIVE_SOURCE(obj) OBJECT_CHECK(XiveSource, (obj), TYPE_XIVE_SOURCE)
|
||||
|
||||
/*
|
||||
* XIVE Interrupt Source characteristics, which define how the ESB are
|
||||
* controlled.
|
||||
*/
|
||||
#define XIVE_SRC_H_INT_ESB 0x1 /* ESB managed with hcall H_INT_ESB */
|
||||
#define XIVE_SRC_STORE_EOI 0x2 /* Store EOI supported */
|
||||
|
||||
typedef struct XiveSource {
|
||||
DeviceState parent;
|
||||
|
||||
/* IRQs */
|
||||
uint32_t nr_irqs;
|
||||
qemu_irq *qirqs;
|
||||
|
||||
/* PQ bits */
|
||||
uint8_t *status;
|
||||
|
||||
/* ESB memory region */
|
||||
uint64_t esb_flags;
|
||||
uint32_t esb_shift;
|
||||
MemoryRegion esb_mmio;
|
||||
} XiveSource;
|
||||
|
||||
/*
|
||||
* ESB MMIO setting. Can be one page, for both source triggering and
|
||||
* source management, or two different pages. See below for magic
|
||||
* values.
|
||||
*/
|
||||
#define XIVE_ESB_4K 12 /* PSI HB only */
|
||||
#define XIVE_ESB_4K_2PAGE 13
|
||||
#define XIVE_ESB_64K 16
|
||||
#define XIVE_ESB_64K_2PAGE 17
|
||||
|
||||
static inline bool xive_source_esb_has_2page(XiveSource *xsrc)
|
||||
{
|
||||
return xsrc->esb_shift == XIVE_ESB_64K_2PAGE ||
|
||||
xsrc->esb_shift == XIVE_ESB_4K_2PAGE;
|
||||
}
|
||||
|
||||
/* The trigger page is always the first/even page */
|
||||
static inline hwaddr xive_source_esb_page(XiveSource *xsrc, uint32_t srcno)
|
||||
{
|
||||
assert(srcno < xsrc->nr_irqs);
|
||||
return (1ull << xsrc->esb_shift) * srcno;
|
||||
}
|
||||
|
||||
/* In a two pages ESB MMIO setting, the odd page is for management */
|
||||
static inline hwaddr xive_source_esb_mgmt(XiveSource *xsrc, int srcno)
|
||||
{
|
||||
hwaddr addr = xive_source_esb_page(xsrc, srcno);
|
||||
|
||||
if (xive_source_esb_has_2page(xsrc)) {
|
||||
addr += (1 << (xsrc->esb_shift - 1));
|
||||
}
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Each interrupt source has a 2-bit state machine which can be
|
||||
* controlled by MMIO. P indicates that an interrupt is pending (has
|
||||
* been sent to a queue and is waiting for an EOI). Q indicates that
|
||||
* the interrupt has been triggered while pending.
|
||||
*
|
||||
* This acts as a coalescing mechanism in order to guarantee that a
|
||||
* given interrupt only occurs at most once in a queue.
|
||||
*
|
||||
* When doing an EOI, the Q bit will indicate if the interrupt
|
||||
* needs to be re-triggered.
|
||||
*/
|
||||
#define XIVE_ESB_VAL_P 0x2
|
||||
#define XIVE_ESB_VAL_Q 0x1
|
||||
|
||||
#define XIVE_ESB_RESET 0x0
|
||||
#define XIVE_ESB_PENDING XIVE_ESB_VAL_P
|
||||
#define XIVE_ESB_QUEUED (XIVE_ESB_VAL_P | XIVE_ESB_VAL_Q)
|
||||
#define XIVE_ESB_OFF XIVE_ESB_VAL_Q
|
||||
|
||||
/*
|
||||
* "magic" Event State Buffer (ESB) MMIO offsets.
|
||||
*
|
||||
* The following offsets into the ESB MMIO allow to read or manipulate
|
||||
* the PQ bits. They must be used with an 8-byte load instruction.
|
||||
* They all return the previous state of the interrupt (atomically).
|
||||
*
|
||||
* Additionally, some ESB pages support doing an EOI via a store and
|
||||
* some ESBs support doing a trigger via a separate trigger page.
|
||||
*/
|
||||
#define XIVE_ESB_STORE_EOI 0x400 /* Store */
|
||||
#define XIVE_ESB_LOAD_EOI 0x000 /* Load */
|
||||
#define XIVE_ESB_GET 0x800 /* Load */
|
||||
#define XIVE_ESB_SET_PQ_00 0xc00 /* Load */
|
||||
#define XIVE_ESB_SET_PQ_01 0xd00 /* Load */
|
||||
#define XIVE_ESB_SET_PQ_10 0xe00 /* Load */
|
||||
#define XIVE_ESB_SET_PQ_11 0xf00 /* Load */
|
||||
|
||||
uint8_t xive_source_esb_get(XiveSource *xsrc, uint32_t srcno);
|
||||
uint8_t xive_source_esb_set(XiveSource *xsrc, uint32_t srcno, uint8_t pq);
|
||||
|
||||
void xive_source_pic_print_info(XiveSource *xsrc, uint32_t offset,
|
||||
Monitor *mon);
|
||||
|
||||
static inline qemu_irq xive_source_qirq(XiveSource *xsrc, uint32_t srcno)
|
||||
{
|
||||
assert(srcno < xsrc->nr_irqs);
|
||||
return xsrc->qirqs[srcno];
|
||||
}
|
||||
|
||||
#endif /* PPC_XIVE_H */
|
Loading…
Reference in New Issue
Block a user