acrn-hypervisor/hypervisor/arch/x86/ioapic.c
Junjie Mao cfc3811a0a HV: treewide: drop debug-only helpers in release build
There are multiple helpers for collecting info to be printed on the ACRN uart
console. They are unreachable code in release builds in which the uart console
is removed.

To define a precise boundary for safety-related activities, this patch wraps the
declarations and definitions to these helpers with "#ifdef HV_DEBUG" so that
these unreachable APIs will be dropped in release builds.

v1 -> v2:

    * Fix coding style: no empty lines between #ifdef and the wrapped code.
    * Also drop get_rte_info() in ioapic.c, which is solely used by
      get_ioapic_info().

Signed-off-by: Junjie Mao <junjie.mao@intel.com>
Acked-by: Eddie Dong <eddie.dong@intel.com>
2018-07-02 14:35:39 +08:00

474 lines
10 KiB
C

/*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <hypervisor.h>
/* Register offsets */
#define IOAPIC_REGSEL_OFFSET 0
#define IOAPIC_WINSWL_OFFSET 0x10
#define IOAPIC_MAX_PIN 256
/* 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 uint32_t nr_gsi;
static spinlock_t ioapic_lock;
static struct ioapic_rte saved_rte[CONFIG_NR_IOAPICS][IOAPIC_MAX_PIN];
/*
* the irq to ioapic pin mapping should extract from ACPI MADT table
* hardcoded here
*/
uint16_t legacy_irq_to_pin[NR_LEGACY_IRQ] = {
2U, /* IRQ0*/
1U, /* IRQ1*/
0U, /* IRQ2 connected to Pin0 (ExtInt source of PIC) if existing */
3U, /* IRQ3*/
4U, /* IRQ4*/
5U, /* IRQ5*/
6U, /* IRQ6*/
7U, /* IRQ7*/
8U, /* IRQ8*/
9U | IOAPIC_RTE_TRGRLVL, /* IRQ9*/
10U, /* IRQ10*/
11U, /* IRQ11*/
12U, /* IRQ12*/
13U, /* IRQ13*/
14U, /* IRQ14*/
15U, /* IRQ15*/
};
uint16_t pic_ioapic_pin_map[NR_LEGACY_PIN] = {
2U, /* pin0*/
1U, /* pin1*/
0U, /* pin2*/
3U, /* pin3*/
4U, /* pin4*/
5U, /* pin5*/
6U, /* pin6*/
7U, /* pin7*/
8U, /* pin8*/
9U, /* pin9*/
10U, /* pin10*/
11U, /* pin11*/
12U, /* pin12*/
13U, /* pin13*/
14U, /* pin14*/
15U, /* 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(uint32_t irq, uint32_t 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 |= 1U << 24;
return rte;
}
static inline struct ioapic_rte
create_rte_for_gsi_irq(uint32_t irq, uint32_t 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 << 24U;
return rte;
}
static void ioapic_set_routing(uint32_t gsi, uint32_t 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) != 0U)
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(uint32_t 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(uint32_t 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);
}
uint32_t irq_gsi_num(void)
{
return nr_gsi;
}
bool irq_is_gsi(uint32_t irq)
{
return irq < nr_gsi;
}
int irq_to_pin(uint32_t irq)
{
if (irq_is_gsi(irq))
return gsi_table[irq].pin;
else
return -1;
}
uint32_t pin_to_irq(int pin)
{
uint32_t 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(uint32_t 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;
uint32_t gsi;
uint32_t vr;
spinlock_init(&ioapic_lock);
for (ioapic_id = 0, gsi = 0; ioapic_id < CONFIG_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] & 0xffU;
else
gsi_table[gsi].pin = pin;
/* pinned irq before use it */
if (irq_mark_used(gsi) > NR_MAX_IRQS) {
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 > NR_MAX_VECTOR) {
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)
{
uint32_t 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 suspend_ioapic(void)
{
int ioapic_id, ioapic_pin;
for (ioapic_id = 0; ioapic_id < CONFIG_NR_IOAPICS; ioapic_id++) {
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;
for (ioapic_pin = 0; ioapic_pin < max_pins; ioapic_pin++)
ioapic_get_rte_entry(addr, ioapic_pin,
&saved_rte[ioapic_id][ioapic_pin]);
}
}
void resume_ioapic(void)
{
int ioapic_id, ioapic_pin;
for (ioapic_id = 0; ioapic_id < CONFIG_NR_IOAPICS; ioapic_id++) {
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;
for (ioapic_pin = 0; ioapic_pin < max_pins; ioapic_pin++)
ioapic_set_rte_entry(addr, ioapic_pin,
&saved_rte[ioapic_id][ioapic_pin]);
}
}
#ifdef HV_DEBUG
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) != 0U) ? 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)
{
uint32_t 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;
}
#endif /* HV_DEBUG */