mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-06-04 21:29:43 +00:00
a guest may use per-cpu vector allocation mechanism which may lead to same vector on different vcpu for different vioapic pins. when we broadcast EOI from vlapic to vioapic, it could trigger ptdev_intx_ack for incorrect vioapic pin which will lead to incorrect vioapic irq deassert. old implementation is recording assert & deassert by acnt, like: - assert -> acnt++ - deassert -> acnt-- which means an incorrect deassert may descrease acnt from 0 to -1, it leads to error. this patch change the recording method for a pin assert/deassert, it just use pin_state bit set/clear to indicate line state: - high -> pin_state bitmap set - low -> pin_state bitmap clear and the irq assert will be triggered based on polarity setting during line state changing. it can avoid the failure of incorrect deassert on old implementation, and for ptdev sharing irq, the dev hardware could re-trigger interrupt after deassert. but for emulated device, there is no mechanism to re-trigger interrupt if there is irq sharing - for DM, we already has constraint to restrict vGSI sharing, so there is no problem of it. Tracked-On: #1269 Signed-off-by: Jason Chen CJ <jason.cj.chen@intel.com>
615 lines
16 KiB
C
615 lines
16 KiB
C
/*-
|
|
* Copyright (c) 2013 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
|
|
* Copyright (c) 2013 Neel Natu <neel@freebsd.org>
|
|
* Copyright (c) 2017 Intel Corporation
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* $FreeBSD$
|
|
*/
|
|
|
|
#define pr_prefix "vioapic: "
|
|
|
|
#include <hypervisor.h>
|
|
|
|
#define RTBL_RO_BITS (uint32_t)(IOAPIC_RTE_REM_IRR | IOAPIC_RTE_DELIVS)
|
|
#define NEED_TMR_UPDATE (IOAPIC_RTE_TRGRMOD | IOAPIC_RTE_DELMOD | IOAPIC_RTE_INTVEC)
|
|
|
|
#define ACRN_DBG_IOAPIC 6U
|
|
#define ACRN_IOAPIC_VERSION 0x11U
|
|
|
|
#define IOAPIC_ID_MASK 0x0f000000U
|
|
#define MASK_ALL_INTERRUPTS 0x0001000000010000UL
|
|
#define IOAPIC_RTE_LOW_INTVEC ((uint32_t)IOAPIC_RTE_INTVEC)
|
|
|
|
/**
|
|
* @pre pin < vioapic_pincount(vm)
|
|
*/
|
|
static void
|
|
vioapic_send_intr(struct acrn_vioapic *vioapic, uint32_t pin)
|
|
{
|
|
uint32_t vector, dest, delmode;
|
|
union ioapic_rte rte;
|
|
bool level, phys;
|
|
|
|
rte = vioapic->rtbl[pin];
|
|
|
|
if ((rte.full & IOAPIC_RTE_INTMASK) == IOAPIC_RTE_INTMSET) {
|
|
dev_dbg(ACRN_DBG_IOAPIC, "ioapic pin%hhu: masked", pin);
|
|
return;
|
|
}
|
|
|
|
phys = ((rte.full & IOAPIC_RTE_DESTMOD) == IOAPIC_RTE_DESTPHY);
|
|
delmode = (uint32_t)(rte.full & IOAPIC_RTE_DELMOD);
|
|
level = ((rte.full & IOAPIC_RTE_TRGRLVL) != 0UL);
|
|
/* For level trigger irq, avoid send intr if
|
|
* previous one hasn't received EOI
|
|
*/
|
|
if (level) {
|
|
if ((vioapic->rtbl[pin].full & IOAPIC_RTE_REM_IRR) != 0UL) {
|
|
return;
|
|
}
|
|
vioapic->rtbl[pin].full |= IOAPIC_RTE_REM_IRR;
|
|
}
|
|
|
|
vector = rte.u.lo_32 & IOAPIC_RTE_LOW_INTVEC;
|
|
dest = (uint32_t)(rte.full >> IOAPIC_RTE_DEST_SHIFT);
|
|
vlapic_deliver_intr(vioapic->vm, level, dest, phys,
|
|
delmode, vector, false);
|
|
}
|
|
|
|
/**
|
|
* @pre pin < vioapic_pincount(vm)
|
|
*/
|
|
static void
|
|
vioapic_set_pinstate(struct acrn_vioapic *vioapic, uint16_t pin, uint32_t level)
|
|
{
|
|
uint32_t old_lvl;
|
|
union ioapic_rte rte = vioapic->rtbl[pin];
|
|
|
|
old_lvl = (uint32_t)bitmap_test(pin & 0x3FU, &vioapic->pin_state[pin >> 6U]);
|
|
if (level == 0U) {
|
|
/* clear pin_state and deliver interrupt according to polarity */
|
|
bitmap_clear_nolock(pin & 0x3FU,
|
|
&vioapic->pin_state[pin >> 6U]);
|
|
if (((rte.full & IOAPIC_RTE_INTPOL) != 0UL)
|
|
&& old_lvl != level) {
|
|
vioapic_send_intr(vioapic, pin);
|
|
}
|
|
} else {
|
|
/* set pin_state and deliver intrrupt according to polarity */
|
|
bitmap_set_nolock(pin & 0x3FU, &vioapic->pin_state[pin >> 6U]);
|
|
if (((rte.full & IOAPIC_RTE_INTPOL) == 0UL)
|
|
&& old_lvl != level) {
|
|
vioapic_send_intr(vioapic, pin);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @pre irq < vioapic_pincount(vm)
|
|
* @pre operation value shall be one of the folllowing values:
|
|
* GSI_SET_HIGH
|
|
* GSI_SET_LOW
|
|
* GSI_RAISING_PULSE
|
|
* GSI_FALLING_PULSE
|
|
* @pre call with vioapic lock
|
|
*/
|
|
void
|
|
vioapic_set_irq_nolock(struct vm *vm, uint32_t irq, uint32_t operation)
|
|
{
|
|
struct acrn_vioapic *vioapic;
|
|
uint16_t pin = (uint16_t)irq;
|
|
|
|
vioapic = vm_ioapic(vm);
|
|
|
|
switch (operation) {
|
|
case GSI_SET_HIGH:
|
|
vioapic_set_pinstate(vioapic, pin, 1U);
|
|
break;
|
|
case GSI_SET_LOW:
|
|
vioapic_set_pinstate(vioapic, pin, 0U);
|
|
break;
|
|
case GSI_RAISING_PULSE:
|
|
vioapic_set_pinstate(vioapic, pin, 1U);
|
|
vioapic_set_pinstate(vioapic, pin, 0U);
|
|
break;
|
|
case GSI_FALLING_PULSE:
|
|
vioapic_set_pinstate(vioapic, pin, 0U);
|
|
vioapic_set_pinstate(vioapic, pin, 1U);
|
|
break;
|
|
default:
|
|
/*
|
|
* The function caller could guarantee the pre condition.
|
|
*/
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @pre irq < vioapic_pincount(vm)
|
|
* @pre operation value shall be one of the folllowing values:
|
|
* GSI_SET_HIGH
|
|
* GSI_SET_LOW
|
|
* GSI_RAISING_PULSE
|
|
* GSI_FALLING_PULSE
|
|
*/
|
|
void
|
|
vioapic_set_irq(struct vm *vm, uint32_t irq, uint32_t operation)
|
|
{
|
|
struct acrn_vioapic *vioapic = vm_ioapic(vm);
|
|
|
|
spinlock_obtain(&(vioapic->mtx));
|
|
vioapic_set_irq_nolock(vm, irq, operation);
|
|
spinlock_release(&(vioapic->mtx));
|
|
}
|
|
|
|
/*
|
|
* Reset the vlapic's trigger-mode register to reflect the ioapic pin
|
|
* configuration.
|
|
*/
|
|
void
|
|
vioapic_update_tmr(struct vcpu *vcpu)
|
|
{
|
|
struct acrn_vioapic *vioapic;
|
|
struct acrn_vlapic *vlapic;
|
|
union ioapic_rte rte;
|
|
uint32_t vector, delmode;
|
|
bool level;
|
|
uint32_t pin, pincount;
|
|
|
|
vlapic = vcpu_vlapic(vcpu);
|
|
vioapic = vm_ioapic(vcpu->vm);
|
|
|
|
spinlock_obtain(&(vioapic->mtx));
|
|
pincount = vioapic_pincount(vcpu->vm);
|
|
for (pin = 0U; pin < pincount; pin++) {
|
|
rte = vioapic->rtbl[pin];
|
|
|
|
level = ((rte.full & IOAPIC_RTE_TRGRLVL) != 0UL);
|
|
|
|
/*
|
|
* For a level-triggered 'pin' let the vlapic figure out if
|
|
* an assertion on this 'pin' would result in an interrupt
|
|
* being delivered to it. If yes, then it will modify the
|
|
* TMR bit associated with this vector to level-triggered.
|
|
*/
|
|
delmode = (uint32_t)(rte.full & IOAPIC_RTE_DELMOD);
|
|
vector = rte.u.lo_32 & IOAPIC_RTE_LOW_INTVEC;
|
|
vlapic_set_tmr_one_vec(vlapic, delmode, vector, level);
|
|
}
|
|
vlapic_apicv_batch_set_tmr(vlapic);
|
|
spinlock_release(&(vioapic->mtx));
|
|
}
|
|
|
|
static uint32_t
|
|
vioapic_indirect_read(struct acrn_vioapic *vioapic, uint32_t addr)
|
|
{
|
|
uint32_t regnum;
|
|
uint32_t pin, pincount = vioapic_pincount(vioapic->vm);
|
|
|
|
regnum = addr & 0xffU;
|
|
switch (regnum) {
|
|
case IOAPIC_ID:
|
|
return vioapic->id;
|
|
case IOAPIC_VER:
|
|
return (((uint32_t)pincount - 1U) << MAX_RTE_SHIFT) |
|
|
ACRN_IOAPIC_VERSION;
|
|
case IOAPIC_ARB:
|
|
return vioapic->id;
|
|
default:
|
|
/*
|
|
* In this switch statement, regnum shall either be IOAPIC_ID or
|
|
* IOAPIC_VER or IOAPIC_ARB.
|
|
* All the other cases will be handled properly later after this
|
|
* switch statement.
|
|
*/
|
|
break;
|
|
}
|
|
|
|
/* redirection table entries */
|
|
if ((regnum >= IOAPIC_REDTBL) &&
|
|
(regnum < (IOAPIC_REDTBL + (pincount * 2U)))) {
|
|
uint32_t addr_offset = regnum - IOAPIC_REDTBL;
|
|
uint32_t rte_offset = addr_offset >> 1U;
|
|
pin = rte_offset;
|
|
if ((addr_offset & 0x1U) != 0U) {
|
|
return vioapic->rtbl[pin].u.hi_32;
|
|
} else {
|
|
return vioapic->rtbl[pin].u.lo_32;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline bool vioapic_need_intr(struct acrn_vioapic *vioapic, uint16_t pin)
|
|
{
|
|
uint32_t lvl =(uint32_t)bitmap_test(pin & 0x3FU,
|
|
&vioapic->pin_state[pin >> 6U]);
|
|
union ioapic_rte rte = vioapic->rtbl[pin];
|
|
|
|
return !!((((rte.full & IOAPIC_RTE_INTPOL) != 0UL) && lvl == 0U) ||
|
|
(((rte.full & IOAPIC_RTE_INTPOL) == 0UL) && lvl != 0U));
|
|
}
|
|
|
|
/*
|
|
* Due to the race between vcpus, ensure to do spinlock_obtain(&(vioapic->mtx))
|
|
* & spinlock_release(&(vioapic->mtx)) by caller.
|
|
*/
|
|
static void
|
|
vioapic_indirect_write(struct acrn_vioapic *vioapic, uint32_t addr,
|
|
uint32_t data)
|
|
{
|
|
union ioapic_rte last, new;
|
|
uint64_t changed;
|
|
uint32_t regnum;
|
|
uint32_t pin, pincount = vioapic_pincount(vioapic->vm);
|
|
|
|
regnum = addr & 0xffUL;
|
|
switch (regnum) {
|
|
case IOAPIC_ID:
|
|
vioapic->id = data & IOAPIC_ID_MASK;
|
|
break;
|
|
case IOAPIC_VER:
|
|
case IOAPIC_ARB:
|
|
/* readonly */
|
|
break;
|
|
default:
|
|
/*
|
|
* In this switch statement, regnum shall either be IOAPIC_ID or
|
|
* IOAPIC_VER or IOAPIC_ARB.
|
|
* All the other cases will be handled properly later after this
|
|
* switch statement.
|
|
*/
|
|
break;
|
|
}
|
|
|
|
/* redirection table entries */
|
|
if ((regnum >= IOAPIC_REDTBL) &&
|
|
(regnum < (IOAPIC_REDTBL + (pincount * 2U)))) {
|
|
uint32_t addr_offset = regnum - IOAPIC_REDTBL;
|
|
uint32_t rte_offset = addr_offset >> 1U;
|
|
pin = rte_offset;
|
|
|
|
last = vioapic->rtbl[pin];
|
|
new = last;
|
|
if ((addr_offset & 1U) != 0U) {
|
|
new.u.hi_32 = data;
|
|
} else {
|
|
new.u.lo_32 &= RTBL_RO_BITS;
|
|
new.u.lo_32 |= (data & ~RTBL_RO_BITS);
|
|
}
|
|
|
|
/* In some special scenarios, the LAPIC somehow hasn't send
|
|
* EOI to IOAPIC which cause the Remote IRR bit can't be clear.
|
|
* To clear it, some OSes will use EOI Register to clear it for
|
|
* 0x20 version IOAPIC, otherwise use switch Trigger Mode to
|
|
* Edge Sensitive to clear it.
|
|
*/
|
|
if ((new.full & IOAPIC_RTE_TRGRLVL) == 0U) {
|
|
new.full &= ~IOAPIC_RTE_REM_IRR;
|
|
}
|
|
|
|
changed = last.full ^ new.full;
|
|
/* pin0 from vpic mask/unmask */
|
|
if ((pin == 0U) && ((changed & IOAPIC_RTE_INTMASK) != 0UL)) {
|
|
/* mask -> umask */
|
|
if ((last.full & IOAPIC_RTE_INTMASK) != 0UL) {
|
|
if ((vioapic->vm->wire_mode ==
|
|
VPIC_WIRE_NULL) ||
|
|
(vioapic->vm->wire_mode ==
|
|
VPIC_WIRE_INTR)) {
|
|
vioapic->vm->wire_mode =
|
|
VPIC_WIRE_IOAPIC;
|
|
dev_dbg(ACRN_DBG_IOAPIC,
|
|
"vpic wire mode -> IOAPIC");
|
|
} else {
|
|
pr_err("WARNING: invalid vpic wire mode change");
|
|
return;
|
|
}
|
|
/* unmask -> mask */
|
|
} else {
|
|
if (vioapic->vm->wire_mode ==
|
|
VPIC_WIRE_IOAPIC) {
|
|
vioapic->vm->wire_mode =
|
|
VPIC_WIRE_INTR;
|
|
dev_dbg(ACRN_DBG_IOAPIC,
|
|
"vpic wire mode -> INTR");
|
|
}
|
|
}
|
|
}
|
|
vioapic->rtbl[pin] = new;
|
|
dev_dbg(ACRN_DBG_IOAPIC, "ioapic pin%hhu: redir table entry %#lx",
|
|
pin, vioapic->rtbl[pin].full);
|
|
/*
|
|
* If "Trigger Mode" or "Delivery Mode" or "Vector"
|
|
* in the redirection table entry have changed then
|
|
* rendezvous all the vcpus to update their vlapic
|
|
* trigger-mode registers.
|
|
*/
|
|
if ((changed & NEED_TMR_UPDATE) != 0UL) {
|
|
uint16_t i;
|
|
struct vcpu *vcpu;
|
|
|
|
dev_dbg(ACRN_DBG_IOAPIC,
|
|
"ioapic pin%hhu: recalculate vlapic trigger-mode reg",
|
|
pin);
|
|
|
|
foreach_vcpu(i, vioapic->vm, vcpu) {
|
|
vcpu_make_request(vcpu, ACRN_REQUEST_TMR_UPDATE);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Generate an interrupt if the following conditions are met:
|
|
* - pin is not masked
|
|
* - previous interrupt has been EOIed
|
|
* - pin level is asserted
|
|
*/
|
|
if (((vioapic->rtbl[pin].full & IOAPIC_RTE_INTMASK) ==
|
|
IOAPIC_RTE_INTMCLR) &&
|
|
((vioapic->rtbl[pin].full & IOAPIC_RTE_REM_IRR) == 0UL)
|
|
&& vioapic_need_intr(vioapic, (uint16_t)pin)) {
|
|
dev_dbg(ACRN_DBG_IOAPIC,
|
|
"ioapic pin%hhu: asserted at rtbl write", pin);
|
|
vioapic_send_intr(vioapic, pin);
|
|
}
|
|
|
|
/* remap for ptdev */
|
|
if (((new.full & IOAPIC_RTE_INTMASK) == 0UL) ||
|
|
((last.full & IOAPIC_RTE_INTMASK) == 0UL)) {
|
|
/* VM enable intr */
|
|
/* NOTE: only support max 256 pin */
|
|
ptdev_intx_pin_remap(vioapic->vm,
|
|
(uint8_t)pin, PTDEV_VPIN_IOAPIC);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
vioapic_mmio_rw(struct acrn_vioapic *vioapic, uint64_t gpa,
|
|
uint32_t *data, bool do_read)
|
|
{
|
|
uint32_t offset;
|
|
|
|
offset = (uint32_t)(gpa - VIOAPIC_BASE);
|
|
|
|
spinlock_obtain(&(vioapic->mtx));
|
|
|
|
/* The IOAPIC specification allows 32-bit wide accesses to the
|
|
* IOAPIC_REGSEL (offset 0) and IOAPIC_WINDOW (offset 16) registers.
|
|
*/
|
|
switch (offset) {
|
|
case IOAPIC_REGSEL:
|
|
if (do_read) {
|
|
*data = vioapic->ioregsel;
|
|
} else {
|
|
vioapic->ioregsel = *data & 0xFFU;
|
|
}
|
|
break;
|
|
case IOAPIC_WINDOW:
|
|
if (do_read) {
|
|
*data = vioapic_indirect_read(vioapic,
|
|
vioapic->ioregsel);
|
|
} else {
|
|
vioapic_indirect_write(vioapic,
|
|
vioapic->ioregsel, *data);
|
|
}
|
|
break;
|
|
default:
|
|
if (do_read) {
|
|
*data = 0xFFFFFFFFU;
|
|
}
|
|
break;
|
|
}
|
|
|
|
spinlock_release(&(vioapic->mtx));
|
|
}
|
|
|
|
void
|
|
vioapic_process_eoi(struct vm *vm, uint32_t vector)
|
|
{
|
|
struct acrn_vioapic *vioapic;
|
|
uint32_t pin, pincount = vioapic_pincount(vm);
|
|
union ioapic_rte rte;
|
|
|
|
if ((vector < VECTOR_DYNAMIC_START) || (vector > NR_MAX_VECTOR)) {
|
|
pr_err("vioapic_process_eoi: invalid vector %u", vector);
|
|
}
|
|
|
|
vioapic = vm_ioapic(vm);
|
|
dev_dbg(ACRN_DBG_IOAPIC, "ioapic processing eoi for vector %u", vector);
|
|
|
|
/* notify device to ack if assigned pin */
|
|
for (pin = 0U; pin < pincount; pin++) {
|
|
rte = vioapic->rtbl[pin];
|
|
if (((rte.u.lo_32 & IOAPIC_RTE_LOW_INTVEC) != vector) ||
|
|
((rte.full & IOAPIC_RTE_REM_IRR) == 0UL)) {
|
|
continue;
|
|
}
|
|
|
|
ptdev_intx_ack(vm, (uint8_t)pin, PTDEV_VPIN_IOAPIC);
|
|
}
|
|
|
|
/*
|
|
* XXX keep track of the pins associated with this vector instead
|
|
* of iterating on every single pin each time.
|
|
*/
|
|
spinlock_obtain(&(vioapic->mtx));
|
|
for (pin = 0U; pin < pincount; pin++) {
|
|
rte = vioapic->rtbl[pin];
|
|
if (((rte.u.lo_32 & IOAPIC_RTE_LOW_INTVEC) != vector) ||
|
|
((rte.full & IOAPIC_RTE_REM_IRR) == 0UL)) {
|
|
continue;
|
|
}
|
|
|
|
vioapic->rtbl[pin].full &= (~IOAPIC_RTE_REM_IRR);
|
|
if (vioapic_need_intr(vioapic, (uint16_t)pin)) {
|
|
dev_dbg(ACRN_DBG_IOAPIC,
|
|
"ioapic pin%hhu: asserted at eoi", pin);
|
|
vioapic_send_intr(vioapic, pin);
|
|
}
|
|
}
|
|
spinlock_release(&(vioapic->mtx));
|
|
}
|
|
|
|
void
|
|
vioapic_reset(struct acrn_vioapic *vioapic)
|
|
{
|
|
uint32_t pin, pincount;
|
|
|
|
/* Initialize all redirection entries to mask all interrupts */
|
|
pincount = vioapic_pincount(vioapic->vm);
|
|
for (pin = 0U; pin < pincount; pin++) {
|
|
vioapic->rtbl[pin].full = MASK_ALL_INTERRUPTS;
|
|
}
|
|
vioapic->id = 0U;
|
|
vioapic->ioregsel = 0U;
|
|
}
|
|
|
|
void
|
|
vioapic_init(struct vm *vm)
|
|
{
|
|
vm->arch_vm.vioapic.vm = vm;
|
|
spinlock_init(&(vm->arch_vm.vioapic.mtx));
|
|
|
|
vioapic_reset(vm_ioapic(vm));
|
|
|
|
register_mmio_emulation_handler(vm,
|
|
vioapic_mmio_access_handler,
|
|
(uint64_t)VIOAPIC_BASE,
|
|
(uint64_t)VIOAPIC_BASE + VIOAPIC_SIZE,
|
|
NULL);
|
|
}
|
|
|
|
void
|
|
vioapic_cleanup(struct acrn_vioapic *vioapic)
|
|
{
|
|
unregister_mmio_emulation_handler(vioapic->vm,
|
|
(uint64_t)VIOAPIC_BASE,
|
|
(uint64_t)VIOAPIC_BASE + VIOAPIC_SIZE);
|
|
}
|
|
|
|
uint32_t
|
|
vioapic_pincount(struct vm *vm)
|
|
{
|
|
if (is_vm0(vm)) {
|
|
return REDIR_ENTRIES_HW;
|
|
} else {
|
|
return VIOAPIC_RTE_NUM;
|
|
}
|
|
}
|
|
|
|
int vioapic_mmio_access_handler(struct vcpu *vcpu, struct io_request *io_req)
|
|
{
|
|
struct vm *vm = vcpu->vm;
|
|
struct acrn_vioapic *vioapic;
|
|
struct mmio_request *mmio = &io_req->reqs.mmio;
|
|
uint64_t gpa = mmio->address;
|
|
int ret = 0;
|
|
|
|
vioapic = vm_ioapic(vm);
|
|
|
|
/* Note all RW to IOAPIC are 32-Bit in size */
|
|
if (mmio->size == 4UL) {
|
|
uint32_t data = (uint32_t)mmio->value;
|
|
|
|
if (mmio->direction == REQUEST_READ) {
|
|
vioapic_mmio_rw(vioapic, gpa, &data, true);
|
|
mmio->value = (uint64_t)data;
|
|
|
|
} else if (mmio->direction == REQUEST_WRITE) {
|
|
vioapic_mmio_rw(vioapic, gpa, &data, false);
|
|
|
|
} else
|
|
ret = -EINVAL;
|
|
} else {
|
|
pr_err("All RW to IOAPIC must be 32-bits in size");
|
|
ret = -EINVAL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @pre vm->arch_vm.vioapic != NULL
|
|
* @pre rte != NULL
|
|
*/
|
|
void vioapic_get_rte(struct vm *vm, uint32_t pin, union ioapic_rte *rte)
|
|
{
|
|
struct acrn_vioapic *vioapic;
|
|
|
|
vioapic = vm_ioapic(vm);
|
|
*rte = vioapic->rtbl[pin];
|
|
}
|
|
|
|
#ifdef HV_DEBUG
|
|
void get_vioapic_info(char *str_arg, size_t str_max, uint16_t vmid)
|
|
{
|
|
char *str = str_arg;
|
|
size_t len, size = str_max;
|
|
union ioapic_rte rte;
|
|
uint32_t delmode, vector, dest;
|
|
bool level, phys, remote_irr, mask;
|
|
struct vm *vm = get_vm_from_vmid(vmid);
|
|
uint32_t pin, pincount;
|
|
|
|
if (vm == NULL) {
|
|
len = snprintf(str, size,
|
|
"\r\nvm is not exist for vmid %hu", vmid);
|
|
size -= len;
|
|
str += len;
|
|
goto END;
|
|
}
|
|
|
|
len = snprintf(str, size,
|
|
"\r\nPIN\tVEC\tDM\tDEST\tTM\tDELM\tIRR\tMASK");
|
|
size -= len;
|
|
str += len;
|
|
|
|
pincount = vioapic_pincount(vm);
|
|
rte.full = 0UL;
|
|
for (pin = 0U; pin < pincount; pin++) {
|
|
vioapic_get_rte(vm, pin, &rte);
|
|
mask = ((rte.full & IOAPIC_RTE_INTMASK) == IOAPIC_RTE_INTMSET);
|
|
remote_irr = ((rte.full & IOAPIC_RTE_REM_IRR) == IOAPIC_RTE_REM_IRR);
|
|
phys = ((rte.full & IOAPIC_RTE_DESTMOD) == IOAPIC_RTE_DESTPHY);
|
|
delmode = (uint32_t)(rte.full & IOAPIC_RTE_DELMOD);
|
|
level = ((rte.full & IOAPIC_RTE_TRGRLVL) != 0UL);
|
|
vector = rte.u.lo_32 & IOAPIC_RTE_LOW_INTVEC;
|
|
dest = (uint32_t)(rte.full >> IOAPIC_RTE_DEST_SHIFT);
|
|
|
|
len = snprintf(str, size,
|
|
"\r\n%hhu\t0x%X\t%s\t0x%X\t%s\t%u\t%d\t%d",
|
|
pin, vector, phys ? "phys" : "logic",
|
|
dest, level ? "level" : "edge",
|
|
delmode >> 8U, remote_irr, mask);
|
|
size -= len;
|
|
str += len;
|
|
}
|
|
END:
|
|
snprintf(str, size, "\r\n");
|
|
}
|
|
#endif /* HV_DEBUG */
|