diff --git a/drivers/interrupt_controller/CMakeLists.txt b/drivers/interrupt_controller/CMakeLists.txt index 5cf84b88494..95950ac1ae2 100644 --- a/drivers/interrupt_controller/CMakeLists.txt +++ b/drivers/interrupt_controller/CMakeLists.txt @@ -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() diff --git a/drivers/interrupt_controller/intc_intel_vtd.c b/drivers/interrupt_controller/intc_intel_vtd.c index f90c20d6c33..77b5aef05b3 100644 --- a/drivers/interrupt_controller/intc_intel_vtd.c +++ b/drivers/interrupt_controller/intc_intel_vtd.c @@ -20,9 +20,27 @@ #include #include #include +#include + +#include #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)) { diff --git a/drivers/interrupt_controller/intc_intel_vtd.h b/drivers/interrupt_controller/intc_intel_vtd.h index 82ed0936275..2789dda921d 100644 --- a/drivers/interrupt_controller/intc_intel_vtd.h +++ b/drivers/interrupt_controller/intc_intel_vtd.h @@ -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 { diff --git a/include/arch/x86/intel_vtd.h b/include/arch/x86/intel_vtd.h index 98ff7a8b585..2f6ebbd1b67 100644 --- a/include/arch/x86/intel_vtd.h +++ b/include/arch/x86/intel_vtd.h @@ -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 */