From: CLEMENT MATHIEU--DRIF <clement.mathieu--drif@eviden.com>
To: "qemu-devel@nongnu.org" <qemu-devel@nongnu.org>
Cc: "jasowang@redhat.com" <jasowang@redhat.com>,
CLEMENT MATHIEU--DRIF <clement.mathieu--drif@eviden.com>
Subject: [PATCH intel_iommu 4/7] intel_iommu: add support for first-stage translation
Date: Mon, 22 Apr 2024 15:52:53 +0000 [thread overview]
Message-ID: <20240422155236.129179-5-clement.mathieu--drif@eviden.com> (raw)
In-Reply-To: <20240422155236.129179-1-clement.mathieu--drif@eviden.com>
This translation mode will only be made available in scalable mode
Signed-off-by: Clément Mathieu--Drif <clement.mathieu--drif@eviden.com>
---
hw/i386/intel_iommu.c | 364 ++++++++++++++++++++++++++++-----
hw/i386/intel_iommu_internal.h | 51 ++++-
2 files changed, 362 insertions(+), 53 deletions(-)
diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
index ba545590b1..3b9f120dec 100644
--- a/hw/i386/intel_iommu.c
+++ b/hw/i386/intel_iommu.c
@@ -713,6 +713,21 @@ static uint64_t vtd_get_pte(dma_addr_t base_addr, uint32_t index)
return pte;
}
+static MemTxResult vtd_set_flag_in_pte(dma_addr_t base_addr, uint32_t index,
+ uint64_t pte, uint64_t flag)
+{
+ assert(index < VTD_PT_ENTRY_NR);
+ if (pte & flag) {
+ return MEMTX_OK;
+ }
+ pte |= flag;
+ pte = cpu_to_le64(pte);
+ return dma_memory_write(&address_space_memory,
+ base_addr + index * sizeof(pte),
+ &pte, sizeof(pte),
+ MEMTXATTRS_UNSPECIFIED);
+}
+
/* Given an iova and the level of paging structure, return the offset
* of current level.
*/
@@ -730,11 +745,17 @@ static inline bool vtd_is_level_supported(IntelIOMMUState *s, uint32_t level)
}
/* Return true if check passed, otherwise false */
-static inline bool vtd_pe_type_check(X86IOMMUState *x86_iommu,
+static inline bool vtd_pe_type_check(IntelIOMMUState *s,
VTDPASIDEntry *pe)
{
+ X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s);
+
switch (VTD_PE_GET_TYPE(pe)) {
case VTD_SM_PASID_ENTRY_FLT:
+ if (!(s->ecap & VTD_ECAP_FLTS)) {
+ return false;
+ }
+ break;
case VTD_SM_PASID_ENTRY_SLT:
case VTD_SM_PASID_ENTRY_NESTED:
break;
@@ -784,6 +805,11 @@ static inline bool vtd_pe_present(VTDPASIDEntry *pe)
return pe->val[0] & VTD_PASID_ENTRY_P;
}
+static inline bool vtd_fl_pte_present(uint64_t pte)
+{
+ return pte & VTD_FL_PTE_P;
+}
+
static int vtd_get_pe_in_pasid_leaf_table(IntelIOMMUState *s,
uint32_t pasid,
dma_addr_t addr,
@@ -791,7 +817,6 @@ static int vtd_get_pe_in_pasid_leaf_table(IntelIOMMUState *s,
{
uint32_t index;
dma_addr_t entry_size;
- X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s);
index = VTD_PASID_TABLE_INDEX(pasid);
entry_size = VTD_PASID_ENTRY_SIZE;
@@ -805,7 +830,7 @@ static int vtd_get_pe_in_pasid_leaf_table(IntelIOMMUState *s,
}
/* Do translation type check */
- if (!vtd_pe_type_check(x86_iommu, pe)) {
+ if (!vtd_pe_type_check(s, pe)) {
return -VTD_FR_PASID_TABLE_INV;
}
@@ -1027,6 +1052,34 @@ static inline bool vtd_iova_sl_range_check(IntelIOMMUState *s,
return !(iova & ~(vtd_iova_limit(s, ce, aw, pasid) - 1));
}
+/* Return true if IOVA is canonical, otherwise false. */
+static bool vtd_iova_fl_check_canonical(IntelIOMMUState *s,
+ uint64_t iova, VTDContextEntry *ce,
+ uint8_t aw, uint32_t pasid)
+{
+ uint64_t iova_limit = vtd_iova_limit(s, ce, aw, pasid);
+ uint64_t upper_bits_mask = ~(iova_limit - 1);
+ uint64_t upper_bits = iova & upper_bits_mask;
+ bool msb = ((iova & (iova_limit >> 1)) != 0);
+ return !(
+ (!msb && (upper_bits != 0)) ||
+ (msb && (upper_bits != upper_bits_mask))
+ );
+}
+
+/* Return the page table base address corresponding to the translation type. */
+static dma_addr_t vtd_pe_get_pgtbl_base(VTDPASIDEntry *pe)
+{
+ uint16_t pgtt = VTD_PE_GET_TYPE(pe);
+ if (pgtt == VTD_SM_PASID_ENTRY_FLT) {
+ return pe->val[2] & VTD_SM_PASID_ENTRY_PTPTR;
+ } else if (pgtt == VTD_SM_PASID_ENTRY_SLT) {
+ return pe->val[0] & VTD_SM_PASID_ENTRY_PTPTR;
+ }
+
+ return 0; /* Not supported */
+}
+
static dma_addr_t vtd_get_iova_pgtbl_base(IntelIOMMUState *s,
VTDContextEntry *ce,
uint32_t pasid)
@@ -1035,7 +1088,7 @@ static dma_addr_t vtd_get_iova_pgtbl_base(IntelIOMMUState *s,
if (s->root_scalable) {
vtd_ce_get_rid2pasid_entry(s, ce, &pe, pasid);
- return pe.val[0] & VTD_SM_PASID_ENTRY_SLPTPTR;
+ return vtd_pe_get_pgtbl_base(&pe);
}
return vtd_ce_get_slpt_base(ce);
@@ -1053,6 +1106,10 @@ static dma_addr_t vtd_get_iova_pgtbl_base(IntelIOMMUState *s,
static uint64_t vtd_spte_rsvd[VTD_SPTE_RSVD_LEN];
static uint64_t vtd_spte_rsvd_large[VTD_SPTE_RSVD_LEN];
+#define VTD_FPTE_RSVD_LEN 5
+static uint64_t vtd_fpte_rsvd[VTD_FPTE_RSVD_LEN];
+static uint64_t vtd_fpte_rsvd_large[VTD_FPTE_RSVD_LEN];
+
static bool vtd_slpte_nonzero_rsvd(uint64_t slpte, uint32_t level)
{
uint64_t rsvd_mask;
@@ -1079,21 +1136,140 @@ static bool vtd_slpte_nonzero_rsvd(uint64_t slpte, uint32_t level)
return slpte & rsvd_mask;
}
-/* Given the @iova, get relevant @slptep. @slpte_level will be the last level
- * of the translation, can be used for deciding the size of large page.
- */
-static int vtd_iova_to_slpte(IntelIOMMUState *s, VTDContextEntry *ce,
- uint64_t iova, bool is_write,
- uint64_t *slptep, uint32_t *slpte_level,
- bool *reads, bool *writes, uint8_t aw_bits,
- uint32_t pasid)
+static bool vtd_flpte_nonzero_rsvd(uint64_t flpte, uint32_t level)
+{
+ uint64_t rsvd_mask;
+ assert(level < VTD_FPTE_RSVD_LEN);
+ assert(level);
+
+ if ((level == VTD_FL_PD_LEVEL || level == VTD_FL_PDP_LEVEL) &&
+ (flpte & VTD_PT_PAGE_SIZE_MASK)) {
+ /* large page */
+ rsvd_mask = vtd_fpte_rsvd_large[level];
+ } else {
+ rsvd_mask = vtd_fpte_rsvd[level];
+ }
+
+ return flpte & rsvd_mask;
+}
+
+static int vtd_iova_to_pte_check_read_error(IntelIOMMUState *s,
+ VTDContextEntry *ce, uint64_t iova,
+ uint64_t pte, uint32_t pasid,
+ uint32_t level, uint16_t pgtt)
+{
+ if (pte == (uint64_t)-1) {
+ error_report_once("%s: detected read error on DMAR pte "
+ "(iova=0x%" PRIx64 ", pasid=0x%" PRIx32 ")",
+ __func__, iova, pasid);
+ if (level == vtd_get_iova_level(s, ce, pasid)) {
+ /* Invalid programming of context-entry */
+ if (s->root_scalable) {
+ return pgtt == VTD_SM_PASID_ENTRY_FLT ?
+ -VTD_FR_FIRST_FSPE_ACCESS :
+ -VTD_FR_FIRST_SSPE_ACCESS;
+ } else {
+ return -VTD_FR_CONTEXT_ENTRY_INV;
+ }
+ } else {
+ if (s->root_scalable) {
+ return pgtt == VTD_SM_PASID_ENTRY_FLT ?
+ -VTD_FR_FSPE_ACCESS :
+ -VTD_FR_SSPE_ACCESS;
+ } else {
+ return -VTD_FR_PAGING_ENTRY_INV;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static inline bool vtd_addr_in_interrup_range(hwaddr addr, uint64_t size)
+{
+ return !((addr > VTD_INTERRUPT_ADDR_LAST) ||
+ (addr + size - 1 < VTD_INTERRUPT_ADDR_FIRST));
+}
+
+static int vtd_iova_to_pte_fl(IntelIOMMUState *s, VTDContextEntry *ce,
+ uint64_t iova, bool is_write, uint64_t *ptep,
+ uint32_t *pte_level, bool *reads, bool *writes,
+ uint8_t aw_bits, uint32_t pasid, dma_addr_t addr)
+{
+ uint32_t offset;
+ uint64_t pte;
+ uint64_t access_right_check = is_write ? VTD_FL_W : 0;
+ int ret;
+
+ if (!vtd_iova_fl_check_canonical(s, iova, ce, aw_bits, pasid)) {
+ error_report_once("%s: detected non canonical IOVA (iova=0x%" PRIx64 ","
+ "pasid=0x%" PRIx32 ")", __func__, iova, pasid);
+ return -VTD_FR_FS_NON_CANONICAL;
+ }
+
+ while (true) {
+ offset = vtd_iova_level_offset(iova, *pte_level);
+ pte = vtd_get_pte(addr, offset);
+
+ ret = vtd_iova_to_pte_check_read_error(s, ce, iova, pte,
+ pasid, *pte_level,
+ VTD_SM_PASID_ENTRY_FLT);
+ if (ret != 0) {
+ return ret;
+ }
+
+ if (!vtd_fl_pte_present(pte)) {
+ return -VTD_FR_FSPE_NOT_PRESENT;
+ }
+
+ *reads = true;
+ *writes = (*writes) && (pte & VTD_FL_W);
+
+ if (vtd_set_flag_in_pte(addr, offset, pte, VTD_FL_PTE_A) != MEMTX_OK) {
+ return -VTD_FR_FS_BIT_UPDATE_FAILED;
+ }
+
+ if ((pte & access_right_check) != access_right_check) {
+ error_report_once("%s: detected pte permission error "
+ "(iova=0x%" PRIx64 ", level=0x%" PRIx32 ", "
+ "pte=0x%" PRIx64 ", write=%d, pasid=0x%"
+ PRIx32 ")", __func__, iova, *pte_level,
+ pte, is_write, pasid);
+ return is_write ? -VTD_FR_SM_WRITE : -VTD_FR_SM_READ;
+ }
+ if (vtd_flpte_nonzero_rsvd(pte, *pte_level)) {
+ error_report_once("%s: detected flpte reserved non-zero "
+ "iova=0x%" PRIx64 ", level=0x%" PRIx32
+ "pte=0x%" PRIx64 ", pasid=0x%" PRIX32 ")",
+ __func__, iova, *pte_level, pte, pasid);
+ return -VTD_FR_PAGING_ENTRY_RSVD;
+ }
+
+ if (vtd_is_last_pte(pte, *pte_level)) {
+ *ptep = pte;
+ break;
+ }
+ addr = vtd_get_pte_addr(pte, aw_bits);
+ (*pte_level)--;
+ }
+
+ if (is_write &&
+ (vtd_set_flag_in_pte(addr, offset, pte, VTD_FL_PTE_D) != MEMTX_OK)) {
+ return -VTD_FR_FS_BIT_UPDATE_FAILED;
+ }
+
+ return 0;
+}
+
+static int vtd_iova_to_pte_sl(IntelIOMMUState *s, VTDContextEntry *ce,
+ uint64_t iova, bool is_write, uint64_t *slptep,
+ uint32_t *pte_level, bool *reads, bool *writes,
+ uint8_t aw_bits, uint32_t pasid, dma_addr_t addr)
{
- dma_addr_t addr = vtd_get_iova_pgtbl_base(s, ce, pasid);
- uint32_t level = vtd_get_iova_level(s, ce, pasid);
uint32_t offset;
uint64_t slpte;
- uint64_t access_right_check;
- uint64_t xlat, size;
+ uint64_t access_right_check = is_write ? VTD_SL_W : VTD_SL_R;
+ int ret;
if (!vtd_iova_sl_range_check(s, iova, ce, aw_bits, pasid)) {
error_report_once("%s: detected IOVA overflow (iova=0x%" PRIx64 ","
@@ -1101,72 +1277,128 @@ static int vtd_iova_to_slpte(IntelIOMMUState *s, VTDContextEntry *ce,
return -VTD_FR_ADDR_BEYOND_MGAW;
}
- /* FIXME: what is the Atomics request here? */
- access_right_check = is_write ? VTD_SL_W : VTD_SL_R;
-
while (true) {
- offset = vtd_iova_level_offset(iova, level);
+ offset = vtd_iova_level_offset(iova, *pte_level);
slpte = vtd_get_pte(addr, offset);
- if (slpte == (uint64_t)-1) {
- error_report_once("%s: detected read error on DMAR slpte "
- "(iova=0x%" PRIx64 ", pasid=0x%" PRIx32 ")",
- __func__, iova, pasid);
- if (level == vtd_get_iova_level(s, ce, pasid)) {
- /* Invalid programming of context-entry */
- return -VTD_FR_CONTEXT_ENTRY_INV;
- } else {
- return -VTD_FR_PAGING_ENTRY_INV;
- }
+ ret = vtd_iova_to_pte_check_read_error(s, ce, iova, slpte,
+ pasid, *pte_level,
+ VTD_SM_PASID_ENTRY_SLT);
+ if (ret != 0) {
+ return ret;
}
*reads = (*reads) && (slpte & VTD_SL_R);
*writes = (*writes) && (slpte & VTD_SL_W);
- if (!(slpte & access_right_check)) {
+ if ((slpte & access_right_check) != access_right_check) {
error_report_once("%s: detected slpte permission error "
"(iova=0x%" PRIx64 ", level=0x%" PRIx32 ", "
"slpte=0x%" PRIx64 ", write=%d, pasid=0x%"
- PRIx32 ")", __func__, iova, level,
+ PRIx32 ")", __func__, iova, *pte_level,
slpte, is_write, pasid);
- return is_write ? -VTD_FR_WRITE : -VTD_FR_READ;
+ if (s->root_scalable) {
+ return is_write ? -VTD_FR_SM_WRITE : -VTD_FR_SM_READ;
+ } else {
+ return is_write ? -VTD_FR_WRITE : -VTD_FR_READ;
+ }
}
- if (vtd_slpte_nonzero_rsvd(slpte, level)) {
+ if (vtd_slpte_nonzero_rsvd(slpte, *pte_level)) {
error_report_once("%s: detected splte reserve non-zero "
"iova=0x%" PRIx64 ", level=0x%" PRIx32
"slpte=0x%" PRIx64 ", pasid=0x%" PRIX32 ")",
- __func__, iova, level, slpte, pasid);
+ __func__, iova, *pte_level, slpte, pasid);
return -VTD_FR_PAGING_ENTRY_RSVD;
}
- if (vtd_is_last_pte(slpte, level)) {
+ if (vtd_is_last_pte(slpte, *pte_level)) {
*slptep = slpte;
- *slpte_level = level;
break;
}
addr = vtd_get_pte_addr(slpte, aw_bits);
- level--;
+ (*pte_level)--;
+ }
+
+ return 0;
+}
+
+/*
+ * Given the @iova, get relevant @ptep. @pte_level will be the last level
+ * of the translation, can be used for deciding the size of large page.
+ */
+static int vtd_iova_to_pte(IntelIOMMUState *s, VTDContextEntry *ce,
+ uint64_t iova, bool is_write,
+ uint64_t *ptep, uint32_t *pte_level,
+ bool *reads, bool *writes, uint8_t aw_bits,
+ uint32_t pasid)
+{
+ dma_addr_t addr;
+ uint16_t pgtt;
+ uint64_t xlat, size;
+ VTDPASIDEntry pe;
+ int ret;
+
+ /* FIXME: what is the Atomics request in access rights? */
+
+ if (s->root_scalable) {
+ vtd_ce_get_rid2pasid_entry(s, ce, &pe, pasid);
+ pgtt = VTD_PE_GET_TYPE(&pe);
+ *pte_level = VTD_PE_GET_LEVEL(&pe);
+ addr = vtd_pe_get_pgtbl_base(&pe);
+ switch (pgtt) {
+ case VTD_SM_PASID_ENTRY_FLT:
+ if (s->ecap & VTD_ECAP_FLTS) {
+ ret = vtd_iova_to_pte_fl(s, ce, iova, is_write, ptep, pte_level,
+ reads, writes, aw_bits, pasid, addr);
+ } else {
+ error_report_once("First-stage translation not supported : %d",
+ pgtt);
+ return -VTD_FR_INVALID_PGTT;
+ }
+ break;
+ case VTD_SM_PASID_ENTRY_SLT:
+ if (s->ecap & VTD_ECAP_SLTS) {
+ ret = vtd_iova_to_pte_sl(s, ce, iova, is_write, ptep, pte_level,
+ reads, writes, aw_bits, pasid, addr);
+ } else {
+ error_report_once("Second-stage translation not supported : %d",
+ pgtt);
+ return -VTD_FR_INVALID_PGTT;
+ }
+ break;
+ default:
+ error_report_once("Unsupported PGTT : %d", pgtt);
+ ret = -VTD_FR_INVALID_PGTT;
+ break;
+ }
+ } else {
+ *pte_level = vtd_ce_get_level(ce);
+ addr = vtd_ce_get_slpt_base(ce);
+ ret = vtd_iova_to_pte_sl(s, ce, iova, is_write, ptep, pte_level,
+ reads, writes, aw_bits, pasid, addr);
}
- xlat = vtd_get_pte_addr(*slptep, aw_bits);
- size = ~vtd_pt_level_page_mask(level) + 1;
+ if (ret != 0) {
+ return ret;
+ }
+
+ xlat = vtd_get_pte_addr(*ptep, aw_bits);
+ size = ~vtd_pt_level_page_mask(*pte_level) + 1;
/*
* From VT-d spec 3.14: Untranslated requests and translation
* requests that result in an address in the interrupt range will be
* blocked with condition code LGN.4 or SGN.8.
*/
- if ((xlat > VTD_INTERRUPT_ADDR_LAST ||
- xlat + size - 1 < VTD_INTERRUPT_ADDR_FIRST)) {
+ if (!vtd_addr_in_interrup_range(xlat, size)) {
return 0;
} else {
error_report_once("%s: xlat address is in interrupt range "
"(iova=0x%" PRIx64 ", level=0x%" PRIx32 ", "
- "slpte=0x%" PRIx64 ", write=%d, "
+ "pte=0x%" PRIx64 ", write=%d, "
"xlat=0x%" PRIx64 ", size=0x%" PRIx64 ", "
"pasid=0x%" PRIx32 ")",
- __func__, iova, level, slpte, is_write,
+ __func__, iova, *pte_level, *ptep, is_write,
xlat, size, pasid);
- return s->scalable_mode ? -VTD_FR_SM_INTERRUPT_ADDR :
- -VTD_FR_INTERRUPT_ADDR;
+ return -VTD_INTERRUPT_RANGE_ERROR(s);
}
}
@@ -1772,6 +2004,16 @@ static const bool vtd_qualified_faults[] = {
[VTD_FR_CONTEXT_ENTRY_TT] = true,
[VTD_FR_PASID_TABLE_INV] = false,
[VTD_FR_SM_INTERRUPT_ADDR] = true,
+ [VTD_FR_INVALID_PGTT] = true,
+ [VTD_FR_FSPE_ACCESS] = true,
+ [VTD_FR_FSPE_NOT_PRESENT] = true,
+ [VTD_FR_FIRST_FSPE_ACCESS] = true,
+ [VTD_FR_SSPE_ACCESS] = true,
+ [VTD_FR_FIRST_SSPE_ACCESS] = true,
+ [VTD_FR_FS_NON_CANONICAL] = true,
+ [VTD_FR_SM_WRITE] = true,
+ [VTD_FR_SM_READ] = true,
+ [VTD_FR_FS_BIT_UPDATE_FAILED] = true,
[VTD_FR_MAX] = false,
};
@@ -1870,7 +2112,8 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus,
uint8_t bus_num = pci_bus_num(bus);
VTDContextCacheEntry *cc_entry;
uint64_t pte, page_mask;
- uint32_t level, pasid = vtd_as->pasid;
+ uint32_t level = UINT32_MAX;
+ uint32_t pasid = vtd_as->pasid;
uint16_t source_id = PCI_BUILD_BDF(bus_num, devfn);
int ret_fr;
bool is_fpd_set = false;
@@ -1981,7 +2224,7 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus,
}
}
- ret_fr = vtd_iova_to_slpte(s, &ce, addr, is_write, &pte, &level,
+ ret_fr = vtd_iova_to_pte(s, &ce, addr, is_write, &pte, &level,
&reads, &writes, s->aw_bits, pasid);
if (ret_fr) {
vtd_report_fault(s, -ret_fr, is_fpd_set, source_id,
@@ -1990,6 +2233,7 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus,
}
page_mask = vtd_pt_level_page_mask(level);
+ /* Exe is always set to 0 in the spec */
access_flags = IOMMU_ACCESS_FLAG(reads, writes);
vtd_update_iotlb(s, source_id, vtd_get_domain_id(s, &ce, pasid),
addr, pte, access_flags, level, pasid);
@@ -2005,7 +2249,12 @@ error:
vtd_iommu_unlock(s);
entry->iova = 0;
entry->translated_addr = 0;
- entry->addr_mask = 0;
+ /*
+ * Set the mask for ATS (the range must be present even when the
+ * translation fails : PCIe rev 5 10.2.3.5)
+ */
+ entry->addr_mask = (level != UINT32_MAX) ?
+ (~vtd_pt_level_page_mask(level)) : (~VTD_PAGE_MASK_4K);
entry->perm = IOMMU_NONE;
return false;
}
@@ -3187,6 +3436,8 @@ static IOMMUTLBEntry vtd_iommu_translate(IOMMUMemoryRegion *iommu, hwaddr addr,
};
bool success;
+ /* IOMMUAccessFlags : IOMMU_EXEC and IOMMU_PRIV not supported */
+
if (likely(s->dmar_enabled)) {
success = vtd_do_iommu_translate(vtd_as, vtd_as->bus, vtd_as->devfn,
addr, flag & IOMMU_WO, &iotlb);
@@ -3989,6 +4240,21 @@ static void vtd_init(IntelIOMMUState *s)
vtd_spte_rsvd_large[3] = VTD_SPTE_LPAGE_L3_RSVD_MASK(s->aw_bits,
x86_iommu->dt_supported);
+ /*
+ * Rsvd field masks for fpte
+ */
+ vtd_fpte_rsvd[0] = ~0ULL;
+ vtd_fpte_rsvd[1] = VTD_FPTE_PAGE_L1_RSVD_MASK(s->aw_bits);
+ vtd_fpte_rsvd[2] = VTD_FPTE_PAGE_L2_RSVD_MASK(s->aw_bits);
+ vtd_fpte_rsvd[3] = VTD_FPTE_PAGE_L3_RSVD_MASK(s->aw_bits);
+ vtd_fpte_rsvd[4] = VTD_FPTE_PAGE_L4_RSVD_MASK(s->aw_bits);
+
+ vtd_fpte_rsvd_large[0] = ~0ULL;
+ vtd_fpte_rsvd_large[1] = ~0ULL;
+ vtd_fpte_rsvd_large[2] = VTD_FPTE_PAGE_L2_FS2MP_RSVD_MASK(s->aw_bits);
+ vtd_fpte_rsvd_large[3] = VTD_FPTE_PAGE_L3_FS1GP_RSVD_MASK(s->aw_bits);
+ vtd_fpte_rsvd_large[4] = ~0ULL;
+
if (s->scalable_mode || s->snoop_control) {
vtd_spte_rsvd[1] &= ~VTD_SPTE_SNP;
vtd_spte_rsvd_large[2] &= ~VTD_SPTE_SNP;
diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h
index 8d27b1c15b..ed61979934 100644
--- a/hw/i386/intel_iommu_internal.h
+++ b/hw/i386/intel_iommu_internal.h
@@ -195,6 +195,7 @@
#define VTD_ECAP_PASID (1ULL << 40)
#define VTD_ECAP_SMTS (1ULL << 43)
#define VTD_ECAP_SLTS (1ULL << 46)
+#define VTD_ECAP_FLTS (1ULL << 47)
/* CAP_REG */
/* (offset >> 4) << 24 */
@@ -215,6 +216,7 @@
#define VTD_CAP_CM (1ULL << 7)
#define VTD_PASID_ID_SHIFT 20
#define VTD_PASID_ID_MASK ((1ULL << VTD_PASID_ID_SHIFT) - 1)
+#define VTD_CAP_FS1GP (1ULL << 56)
/* Supported Adjusted Guest Address Widths */
#define VTD_CAP_SAGAW_SHIFT 8
@@ -270,6 +272,10 @@
#define VTD_FRCD_PP(val) (((val) & 0x1ULL) << 31)
#define VTD_FRCD_IR_IDX(val) (((val) & 0xffffULL) << 48)
+#define VTD_INTERRUPT_RANGE_ERROR(s) ((s)->scalable_mode ? \
+ VTD_FR_SM_INTERRUPT_ADDR : \
+ VTD_FR_INTERRUPT_ADDR)
+
/* DMA Remapping Fault Conditions */
typedef enum VTDFaultReason {
VTD_FR_RESERVED = 0, /* Reserved for Advanced Fault logging */
@@ -312,9 +318,21 @@ typedef enum VTDFaultReason {
VTD_FR_IR_SID_ERR = 0x26, /* Invalid Source-ID */
VTD_FR_PASID_TABLE_INV = 0x58, /*Invalid PASID table entry */
+ VTD_FR_INVALID_PGTT = 0x5b, /* SPT.4.2 Invalid PGTT */
+
+ VTD_FR_FSPE_ACCESS = 0x70, /* SFS.1 */
+ VTD_FR_FSPE_NOT_PRESENT = 0x71, /* SFS.2 : Present (P) field is 0.*/
+ VTD_FR_FIRST_FSPE_ACCESS = 0x73, /* SFS.4 */
+ VTD_FR_SSPE_ACCESS = 0x78, /* SSS.1 */
+ VTD_FR_FIRST_SSPE_ACCESS = 0x7b, /* SSS.4 */
+ VTD_FR_FS_NON_CANONICAL = 0x80, /* SNG.1 : Address for FS not canonical.*/
+
+ VTD_FR_SM_WRITE = 0x85, /* SGN.6 */
+ VTD_FR_SM_READ = 0x86, /* SGN.7 */
/* Output address in the interrupt address range for scalable mode */
- VTD_FR_SM_INTERRUPT_ADDR = 0x87,
+ VTD_FR_SM_INTERRUPT_ADDR = 0x87, /* SGN.8 */
+ VTD_FR_FS_BIT_UPDATE_FAILED = 0x91, /* SFS.10 */
VTD_FR_MAX, /* Guard */
} VTDFaultReason;
@@ -431,6 +449,23 @@ typedef union VTDInvDesc VTDInvDesc;
(0x3ffff800ULL | ~(VTD_HAW_MASK(aw) | VTD_SL_IGN_COM | VTD_SL_TM)) : \
(0x3ffff800ULL | ~(VTD_HAW_MASK(aw) | VTD_SL_IGN_COM))
+#define VTD_FS_UPPER_IGNORED 0xfff0000000000000ULL
+#define VTD_FPTE_PAGE_L1_RSVD_MASK(aw) (~(VTD_HAW_MASK(aw)) & \
+ (~VTD_FS_UPPER_IGNORED))
+#define VTD_FPTE_PAGE_L2_RSVD_MASK(aw) (~(VTD_HAW_MASK(aw)) & \
+ (~VTD_FS_UPPER_IGNORED))
+#define VTD_FPTE_PAGE_L3_RSVD_MASK(aw) (~(VTD_HAW_MASK(aw)) & \
+ (~VTD_FS_UPPER_IGNORED))
+#define VTD_FPTE_PAGE_L3_FS1GP_RSVD_MASK(aw) ((0x3fffe000ULL | \
+ ~(VTD_HAW_MASK(aw))) \
+ & (~VTD_FS_UPPER_IGNORED))
+#define VTD_FPTE_PAGE_L2_FS2MP_RSVD_MASK(aw) ((0x1fe000ULL | \
+ ~(VTD_HAW_MASK(aw))) \
+ & (~VTD_FS_UPPER_IGNORED))
+#define VTD_FPTE_PAGE_L4_RSVD_MASK(aw) ((0x80ULL | \
+ ~(VTD_HAW_MASK(aw))) \
+ & (~VTD_FS_UPPER_IGNORED))
+
/* Information about page-selective IOTLB invalidate */
struct VTDIOTLBPageInvInfo {
uint16_t domain_id;
@@ -514,9 +549,6 @@ typedef struct VTDRootEntry VTDRootEntry;
#define VTD_SM_PASID_ENTRY_AW 7ULL /* Adjusted guest-address-width */
#define VTD_SM_PASID_ENTRY_DID(val) ((val) & VTD_DOMAIN_ID_MASK)
-/* Second Level Page Translation Pointer*/
-#define VTD_SM_PASID_ENTRY_SLPTPTR (~0xfffULL)
-
/* Paging Structure common */
#define VTD_SM_PASID_ENTRY_PTPTR (~0xfffULL)
#define VTD_PT_PAGE_SIZE_MASK (1ULL << 7)
@@ -532,6 +564,14 @@ typedef struct VTDRootEntry VTDRootEntry;
#define VTD_SL_PD_LEVEL 2
#define VTD_SL_PT_LEVEL 1
+/* First Level Paging Structure */
+#define VTD_FL_PDP_LEVEL 3
+#define VTD_FL_PD_LEVEL 2
+#define VTD_FL_PTE_P 0x1
+#define VTD_FL_PTE_A 0x20
+#define VTD_FL_PTE_D 0x40
+#define VTD_FL_PTE_EA 0x400
+
/* Masks for Second Level Paging Entry */
#define VTD_SL_RW_MASK 3ULL
#define VTD_SL_R 1ULL
@@ -539,4 +579,7 @@ typedef struct VTDRootEntry VTDRootEntry;
#define VTD_SL_IGN_COM 0xbff0000000000000ULL
#define VTD_SL_TM (1ULL << 62)
+/* Masks for First Level Paging Entry */
+#define VTD_FL_W (1ULL << 1)
+
#endif
--
2.44.0
next prev parent reply other threads:[~2024-04-22 15:54 UTC|newest]
Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-04-22 15:52 [PATCH intel_iommu 0/7] FLTS for VT-d CLEMENT MATHIEU--DRIF
2024-04-22 15:52 ` [PATCH intel_iommu 1/7] intel_iommu: fix FRCD construction macro CLEMENT MATHIEU--DRIF
2024-04-22 15:52 ` [PATCH intel_iommu 2/7] intel_iommu: rename slpte to pte before adding FLTS CLEMENT MATHIEU--DRIF
2024-04-22 15:52 ` [PATCH intel_iommu 6/7] intel_iommu: add PASID-based IOTLB invalidation CLEMENT MATHIEU--DRIF
2024-04-22 15:52 ` [PATCH intel_iommu 7/7] intel_iommu: add a CLI option to enable FLTS CLEMENT MATHIEU--DRIF
2024-04-22 15:52 ` [PATCH intel_iommu 5/7] intel_iommu: extract device IOTLB invalidation logic CLEMENT MATHIEU--DRIF
2024-04-22 16:59 ` Philippe Mathieu-Daudé
2024-04-23 5:07 ` CLEMENT MATHIEU--DRIF
2024-04-22 15:52 ` CLEMENT MATHIEU--DRIF [this message]
2024-04-22 15:52 ` [PATCH intel_iommu 3/7] intel_iommu: make types match CLEMENT MATHIEU--DRIF
2024-04-22 17:03 ` Philippe Mathieu-Daudé
2024-04-23 5:05 ` CLEMENT MATHIEU--DRIF
2024-04-23 8:19 ` Philippe Mathieu-Daudé
2024-04-23 15:17 ` CLEMENT MATHIEU--DRIF
2024-04-30 14:04 ` [PATCH intel_iommu 0/7] FLTS for VT-d Philippe Mathieu-Daudé
2024-04-30 14:13 ` Cédric Le Goater
2024-05-01 12:40 ` Duan, Zhenzhong
2024-05-02 5:03 ` CLEMENT MATHIEU--DRIF
2024-05-06 1:38 ` Duan, Zhenzhong
2024-05-14 5:02 ` CLEMENT MATHIEU--DRIF
2024-05-14 5:50 ` Duan, Zhenzhong
2024-05-14 6:13 ` CLEMENT MATHIEU--DRIF
2024-06-02 14:10 ` Michael S. Tsirkin
2024-06-03 5:31 ` CLEMENT MATHIEU--DRIF
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240422155236.129179-5-clement.mathieu--drif@eviden.com \
--to=clement.mathieu--drif@eviden.com \
--cc=jasowang@redhat.com \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).