Changeset 84221 in vbox
- Timestamp:
- May 8, 2020 6:14:43 PM (5 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/Bus/DevIommuAmd.cpp
r84200 r84221 710 710 /** Pointer to a const IOPT_ENTITY_T struct. */ 711 711 typedef IOPTENTITY_T const *PCIOPTENTITY_T; 712 /** Mask of the address field. */ 713 #define IOMMU_PTENTITY_ADDR_MASK UINT64_C(0x000ffffffffff000) 712 714 713 715 /** … … 1003 1005 uint16_t u1Present : 1; /**< Bit 52 - PR: Present. */ 1004 1006 uint16_t u1ReadWrite : 1; /**< Bit 53 - RW: Read/Write. */ 1005 uint16_t u1Perm : 1;/**< Bit 54 - PE: Permission Indicator. */1007 uint16_t u1PermDenied : 1; /**< Bit 54 - PE: Permission Indicator. */ 1006 1008 uint16_t u1RsvdNotZero : 1; /**< Bit 55 - RZ: Reserved bit not Zero (0=invalid level encoding). */ 1007 1009 uint16_t u1Translation : 1; /**< Bit 56 - TN: Translation. */ … … 1072 1074 uint32_t u4EvtCode : 4; /**< Bit 63:60 - Event code. */ 1073 1075 /** @todo r=ramshankar: Figure 55: PAGE_TAB_HARDWARE_ERROR says Addr[31:3] but 1074 * table 58 mentions Addr[31:4]. Looks like a typo in the figure. Use 1075 * table as it makes more sense and matches address size in 1076 * EVT_DEV_TAB_HARDWARE_ERROR. See AMD AMD IOMMU spec (3.05-PUB, Jan 1077 * 2020). */ 1078 uint32_t u4Rsvd0 : 4; /**< Bits 67:64 - Reserved. */ 1079 uint32_t u28AddrLo : 28; /**< Bits 95:68 - Address: SPA of page table entry (Lo). */ 1080 uint32_t u32AddrHi; /**< Bits 127:96 - Address: SPA of page table entry (Hi). */ 1076 * table 58 mentions Addr[31:4], we just use the full 64-bits. Looks like a 1077 * typo in the figure.See AMD AMD IOMMU spec (3.05-PUB, Jan 2020). */ 1078 uint64_t u64Addr; /** Bits 127:64 - Address: SPA of the page table entry. */ 1081 1079 } n; 1082 1080 /** The 32-bit unsigned integer view. */ … … 1084 1082 } EVT_PAGE_TAB_HW_ERR_T; 1085 1083 AssertCompileSize(EVT_PAGE_TAB_HW_ERR_T, 16); 1084 /** Pointer to a page table hardware error event. */ 1085 typedef EVT_PAGE_TAB_HW_ERR_T *PEVT_PAGE_TAB_HW_ERR_T; 1086 /** Pointer to a const page table hardware error event. */ 1087 typedef EVT_PAGE_TAB_HW_ERR_T const *PCEVT_PAGE_TAB_HW_ERR_T; 1088 1086 1089 1087 1090 /** … … 2105 2108 kIoPageFaultType_DteTranslationDisabled, 2106 2109 kIoPageFaultType_PasidInvalidRange, 2107 kIoPageFaultType_ReadProtect, 2108 kIoPageFaultType_WriteProtect, 2109 kIoPageFaultType_ExecuteProtect, 2110 kIoPageFaultType_PermDenied, 2110 2111 kIoPageFaultType_UserSupervisor, 2111 2112 /* Interrupt remapping */ … … 2122 2123 2123 2124 /** 2124 * DEV_TAB_HARDWARE_ERROR Event Types. 2125 * In accordance with the AMD spec. 2126 */ 2127 typedef enum EVT_DEV_TAB_HW_ERROR_TYPE_T 2128 { 2129 kDevTabHwErrType_MasterAbort = 0, 2130 kDevTabHwErrType_TargetAbort, 2131 kDevTabHwErrType_PoisonedData 2132 } EVT_DEV_TAB_HW_ERROR_TYPE_T; 2133 2134 /** 2135 * PAGE_TAB_HARDWARE_ERROR Even Types. 2136 * In accordance with the AMD spec. 2137 */ 2138 typedef enum EVT_PAGE_TAB_HW_ERR_TYPE_T 2139 { 2140 kPageTabHwErrType_MasterAbort = 0, 2141 kPageTabHwErrType_TargetAbort, 2142 kPageTabHwErrType_PoisonedData, 2143 } EVT_PAGE_TAB_HW_ERR_TYPE_T; 2144 2145 /** 2146 * COMMAND_HARDWARE_ERROR Event Types. 2147 * In accordance with the AMD spec. 2148 */ 2149 typedef enum EVT_CMD_HW_ERROR_TYPE_T 2150 { 2151 kCmdHwErrType_MasterAbort = 0, 2152 kCmdHwErrType_TargetAbort, 2153 kCmdHwErrType_PoisonedData 2154 } EVT_CMD_HW_ERROR_TYPE_T; 2125 * DEV_TAB_HARDWARE_ERROR, PAGE_TAB_HARDWARE_ERROR and COMMAND_HARDWARE_ERROR Event 2126 * Types. 2127 * In accordance with the AMD spec. 2128 */ 2129 typedef enum EVT_HW_ERR_TYPE_T 2130 { 2131 kHwErrType_MasterAbort = 0, 2132 kHwErrType_TargetAbort, 2133 kHwErrType_PoisonedData 2134 } EVT_HW_ERR_TYPE_T; 2155 2135 2156 2136 /** … … 3560 3540 3561 3541 /** 3542 * Initializes a PAGE_TAB_HARDWARE_ERROR event. 3543 * 3544 * @param uDevId The device ID. 3545 * @param uDomainId The domain ID. 3546 * @param GCPhysPtEntity The system physical address of the page table 3547 * entity. 3548 * @param enmOp The IOMMU operation being performed. 3549 * @param pEvtPageTabHwErr Where to store the initialized event. 3550 */ 3551 static void iommuAmdInitPageTabHwErrorEvent(uint16_t uDevId, uint16_t uDomainId, RTGCPHYS GCPhysPtEntity, IOMMUOP enmOp, 3552 PEVT_PAGE_TAB_HW_ERR_T pEvtPageTabHwErr) 3553 { 3554 memset(pEvtPageTabHwErr, 0, sizeof(*pEvtPageTabHwErr)); 3555 pEvtPageTabHwErr->n.u16DevId = uDevId; 3556 pEvtPageTabHwErr->n.u16DomainOrPasidLo = uDomainId; 3557 //pEvtPageTabHwErr->n.u1GuestOrNested = 0; 3558 pEvtPageTabHwErr->n.u1Interrupt = RT_BOOL(enmOp == IOMMUOP_INTR_REQ); 3559 pEvtPageTabHwErr->n.u1ReadWrite = RT_BOOL(enmOp == IOMMUOP_MEM_WRITE); 3560 pEvtPageTabHwErr->n.u1Translation = RT_BOOL(enmOp == IOMMUOP_TRANSLATE_REQ); 3561 pEvtPageTabHwErr->n.u2Type = enmOp == IOMMUOP_CMD ? HWEVTTYPE_DATA_ERROR : HWEVTTYPE_TARGET_ABORT;; 3562 pEvtPageTabHwErr->n.u4EvtCode = IOMMU_EVT_PAGE_TAB_HW_ERROR; 3563 pEvtPageTabHwErr->n.u64Addr = GCPhysPtEntity; 3564 } 3565 3566 3567 /** 3568 * Raises a PAGE_TAB_HARDWARE_ERROR event. 3569 * 3570 * @param pDevIns The IOMMU device instance. 3571 * @param enmOp The IOMMU operation being performed. 3572 * @param pEvtPageTabHwErr The page table hardware error event. 3573 * @param enmEvtType The hardware error event type. 3574 */ 3575 static void iommuAmdRaisePageTabHwErrorEvent(PPDMDEVINS pDevIns, IOMMUOP enmOp, PEVT_PAGE_TAB_HW_ERR_T pEvtPageTabHwErr, 3576 EVT_HW_ERR_TYPE_T enmEvtType) 3577 { 3578 AssertCompile(sizeof(EVT_GENERIC_T) == sizeof(EVT_PAGE_TAB_HW_ERR_T)); 3579 PCEVT_GENERIC_T pEvent = (PCEVT_GENERIC_T)pEvtPageTabHwErr; 3580 3581 iommuAmdSetHwError(pDevIns, (PCEVT_GENERIC_T)pEvent); 3582 iommuAmdWriteEvtLogEntry(pDevIns, (PCEVT_GENERIC_T)pEvent); 3583 if (enmOp != IOMMUOP_CMD) 3584 iommuAmdSetPciTargetAbort(pDevIns); 3585 3586 Log((IOMMU_LOG_PFX ": Raised PAGE_TAB_HARDWARE_ERROR. uDevId=%#x uDomainId=%#x GCPhysPtEntity=%#RGp enmOp=%u enmType=%u\n", 3587 pEvtPageTabHwErr->n.u16DevId, pEvtPageTabHwErr->n.u16DomainOrPasidLo, pEvtPageTabHwErr->n.u64Addr, enmOp, enmEvtType)); 3588 NOREF(enmEvtType); 3589 } 3590 3591 3592 /** 3562 3593 * Initializes a DEV_TAB_HARDWARE_ERROR event. 3563 3594 * … … 3567 3598 * @param enmOp The IOMMU operation being performed. 3568 3599 * @param pEvtDevTabHwErr Where to store the initialized event. 3569 *3570 * @thread Any.3571 3600 */ 3572 3601 static void iommuAmdInitDevTabHwErrorEvent(uint16_t uDevId, RTGCPHYS GCPhysDte, IOMMUOP enmOp, … … 3591 3620 * @param enmOp The IOMMU operation being performed. 3592 3621 * @param pEvtDevTabHwErr The device table hardware error event. 3593 * @param enmEvtType The device tablehardware error event type.3622 * @param enmEvtType The hardware error event type. 3594 3623 */ 3595 3624 static void iommuAmdRaiseDevTabHwErrorEvent(PPDMDEVINS pDevIns, IOMMUOP enmOp, PEVT_DEV_TAB_HW_ERROR_T pEvtDevTabHwErr, 3596 EVT_ DEV_TAB_HW_ERROR_TYPE_T enmEvtType)3625 EVT_HW_ERR_TYPE_T enmEvtType) 3597 3626 { 3598 3627 AssertCompile(sizeof(EVT_GENERIC_T) == sizeof(EVT_DEV_TAB_HW_ERROR_T)); … … 3672 3701 * the I/O page fault was caused by invalid level 3673 3702 * encoding. 3703 * @param fPermDenied Permission denied for the address being accessed. 3674 3704 * @param enmOp The IOMMU operation being performed. 3675 3705 * @param pEvtIoPageFault Where to store the initialized event. 3676 3706 */ 3677 3707 static void iommuAmdInitIoPageFaultEvent(uint16_t uDevId, uint16_t uDomainId, uint64_t uIova, bool fPresent, bool fRsvdNotZero, 3678 IOMMUOP enmOp, PEVT_IO_PAGE_FAULT_T pEvtIoPageFault) 3679 { 3708 bool fPermDenied, IOMMUOP enmOp, PEVT_IO_PAGE_FAULT_T pEvtIoPageFault) 3709 { 3710 Assert(!fPermDenied || fPresent); 3680 3711 memset(pEvtIoPageFault, 0, sizeof(*pEvtIoPageFault)); 3681 3712 pEvtIoPageFault->n.u16DevId = uDevId; … … 3688 3719 pEvtIoPageFault->n.u1Present = fPresent; 3689 3720 pEvtIoPageFault->n.u1ReadWrite = RT_BOOL(enmOp == IOMMUOP_MEM_WRITE); 3690 //pEvtIoPageFault->n.u1PermIndicator = 0;3721 pEvtIoPageFault->n.u1PermDenied = fPermDenied; 3691 3722 pEvtIoPageFault->n.u1RsvdNotZero = fRsvdNotZero; 3692 3723 pEvtIoPageFault->n.u1Translation = RT_BOOL(enmOp == IOMMUOP_TRANSLATE_REQ); … … 3743 3774 switch (enmEvtType) 3744 3775 { 3745 case kIoPageFaultType_ReadProtect: 3746 case kIoPageFaultType_WriteProtect: 3747 case kIoPageFaultType_ExecuteProtect: 3776 case kIoPageFaultType_PermDenied: 3748 3777 { 3749 3778 /* Cannot be triggered by a command. */ … … 3921 3950 EVT_DEV_TAB_HW_ERROR_T EvtDevTabHwErr; 3922 3951 iommuAmdInitDevTabHwErrorEvent(uDevId, GCPhysDte, enmOp, &EvtDevTabHwErr); 3923 iommuAmdRaiseDevTabHwErrorEvent(pDevIns, enmOp, &EvtDevTabHwErr, kDevTabHwErrType_TargetAbort); 3952 iommuAmdRaiseDevTabHwErrorEvent(pDevIns, enmOp, &EvtDevTabHwErr, kHwErrType_TargetAbort); 3953 return VERR_IOMMU_IPE_1; 3924 3954 } 3925 3955 … … 3964 3994 EVT_IO_PAGE_FAULT_T EvtIoPageFault; 3965 3995 iommuAmdInitIoPageFaultEvent(uDevId, pDte->n.u16DomainId, uIova, false /* fPresent */, false /* fRsvdNotZero */, 3966 enmOp, &EvtIoPageFault);3996 false /* fPermDenied */, enmOp, &EvtIoPageFault); 3967 3997 iommuAmdRaiseIoPageFaultEvent(pDevIns, pDte, NULL /* pIrte */, enmOp, &EvtIoPageFault, 3968 3998 kIoPageFaultType_DteTranslationDisabled); 3969 iommuAmdUpdateIotlbe(NIL_RTGCPHYS, 0 /* cShift */, IOMMU_IO_PERM_NONE, pIotlbe);3970 3999 return VERR_IOMMU_ADDR_TRANSLATION_FAILED; 3971 4000 } 3972 4001 3973 /* If the page table level (depth) is 0, translation is disabled and access is controlled by the permission bits. */3974 uint8_t const u Level = pDte->n.u3Mode;3975 if (u Level == 0)4002 /* If the root page table level is 0, translation is skipped and access is controlled by the permission bits. */ 4003 uint8_t const uMaxLevel = pDte->n.u3Mode; 4004 if (uMaxLevel == 0) 3976 4005 { 3977 4006 uint8_t const fDtePerm = (pDte->au64[0] >> IOMMU_IO_PERM_SHIFT) & IOMMU_IO_PERM_MASK; … … 3985 4014 } 3986 4015 3987 /* If the page table level (depth) exceeds the allowed host-address translation levels, page walk terminates. */3988 if (u Level > IOMMU_MAX_HOST_PT_LEVEL)4016 /* 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) 3989 4018 { 3990 4019 /** @todo r=ramshankar: I cannot make out from the AMD IOMMU spec. if I should be … … 3992 4021 * I'm just going with I/O page fault. */ 3993 4022 EVT_IO_PAGE_FAULT_T EvtIoPageFault; 3994 iommuAmdInitIoPageFaultEvent(uDevId, pDte->n.u16DomainId, uIova, true /* fPresent */, false /* fRsvdNotZero */, enmOp, 3995 &EvtIoPageFault); 3996 iommuAmdRaiseIoPageFaultEvent(pDevIns, pDte, NULL /* pIrte */, enmOp, &EvtIoPageFault,kIoPageFaultType_PteInvalidLvlEncoding); 3997 3998 iommuAmdUpdateIotlbe(NIL_RTGCPHYS, 0 /* cShift */, IOMMU_IO_PERM_NONE, pIotlbe); 4023 iommuAmdInitIoPageFaultEvent(uDevId, pDte->n.u16DomainId, uIova, true /* fPresent */, false /* fRsvdNotZero */, 4024 false /* fPermDenied */, enmOp, &EvtIoPageFault); 4025 iommuAmdRaiseIoPageFaultEvent(pDevIns, pDte, NULL /* pIrte */, enmOp, &EvtIoPageFault, 4026 kIoPageFaultType_PteInvalidLvlEncoding); 3999 4027 return VERR_IOMMU_ADDR_TRANSLATION_FAILED; 4000 4028 } 4001 4029 4002 /* Traverse the I/O page table starting with the validated DTE. */ 4003 PIOPTENTITY_T pPtEntity = (PIOPTENTITY_T)&pDte->au64[0]; 4030 /* Check permissions bits of the root page table. */ 4031 uint8_t const fPtePerm = (pDte->au64[0] >> IOMMU_IO_PERM_SHIFT) & IOMMU_IO_PERM_MASK; 4032 if ((fAccess & fPtePerm) != fAccess) 4033 { 4034 EVT_IO_PAGE_FAULT_T EvtIoPageFault; 4035 iommuAmdInitIoPageFaultEvent(uDevId, pDte->n.u16DomainId, uIova, true /* fPresent */, false /* fRsvdNotZero */, 4036 true /* fPermDenied */, enmOp, &EvtIoPageFault); 4037 iommuAmdRaiseIoPageFaultEvent(pDevIns, pDte, NULL /* pIrte */, enmOp, &EvtIoPageFault, kIoPageFaultType_PermDenied); 4038 return VERR_IOMMU_ADDR_TRANSLATION_FAILED; 4039 } 4040 4041 /* Virtual address bits indexing table. */ 4042 static uint8_t const s_acIovaLvlShifts[] = { 0, 12, 21, 30, 39, 48, 57, 0 }; 4043 4044 /* Traverse the I/O page table starting with the page directory in the DTE. */ 4045 IOPTENTITY_T PtEntity; 4046 PtEntity.u64 = pDte->au64[0]; 4004 4047 for (;;) 4005 4048 { 4006 /* If the page entity isn't present or has insufficient permissions for the access being made, raise an I/O page fault. */ 4007 uint8_t const fPtePerm = (pPtEntity->u64 >> IOMMU_IO_PERM_SHIFT) & IOMMU_IO_PERM_MASK; 4008 if ( !pPtEntity->n.u1Present 4009 || (fAccess & fPtePerm) != fAccess) 4010 { 4011 EVT_IO_PAGE_FAULT_TYPE_T const EvtIoPageFaultType = (fAccess & IOMMU_IO_PERM_WRITE) ? kIoPageFaultType_WriteProtect 4012 : kIoPageFaultType_ReadProtect; 4049 /* Figure out the system physical address of the page table at the next level. */ 4050 uint8_t const uLevel = PtEntity.n.u3NextLevel; 4051 Assert(uLevel > 0 && uLevel < RT_ELEMENTS(s_acIovaLvlShifts)); 4052 Assert(uLevel <= IOMMU_MAX_HOST_PT_LEVEL); 4053 uint16_t const idxPte = (uIova >> s_acIovaLvlShifts[uLevel]) & UINT64_C(0x1ff); 4054 uint64_t const offPte = idxPte << 3; 4055 RTGCPHYS const GCPhysPtEntity = (PtEntity.u64 & IOMMU_PTENTITY_ADDR_MASK) + offPte; 4056 4057 /* Read the page table entity at the next level. */ 4058 int rc = PDMDevHlpPCIPhysRead(pDevIns, GCPhysPtEntity, &PtEntity.u64, sizeof(PtEntity)); 4059 if (RT_FAILURE(rc)) 4060 { 4061 Log((IOMMU_LOG_PFX ": Failed to read page table entry at %#RGp. rc=%Rrc -> PageTabHwError\n", GCPhysPtEntity, rc)); 4062 EVT_PAGE_TAB_HW_ERR_T EvtPageTabHwErr; 4063 iommuAmdInitPageTabHwErrorEvent(uDevId, pDte->n.u16DomainId, GCPhysPtEntity, enmOp, &EvtPageTabHwErr); 4064 iommuAmdRaisePageTabHwErrorEvent(pDevIns, enmOp, &EvtPageTabHwErr, kHwErrType_TargetAbort); 4065 return VERR_IOMMU_IPE_2; 4066 } 4067 4068 /* Check present bit. */ 4069 if (!PtEntity.n.u1Present) 4070 { 4071 EVT_IO_PAGE_FAULT_T EvtIoPageFault; 4072 iommuAmdInitIoPageFaultEvent(uDevId, pDte->n.u16DomainId, uIova, false /* fPresent */, false /* fRsvdNotZero */, 4073 false /* fPermDenied */, enmOp, &EvtIoPageFault); 4074 iommuAmdRaiseIoPageFaultEvent(pDevIns, pDte, NULL /* pIrte */, enmOp, &EvtIoPageFault, kIoPageFaultType_PermDenied); 4075 return VERR_IOMMU_ADDR_TRANSLATION_FAILED; 4076 } 4077 4078 /* Check permission bits. */ 4079 uint8_t const fPtePerm = (PtEntity.u64 >> IOMMU_IO_PERM_SHIFT) & IOMMU_IO_PERM_MASK; 4080 if ((fAccess & fPtePerm) != fAccess) 4081 { 4082 EVT_IO_PAGE_FAULT_T EvtIoPageFault; 4083 iommuAmdInitIoPageFaultEvent(uDevId, pDte->n.u16DomainId, uIova, true /* fPresent */, false /* fRsvdNotZero */, 4084 true /* fPermDenied */, enmOp, &EvtIoPageFault); 4085 iommuAmdRaiseIoPageFaultEvent(pDevIns, pDte, NULL /* pIrte */, enmOp, &EvtIoPageFault, kIoPageFaultType_PermDenied); 4086 return VERR_IOMMU_ADDR_TRANSLATION_FAILED; 4087 } 4088 4089 uint8_t const uNextLevel = PtEntity.n.u3NextLevel; 4090 bool const fIsPte = RT_BOOL(uNextLevel == 0 || uNextLevel == 7); 4091 if (fIsPte) 4092 { 4093 /** @todo IOMMU: Compute final SPA and return. */ 4094 return VERR_NOT_IMPLEMENTED; 4095 } 4096 4097 /* Check level encoding of the PDE. */ 4098 #if IOMMU_MAX_HOST_PT_LEVEL < 6 4099 if (uNextLevel > IOMMU_MAX_HOST_PT_LEVEL) 4100 { 4013 4101 EVT_IO_PAGE_FAULT_T EvtIoPageFault; 4014 4102 iommuAmdInitIoPageFaultEvent(uDevId, pDte->n.u16DomainId, uIova, true /* fPresent */, false /* fRsvdNotZero */, 4015 4103 enmOp, &EvtIoPageFault); 4016 iommuAmdRaiseIoPageFaultEvent(pDevIns, pDte, NULL /* pIrte */, enmOp, &EvtIoPageFault, EvtIoPageFaultType);4017 iommuAmdUpdateIotlbe(NIL_RTGCPHYS, 0 /* cShift */, IOMMU_IO_PERM_NONE, pIotlbe);4104 iommuAmdRaiseIoPageFaultEvent(pDevIns, pDte, NULL /* pIrte */, enmOp, &EvtIoPageFault, 4105 kIoPageFaultType_PteInvalidLvlEncoding); 4018 4106 return VERR_IOMMU_ADDR_TRANSLATION_FAILED; 4019 4107 } 4020 4021 /** @todo IOMMU: rest of page walk. */ 4108 #else 4109 Assert(uNextLevel <= IOMMU_MAX_HOST_PT_LEVEL); 4110 #endif 4111 4112 /** @todo IOMMU: rest of page walk. */ 4022 4113 } 4023 4114 4024 4115 return VERR_NOT_IMPLEMENTED; 4025 4116 } 4117 4026 4118 4027 4119 /** … … 4034 4126 * @param cbAccess The size of the access. 4035 4127 * @param enmOp The IOMMU operation being performed. 4036 * @param pIotlbe The IOTLBE to update. 4128 * @param pIotlbe The IOTLBE to update. Only updated when VINF_SUCCESS is 4129 * returned, see remarks. 4037 4130 * 4038 4131 * @remarks Only the translated address and permission bits are updated in @a 4039 4132 * pIotlbe when this function returns VINF_SUCCESS. Caller is expected to 4040 * know and fill in the restalready.4133 * have updated any other the fields already. 4041 4134 * 4042 4135 * @thread Any.
Note:
See TracChangeset
for help on using the changeset viewer.