hv: vioapic init for SOS VM on platforms with multiple IO-APICs

For SOS VM, when the target platform has multiple IO-APICs, there
should be equal number of virtual IO-APICs.

This patch adds support for emulating multiple vIOAPICs per VM.

Tracked-On: #4151
Signed-off-by: Sainath Grandhi <sainath.grandhi@intel.com>
Acked-by: Eddie Dong <eddie.dong@Intel.com>
This commit is contained in:
Sainath Grandhi
2020-02-16 19:22:26 -08:00
committed by wenlingz
parent f3cf93656a
commit 4626c9154f
9 changed files with 206 additions and 73 deletions

View File

@@ -36,6 +36,7 @@
#include <ept.h>
#include <assign.h>
#include <logmsg.h>
#include <ioapic.h>
#define RTBL_RO_BITS ((uint32_t)0x00004000U | (uint32_t)0x00001000U) /*Remote IRR and Delivery Status bits*/
@@ -45,16 +46,16 @@
#define IOAPIC_ID_MASK 0x0f000000U
#define MASK_ALL_INTERRUPTS 0x0001000000010000UL
static inline struct acrn_vioapic *vm_ioapic(const struct acrn_vm *vm)
static inline struct acrn_vioapics *vm_ioapics(const struct acrn_vm *vm)
{
return (struct acrn_vioapic *)&(vm->arch_vm.vioapic);
return (struct acrn_vioapics *)&(vm->arch_vm.vioapics);
}
/**
* @pre pin < vioapic_pincount(vm)
* @pre pin < vioapic->nr_pins
*/
static void
vioapic_generate_intr(struct acrn_vioapic *vioapic, uint32_t pin)
vioapic_generate_intr(struct acrn_single_vioapic *vioapic, uint32_t pin)
{
uint32_t vector, dest, delmode;
union ioapic_rte rte;
@@ -84,10 +85,10 @@ vioapic_generate_intr(struct acrn_vioapic *vioapic, uint32_t pin)
}
/**
* @pre pin < vioapic_pincount(vm)
* @pre pin < vioapic->nr_pins
*/
static void
vioapic_set_pinstate(struct acrn_vioapic *vioapic, uint32_t pin, uint32_t level)
vioapic_set_pinstate(struct acrn_single_vioapic *vioapic, uint32_t pin, uint32_t level)
{
uint32_t old_lvl;
union ioapic_rte rte;
@@ -113,6 +114,30 @@ vioapic_set_pinstate(struct acrn_vioapic *vioapic, uint32_t pin, uint32_t level)
}
}
static struct acrn_single_vioapic *
vgsi_to_vioapic_and_vpin(const struct acrn_vm *vm, uint32_t vgsi, uint32_t *vpin)
{
struct acrn_single_vioapic *vioapic;
uint8_t vioapic_index = 0U;
if (is_sos_vm(vm)) {
/*
* Utilize platform ioapic_info for SOS VM
*/
vioapic_index = get_gsi_to_ioapic_index(vgsi);
if (vpin != NULL) {
*vpin = gsi_to_ioapic_pin(vgsi);
}
} else {
if (vpin != NULL) {
*vpin = vgsi;
}
}
vioapic = (struct acrn_single_vioapic *)&(vm->arch_vm.vioapics.vioapic_array[vioapic_index]);
return vioapic;
}
/**
* @brief Set vIOAPIC IRQ line status.
*
@@ -120,11 +145,11 @@ vioapic_set_pinstate(struct acrn_vioapic *vioapic, uint32_t pin, uint32_t level)
* operation be done with ioapic lock.
*
* @param[in] vm Pointer to target VM
* @param[in] irqline Target IRQ number
* @param[in] vgsi Target GSI number
* @param[in] operation Action options: GSI_SET_HIGH/GSI_SET_LOW/
* GSI_RAISING_PULSE/GSI_FALLING_PULSE
*
* @pre irqline < vioapic_pincount(vm)
* @pre vgsi < get_vm_gsicount(vm)
* @pre vm != NULL
* @pre vioapic->ready == true
* @return None
@@ -132,10 +157,10 @@ vioapic_set_pinstate(struct acrn_vioapic *vioapic, uint32_t pin, uint32_t level)
void
vioapic_set_irqline_nolock(const struct acrn_vm *vm, uint32_t vgsi, uint32_t operation)
{
struct acrn_vioapic *vioapic;
uint32_t pin = vgsi;
struct acrn_single_vioapic *vioapic;
uint32_t pin;
vioapic = vm_ioapic(vm);
vioapic = vgsi_to_vioapic_and_vpin(vm, vgsi, &pin);
switch (operation) {
case GSI_SET_HIGH:
@@ -164,11 +189,11 @@ vioapic_set_irqline_nolock(const struct acrn_vm *vm, uint32_t vgsi, uint32_t ope
* @brief Set vIOAPIC IRQ line status.
*
* @param[in] vm Pointer to target VM
* @param[in] irqline Target IRQ number
* @param[in] vgsi Target GSI number
* @param[in] operation Action options: GSI_SET_HIGH/GSI_SET_LOW/
* GSI_RAISING_PULSE/GSI_FALLING_PULSE
*
* @pre irqline < vioapic_pincount(vm)
* @pre vgsi < get_vm_gsicount(vm)
* @pre vm != NULL
*
* @return None
@@ -177,20 +202,21 @@ void
vioapic_set_irqline_lock(const struct acrn_vm *vm, uint32_t vgsi, uint32_t operation)
{
uint64_t rflags;
uint32_t irqline = vgsi;
struct acrn_vioapic *vioapic = vm_ioapic(vm);
struct acrn_single_vioapic *vioapic;
vioapic = vgsi_to_vioapic_and_vpin(vm, vgsi, NULL);
if (vioapic->ready) {
spinlock_irqsave_obtain(&(vioapic->mtx), &rflags);
vioapic_set_irqline_nolock(vm, irqline, operation);
vioapic_set_irqline_nolock(vm, vgsi, operation);
spinlock_irqrestore_release(&(vioapic->mtx), rflags);
}
}
static uint32_t
vioapic_indirect_read(const struct acrn_vioapic *vioapic, uint32_t addr)
vioapic_indirect_read(const struct acrn_single_vioapic *vioapic, uint32_t addr)
{
uint32_t regnum, ret = 0U;
uint32_t pin, pincount = vioapic_pincount(vioapic->vm);
uint32_t pin, pincount = vioapic->nr_pins;
regnum = addr & 0xffU;
switch (regnum) {
@@ -229,7 +255,7 @@ vioapic_indirect_read(const struct acrn_vioapic *vioapic, uint32_t addr)
return ret;
}
static inline bool vioapic_need_intr(const struct acrn_vioapic *vioapic, uint16_t pin)
static inline bool vioapic_need_intr(const struct acrn_single_vioapic *vioapic, uint16_t pin)
{
uint32_t lvl;
union ioapic_rte rte;
@@ -250,11 +276,11 @@ static inline bool vioapic_need_intr(const struct acrn_vioapic *vioapic, uint16_
* spinlock_irqsave_obtain(&(vioapic->mtx), &rflags) & spinlock_irqrestore_release(&(vioapic->mtx), rflags)
* by caller.
*/
static void vioapic_indirect_write(struct acrn_vioapic *vioapic, uint32_t addr, uint32_t data)
static void vioapic_indirect_write(struct acrn_single_vioapic *vioapic, uint32_t addr, uint32_t data)
{
union ioapic_rte last, new, changed;
uint32_t regnum;
uint32_t pin, pincount = vioapic_pincount(vioapic->vm);
uint32_t pin, pincount = vioapic->nr_pins;
regnum = addr & 0xffUL;
switch (regnum) {
@@ -332,7 +358,8 @@ static void vioapic_indirect_write(struct acrn_vioapic *vioapic, uint32_t addr,
if ((new.bits.intr_mask == IOAPIC_RTE_MASK_CLR) || (last.bits.intr_mask == IOAPIC_RTE_MASK_CLR)) {
/* VM enable intr */
/* NOTE: only support max 256 pin */
(void)ptirq_intx_pin_remap(vioapic->vm, pin, INTX_CTLR_IOAPIC);
(void)ptirq_intx_pin_remap(vioapic->vm, vioapic->gsi_base + pin, INTX_CTLR_IOAPIC);
}
/*
@@ -352,7 +379,7 @@ static void vioapic_indirect_write(struct acrn_vioapic *vioapic, uint32_t addr,
}
static void
vioapic_mmio_rw(struct acrn_vioapic *vioapic, uint64_t gpa,
vioapic_mmio_rw(struct acrn_single_vioapic *vioapic, uint64_t gpa,
uint32_t *data, bool do_read)
{
uint32_t offset;
@@ -396,11 +423,10 @@ vioapic_mmio_rw(struct acrn_vioapic *vioapic, uint64_t gpa,
* @pre vm != NULL
* @pre vioapic->ready == true
*/
void
vioapic_process_eoi(struct acrn_vm *vm, uint32_t vector)
static void
vioapic_process_eoi(struct acrn_single_vioapic *vioapic, uint32_t vector)
{
struct acrn_vioapic *vioapic;
uint32_t pin, pincount = vioapic_pincount(vm);
uint32_t pin, pincount = vioapic->nr_pins;
union ioapic_rte rte;
uint64_t rflags;
@@ -408,7 +434,6 @@ vioapic_process_eoi(struct acrn_vm *vm, uint32_t vector)
pr_err("vioapic_process_eoi: invalid vector %u", vector);
}
vioapic = vm_ioapic(vm);
dev_dbg(DBG_LEVEL_VIOAPIC, "ioapic processing eoi for vector %u", vector);
/* notify device to ack if assigned pin */
@@ -419,7 +444,7 @@ vioapic_process_eoi(struct acrn_vm *vm, uint32_t vector)
continue;
}
ptirq_intx_ack(vm, pin, INTX_CTLR_IOAPIC);
ptirq_intx_ack(vioapic->vm, vioapic->gsi_base + pin, INTX_CTLR_IOAPIC);
}
/*
@@ -444,14 +469,28 @@ vioapic_process_eoi(struct acrn_vm *vm, uint32_t vector)
spinlock_irqrestore_release(&(vioapic->mtx), rflags);
}
void
vioapic_reset(struct acrn_vm *vm)
void vioapic_broadcast_eoi(const struct acrn_vm *vm, uint32_t vector)
{
struct acrn_single_vioapic *vioapic;
uint8_t vioapic_index;
/*
* For platforms with multiple IO-APICs, EOI message from LAPIC is
* broadcast to all IO-APICs. Emulating the same behavior here.
*/
for (vioapic_index = 0U; vioapic_index < vm->arch_vm.vioapics.ioapic_num; vioapic_index++) {
vioapic = &(vm_ioapics(vm)->vioapic_array[vioapic_index]);
vioapic_process_eoi(vioapic, vector);
}
}
static void reset_one_vioapic(struct acrn_single_vioapic *vioapic)
{
uint32_t pin, pincount;
struct acrn_vioapic *vioapic = vm_ioapic(vm);
/* Initialize all redirection entries to mask all interrupts */
pincount = vioapic_pincount(vm);
pincount = vioapic->nr_pins;
for (pin = 0U; pin < pincount; pin++) {
vioapic->rtbl[pin].full = MASK_ALL_INTERRUPTS;
}
@@ -459,35 +498,80 @@ vioapic_reset(struct acrn_vm *vm)
vioapic->ioregsel = 0U;
}
void reset_vioapics(const struct acrn_vm *vm)
{
struct acrn_vioapics *vioapics = vm_ioapics(vm);
uint8_t vioapic_index;
for (vioapic_index = 0U; vioapic_index < vioapics->ioapic_num; vioapic_index++) {
reset_one_vioapic(&vioapics->vioapic_array[vioapic_index]);
}
}
void
vioapic_init(struct acrn_vm *vm)
{
vm->arch_vm.vioapic.vm = vm;
spinlock_init(&(vm->arch_vm.vioapic.mtx));
struct ioapic_info *platform_ioapic_info;
uint8_t platform_ioapic_num;
uint8_t vioapic_index;
struct acrn_single_vioapic *vioapic;
vm->arch_vm.vioapic.base_addr = VIOAPIC_BASE;
if (is_sos_vm(vm)) {
vm->arch_vm.vioapic.nr_pins = REDIR_ENTRIES_HW;
platform_ioapic_num = get_platform_ioapic_info(&platform_ioapic_info);
vm->arch_vm.vioapics.ioapic_num = platform_ioapic_num;
for (vioapic_index = 0U; vioapic_index < platform_ioapic_num; vioapic_index++) {
vioapic = &vm->arch_vm.vioapics.vioapic_array[vioapic_index];
spinlock_init(&(vioapic->mtx));
vioapic->nr_pins = platform_ioapic_info[vioapic_index].nr_pins;
vioapic->base_addr = platform_ioapic_info[vioapic_index].addr;
vioapic->gsi_base = platform_ioapic_info[vioapic_index].gsi_base;
vioapic->vm = vm;
reset_one_vioapic(vioapic);
register_mmio_emulation_handler(vm,
vioapic_mmio_access_handler,
(uint64_t)vioapic->base_addr,
(uint64_t)vioapic->base_addr + VIOAPIC_SIZE,
(void *)vioapic, false);
ept_del_mr(vm, (uint64_t *)vm->arch_vm.nworld_eptp,
(uint64_t)vioapic->base_addr, VIOAPIC_SIZE);
vioapic->ready = true;
}
/*
* Maximum number of GSI is computed as GSI base of the IOAPIC i.e. enumerated last in ACPI MADT
* plus the number of interrupt pins of that IOAPIC.
*/
vm->arch_vm.vioapics.nr_gsi = platform_ioapic_info[platform_ioapic_num - 1U].gsi_base +
platform_ioapic_info[platform_ioapic_num - 1U].nr_pins;
} else {
vm->arch_vm.vioapic.nr_pins = VIOAPIC_RTE_NUM;
vm->arch_vm.vioapics.ioapic_num = 1U;
vioapic = &vm->arch_vm.vioapics.vioapic_array[0U];
spinlock_init(&(vioapic->mtx));
vioapic->nr_pins = VIOAPIC_RTE_NUM;
vioapic->base_addr = VIOAPIC_BASE;
vioapic->gsi_base = 0U;
vioapic->vm = vm;
reset_one_vioapic(&vm->arch_vm.vioapics.vioapic_array[0U]);
register_mmio_emulation_handler(vm,
vioapic_mmio_access_handler,
(uint64_t)vioapic->base_addr,
(uint64_t)vioapic->base_addr + VIOAPIC_SIZE,
(void *)vioapic, false);
ept_del_mr(vm, (uint64_t *)vm->arch_vm.nworld_eptp,
(uint64_t)vioapic->base_addr, VIOAPIC_SIZE);
vioapic->ready = true;
vm->arch_vm.vioapics.nr_gsi = VIOAPIC_RTE_NUM;
}
vioapic_reset(vm);
register_mmio_emulation_handler(vm,
vioapic_mmio_access_handler,
(uint64_t)vm->arch_vm.vioapic.base_addr,
(uint64_t)vm->arch_vm.vioapic.base_addr + VIOAPIC_SIZE,
(void *)&vm->arch_vm.vioapic, false);
ept_del_mr(vm, (uint64_t *)vm->arch_vm.nworld_eptp,
(uint64_t)vm->arch_vm.vioapic.base_addr, VIOAPIC_SIZE);
vm->arch_vm.vioapic.ready = true;
}
uint32_t
vioapic_pincount(const struct acrn_vm *vm)
get_vm_gsicount(const struct acrn_vm *vm)
{
return vm->arch_vm.vioapic.nr_pins;
return vm->arch_vm.vioapics.nr_gsi;
}
/*
@@ -496,7 +580,7 @@ vioapic_pincount(const struct acrn_vm *vm)
*/
int32_t vioapic_mmio_access_handler(struct io_request *io_req, void *handler_private_data)
{
struct acrn_vioapic *vioapic = (struct acrn_vioapic *)handler_private_data;
struct acrn_single_vioapic *vioapic = (struct acrn_single_vioapic *)handler_private_data;
struct mmio_request *mmio = &io_req->reqs.mmio;
uint64_t gpa = mmio->address;
int32_t ret = 0;
@@ -522,14 +606,15 @@ int32_t vioapic_mmio_access_handler(struct io_request *io_req, void *handler_pri
}
/**
* @pre vm->arch_vm.vioapic != NULL
* @pre vm->arch_vm.vioapics != NULL
* @pre vgsi < get_vm_gsicount(vm)
* @pre rte != NULL
*/
void vioapic_get_rte(const struct acrn_vm *vm, uint32_t vgsi, union ioapic_rte *rte)
{
struct acrn_vioapic *vioapic;
uint32_t pin = vgsi;
struct acrn_single_vioapic *vioapic;
uint32_t pin;
vioapic = vgsi_to_vioapic_and_vpin(vm, vgsi, &pin);
vioapic = vm_ioapic(vm);
*rte = vioapic->rtbl[pin];
}