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:
Tomasz Bursztyka 2021-01-07 14:10:48 +01:00 committed by Anas Nashif
commit 640e651cd8
4 changed files with 214 additions and 7 deletions

View file

@ -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()

View file

@ -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)) {

View file

@ -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 {

View file

@ -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 */