Changeset 84242 in vbox
- Timestamp:
- May 11, 2020 9:13:23 AM (5 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/Bus/DevIommuAmd.cpp
r84237 r84242 2203 2203 2204 2204 /** 2205 * I/O page walk result. 2206 */ 2207 typedef struct 2208 { 2209 /** The translated system physical address. */ 2210 RTGCPHYS GCPhysSpa; 2211 /** The number of offset bits in the system physical address. */ 2212 uint8_t cShift; 2213 /** The I/O permissions allowed by the translation (IOMMU_IO_PERM_XXX). */ 2214 uint8_t fIoPerm; 2215 /** Padding. */ 2216 uint8_t abPadding[2]; 2217 } IOWALKRESULT; 2218 /** Pointer to an I/O walk result struct. */ 2219 typedef IOWALKRESULT *PIOWALKRESULT; 2220 /** Pointer to a const I/O walk result struct. */ 2221 typedef IOWALKRESULT *PCIOWALKRESULT; 2222 2223 /** 2205 2224 * IOMMU I/O TLB Entry. 2206 2225 * @note Update iommuAmdInitIotlbe() when changes are made. … … 3605 3624 pEvtDevTabHwErr->n.u16DevId = uDevId; 3606 3625 pEvtDevTabHwErr->n.u1Intr = RT_BOOL(enmOp == IOMMUOP_INTR_REQ); 3607 /** @todo r=ramshankar: Any other transaction type that can set read/write bit? */3626 /** @todo IOMMU: Any other transaction type that can set read/write bit? */ 3608 3627 pEvtDevTabHwErr->n.u1ReadWrite = RT_BOOL(enmOp == IOMMUOP_MEM_WRITE); 3609 3628 pEvtDevTabHwErr->n.u1Translation = RT_BOOL(enmOp == IOMMUOP_TRANSLATE_REQ); … … 3959 3978 3960 3979 /** 3961 * Walks the I/O page table (s)to translate the I/O virtual address to a system3980 * Walks the I/O page table to translate the I/O virtual address to a system 3962 3981 * physical address. 3963 3982 * 3964 3983 * @returns VBox status code. 3965 * @param pDevIns The IOMMU device instance.3966 * @param uIova The I/O virtual address to translate. Must be 4K aligned!3967 * @param uDevId The device ID.3968 * @param fAccess The access permissions (IOMMU_IO_PERM_XXX). This is the3969 * permissions for the access being made.3970 * @param pDte The device table entry.3971 * @param enmOp The IOMMU operation being performed.3972 * @param p Iotlbe The IOTLB entry to update with the results of the3973 * translation.3984 * @param pDevIns The IOMMU device instance. 3985 * @param uIova The I/O virtual address to translate. Must be 4K aligned. 3986 * @param uDevId The device ID. 3987 * @param fAccess The access permissions (IOMMU_IO_PERM_XXX). This is the 3988 * permissions for the access being made. 3989 * @param pDte The device table entry. 3990 * @param enmOp The IOMMU operation being performed. 3991 * @param pWalkResult Where to store the results of the I/O page walk. This is 3992 * only updated when VINF_SUCCESS is returned. 3974 3993 * 3975 3994 * @thread Any. 3976 3995 */ 3977 static int iommuAmdWalkIoPageTable s(PPDMDEVINS pDevIns, uint16_t uDevId, uint64_t uIova, uint8_t fAccess, PCDTE_T pDte,3978 IOMMUOP enmOp, PIOTLBE_T pIotlbe)3996 static int iommuAmdWalkIoPageTable(PPDMDEVINS pDevIns, uint16_t uDevId, uint64_t uIova, uint8_t fAccess, PCDTE_T pDte, 3997 IOMMUOP enmOp, PIOWALKRESULT pWalkResult) 3979 3998 { 3980 3999 Assert(pDte->n.u1Valid); 3981 /* The input I/O virtual address must be 4K page aligned. */3982 4000 Assert(!(uIova & X86_PAGE_4K_OFFSET_MASK)); 3983 4001 … … 4002 4020 /* If the root page table level is 0, translation is skipped and access is controlled by the permission bits. */ 4003 4021 uint8_t const uMaxLevel = pDte->n.u3Mode; 4004 if (uMaxLevel == 0) 4022 if (uMaxLevel != 0) 4023 { /* likely */ } 4024 else 4005 4025 { 4006 4026 uint8_t const fDtePerm = (pDte->au64[0] >> IOMMU_IO_PERM_SHIFT) & IOMMU_IO_PERM_MASK; … … 4010 4030 return VERR_IOMMU_ADDR_ACCESS_DENIED; 4011 4031 } 4012 iommuAmdUpdateIotlbe(uIova, 0 /* cShift */, fDtePerm, pIotlbe); 4032 pWalkResult->GCPhysSpa = uIova; 4033 pWalkResult->cShift = 0; 4034 pWalkResult->fIoPerm = fDtePerm; 4013 4035 return VINF_SUCCESS; 4014 4036 } 4015 4037 4016 4038 /* If the root page table level exceeds the allowed host-address translation level, page walk is terminated. */ 4017 if (uMaxLevel > IOMMU_MAX_HOST_PT_LEVEL) 4039 if (uMaxLevel <= IOMMU_MAX_HOST_PT_LEVEL) 4040 { /* likely */ } 4041 else 4018 4042 { 4019 4043 /** @todo r=ramshankar: I cannot make out from the AMD IOMMU spec. if I should be … … 4030 4054 /* Check permissions bits of the root page table. */ 4031 4055 uint8_t const fPtePerm = (pDte->au64[0] >> IOMMU_IO_PERM_SHIFT) & IOMMU_IO_PERM_MASK; 4032 if ((fAccess & fPtePerm) != fAccess) 4056 if ((fAccess & fPtePerm) == fAccess) 4057 { /* likely */ } 4058 else 4033 4059 { 4034 4060 EVT_IO_PAGE_FAULT_T EvtIoPageFault; … … 4039 4065 } 4040 4066 4041 /** @todo IOMMU: Split the rest of this into a separate function called 4042 * iommuAmdWalkIoPageDirectory() and call it for multi-page accesses. We can 4043 * avoid re-checking the DTE root-page table entry every time. */ 4067 /** @todo r=ramshankar: IOMMU: Consider splitting the rest of this into a separate 4068 * function called iommuAmdWalkIoPageDirectory() and call it for multi-page 4069 * accesses from the 2nd page. We can avoid re-checking the DTE root-page 4070 * table entry every time. Not sure if it's worth optimizing that case now 4071 * or if at all. */ 4044 4072 4045 4073 /* The virtual address bits indexing table. */ … … 4112 4140 { 4113 4141 /* The page size of the translation is the default (4K). */ 4114 p Iotlbe->GCPhysSpa = PtEntity.u64 & IOMMU_PTENTITY_ADDR_MASK;4115 p Iotlbe->cShift = X86_PAGE_4K_SHIFT;4116 p Iotlbe->fIoPerm = fPtePerm;4142 pWalkResult->GCPhysSpa = PtEntity.u64 & IOMMU_PTENTITY_ADDR_MASK; 4143 pWalkResult->cShift = X86_PAGE_4K_SHIFT; 4144 pWalkResult->fIoPerm = fPtePerm; 4117 4145 return VINF_SUCCESS; 4118 4146 } … … 4130 4158 && cShift < s_acIovaLevelShifts[uLevel + 1]) 4131 4159 { 4132 p Iotlbe->GCPhysSpa = GCPhysPte;4133 p Iotlbe->cShift = cShift;4134 p Iotlbe->fIoPerm = fPtePerm;4160 pWalkResult->GCPhysSpa = GCPhysPte; 4161 pWalkResult->cShift = cShift; 4162 pWalkResult->fIoPerm = fPtePerm; 4135 4163 return VINF_SUCCESS; 4136 4164 } … … 4200 4228 4201 4229 /** 4202 * Looks up an I/O virtual address from the device table (s).4230 * Looks up an I/O virtual address from the device table. 4203 4231 * 4204 4232 * @returns VBox status code. … … 4216 4244 * @thread Any. 4217 4245 */ 4218 static int iommuAmdLookupDeviceTable s(PPDMDEVINS pDevIns, uint16_t uDevId, uint64_t uIova, size_t cbAccess, uint8_t fAccess,4219 4246 static int iommuAmdLookupDeviceTable(PPDMDEVINS pDevIns, uint16_t uDevId, uint64_t uIova, size_t cbAccess, uint8_t fAccess, 4247 IOMMUOP enmOp, PRTGCPHYS pGCPhysSpa) 4220 4248 { 4221 4249 PIOMMU pThis = PDMDEVINS_2_DATA(pDevIns, PIOMMU); … … 4231 4259 else 4232 4260 { 4261 /** @todo IOMMU: Add to IOLTB cache. */ 4233 4262 *pGCPhysSpa = uIova; 4234 4263 return VINF_SUCCESS; … … 4256 4285 else 4257 4286 { 4287 /** @todo IOMMU: Add to IOLTB cache. */ 4258 4288 *pGCPhysSpa = uIova; 4259 4289 return VINF_SUCCESS; … … 4262 4292 /** @todo IOMMU: Perhaps do the <= 4K access case first, if the generic loop 4263 4293 * below gets too expensive and when we have iommuAmdWalkIoPageDirectory. */ 4264 4265 IOTLBE_T Iotlbe;4266 iommuAmdInitIotlbe(NIL_RTGCPHYS, 0 /* cShift */, IOMMU_IO_PERM_NONE, &Iotlbe);4267 4294 4268 4295 uint64_t uBaseIova = uIova & X86_PAGE_4K_BASE_MASK; … … 4272 4299 { 4273 4300 /* Walk the I/O page tables to translate the IOVA and check permission for the access. */ 4274 rc = iommuAmdWalkIoPageTables(pDevIns, uDevId, uBaseIova, fAccess, &Dte, enmOp, &Iotlbe); 4301 IOWALKRESULT WalkResult; 4302 rc = iommuAmdWalkIoPageTable(pDevIns, uDevId, uBaseIova, fAccess, &Dte, enmOp, &WalkResult); 4275 4303 if (RT_SUCCESS(rc)) 4276 4304 { 4305 /** @todo IOMMU: Split large pages into 4K IOTLB entries and add to IOTLB cache. */ 4306 4277 4307 /* Store the translated base address before continuing to check permissions for any more pages. */ 4278 4308 if (cbRemaining == cbAccess) 4279 4309 { 4280 RTGCPHYS const offSpa = ~(UINT64_C(0xffffffffffffffff) << Iotlbe.cShift);4281 *pGCPhysSpa = Iotlbe.GCPhysSpa | offSpa;4310 RTGCPHYS const offSpa = ~(UINT64_C(0xffffffffffffffff) << WalkResult.cShift); 4311 *pGCPhysSpa = WalkResult.GCPhysSpa | offSpa; 4282 4312 } 4283 4313 4284 /** @todo IOMMU: Split large pages into 4K IOTLB entries and add to IOTLB cache. */ 4285 4286 uint64_t const cbPhysPage = UINT64_C(1) << Iotlbe.cShift; 4314 uint64_t const cbPhysPage = UINT64_C(1) << WalkResult.cShift; 4287 4315 if (cbRemaining > cbPhysPage - offIova) 4288 4316 { … … 4337 4365 /** @todo IOMMU: IOTLB cache lookup. */ 4338 4366 4339 /* Lookup the IOVA from the device table s. */4340 return iommuAmdLookupDeviceTable s(pDevIns, uDevId, uIova, cbRead, IOMMU_IO_PERM_READ, IOMMUOP_MEM_READ, pGCPhysSpa);4367 /* Lookup the IOVA from the device table. */ 4368 return iommuAmdLookupDeviceTable(pDevIns, uDevId, uIova, cbRead, IOMMU_IO_PERM_READ, IOMMUOP_MEM_READ, pGCPhysSpa); 4341 4369 } 4342 4370 … … 4360 4388 static int iommuAmdDeviceMemWrite(PPDMDEVINS pDevIns, uint16_t uDevId, uint64_t uIova, size_t cbWrite, PRTGCPHYS pGCPhysSpa) 4361 4389 { 4362 RT_NOREF(pDevIns, uDevId, uIova, cbWrite, pGCPhysSpa); 4363 return VERR_NOT_IMPLEMENTED; 4390 Assert(pDevIns); 4391 Assert(pGCPhysSpa); 4392 Assert(cbWrite > 0); 4393 4394 PIOMMU pThis = PDMDEVINS_2_DATA(pDevIns, PIOMMU); 4395 4396 /* Addresses are forwarded without translation when the IOMMU is disabled. */ 4397 IOMMU_CTRL_T const Ctrl = iommuAmdGetCtrl(pThis); 4398 if (Ctrl.n.u1IommuEn) 4399 { 4400 /** @todo IOMMU: IOTLB cache lookup. */ 4401 4402 /* Lookup the IOVA from the device table. */ 4403 return iommuAmdLookupDeviceTable(pDevIns, uDevId, uIova, cbWrite, IOMMU_IO_PERM_WRITE, IOMMUOP_MEM_WRITE, pGCPhysSpa); 4404 } 4405 4406 *pGCPhysSpa = uIova; 4407 return VINF_SUCCESS; 4364 4408 } 4365 4409
Note:
See TracChangeset
for help on using the changeset viewer.