mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-05-03 05:56:57 +00:00
currently, pass-thru devices are managed by per-vm's remapping entries which is virtual based: - MSI entry is identified by virt_bdf+msix_index - INTx entry is identified by virt_pin+vpin_src it works but it's not a good design for physical resource management, for example a physical IOAPIC pin could belong to different vm's INTx entries, the Device Model then must make sure there is no resource conflict from application's level. This patch change the design from virtual to physical based: - MSI entry is identified by phys_bdf+msix_index - INTx entry is identified by phys_pin The physical resource is directly managed in hypervisor, a miss adding entry will be found by hypervisor and return error message with failure. Signed-off-by: Jason Chen CJ <jason.cj.chen@intel.com> Acked-by: Xu, Anthony <anthony.xu@intel.com>
458 lines
10 KiB
C
458 lines
10 KiB
C
/*
|
|
* Copyright (C) 2018 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:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * 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.
|
|
* * Neither the name of Intel Corporation nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "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 THE COPYRIGHT
|
|
* OWNER 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.
|
|
*/
|
|
|
|
#include <hypervisor.h>
|
|
#include <hv_lib.h>
|
|
#include <acrn_common.h>
|
|
#include <hv_arch.h>
|
|
#include <hv_debug.h>
|
|
|
|
/* Register offsets */
|
|
#define IOAPIC_REGSEL_OFFSET 0
|
|
#define IOAPIC_WINSWL_OFFSET 0x10
|
|
|
|
/* IOAPIC Redirection Table (RTE) Entry structure */
|
|
struct ioapic_rte {
|
|
uint32_t lo_32;
|
|
uint32_t hi_32;
|
|
} ioapic_rte;
|
|
|
|
struct gsi_table {
|
|
uint8_t ioapic_id;
|
|
uint8_t pin;
|
|
void *addr;
|
|
};
|
|
static struct gsi_table gsi_table[NR_MAX_GSI];
|
|
static int nr_gsi;
|
|
static spinlock_t ioapic_lock;
|
|
|
|
/*
|
|
* the irq to ioapic pin mapping should extract from ACPI MADT table
|
|
* hardcoded here
|
|
*/
|
|
uint16_t legacy_irq_to_pin[NR_LEGACY_IRQ] = {
|
|
2, /* IRQ0*/
|
|
1, /* IRQ1*/
|
|
0, /* IRQ2 connected to Pin0 (ExtInt source of PIC) if existing */
|
|
3, /* IRQ3*/
|
|
4, /* IRQ4*/
|
|
5, /* IRQ5*/
|
|
6, /* IRQ6*/
|
|
7, /* IRQ7*/
|
|
8, /* IRQ8*/
|
|
9 | IOAPIC_RTE_TRGRLVL, /* IRQ9*/
|
|
10, /* IRQ10*/
|
|
11, /* IRQ11*/
|
|
12, /* IRQ12*/
|
|
13, /* IRQ13*/
|
|
14, /* IRQ14*/
|
|
15, /* IRQ15*/
|
|
};
|
|
|
|
uint16_t pic_ioapic_pin_map[NR_LEGACY_PIN] = {
|
|
2, /* pin0*/
|
|
1, /* pin1*/
|
|
0, /* pin2*/
|
|
3, /* pin3*/
|
|
4, /* pin4*/
|
|
5, /* pin5*/
|
|
6, /* pin6*/
|
|
7, /* pin7*/
|
|
8, /* pin8*/
|
|
9, /* pin9*/
|
|
10, /* pin10*/
|
|
11, /* pin11*/
|
|
12, /* pin12*/
|
|
13, /* pin13*/
|
|
14, /* pin14*/
|
|
15, /* pin15*/
|
|
};
|
|
|
|
static void *map_ioapic(uint64_t ioapic_paddr)
|
|
{
|
|
/* At some point we may need to translate this paddr to a vaddr.
|
|
* 1:1 mapping for now.
|
|
*/
|
|
return HPA2HVA(ioapic_paddr);
|
|
}
|
|
|
|
static inline uint32_t
|
|
ioapic_read_reg32(const void *ioapic_base, const uint8_t offset)
|
|
{
|
|
uint32_t v;
|
|
|
|
spinlock_rflags;
|
|
|
|
spinlock_irqsave_obtain(&ioapic_lock);
|
|
|
|
/* Write IOREGSEL */
|
|
mmio_write_long(offset, (void *)ioapic_base);
|
|
/* Read IOWIN */
|
|
v = mmio_read_long((void *)ioapic_base + IOAPIC_WINSWL_OFFSET);
|
|
|
|
spinlock_irqrestore_release(&ioapic_lock);
|
|
return v;
|
|
}
|
|
|
|
static inline void
|
|
ioapic_write_reg32(const void *ioapic_base,
|
|
const uint8_t offset, const uint32_t value)
|
|
{
|
|
spinlock_rflags;
|
|
|
|
spinlock_irqsave_obtain(&ioapic_lock);
|
|
|
|
/* Write IOREGSEL */
|
|
mmio_write_long(offset, (void *)ioapic_base);
|
|
/* Write IOWIN */
|
|
mmio_write_long(value, (void *)ioapic_base + IOAPIC_WINSWL_OFFSET);
|
|
|
|
spinlock_irqrestore_release(&ioapic_lock);
|
|
}
|
|
|
|
static inline uint64_t
|
|
get_ioapic_base(int apic_id)
|
|
{
|
|
uint64_t addr = -1UL;
|
|
|
|
/* should extract next ioapic from ACPI MADT table */
|
|
if (apic_id == 0)
|
|
addr = DEFAULT_IO_APIC_BASE;
|
|
else if (apic_id == 1)
|
|
addr = 0xfec3f000;
|
|
else if (apic_id == 2)
|
|
addr = 0xfec7f000;
|
|
else
|
|
ASSERT(apic_id <= 2, "ACPI MADT table missing");
|
|
return addr;
|
|
}
|
|
|
|
|
|
static inline void
|
|
ioapic_get_rte_entry(void *ioapic_addr,
|
|
int pin, struct ioapic_rte *rte)
|
|
{
|
|
rte->lo_32 = ioapic_read_reg32(ioapic_addr, pin*2 + 0x10);
|
|
rte->hi_32 = ioapic_read_reg32(ioapic_addr, pin*2 + 0x11);
|
|
}
|
|
|
|
static inline void
|
|
ioapic_set_rte_entry(void *ioapic_addr,
|
|
int pin, struct ioapic_rte *rte)
|
|
{
|
|
ioapic_write_reg32(ioapic_addr, pin*2 + 0x10, rte->lo_32);
|
|
ioapic_write_reg32(ioapic_addr, pin*2 + 0x11, rte->hi_32);
|
|
}
|
|
|
|
static inline struct ioapic_rte
|
|
create_rte_for_legacy_irq(int irq, int vr)
|
|
{
|
|
struct ioapic_rte rte = {0, 0};
|
|
|
|
/* Legacy IRQ 0-15 setup, default masked
|
|
* are actually defined in either MPTable or ACPI MADT table
|
|
* before we have ACPI table parsing in HV we use common hardcode
|
|
*/
|
|
|
|
rte.lo_32 |= IOAPIC_RTE_INTMSET;
|
|
rte.lo_32 |= (legacy_irq_to_pin[irq] & IOAPIC_RTE_TRGRLVL);
|
|
rte.lo_32 |= DEFAULT_DEST_MODE;
|
|
rte.lo_32 |= DEFAULT_DELIVERY_MODE;
|
|
rte.lo_32 |= (IOAPIC_RTE_INTVEC & vr);
|
|
|
|
/* FIXME: Fixed to active Low? */
|
|
rte.lo_32 |= IOAPIC_RTE_INTALO;
|
|
|
|
/* Dest field: legacy irq fixed to CPU0 */
|
|
rte.hi_32 |= 1 << 24;
|
|
|
|
return rte;
|
|
}
|
|
|
|
static inline struct ioapic_rte
|
|
create_rte_for_gsi_irq(int irq, int vr)
|
|
{
|
|
struct ioapic_rte rte = {0, 0};
|
|
|
|
if (irq < NR_LEGACY_IRQ)
|
|
return create_rte_for_legacy_irq(irq, vr);
|
|
|
|
/* irq default masked, level trig */
|
|
rte.lo_32 |= IOAPIC_RTE_INTMSET;
|
|
rte.lo_32 |= IOAPIC_RTE_TRGRLVL;
|
|
rte.lo_32 |= DEFAULT_DEST_MODE;
|
|
rte.lo_32 |= DEFAULT_DELIVERY_MODE;
|
|
rte.lo_32 |= (IOAPIC_RTE_INTVEC & vr);
|
|
|
|
/* FIXME: Fixed to active Low? */
|
|
rte.lo_32 |= IOAPIC_RTE_INTALO;
|
|
|
|
/* Dest field */
|
|
rte.hi_32 |= ALL_CPUS_MASK << 24;
|
|
|
|
return rte;
|
|
}
|
|
|
|
static void ioapic_set_routing(int gsi, int vr)
|
|
{
|
|
void *addr;
|
|
struct ioapic_rte rte;
|
|
|
|
addr = gsi_table[gsi].addr;
|
|
rte = create_rte_for_gsi_irq(gsi, vr);
|
|
ioapic_set_rte_entry(addr, gsi_table[gsi].pin, &rte);
|
|
|
|
if (rte.lo_32 & IOAPIC_RTE_TRGRMOD)
|
|
update_irq_handler(gsi, handle_level_interrupt_common);
|
|
else
|
|
update_irq_handler(gsi, common_handler_edge);
|
|
|
|
dev_dbg(ACRN_DBG_IRQ, "GSI: irq:%d pin:%d rte:%x",
|
|
gsi, gsi_table[gsi].pin,
|
|
rte.lo_32);
|
|
}
|
|
|
|
void ioapic_get_rte(int irq, uint64_t *rte)
|
|
{
|
|
void *addr;
|
|
struct ioapic_rte _rte;
|
|
|
|
if (!irq_is_gsi(irq))
|
|
return;
|
|
|
|
addr = gsi_table[irq].addr;
|
|
ioapic_get_rte_entry(addr, gsi_table[irq].pin, &_rte);
|
|
|
|
*rte = _rte.hi_32;
|
|
*rte = *rte << 32 | _rte.lo_32;
|
|
}
|
|
|
|
void ioapic_set_rte(int irq, uint64_t raw_rte)
|
|
{
|
|
void *addr;
|
|
struct ioapic_rte rte;
|
|
|
|
if (!irq_is_gsi(irq))
|
|
return;
|
|
|
|
addr = gsi_table[irq].addr;
|
|
rte.lo_32 = raw_rte;
|
|
rte.hi_32 = raw_rte >> 32;
|
|
ioapic_set_rte_entry(addr, gsi_table[irq].pin, &rte);
|
|
|
|
dev_dbg(ACRN_DBG_IRQ, "GSI: irq:%d pin:%d rte:%x",
|
|
irq, gsi_table[irq].pin,
|
|
rte.lo_32);
|
|
}
|
|
|
|
int irq_gsi_num(void)
|
|
{
|
|
return nr_gsi;
|
|
}
|
|
|
|
bool irq_is_gsi(int irq)
|
|
{
|
|
return irq < nr_gsi;
|
|
}
|
|
|
|
int irq_to_pin(int irq)
|
|
{
|
|
if (irq_is_gsi(irq))
|
|
return gsi_table[irq].pin;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
int pin_to_irq(int pin)
|
|
{
|
|
int i;
|
|
|
|
if (pin < 0)
|
|
return IRQ_INVALID;
|
|
|
|
for (i = 0; i < nr_gsi; i++) {
|
|
if (gsi_table[i].pin == (uint8_t) pin)
|
|
return i;
|
|
}
|
|
return IRQ_INVALID;
|
|
}
|
|
|
|
void
|
|
irq_gsi_mask_unmask(int irq, bool mask)
|
|
{
|
|
void *addr = gsi_table[irq].addr;
|
|
int pin = gsi_table[irq].pin;
|
|
struct ioapic_rte rte;
|
|
|
|
if (!irq_is_gsi(irq))
|
|
return;
|
|
|
|
ioapic_get_rte_entry(addr, pin, &rte);
|
|
if (mask)
|
|
rte.lo_32 |= IOAPIC_RTE_INTMSET;
|
|
else
|
|
rte.lo_32 &= ~IOAPIC_RTE_INTMASK;
|
|
ioapic_set_rte_entry(addr, pin, &rte);
|
|
dev_dbg(ACRN_DBG_PTIRQ, "update: irq:%d pin:%d rte:%x",
|
|
irq, pin, rte.lo_32);
|
|
}
|
|
|
|
void setup_ioapic_irq(void)
|
|
{
|
|
int ioapic_id;
|
|
int gsi;
|
|
int vr;
|
|
|
|
spinlock_init(&ioapic_lock);
|
|
|
|
for (ioapic_id = 0, gsi = 0; ioapic_id < NR_IOAPICS; ioapic_id++) {
|
|
int pin;
|
|
int max_pins;
|
|
int version;
|
|
void *addr;
|
|
|
|
addr = map_ioapic(get_ioapic_base(ioapic_id));
|
|
version = ioapic_read_reg32(addr, IOAPIC_VER);
|
|
max_pins = (version & IOAPIC_MAX_RTE_MASK) >> MAX_RTE_SHIFT;
|
|
dev_dbg(ACRN_DBG_IRQ, "IOAPIC version: %x", version);
|
|
ASSERT(max_pins > NR_LEGACY_IRQ,
|
|
"Legacy IRQ num > total GSI");
|
|
|
|
for (pin = 0; pin < max_pins; pin++) {
|
|
gsi_table[gsi].ioapic_id = ioapic_id;
|
|
gsi_table[gsi].addr = addr;
|
|
|
|
if (gsi < NR_LEGACY_IRQ)
|
|
gsi_table[gsi].pin =
|
|
legacy_irq_to_pin[gsi] & 0xff;
|
|
else
|
|
gsi_table[gsi].pin = pin;
|
|
|
|
/* pinned irq before use it */
|
|
if (irq_mark_used(gsi) < 0) {
|
|
pr_err("failed to alloc IRQ[%d]", gsi);
|
|
gsi++;
|
|
continue;
|
|
}
|
|
|
|
/* assign vector for this GSI
|
|
* for legacy irq, reserved vector and never free
|
|
*/
|
|
if (gsi < NR_LEGACY_IRQ) {
|
|
vr = irq_desc_alloc_vector(gsi, false);
|
|
if (vr < 0) {
|
|
pr_err("failed to alloc VR");
|
|
gsi++;
|
|
continue;
|
|
}
|
|
} else
|
|
vr = 0; /* not to allocate VR right now */
|
|
|
|
ioapic_set_routing(gsi, vr);
|
|
gsi++;
|
|
}
|
|
}
|
|
|
|
/* system max gsi numbers */
|
|
nr_gsi = gsi;
|
|
ASSERT(nr_gsi < NR_MAX_GSI, "GSI table overflow");
|
|
}
|
|
|
|
void dump_ioapic(void)
|
|
{
|
|
int irq;
|
|
|
|
for (irq = 0; irq < nr_gsi; irq++) {
|
|
void *addr = gsi_table[irq].addr;
|
|
int pin = gsi_table[irq].pin;
|
|
struct ioapic_rte rte;
|
|
|
|
ioapic_get_rte_entry(addr, pin, &rte);
|
|
dev_dbg(ACRN_DBG_IRQ, "DUMP: irq:%d pin:%d rte:%x",
|
|
irq, pin, rte.lo_32);
|
|
}
|
|
}
|
|
|
|
void get_rte_info(struct ioapic_rte *rte, bool *mask, bool *irr,
|
|
bool *phys, int *delmode, bool *level, int *vector, uint32_t *dest)
|
|
{
|
|
*mask = ((rte->lo_32 & IOAPIC_RTE_INTMASK) == IOAPIC_RTE_INTMSET);
|
|
*irr = ((rte->lo_32 & IOAPIC_RTE_REM_IRR) == IOAPIC_RTE_REM_IRR);
|
|
*phys = ((rte->lo_32 & IOAPIC_RTE_DESTMOD) == IOAPIC_RTE_DESTPHY);
|
|
*delmode = rte->lo_32 & IOAPIC_RTE_DELMOD;
|
|
*level = rte->lo_32 & IOAPIC_RTE_TRGRLVL ? true : false;
|
|
*vector = rte->lo_32 & IOAPIC_RTE_INTVEC;
|
|
*dest = rte->hi_32 >> APIC_ID_SHIFT;
|
|
}
|
|
|
|
int get_ioapic_info(char *str, int str_max_len)
|
|
{
|
|
int irq, len, size = str_max_len;
|
|
|
|
len = snprintf(str, size,
|
|
"\r\nIRQ\tPIN\tRTE.HI32\tRTE.LO32\tVEC\tDST\tDM\tTM\tDELM\tIRR\tMASK");
|
|
size -= len;
|
|
str += len;
|
|
|
|
for (irq = 0; irq < nr_gsi; irq++) {
|
|
void *addr = gsi_table[irq].addr;
|
|
int pin = gsi_table[irq].pin;
|
|
struct ioapic_rte rte;
|
|
|
|
bool irr, phys, level, mask;
|
|
int delmode, vector;
|
|
uint32_t dest;
|
|
|
|
ioapic_get_rte_entry(addr, pin, &rte);
|
|
|
|
get_rte_info(&rte, &mask, &irr, &phys, &delmode, &level,
|
|
&vector, &dest);
|
|
|
|
len = snprintf(str, size, "\r\n%03d\t%03d\t0x%08X\t0x%08X\t",
|
|
irq, pin, rte.hi_32, rte.lo_32);
|
|
size -= len;
|
|
str += len;
|
|
|
|
len = snprintf(str, size, "0x%02X\t0x%02X\t%s\t%s\t%d\t%d\t%d",
|
|
vector, dest, phys ? "phys" : "logic",
|
|
level ? "level" : "edge", delmode >> 8, irr, mask);
|
|
size -= len;
|
|
str += len;
|
|
|
|
if (size < 2) {
|
|
pr_err("\r\nsmall buffer for ioapic dump");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
snprintf(str, size, "\r\n");
|
|
return 0;
|
|
}
|