This translation mode will only be made available in scalable mode Signed-off-by: Clément Mathieu--Drif <clement.mathieu--d...@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