drivers/interrupt_controller: Add fault event support on Intel VT-D
IR faults are non-recoverable, so it's good to know why. Thus let's handle the fault event and print the fault. Other faults are printed as well. Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
This commit is contained in:
parent
a8c8abd27e
commit
640e651cd8
4 changed files with 214 additions and 7 deletions
|
@ -28,3 +28,7 @@ zephyr_library_sources_ifdef(CONFIG_INTC_ESP32C3 intc_esp32c3.c)
|
|||
zephyr_library_sources_ifdef(CONFIG_SWERV_PIC intc_swerv_pic.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_VEXRISCV_LITEX_IRQ intc_vexriscv_litex.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_NUCLEI_ECLIC intc_nuclei_eclic.c)
|
||||
|
||||
if(CONFIG_INTEL_VTD_ICTL)
|
||||
zephyr_library_include_directories(${ZEPHYR_BASE}/arch/x86/include)
|
||||
endif()
|
||||
|
|
|
@ -20,9 +20,27 @@
|
|||
#include <arch/x86/intel_vtd.h>
|
||||
#include <drivers/interrupt_controller/intel_vtd.h>
|
||||
#include <drivers/interrupt_controller/ioapic.h>
|
||||
#include <drivers/pcie/msi.h>
|
||||
|
||||
#include <kernel_arch_func.h>
|
||||
|
||||
#include "intc_intel_vtd.h"
|
||||
|
||||
static void vtd_write_reg32(const struct device *dev,
|
||||
uint16_t reg, uint32_t value)
|
||||
{
|
||||
uintptr_t base_address = DEVICE_MMIO_GET(dev);
|
||||
|
||||
sys_write32(value, (base_address + reg));
|
||||
}
|
||||
|
||||
static uint32_t vtd_read_reg(const struct device *dev, uint16_t reg)
|
||||
{
|
||||
uintptr_t base_address = DEVICE_MMIO_GET(dev);
|
||||
|
||||
return sys_read32(base_address + reg);
|
||||
}
|
||||
|
||||
static void vtd_write_reg64(const struct device *dev,
|
||||
uint16_t reg, uint64_t value)
|
||||
{
|
||||
|
@ -31,11 +49,11 @@ static void vtd_write_reg64(const struct device *dev,
|
|||
sys_write64(value, (base_address + reg));
|
||||
}
|
||||
|
||||
static uint32_t vtd_read_reg(const struct device *dev, uint16_t reg)
|
||||
static uint64_t vtd_read_reg64(const struct device *dev, uint16_t reg)
|
||||
{
|
||||
uintptr_t base_address = DEVICE_MMIO_GET(dev);
|
||||
|
||||
return sys_read32(base_address + reg);
|
||||
return sys_read64(base_address + reg);
|
||||
}
|
||||
|
||||
static void vtd_send_cmd(const struct device *dev,
|
||||
|
@ -51,6 +69,120 @@ static void vtd_send_cmd(const struct device *dev,
|
|||
}
|
||||
}
|
||||
|
||||
static void fault_status_description(uint32_t status)
|
||||
{
|
||||
if (status & VTD_FSTS_PFO) {
|
||||
printk("Primary Fault Overflow (PFO)\n");
|
||||
}
|
||||
|
||||
if (status & VTD_FSTS_AFO) {
|
||||
printk("Advanced Fault Overflow (AFO)\n");
|
||||
}
|
||||
|
||||
if (status & VTD_FSTS_APF) {
|
||||
printk("Advanced Primary Fault (APF)\n");
|
||||
}
|
||||
|
||||
if (status & VTD_FSTS_IQE) {
|
||||
printk("Invalidation Queue Error (IQE)\n");
|
||||
}
|
||||
|
||||
if (status & VTD_FSTS_ICE) {
|
||||
printk("Invalidation Completion Error (ICE)\n");
|
||||
}
|
||||
|
||||
if (status & VTD_FSTS_ITE) {
|
||||
printk("Invalidation Timeout Error\n");
|
||||
}
|
||||
|
||||
if (status & VTD_FSTS_PPF) {
|
||||
printk("Primary Pending Fault (PPF) %u\n",
|
||||
VTD_FSTS_FRI(status));
|
||||
}
|
||||
}
|
||||
|
||||
static void fault_record_description(uint64_t low, uint64_t high)
|
||||
{
|
||||
printk("Fault %s request: Reason 0x%x info 0x%llx src 0x%x\n",
|
||||
(high & VTD_FRCD_T) ? "Read/Atomic" : "Write/Page",
|
||||
VTD_FRCD_FR(high), VTD_FRCD_FI(low), VTD_FRCD_SID(high));
|
||||
}
|
||||
|
||||
static void fault_event_isr(const void *arg)
|
||||
{
|
||||
const struct device *dev = arg;
|
||||
struct vtd_ictl_data *data = dev->data;
|
||||
uint32_t status;
|
||||
uint8_t f_idx;
|
||||
|
||||
status = vtd_read_reg(dev, VTD_FSTS_REG);
|
||||
fault_status_description(status);
|
||||
|
||||
if (!(status & VTD_FSTS_PPF)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
f_idx = VTD_FSTS_FRI(status);
|
||||
while (f_idx < data->fault_record_num) {
|
||||
uint64_t fault_l, fault_h;
|
||||
|
||||
/* Reading fault's 64 lowest bits */
|
||||
fault_l = vtd_read_reg64(dev, data->fault_record_reg +
|
||||
(VTD_FRCD_REG_SIZE * f_idx));
|
||||
/* Reading fault's 64 highest bits */
|
||||
fault_h = vtd_read_reg64(dev, data->fault_record_reg +
|
||||
(VTD_FRCD_REG_SIZE * f_idx) + 8);
|
||||
|
||||
if (fault_h & VTD_FRCD_F) {
|
||||
fault_record_description(fault_l, fault_h);
|
||||
}
|
||||
|
||||
/* Clearing the fault */
|
||||
vtd_write_reg64(dev, data->fault_record_reg +
|
||||
(VTD_FRCD_REG_SIZE * f_idx), fault_l);
|
||||
vtd_write_reg64(dev, data->fault_record_reg +
|
||||
(VTD_FRCD_REG_SIZE * f_idx) + 8, fault_h);
|
||||
f_idx++;
|
||||
}
|
||||
out:
|
||||
/* Clearing fault status */
|
||||
vtd_write_reg32(dev, VTD_FSTS_REG, VTD_FSTS_CLEAR(status));
|
||||
}
|
||||
|
||||
static void vtd_fault_event_init(const struct device *dev)
|
||||
{
|
||||
struct vtd_ictl_data *data = dev->data;
|
||||
uint64_t value;
|
||||
uint32_t reg;
|
||||
|
||||
value = vtd_read_reg64(dev, VTD_CAP_REG);
|
||||
data->fault_record_num = VTD_CAP_NFR(value) + 1;
|
||||
data->fault_record_reg = DEVICE_MMIO_GET(dev) +
|
||||
(uintptr_t)(16 * VTD_CAP_FRO(value));
|
||||
|
||||
/* Allocating IRQ & vector and connecting the ISR handler,
|
||||
* by-passing remapping by using x86 functions directly.
|
||||
*/
|
||||
data->fault_irq = arch_irq_allocate();
|
||||
data->fault_vector = z_x86_allocate_vector(0, -1);
|
||||
|
||||
vtd_write_reg32(dev, VTD_FEDATA_REG, data->fault_vector);
|
||||
vtd_write_reg32(dev, VTD_FEADDR_REG,
|
||||
pcie_msi_map(data->fault_irq, NULL, 0));
|
||||
vtd_write_reg32(dev, VTD_FEUADDR_REG, 0);
|
||||
|
||||
z_x86_irq_connect_on_vector(data->fault_irq, data->fault_vector,
|
||||
fault_event_isr, dev);
|
||||
|
||||
vtd_write_reg32(dev, VTD_FSTS_REG,
|
||||
VTD_FSTS_CLEAR(vtd_read_reg32(dev, VTD_FSTS_REG)));
|
||||
|
||||
/* Unmasking interrupts */
|
||||
reg = vtd_read_reg32(dev, VTD_FECTL_REG);
|
||||
reg &= ~BIT(VTD_FECTL_REG_IM);
|
||||
vtd_write_reg32(dev, VTD_FECTL_REG, reg);
|
||||
}
|
||||
|
||||
static int vtd_ictl_allocate_entries(const struct device *dev,
|
||||
uint8_t n_entries)
|
||||
{
|
||||
|
@ -185,18 +317,20 @@ static int vtd_ictl_init(const struct device *dev)
|
|||
struct vtd_ictl_data *data = dev->data;
|
||||
unsigned int key = irq_lock();
|
||||
uint64_t eime = 0;
|
||||
uint64_t irta;
|
||||
uint64_t value;
|
||||
|
||||
DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE);
|
||||
|
||||
vtd_fault_event_init(dev);
|
||||
|
||||
if (IS_ENABLED(CONFIG_X2APIC)) {
|
||||
eime = VTD_IRTA_EIME;
|
||||
}
|
||||
|
||||
irta = VTD_IRTA_REG_GEN_CONTENT((uintptr_t)data->irte,
|
||||
IRTA_SIZE, eime);
|
||||
value = VTD_IRTA_REG_GEN_CONTENT((uintptr_t)data->irte,
|
||||
IRTA_SIZE, eime);
|
||||
|
||||
vtd_write_reg64(dev, VTD_IRTA_REG, irta);
|
||||
vtd_write_reg64(dev, VTD_IRTA_REG, value);
|
||||
|
||||
if (!IS_ENABLED(CONFIG_X2APIC) &&
|
||||
IS_ENABLED(CONFIG_INTEL_VTD_ICTL_XAPIC_PASSTHROUGH)) {
|
||||
|
|
|
@ -58,6 +58,10 @@ struct vtd_ictl_data {
|
|||
int vectors[IRTE_NUM];
|
||||
bool msi[IRTE_NUM];
|
||||
int irte_num_used;
|
||||
unsigned int fault_irq;
|
||||
uintptr_t fault_record_reg;
|
||||
uint16_t fault_record_num;
|
||||
uint8_t fault_vector;
|
||||
};
|
||||
|
||||
struct vtd_ictl_cfg {
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#define VTD_RTADDR_REG 0x020 /* Root Table Address */
|
||||
#define VTD_CCMD_REG 0x028 /* Context Command */
|
||||
#define VTD_FSTS_REG 0x034 /* Fault Status */
|
||||
#define VTD_FECTL_REG 0x038 /* Fault Event Control Register*/
|
||||
#define VTD_FECTL_REG 0x038 /* Fault Event Control */
|
||||
#define VTD_FEDATA_REG 0x03C /* Fault Event Data */
|
||||
#define VTD_FEADDR_REG 0x040 /* Fault Event Address */
|
||||
#define VTD_FEUADDR_REG 0x044 /* Fault Event Upper Address */
|
||||
|
@ -85,6 +85,17 @@
|
|||
#define VTD_VCMD 0xE10 /* Virtual Command */
|
||||
#define VTD_VCRSP 0xE20 /* Virtual Command Response */
|
||||
|
||||
/* Capability Register details */
|
||||
#define VTD_CAP_NFR_POS 40
|
||||
#define VTD_CAP_NFR_MASK ((uint64_t)0xFFUL << VTD_CAP_NFR_POS)
|
||||
#define VTD_CAP_NFR(cap) \
|
||||
(((uint64_t)cap & VTD_CAP_NFR_MASK) >> VTD_CAP_NFR_POS)
|
||||
|
||||
#define VTD_CAP_FRO_POS 24
|
||||
#define VTD_CAP_FRO_MASK ((uint64_t)0x3FFUL << VTD_CAP_FRO_POS)
|
||||
#define VTD_CAP_FRO(cap) \
|
||||
(((uint64_t)cap & VTD_CAP_FRO_MASK) >> VTD_CAP_FRO_POS)
|
||||
|
||||
/* Global Command Register details */
|
||||
#define VTD_GCMD_CFI 23
|
||||
#define VTD_GCMD_SIRTP 24
|
||||
|
@ -117,6 +128,60 @@
|
|||
(addr << VTD_IRTA_ADDR_SHIFT) | \
|
||||
(mode) | (size & VTD_IRTA_SIZE_MASK))
|
||||
|
||||
/* Fault event control register details */
|
||||
#define VTD_FECTL_REG_IP 30
|
||||
#define VTD_FECTL_REG_IM 31
|
||||
|
||||
/* Fault event status register details */
|
||||
#define VTD_FSTS_PFO BIT(0)
|
||||
#define VTD_FSTS_PPF BIT(1)
|
||||
#define VTD_FSTS_AFO BIT(2)
|
||||
#define VTD_FSTS_APF BIT(3)
|
||||
#define VTD_FSTS_IQE BIT(4)
|
||||
#define VTD_FSTS_ICE BIT(5)
|
||||
#define VTD_FSTS_ITE BIT(6)
|
||||
#define VTD_FSTS_FRI_POS 8
|
||||
#define VTD_FSTS_FRI_MASK (0xF << VTD_FSTS_FRI_POS)
|
||||
#define VTD_FSTS_FRI(status) \
|
||||
((status & VTD_FSTS_FRI_MASK) >> VTD_FSTS_FRI_POS)
|
||||
|
||||
#define VTD_FSTS_CLEAR_STATUS \
|
||||
(VTD_FSTS_PFO | VTD_FSTS_AFO | VTD_FSTS_APF | \
|
||||
VTD_FSTS_IQE | VTD_FSTS_ICE | VTD_FSTS_ITE)
|
||||
|
||||
#define VTD_FSTS_CLEAR(status) \
|
||||
(status & VTD_FSTS_CLEAR_STATUS)
|
||||
|
||||
/* Fault recording register(s) details
|
||||
* Note: parts of the register are split into highest and lowest 64bits
|
||||
* so bit positions are depending on it and are not based on 128bits reg.
|
||||
*/
|
||||
#define VTD_FRCD_REG_SIZE 16
|
||||
|
||||
/* Highest 64bits info */
|
||||
#define VTD_FRCD_F BIT(63)
|
||||
#define VTD_FRCD_T BIT(62)
|
||||
|
||||
#define VTD_FRCD_FR_POS 32
|
||||
#define VTD_FRCD_FR_MASK ((uint64_t)0xFF << VTD_FRCD_FR_POS)
|
||||
#define VTD_FRCD_FR(fault) \
|
||||
((uint8_t)((fault & VTD_FRCD_FR_MASK) >> VTD_FRCD_FR_POS))
|
||||
|
||||
#define VTD_FRCD_SID_MASK 0xFFFF
|
||||
#define VTD_FRCD_SID(fault) \
|
||||
((uint16_t)(fault & VTD_FRCD_SID_MASK))
|
||||
|
||||
/* Lowest 64bits info */
|
||||
#define VTD_FRCD_FI_POS 12
|
||||
#define VTD_FRCD_FI_MASK ((uint64_t)0xFFFFFFFFFFFFF << VTD_FRCD_FI_POS)
|
||||
#define VTD_FRCD_FI(fault) \
|
||||
((fault & VTD_FRCD_FI_MASK) >> VTD_FRCD_FI_POS)
|
||||
|
||||
#define VTD_FRCD_FI_IR_POS 48
|
||||
#define VTD_FRCD_FI_IR_MASK ((uint64_t)0xFFFF << VTD_FRCD_FI_IR_POS)
|
||||
#define VTD_FRCD_FI_IR(fault) \
|
||||
((fault & VTD_FRCD_FI_IR_MASK) >> VTD_FRCD_FI_IR_POS)
|
||||
|
||||
#endif /* _ASMLANGUAGE */
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_ARCH_X86_INTEL_VTD_H */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue