drivers/interrupt_controller: Adding QI support in VT-D
Looks like it's mandatory to invalidate the Interrupt Entry Cache in
VT-D and the only way to do so is to enable Queued Interface.
Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
diff --git a/drivers/interrupt_controller/intc_intel_vtd.c b/drivers/interrupt_controller/intc_intel_vtd.c
index 3cce54d..61303f9 100644
--- a/drivers/interrupt_controller/intc_intel_vtd.c
+++ b/drivers/interrupt_controller/intc_intel_vtd.c
@@ -26,6 +26,11 @@
#include "intc_intel_vtd.h"
+static inline void vtd_pause_cpu(void)
+{
+ __asm__ volatile("pause" ::: "memory");
+}
+
static void vtd_write_reg32(const struct device *dev,
uint16_t reg, uint32_t value)
{
@@ -69,6 +74,94 @@
}
}
+static void vtd_qi_init(const struct device *dev)
+{
+ struct vtd_ictl_data *data = dev->data;
+ uint64_t value;
+
+ vtd_write_reg64(dev, VTD_IQT_REG, 0);
+ data->qi_tail = 0;
+
+ value = VTD_IQA_REG_GEN_CONTENT((uintptr_t)data->qi,
+ VTD_IQA_WIDTH_128_BIT, QI_SIZE);
+ vtd_write_reg64(dev, VTD_IQA_REG, value);
+
+ vtd_send_cmd(dev, VTD_GCMD_QIE, VTD_GSTS_QIES);
+}
+
+static inline void vtd_qi_tail_inc(const struct device *dev)
+{
+ struct vtd_ictl_data *data = dev->data;
+
+ data->qi_tail += sizeof(struct qi_descriptor);
+ data->qi_tail %= (QI_NUM * sizeof(struct qi_descriptor));
+}
+
+static int vtd_qi_send(const struct device *dev,
+ struct qi_descriptor *descriptor)
+{
+ struct vtd_ictl_data *data = dev->data;
+ union qi_wait_descriptor wait_desc = { 0 };
+ struct qi_descriptor *desc;
+ uint32_t wait_status;
+
+ desc = (struct qi_descriptor *)((uintptr_t)data->qi + data->qi_tail);
+
+ desc->low = descriptor->low;
+ desc->high = descriptor->high;
+
+ vtd_qi_tail_inc(dev);
+
+ desc++;
+
+ wait_status = QI_WAIT_STATUS_INCOMPLETE;
+
+ wait_desc.wait.type = QI_TYPE_WAIT;
+ wait_desc.wait.status_write = 1;
+ wait_desc.wait.status_data = QI_WAIT_STATUS_COMPLETE;
+ wait_desc.wait.address = ((uintptr_t)&wait_status) >> 2;
+
+ desc->low = wait_desc.desc.low;
+ desc->high = wait_desc.desc.high;
+
+ vtd_qi_tail_inc(dev);
+
+ vtd_write_reg64(dev, VTD_IQT_REG, data->qi_tail);
+
+ while (wait_status != QI_WAIT_STATUS_COMPLETE) {
+ if (vtd_read_reg32(dev, VTD_FSTS_REG) & VTD_FSTS_IQE) {
+ return -EIO;
+ }
+
+ vtd_pause_cpu();
+ }
+
+ return 0;
+}
+
+static int vtd_global_iec_invalidate(const struct device *dev)
+{
+ union qi_iec_descriptor iec_desc = { 0 };
+
+ iec_desc.iec.type = QI_TYPE_IEC;
+ iec_desc.iec.granularity = 0; /* Global Invalidation requested */
+
+ return vtd_qi_send(dev, &iec_desc.desc);
+}
+
+static int vtd_index_iec_invalidate(const struct device *dev, uint8_t irte_idx)
+{
+ union qi_iec_descriptor iec_desc = { 0 };
+
+ iec_desc.iec.type = QI_TYPE_IEC;
+ iec_desc.iec.granularity = 1; /* Index based invalidation requested */
+
+ iec_desc.iec.interrupt_index = irte_idx;
+ iec_desc.iec.index_mask = 0;
+
+ return vtd_qi_send(dev, &iec_desc.desc);
+}
+
static void fault_status_description(uint32_t status)
{
if (status & VTD_FSTS_PFO) {
@@ -233,6 +326,8 @@
data->irte[irte_idx].low = irte.low;
data->irte[irte_idx].high = irte.high;
+ vtd_index_iec_invalidate(dev, irte_idx);
+
return 0;
}
@@ -318,11 +413,14 @@
unsigned int key = irq_lock();
uint64_t eime = 0;
uint64_t value;
+ int ret = 0;
DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE);
vtd_fault_event_init(dev);
+ vtd_qi_init(dev);
+
if (IS_ENABLED(CONFIG_X2APIC)) {
eime = VTD_IRTA_EIME;
}
@@ -332,6 +430,12 @@
vtd_write_reg64(dev, VTD_IRTA_REG, value);
+ if (vtd_global_iec_invalidate(dev) != 0) {
+ printk("Could not perform IEC invalidation\n");
+ ret = -EIO;
+ goto out;
+ }
+
if (!IS_ENABLED(CONFIG_X2APIC) &&
IS_ENABLED(CONFIG_INTEL_VTD_ICTL_XAPIC_PASSTHROUGH)) {
vtd_send_cmd(dev, VTD_GCMD_CFI, VTD_GSTS_CFIS);
@@ -343,9 +447,10 @@
printk("Intel VT-D up and running (status 0x%x)\n",
vtd_read_reg32(dev, VTD_GSTS_REG));
+out:
irq_unlock(key);
- return 0;
+ return ret;
}
static const struct vtd_driver_api vtd_api = {
diff --git a/drivers/interrupt_controller/intc_intel_vtd.h b/drivers/interrupt_controller/intc_intel_vtd.h
index 2789dda..22caea6 100644
--- a/drivers/interrupt_controller/intc_intel_vtd.h
+++ b/drivers/interrupt_controller/intc_intel_vtd.h
@@ -52,8 +52,59 @@
#define IRTE_NUM 256
#define IRTA_SIZE 7 /* size = 2^(X+1) where IRTA_SIZE is X 2^8 = 256 */
+#define QI_NUM 256 /* Which is the minimal number we can set for the queue */
+#define QI_SIZE 0 /* size = 2^(X+8) where QI_SIZE is X: 2^8 = 256 */
+#define QI_WIDTH 128
+
+struct qi_descriptor {
+ uint64_t low;
+ uint64_t high;
+};
+
+#define QI_TYPE_IEC 0x4UL
+
+union qi_iec_descriptor {
+ struct qi_descriptor desc;
+
+ struct iec_bits {
+ uint64_t type : 4;
+ uint64_t granularity : 1;
+ uint64_t _reserved_0 : 4;
+ uint64_t zero : 3;
+ uint64_t _reserved_1 : 15;
+ uint64_t index_mask : 5;
+ uint64_t interrupt_index: 16;
+ uint64_t _reserved_2 : 16;
+ uint64_t reserved;
+ } iec __packed;
+};
+
+#define QI_TYPE_WAIT 0x5UL
+
+union qi_wait_descriptor {
+ struct qi_descriptor desc;
+
+ struct wait_bits {
+ uint64_t type : 4;
+ uint64_t interrupt_flag : 1;
+ uint64_t status_write : 1;
+ uint64_t fence_flag : 1;
+ uint64_t page_req_drain : 1;
+ uint64_t _reserved_0 : 1;
+ uint64_t zero : 3;
+ uint64_t _reserved_1 : 20;
+ uint64_t status_data : 32;
+ uint64_t reserved : 2;
+ uint64_t address : 62;
+ } wait __packed;
+};
+
+#define QI_WAIT_STATUS_INCOMPLETE 0x0UL
+#define QI_WAIT_STATUS_COMPLETE 0x1UL
+
struct vtd_ictl_data {
struct vtd_irte irte[IRTE_NUM];
+ struct qi_descriptor qi[QI_NUM] __aligned(0x1000);
int irqs[IRTE_NUM];
int vectors[IRTE_NUM];
bool msi[IRTE_NUM];
@@ -61,6 +112,7 @@
unsigned int fault_irq;
uintptr_t fault_record_reg;
uint16_t fault_record_num;
+ uint16_t qi_tail;
uint8_t fault_vector;
};
diff --git a/include/arch/x86/intel_vtd.h b/include/arch/x86/intel_vtd.h
index 2f6ebbd..2fe005c 100644
--- a/include/arch/x86/intel_vtd.h
+++ b/include/arch/x86/intel_vtd.h
@@ -140,6 +140,7 @@
#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) \
@@ -182,6 +183,21 @@
#define VTD_FRCD_FI_IR(fault) \
((fault & VTD_FRCD_FI_IR_MASK) >> VTD_FRCD_FI_IR_POS)
+/* Invalidation Queue Address register details */
+#define VTD_IQA_SIZE_MASK 0x7
+#define VTD_IQA_WIDTH_128_BIT 0
+#define VTD_IQA_WIDTH_256_BIT BIT(11)
+#define VTD_IQA_REG_GEN_CONTENT(addr, width, size) \
+ ((uint64_t)0 | (addr) | (width) | (size & VTD_IQA_SIZE_MASK))
+
+/* Invalidation Queue Head register details */
+#define VTD_IQH_QH_POS_128 4
+#define VTD_IQH_QH_MASK ((uint64_t)0xEF << VTD_IQH_QH_POS_128)
+
+/* Invalidation Queue Tail register details */
+#define VTD_IQT_QT_POS_128 4
+#define VTD_IQT_QT_MASK ((uint64_t)0xEF << VTD_IQT_QT_POS_128)
+
#endif /* _ASMLANGUAGE */
#endif /* ZEPHYR_INCLUDE_ARCH_X86_INTEL_VTD_H */