- Timestamp:
- Dec 28, 2023 9:15:52 PM (13 months ago)
- Location:
- trunk/src/VBox/VMM
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/VMM/VMMAll/IEMAllN8veRecompiler.cpp
r102720 r102724 9736 9736 9737 9737 9738 /********************************************************************************************************************************* 9739 * TLB Lookup. * 9740 *********************************************************************************************************************************/ 9741 9742 #if defined(RT_ARCH_AMD64) && 1 9743 # define IEMNATIVE_WITH_TLB_LOOKUP 9744 #endif 9745 9746 9747 /** 9748 * This must be instantiate *before* branching off to the lookup code, 9749 * so that register spilling and whatnot happens for everyone. 9750 */ 9751 typedef struct IEMNATIVEEMITTLBSTATE 9752 { 9753 uint64_t const uAbsPtr; 9754 bool const fSkip; 9755 uint8_t const idxRegPtr; 9756 uint8_t const idxRegSegBase; 9757 uint8_t const idxRegSegLimit; 9758 uint8_t const idxRegSegAttrib; 9759 uint8_t const idxReg1; 9760 uint8_t const idxReg2; 9761 9762 IEMNATIVEEMITTLBSTATE(PIEMRECOMPILERSTATE a_pReNative, uint32_t *a_poff, uint8_t a_idxVarGCPtrMem, 9763 uint8_t a_iSegReg, uint8_t a_cbMem) 9764 : uAbsPtr( a_pReNative->Core.aVars[a_idxVarGCPtrMem].enmKind != kIemNativeVarKind_Immediate 9765 ? UINT64_MAX 9766 : a_pReNative->Core.aVars[a_idxVarGCPtrMem].u.uValue) 9767 #ifdef IEMNATIVE_WITH_TLB_LOOKUP 9768 /* 32-bit and 64-bit wraparound will require special handling, so skip these for absolute addresses. */ 9769 , fSkip( a_pReNative->Core.aVars[a_idxVarGCPtrMem].enmKind == kIemNativeVarKind_Immediate 9770 && ( (a_pReNative->fExec & IEM_F_MODE_CPUMODE_MASK) != IEMMODE_64BIT 9771 ? (uint64_t)(UINT32_MAX - a_cbMem) 9772 : (uint64_t)(UINT64_MAX - a_cbMem)) 9773 < a_pReNative->Core.aVars[a_idxVarGCPtrMem].u.uValue) 9774 #else 9775 , fSkip(true) 9776 #endif 9777 , idxRegPtr(a_pReNative->Core.aVars[a_idxVarGCPtrMem].enmKind != kIemNativeVarKind_Immediate 9778 ? iemNativeVarRegisterAcquire(a_pReNative, a_idxVarGCPtrMem, a_poff, 9779 true /*fInitialized*/, IEMNATIVE_CALL_ARG2_GREG) 9780 : UINT8_MAX) 9781 , idxRegSegBase(a_iSegReg == UINT8_MAX || fSkip 9782 ? UINT8_MAX 9783 : iemNativeRegAllocTmpForGuestReg(a_pReNative, a_poff, IEMNATIVEGSTREG_SEG_BASE(a_iSegReg))) 9784 , idxRegSegLimit((a_iSegReg == UINT8_MAX && (a_pReNative->fExec & IEM_F_MODE_CPUMODE_MASK) != IEMMODE_64BIT) || fSkip 9785 ? UINT8_MAX 9786 : iemNativeRegAllocTmpForGuestReg(a_pReNative, a_poff, IEMNATIVEGSTREG_SEG_LIMIT(a_iSegReg))) 9787 , idxRegSegAttrib((a_iSegReg == UINT8_MAX && (a_pReNative->fExec & IEM_F_MODE_CPUMODE_MASK) != IEMMODE_64BIT) || fSkip 9788 ? UINT8_MAX 9789 : iemNativeRegAllocTmpForGuestReg(a_pReNative, a_poff, IEMNATIVEGSTREG_SEG_ATTRIB(a_iSegReg))) 9790 , idxReg1(!fSkip ? iemNativeRegAllocTmp(a_pReNative, a_poff) : UINT8_MAX) 9791 , idxReg2(!fSkip ? iemNativeRegAllocTmp(a_pReNative, a_poff) : UINT8_MAX) 9792 9793 { 9794 RT_NOREF_PV(a_cbMem); 9795 } 9796 9797 void freeRegsAndReleaseVars(PIEMRECOMPILERSTATE a_pReNative, uint8_t idxVarGCPtrMem) const 9798 { 9799 if (idxRegPtr != UINT8_MAX) 9800 iemNativeVarRegisterRelease(a_pReNative, idxVarGCPtrMem); 9801 if (idxRegSegBase != UINT8_MAX) 9802 iemNativeRegFreeTmp(a_pReNative, idxRegSegBase); 9803 if (idxRegSegLimit != UINT8_MAX) 9804 { 9805 iemNativeRegFreeTmp(a_pReNative, idxRegSegLimit); 9806 iemNativeRegFreeTmp(a_pReNative, idxRegSegAttrib); 9807 } 9808 else 9809 Assert(idxRegSegAttrib == UINT8_MAX); 9810 iemNativeRegFreeTmp(a_pReNative, idxReg2); 9811 iemNativeRegFreeTmp(a_pReNative, idxReg1); 9812 } 9813 } IEMNATIVEEMITTLBSTATE; 9814 9815 9816 #ifdef IEMNATIVE_WITH_TLB_LOOKUP 9817 DECL_INLINE_THROW(uint32_t) 9818 iemNativeEmitTlbLookup(PIEMRECOMPILERSTATE pReNative, uint32_t off, IEMNATIVEEMITTLBSTATE const * const pTlbState, 9819 uint8_t iSegReg, uint8_t cbMem, uint8_t fAlignMask, uint32_t fAccess, 9820 uint32_t idxLabelTlbLookup, uint32_t idxLabelTlbMiss, uint8_t idxRegMemResult, 9821 uint8_t offDisp = 0) 9822 { 9823 RT_NOREF(offDisp); 9824 Assert(!pTlbState->fSkip); 9825 # if defined(RT_ARCH_AMD64) 9826 uint8_t * const pCodeBuf = iemNativeInstrBufEnsure(pReNative, off, 512); 9827 # elif defined(RT_ARCH_ARM64) 9828 uint32_t * const pCodeBuf = iemNativeInstrBufEnsure(pReNative, off, 64); 9829 # endif 9830 9831 /* 9832 * The expand down check isn't use all that much, so we emit here to keep 9833 * the lookup straighter. 9834 */ 9835 /* check_expand_down: ; complicted! */ 9836 uint32_t const offCheckExpandDown = off; 9837 uint32_t offFixupLimitDone = 0; 9838 if (iSegReg != UINT8_MAX && (pReNative->fExec & IEM_F_MODE_CPUMODE_MASK) != IEMMODE_64BIT) 9839 { 9840 off = iemNativeEmitBrkEx(pCodeBuf, off, 1); /** @todo this needs testing */ 9841 /* cmp seglim, regptr */ 9842 if (pTlbState->idxRegPtr != UINT8_MAX) 9843 off = iemNativeEmitCmpGprWithGprEx(pCodeBuf, off, pTlbState->idxRegSegLimit, pTlbState->idxRegPtr); 9844 else 9845 off = iemNativeEmitCmpGpr32WithImmEx(pCodeBuf, off, pTlbState->idxRegSegLimit, (uint32_t)pTlbState->uAbsPtr); 9846 /* ja tlbmiss */ 9847 off = iemNativeEmitJccToLabelEx(pReNative, pCodeBuf, off, idxLabelTlbMiss, kIemNativeInstrCond_nbe); 9848 /* mov reg1, X86DESCATTR_D (0x4000) */ 9849 off = iemNativeEmitCmpGpr32WithImmEx(pCodeBuf, off, pTlbState->idxReg1, X86DESCATTR_D); 9850 /* and reg1, segattr */ 9851 off = iemNativeEmitAndGpr32ByGpr32Ex(pCodeBuf, off, pTlbState->idxReg1, pTlbState->idxRegSegAttrib); 9852 /* xor reg1, X86DESCATTR_D */ 9853 off = iemNativeEmitXorGpr32ByImmEx(pCodeBuf, off, pTlbState->idxReg1, X86DESCATTR_D); 9854 /* shl reg1, 2 (16 - 14) */ 9855 AssertCompile((X86DESCATTR_D << 2) == UINT32_C(0x10000)); 9856 off = iemNativeEmitShiftGpr32LeftEx(pCodeBuf, off, pTlbState->idxReg1, 2); 9857 /* dec reg1 (=> 0xffff if D=0; 0xffffffff if D=1) */ 9858 off = iemNativeEmitSubGpr32ImmEx(pCodeBuf, off, pTlbState->idxReg1, 1); 9859 /* cmp reg1, reg2 (64-bit) / imm (32-bit) */ 9860 if (pTlbState->idxRegPtr != UINT8_MAX) 9861 off = iemNativeEmitCmpGprWithGprEx(pCodeBuf, off, pTlbState->idxReg1, pTlbState->idxReg2); 9862 else 9863 off = iemNativeEmitCmpGpr32WithImmEx(pCodeBuf, off, pTlbState->idxReg1, (uint32_t)(pTlbState->uAbsPtr + cbMem - 1)); 9864 /* jbe tlbmiss */ 9865 off = iemNativeEmitJccToLabelEx(pReNative, pCodeBuf, off, idxLabelTlbMiss, kIemNativeInstrCond_be); 9866 /* jmp limitdone */ 9867 offFixupLimitDone = off; 9868 off = iemNativeEmitJmpToFixedEx(pCodeBuf, off, off /* ASSUME short jump suffices */); 9869 } 9870 9871 /* 9872 * tlblookup: 9873 */ 9874 iemNativeLabelDefine(pReNative, idxLabelTlbLookup, off); 9875 9876 /* 9877 * 1. Segmentation. 9878 * 9879 * 1a. Check segment limit and attributes if non-flat 32-bit code. This is complicated. 9880 */ 9881 if (iSegReg != UINT8_MAX && (pReNative->fExec & IEM_F_MODE_CPUMODE_MASK) != IEMMODE_64BIT) 9882 { 9883 /* If we're accessing more than one byte, put the last address we'll be 9884 accessing in idxReg2 (64-bit). */ 9885 if (cbMem > 1 && pTlbState->idxRegPtr != UINT8_MAX) 9886 { 9887 # if 1 9888 Assert(cbMem - 1 <= 127); 9889 /* mov reg2, regptr */ 9890 off = iemNativeEmitLoadGprFromGpr32Ex(pCodeBuf, off, pTlbState->idxReg2, pTlbState->idxRegPtr); 9891 /* add reg2, cbMem-1 */ 9892 off = iemNativeEmitAddGpr32Imm8Ex(pCodeBuf, off, pTlbState->idxReg2, cbMem - 1); 9893 # else 9894 /* mov reg2, cbMem-1 */ 9895 off = iemNativeEmitLoadGpr32ImmEx(pCodeBuf, off, pTlbState->idxReg2, cbMem - 1); 9896 /* add reg2, regptr */ 9897 off = iemNativeEmitAddTwoGprsEx(pCodeBuf, off, pTlbState->idxReg2, pTlbState->idxRegPtr); 9898 # endif 9899 } 9900 9901 /* Check that we've got a segment loaded and that it allows the access. 9902 For write access this means a writable data segment. 9903 For read-only accesses this means a readable code segment or any data segment. */ 9904 if (fAccess & IEM_ACCESS_TYPE_WRITE) 9905 { 9906 uint32_t const fMustBe1 = X86DESCATTR_P | X86DESCATTR_DT | X86_SEL_TYPE_WRITE; 9907 uint32_t const fMustBe0 = X86DESCATTR_UNUSABLE | X86_SEL_TYPE_CODE; 9908 /* mov reg1, must1|must0 */ 9909 off = iemNativeEmitLoadGpr32ImmEx(pCodeBuf, off, pTlbState->idxReg1, fMustBe1 | fMustBe0); 9910 /* and reg1, segattrs */ 9911 off = iemNativeEmitAndGpr32ByGpr32Ex(pCodeBuf, off, pTlbState->idxReg1, pTlbState->idxRegSegAttrib); 9912 /* cmp reg1, must1 */ 9913 off = iemNativeEmitCmpGpr32WithImmEx(pCodeBuf, off, pTlbState->idxReg1, fMustBe1); 9914 /* jne tlbmiss */ 9915 off = iemNativeEmitJccToLabelEx(pReNative, pCodeBuf, off, idxLabelTlbMiss, kIemNativeInstrCond_ne); 9916 } 9917 else 9918 { 9919 /* U | !P |!DT |!CD | RW | 9920 16 | 8 | 4 | 3 | 1 | 9921 ------------------------------- 9922 0 | 0 | 0 | 0 | 0 | execute-only code segment. - must be excluded 9923 0 | 0 | 0 | 0 | 1 | execute-read code segment. 9924 0 | 0 | 0 | 1 | 0 | read-only data segment. 9925 0 | 0 | 0 | 1 | 1 | read-write data segment. - last valid combination 9926 */ 9927 /* mov reg1, relevant attributes */ 9928 off = iemNativeEmitLoadGpr32ImmEx(pCodeBuf, off, pTlbState->idxReg1, 9929 X86DESCATTR_UNUSABLE | X86DESCATTR_P | X86DESCATTR_DT 9930 | X86_SEL_TYPE_CODE | X86_SEL_TYPE_WRITE); 9931 /* and reg1, segattrs */ 9932 off = iemNativeEmitAndGpr32ByGpr32Ex(pCodeBuf, off, pTlbState->idxReg1, pTlbState->idxRegSegAttrib); 9933 /* xor reg1, X86DESCATTR_P | X86DESCATTR_DT | X86_SEL_TYPE_CODE ; place C=1 RW=0 at the bottom & limit the range. 9934 ; EO-code=0, ER-code=2, RO-data=8, RW-data=10 */ 9935 off = iemNativeEmitXorGpr32ByImmEx(pCodeBuf, off, pTlbState->idxReg1, X86DESCATTR_P | X86DESCATTR_DT | X86_SEL_TYPE_CODE); 9936 /* sub reg1, X86_SEL_TYPE_WRITE ; EO-code=-2, ER-code=0, RO-data=6, RW-data=8 */ 9937 off = iemNativeEmitSubGpr32ImmEx(pCodeBuf, off, pTlbState->idxReg1, X86_SEL_TYPE_WRITE /* ER-code */); 9938 /* cmp reg1, X86_SEL_TYPE_CODE | X86_SEL_TYPE_WRITE */ 9939 AssertCompile(X86_SEL_TYPE_CODE == 8); 9940 off = iemNativeEmitCmpGpr32WithImmEx(pCodeBuf, off, pTlbState->idxReg1, X86_SEL_TYPE_CODE); 9941 /* ja tlbmiss */ 9942 off = iemNativeEmitJccToLabelEx(pReNative, pCodeBuf, off, idxLabelTlbMiss, kIemNativeInstrCond_nbe); 9943 } 9944 9945 /* 9946 * Check the limit. If this is a write access, we know that it's a 9947 * data segment and includes the expand_down bit. For read-only accesses 9948 * we need to check that code/data=0 and expanddown=1 before continuing. 9949 */ 9950 if (fAccess & IEM_ACCESS_TYPE_WRITE) 9951 { 9952 /* test segattrs, X86_SEL_TYPE_DOWN */ 9953 AssertCompile(X86_SEL_TYPE_DOWN < 128); 9954 off = iemNativeEmitTestAnyBitsInGpr8Ex(pCodeBuf, off, pTlbState->idxRegSegAttrib, X86_SEL_TYPE_DOWN); 9955 /* jnz check_expand_down */ 9956 off = iemNativeEmitJccToFixedEx(pCodeBuf, off, offCheckExpandDown, kIemNativeInstrCond_ne); 9957 } 9958 else 9959 { 9960 /* mov reg1, segattrs */ 9961 off = iemNativeEmitLoadGprFromGpr32Ex(pCodeBuf, off, pTlbState->idxReg1, pTlbState->idxRegSegAttrib); 9962 /* and reg1, code | down */ 9963 off = iemNativeEmitAndGpr32ByImmEx(pCodeBuf, off, pTlbState->idxReg1, X86_SEL_TYPE_CODE | X86_SEL_TYPE_DOWN); 9964 /* cmp reg1, down */ 9965 off = iemNativeEmitCmpGpr32WithImmEx(pCodeBuf, off, pTlbState->idxReg1, X86_SEL_TYPE_DOWN); 9966 /* je check_expand_down */ 9967 off = iemNativeEmitJccToFixedEx(pCodeBuf, off, offCheckExpandDown, kIemNativeInstrCond_e); 9968 } 9969 9970 /* expand_up: 9971 cmp seglim, regptr/reg2/imm */ 9972 if (pTlbState->idxRegPtr != UINT8_MAX) 9973 off = iemNativeEmitCmpGprWithGprEx(pCodeBuf, off, pTlbState->idxRegSegLimit, cbMem > 1 ? pTlbState->idxReg2 : pTlbState->idxRegPtr); 9974 else 9975 off = iemNativeEmitCmpGpr32WithImmEx(pCodeBuf, off, pTlbState->idxRegSegLimit, (uint32_t)pTlbState->uAbsPtr); 9976 /* jbe tlbmiss */ 9977 off = iemNativeEmitJccToLabelEx(pReNative, pCodeBuf, off, idxLabelTlbMiss, kIemNativeInstrCond_be); 9978 9979 /* limitdone: */ 9980 iemNativeFixupFixedJump(pReNative, offFixupLimitDone, off); 9981 } 9982 9983 /* 1b. Add the segment base. We use idxRegMemResult for the ptr register if this step is 9984 required or if the address is a constant (simplicity). */ 9985 uint8_t const idxRegFlatPtr = iSegReg != UINT8_MAX || pTlbState->idxRegPtr == UINT8_MAX 9986 ? idxRegMemResult : pTlbState->idxRegPtr; 9987 if (iSegReg != UINT8_MAX) 9988 { 9989 /** @todo this can be done using LEA as well. */ 9990 if ((pReNative->fExec & IEM_F_MODE_MASK) == IEM_F_MODE_X86_64BIT) 9991 { 9992 Assert(iSegReg >= X86_SREG_FS); 9993 /* mov regflat, regptr/imm */ 9994 if (pTlbState->idxRegPtr != UINT8_MAX) 9995 off = iemNativeEmitLoadGprFromGprEx(pCodeBuf, off, idxRegFlatPtr, pTlbState->idxRegPtr); 9996 else 9997 off = iemNativeEmitLoadGprImmEx(pCodeBuf, off, idxRegFlatPtr, pTlbState->uAbsPtr); 9998 /* add regflat, seg.base */ 9999 off = iemNativeEmitAddTwoGprsEx(pCodeBuf, off, idxRegFlatPtr, pTlbState->idxRegSegBase); 10000 } 10001 else 10002 { 10003 /* mov regflat, regptr/imm */ 10004 if (pTlbState->idxRegPtr != UINT8_MAX) 10005 off = iemNativeEmitLoadGprFromGpr32Ex(pCodeBuf, off, idxRegFlatPtr, pTlbState->idxRegPtr); 10006 else 10007 off = iemNativeEmitLoadGpr32ImmEx(pCodeBuf, off, idxRegFlatPtr, pTlbState->uAbsPtr); 10008 /* add regflat, seg.base */ 10009 off = iemNativeEmitAddTwoGprs32Ex(pCodeBuf, off, idxRegFlatPtr, pTlbState->idxRegSegBase); 10010 } 10011 } 10012 else if (pTlbState->idxRegPtr == UINT8_MAX) 10013 off = iemNativeEmitLoadGprImmEx(pCodeBuf, off, idxRegFlatPtr, pTlbState->uAbsPtr); 10014 10015 /* 10016 * 2. Check that the address doesn't cross a page boundrary and doesn't have alignment issues. 10017 * 10018 * 2a. Alignment check using fAlignMask. 10019 */ 10020 if (fAlignMask) 10021 { 10022 Assert(RT_IS_POWER_OF_TWO(fAlignMask + 1)); 10023 Assert(fAlignMask < 128); 10024 /* test regflat, fAlignMask */ 10025 off = iemNativeEmitTestAnyBitsInGpr8Ex(pCodeBuf, off, idxRegFlatPtr, fAlignMask); 10026 /* jnz tlbmiss */ 10027 off = iemNativeEmitJccToLabelEx(pReNative, pCodeBuf, off, idxLabelTlbMiss, kIemNativeInstrCond_ne); 10028 } 10029 10030 /* 10031 * 2b. Check that it's not crossing page a boundrary. This is implicit in 10032 * the previous test if the alignment is same or larger than the type. 10033 */ 10034 if (cbMem > fAlignMask + 1) 10035 { 10036 /* mov reg1, 0xfff */ 10037 off = iemNativeEmitLoadGpr32ImmEx(pCodeBuf, off, pTlbState->idxReg1, GUEST_PAGE_OFFSET_MASK); 10038 /* and reg1, regflat */ 10039 off = iemNativeEmitAndGpr32ByGpr32Ex(pCodeBuf, off, pTlbState->idxReg1, idxRegFlatPtr); 10040 /* neg reg1 */ 10041 off = iemNativeEmitNegGpr32Ex(pCodeBuf, off, pTlbState->idxReg1); 10042 /* add reg1, 0x1000 */ 10043 off = iemNativeEmitAddGpr32ImmEx(pCodeBuf, off, pTlbState->idxReg1, GUEST_PAGE_SIZE); 10044 /* cmp reg1, cbMem */ 10045 off = iemNativeEmitCmpGpr32WithImmEx(pCodeBuf, off, pTlbState->idxReg1, GUEST_PAGE_SIZE); 10046 /* ja tlbmiss */ 10047 off = iemNativeEmitJccToLabelEx(pReNative, pCodeBuf, off, idxLabelTlbMiss, kIemNativeInstrCond_nbe); 10048 } 10049 10050 /* 10051 * 3. TLB lookup. 10052 * 10053 * 3a. Calculate the TLB tag value (IEMTLB_CALC_TAG). 10054 * In 64-bit mode we will also check for non-canonical addresses here. 10055 */ 10056 if ((pReNative->fExec & IEM_F_MODE_MASK) == IEM_F_MODE_X86_64BIT) 10057 { 10058 /* mov reg1, regflat */ 10059 off = iemNativeEmitLoadGprFromGprEx(pCodeBuf, off, pTlbState->idxReg1, idxRegFlatPtr); 10060 /* rol reg1, 16 */ 10061 off = iemNativeEmitRotateGprLeftEx(pCodeBuf, off, pTlbState->idxReg1, 16); 10062 /** @todo Would 'movsx reg2, word reg1' and working on reg2 in dwords be faster? */ 10063 /* inc word reg1 */ 10064 pCodeBuf[off++] = X86_OP_PRF_SIZE_OP; 10065 if (pTlbState->idxReg1 >= 8) 10066 pCodeBuf[off++] = X86_OP_REX_B; 10067 pCodeBuf[off++] = 0xff; 10068 pCodeBuf[off++] = X86_MODRM_MAKE(X86_MOD_REG, 0, pTlbState->idxReg1 & 7); 10069 /* cmp word reg1, 1 */ 10070 pCodeBuf[off++] = X86_OP_PRF_SIZE_OP; 10071 if (pTlbState->idxReg1 >= 8) 10072 pCodeBuf[off++] = X86_OP_REX_B; 10073 pCodeBuf[off++] = 0x83; 10074 pCodeBuf[off++] = X86_MODRM_MAKE(X86_MOD_REG, 7, pTlbState->idxReg1 & 7); 10075 pCodeBuf[off++] = 1; 10076 /* ja tlbmiss */ 10077 off = iemNativeEmitJccToLabelEx(pReNative, pCodeBuf, off, idxLabelTlbMiss, kIemNativeInstrCond_nbe); 10078 /* shr reg1, 16 + GUEST_PAGE_SHIFT */ 10079 off = iemNativeEmitShiftGprRightEx(pCodeBuf, off, pTlbState->idxReg1, 16 + GUEST_PAGE_SHIFT); 10080 } 10081 else 10082 { 10083 /* mov reg1, regflat */ 10084 off = iemNativeEmitLoadGprFromGpr32Ex(pCodeBuf, off, pTlbState->idxReg1, idxRegFlatPtr); 10085 /* shr reg1, GUEST_PAGE_SHIFT */ 10086 off = iemNativeEmitShiftGpr32RightEx(pCodeBuf, off, pTlbState->idxReg1, GUEST_PAGE_SHIFT); 10087 } 10088 /* or reg1, [qword pVCpu->iem.s.DataTlb.uTlbRevision] */ 10089 pCodeBuf[off++] = pTlbState->idxReg1 < 8 ? X86_OP_REX_W : X86_OP_REX_W | X86_OP_REX_R; 10090 pCodeBuf[off++] = 0x0b; /* OR r64,r/m64 */ 10091 off = iemNativeEmitGprByVCpuDisp(pCodeBuf, off, pTlbState->idxReg1, RT_UOFFSETOF(VMCPUCC, iem.s.DataTlb.uTlbRevision)); 10092 10093 /* 10094 * 3b. Calc pTlbe. 10095 */ 10096 /* movzx reg2, byte reg1 */ 10097 off = iemNativeEmitLoadGprFromGpr8Ex(pCodeBuf, off, pTlbState->idxReg2, pTlbState->idxReg1); 10098 /* shl reg2, 5 ; reg2 *= sizeof(IEMTLBENTRY) */ 10099 AssertCompileSize(IEMTLBENTRY, 32); 10100 off = iemNativeEmitShiftGprLeftEx(pCodeBuf, off, pTlbState->idxReg2, 5); 10101 /* lea reg2, [pVCpu->iem.s.DataTlb.aEntries + reg2] */ 10102 AssertCompile(IEMNATIVE_REG_FIXED_PVMCPU < 8); 10103 pCodeBuf[off++] = pTlbState->idxReg2 < 8 ? X86_OP_REX_W : X86_OP_REX_W | X86_OP_REX_X | X86_OP_REX_R; 10104 pCodeBuf[off++] = 0x8d; 10105 pCodeBuf[off++] = X86_MODRM_MAKE(X86_MOD_MEM4, pTlbState->idxReg2 & 7, 4 /*SIB*/); 10106 pCodeBuf[off++] = X86_SIB_MAKE(IEMNATIVE_REG_FIXED_PVMCPU & 7, pTlbState->idxReg2 & 7, 0); 10107 pCodeBuf[off++] = RT_BYTE1(RT_UOFFSETOF(VMCPUCC, iem.s.DataTlb.aEntries)); 10108 pCodeBuf[off++] = RT_BYTE2(RT_UOFFSETOF(VMCPUCC, iem.s.DataTlb.aEntries)); 10109 pCodeBuf[off++] = RT_BYTE3(RT_UOFFSETOF(VMCPUCC, iem.s.DataTlb.aEntries)); 10110 pCodeBuf[off++] = RT_BYTE4(RT_UOFFSETOF(VMCPUCC, iem.s.DataTlb.aEntries)); 10111 10112 /* 10113 * 3c. Compare the TLBE.uTag with the one from 2a (reg1). 10114 */ 10115 /* cmp reg1, [reg2] */ 10116 pCodeBuf[off++] = X86_OP_REX_W | (pTlbState->idxReg1 < 8 ? 0 : X86_OP_REX_R) | (pTlbState->idxReg2 < 8 ? 0 : X86_OP_REX_B); 10117 pCodeBuf[off++] = 0x3b; 10118 off = iemNativeEmitGprByGprDisp(pCodeBuf, off, pTlbState->idxReg1, pTlbState->idxReg2, RT_UOFFSETOF(IEMTLBENTRY, uTag)); 10119 /* jne tlbmiss */ 10120 off = iemNativeEmitJccToLabelEx(pReNative, pCodeBuf, off, idxLabelTlbMiss, kIemNativeInstrCond_ne); 10121 10122 /* 10123 * 4. Check TLB page table level access flags and physical page revision #. 10124 */ 10125 /* mov reg1, mask */ 10126 AssertCompile(IEMTLBE_F_PT_NO_USER == 4); 10127 uint64_t const fNoUser = (((pReNative->fExec >> IEM_F_X86_CPL_SHIFT) & IEM_F_X86_CPL_SMASK) + 1) & IEMTLBE_F_PT_NO_USER; 10128 off = iemNativeEmitLoadGprImmEx(pCodeBuf, off, pTlbState->idxReg1, 10129 IEMTLBE_F_PHYS_REV | IEMTLBE_F_NO_MAPPINGR3 10130 | IEMTLBE_F_PG_UNASSIGNED | IEMTLBE_F_PG_NO_READ 10131 | IEMTLBE_F_PT_NO_ACCESSED | fNoUser); 10132 /* and reg1, [reg2->fFlagsAndPhysRev] */ 10133 pCodeBuf[off++] = X86_OP_REX_W | (pTlbState->idxReg1 < 8 ? 0 : X86_OP_REX_R) | (pTlbState->idxReg2 < 8 ? 0 : X86_OP_REX_B); 10134 pCodeBuf[off++] = 0x23; 10135 off = iemNativeEmitGprByGprDisp(pCodeBuf, off, pTlbState->idxReg1, pTlbState->idxReg2, RT_UOFFSETOF(IEMTLBENTRY, fFlagsAndPhysRev)); 10136 10137 /* cmp reg1, [pVCpu->iem.s.DataTlb.uTlbPhysRev] */ 10138 pCodeBuf[off++] = X86_OP_REX_W | (pTlbState->idxReg1 < 8 ? 0 : X86_OP_REX_R); 10139 pCodeBuf[off++] = 0x3b; 10140 off = iemNativeEmitGprByGprDisp(pCodeBuf, off, pTlbState->idxReg1, IEMNATIVE_REG_FIXED_PVMCPU, 10141 RT_UOFFSETOF(VMCPUCC, iem.s.DataTlb.uTlbPhysRev)); 10142 /* jne tlbmiss */ 10143 off = iemNativeEmitJccToLabelEx(pReNative, pCodeBuf, off, idxLabelTlbMiss, kIemNativeInstrCond_ne); 10144 10145 /* 10146 * 5. Check that pbMappingR3 isn't NULL (paranoia) and calculate the 10147 * resulting pointer. 10148 */ 10149 /* mov reg1, [reg2->pbMappingR3] */ 10150 pCodeBuf[off++] = X86_OP_REX_W | (pTlbState->idxReg1 < 8 ? 0 : X86_OP_REX_R) | (pTlbState->idxReg2 < 8 ? 0 : X86_OP_REX_B); 10151 pCodeBuf[off++] = 0x8b; 10152 off = iemNativeEmitGprByGprDisp(pCodeBuf, off, pTlbState->idxReg1, pTlbState->idxReg2, RT_UOFFSETOF(IEMTLBENTRY, pbMappingR3)); 10153 10154 /** @todo eliminate the need for this test? */ 10155 /* test reg1, reg1 */ 10156 pCodeBuf[off++] = X86_OP_REX_W | (pTlbState->idxReg1 < 8 ? 0 : X86_OP_REX_R | X86_OP_REX_B); 10157 pCodeBuf[off++] = 0x85; 10158 pCodeBuf[off++] = X86_MODRM_MAKE(X86_MOD_REG, pTlbState->idxReg1 & 7, pTlbState->idxReg1 & 7); 10159 10160 /* jz tlbmiss */ 10161 off = iemNativeEmitJccToLabelEx(pReNative, pCodeBuf, off, idxLabelTlbMiss, kIemNativeInstrCond_e); 10162 10163 if (idxRegFlatPtr == idxRegMemResult) /* See step 1b. */ 10164 { 10165 /* and result, 0xfff */ 10166 off = iemNativeEmitAndGpr32ByImmEx(pCodeBuf, off, idxRegMemResult, GUEST_PAGE_OFFSET_MASK); 10167 } 10168 else 10169 { 10170 Assert(idxRegFlatPtr == pTlbState->idxRegPtr); 10171 /* mov result, 0xfff */ 10172 off = iemNativeEmitLoadGpr32ImmEx(pCodeBuf, off, idxRegMemResult, GUEST_PAGE_OFFSET_MASK); 10173 /* and result, regflat */ 10174 off = iemNativeEmitAndGpr32ByGpr32Ex(pCodeBuf, off, idxRegMemResult, idxRegFlatPtr); 10175 } 10176 /* add result, reg1 */ 10177 off = iemNativeEmitAddTwoGprsEx(pCodeBuf, off, idxRegMemResult, pTlbState->idxReg1); 10178 10179 IEMNATIVE_ASSERT_INSTR_BUF_ENSURE(pReNative, off); 10180 10181 return off; 10182 } 10183 #endif 9738 10184 9739 10185 … … 10899 11345 Set the the type to stack here so we don't need to do it twice below. */ 10900 11346 iemNativeVarSetKindToStack(pReNative, idxVarUnmapInfo); 11347 uint8_t const idxRegUnmapInfo = iemNativeVarRegisterAcquire(pReNative, idxVarUnmapInfo, &off); 11348 /** @todo use a tmp register from TlbState, since they'll be free after tlb 11349 * lookup is done. */ 10901 11350 10902 11351 /* … … 10905 11354 */ 10906 11355 uint16_t const uTlbSeqNo = pReNative->uTlbSeqNo++; 10907 uint32_t const idxLabelTlbMiss = iemNativeLabelCreate(pReNative, kIemNativeLabelType_TlbMiss, UINT32_MAX, uTlbSeqNo);10908 uint32_t const idxLabelTlbDone = iemNativeLabelCreate(pReNative, kIemNativeLabelType_TlbDone, UINT32_MAX, uTlbSeqNo);10909 11356 uint8_t const idxRegMemResult = !(pReNative->Core.bmHstRegs & RT_BIT_32(IEMNATIVE_CALL_RET_GREG)) 10910 11357 ? iemNativeVarRegisterSetAndAcquire(pReNative, idxVarMem, IEMNATIVE_CALL_RET_GREG, &off) 10911 11358 : iemNativeVarRegisterAcquire(pReNative, idxVarMem, &off); 10912 11359 10913 /* 10914 * First we try to go via the TLB. 10915 */ 10916 #if defined(RT_ARCH_AMD64) && 1 10917 /* Immediate addresses are a pain if they wrap around the 32-bit space, so 10918 always fall back for those. In 64-bit mode this is less like. */ 10919 if ( pReNative->Core.aVars[idxVarGCPtrMem].enmKind != kIemNativeVarKind_Immediate 10920 || (uint64_t)((uint64_t)UINT32_MAX - pReNative->Core.aVars[idxVarGCPtrMem].u.uValue) <= (uint32_t)UINT32_MAX) 10921 { 10922 uint8_t const idxRegPtr = pReNative->Core.aVars[idxVarGCPtrMem].enmKind != kIemNativeVarKind_Immediate 10923 ? iemNativeVarRegisterAcquire(pReNative, idxVarGCPtrMem, &off, 10924 true /*fInitialized*/, IEMNATIVE_CALL_ARG2_GREG) 10925 : UINT8_MAX; 10926 uint64_t const uAbsPtr = pReNative->Core.aVars[idxVarGCPtrMem].enmKind != kIemNativeVarKind_Immediate 10927 ? UINT32_MAX 10928 : pReNative->Core.aVars[idxVarGCPtrMem].u.uValue; 10929 uint8_t const idxRegSegBase = iSegReg == UINT8_MAX ? UINT8_MAX 10930 : iemNativeRegAllocTmpForGuestReg(pReNative, &off, IEMNATIVEGSTREG_SEG_BASE(iSegReg)); 10931 uint8_t const idxRegSegLimit = iSegReg == UINT8_MAX && (pReNative->fExec & IEM_F_MODE_CPUMODE_MASK) != IEMMODE_64BIT 10932 ? UINT8_MAX 10933 : iemNativeRegAllocTmpForGuestReg(pReNative, &off, IEMNATIVEGSTREG_SEG_LIMIT(iSegReg)); 10934 uint8_t const idxRegSegAttrib = iSegReg == UINT8_MAX && (pReNative->fExec & IEM_F_MODE_CPUMODE_MASK) != IEMMODE_64BIT 10935 ? UINT8_MAX 10936 : iemNativeRegAllocTmpForGuestReg(pReNative, &off, IEMNATIVEGSTREG_SEG_ATTRIB(iSegReg)); 10937 uint8_t const idxReg1 = iemNativeRegAllocTmp(pReNative, &off); 10938 uint8_t const idxReg2 = iemNativeRegAllocTmp(pReNative, &off); 10939 uint8_t * const pbCodeBuf = iemNativeInstrBufEnsure(pReNative, off, 256); 10940 10941 /* 10942 * 1. Segmentation. 10943 * 10944 * 1a. Check segment limit and attributes if non-flat 32-bit code. This is complicated. 10945 */ 10946 if (iSegReg != UINT8_MAX && (pReNative->fExec & IEM_F_MODE_CPUMODE_MASK) != IEMMODE_64BIT) 10947 { 10948 /* If we're accessing more than one byte, put the last address we'll be 10949 accessing in idxReg2 (64-bit). */ 10950 if (cbMem > 1 && idxRegPtr != UINT8_MAX) 10951 { 10952 # if 1 10953 Assert(cbMem - 1 <= 127); 10954 /* mov reg2, regptr */ 10955 off = iemNativeEmitLoadGprFromGpr32Ex(pbCodeBuf, off, idxReg2, idxRegPtr); 10956 /* add reg2, cbMem-1 */ 10957 off = iemNativeEmitAddGpr32Imm8Ex(pbCodeBuf, off, idxReg2, cbMem - 1); 10958 # else 10959 /* mov reg2, cbMem-1 */ 10960 off = iemNativeEmitLoadGpr32ImmEx(pbCodeBuf, off, idxReg2, cbMem - 1); 10961 /* add reg2, regptr */ 10962 off = iemNativeEmitAddTwoGprsEx(pbCodeBuf, off, idxReg2, idxRegPtr); 10963 # endif 10964 } 10965 10966 /* Check that we've got a segment loaded and that it allows the access. 10967 For write access this means a writable data segment. 10968 For read-only accesses this means a readable code segment or any data segment. */ 10969 if (fAccess & IEM_ACCESS_TYPE_WRITE) 10970 { 10971 uint32_t const fMustBe1 = X86DESCATTR_P | X86DESCATTR_DT | X86_SEL_TYPE_WRITE; 10972 uint32_t const fMustBe0 = X86DESCATTR_UNUSABLE | X86_SEL_TYPE_CODE; 10973 /* mov reg1, must1|must0 */ 10974 off = iemNativeEmitLoadGpr32ImmEx(pbCodeBuf, off, idxReg1, fMustBe1 | fMustBe0); 10975 /* and reg1, segattrs */ 10976 off = iemNativeEmitAndGpr32ByGpr32Ex(pbCodeBuf, off, idxReg1, idxRegSegAttrib); 10977 /* cmp reg1, must1 */ 10978 off = iemNativeEmitCmpGpr32WithImmEx(pbCodeBuf, off, idxReg1, fMustBe1); 10979 /* jne tlbmiss */ 10980 off = iemNativeEmitJccToLabelEx(pReNative, pbCodeBuf, off, idxLabelTlbMiss, kIemNativeInstrCond_ne); 10981 } 10982 else 10983 { 10984 /* U | !P |!DT |!CD | RW | 10985 16 | 8 | 4 | 3 | 1 | 10986 ------------------------------- 10987 0 | 0 | 0 | 0 | 0 | execute-only code segment. - must be excluded 10988 0 | 0 | 0 | 0 | 1 | execute-read code segment. 10989 0 | 0 | 0 | 1 | 0 | read-only data segment. 10990 0 | 0 | 0 | 1 | 1 | read-write data segment. - last valid combination 10991 */ 10992 /* mov reg1, relevant attributes */ 10993 off = iemNativeEmitLoadGpr32ImmEx(pbCodeBuf, off, idxReg1, 10994 X86DESCATTR_UNUSABLE | X86DESCATTR_P | X86DESCATTR_DT 10995 | X86_SEL_TYPE_CODE | X86_SEL_TYPE_WRITE); 10996 /* and reg1, segattrs */ 10997 off = iemNativeEmitAndGpr32ByGpr32Ex(pbCodeBuf, off, idxReg1, idxRegSegAttrib); 10998 /* xor reg1, X86DESCATTR_P | X86DESCATTR_DT | X86_SEL_TYPE_CODE ; place C=1 RW=0 at the bottom & limit the range. 10999 ; EO-code=0, ER-code=2, RO-data=8, RW-data=10 */ 11000 off = iemNativeEmitXorGpr32ByImmEx(pbCodeBuf, off, idxReg1, X86DESCATTR_P | X86DESCATTR_DT | X86_SEL_TYPE_CODE); 11001 /* sub reg1, X86_SEL_TYPE_WRITE ; EO-code=-2, ER-code=0, RO-data=6, RW-data=8 */ 11002 off = iemNativeEmitSubGpr32ImmEx(pbCodeBuf, off, idxReg1, X86_SEL_TYPE_WRITE /* ER-code */); 11003 /* cmp reg1, X86_SEL_TYPE_CODE | X86_SEL_TYPE_WRITE */ 11004 AssertCompile(X86_SEL_TYPE_CODE == 8); 11005 off = iemNativeEmitCmpGpr32WithImmEx(pbCodeBuf, off, idxReg1, X86_SEL_TYPE_CODE); 11006 /* ja tlbmiss */ 11007 off = iemNativeEmitJccToLabelEx(pReNative, pbCodeBuf, off, idxLabelTlbMiss, kIemNativeInstrCond_nbe); 11008 } 11009 11010 /* 11011 * Check the limit. If this is a write access, we know that it's a 11012 * data segment and includes the expand_down bit. For read-only accesses 11013 * we need to check that code/data=0 and expanddown=1 before continuing. 11014 */ 11015 uint32_t offFixupCheckExpandDown; 11016 if (fAccess & IEM_ACCESS_TYPE_WRITE) 11017 { 11018 /* test segattrs, X86_SEL_TYPE_DOWN */ 11019 AssertCompile(X86_SEL_TYPE_DOWN < 128); 11020 off = iemNativeEmitTestAnyBitsInGpr8Ex(pbCodeBuf, off, idxRegSegAttrib, X86_SEL_TYPE_DOWN); 11021 /* jnz check_expand_down */ 11022 offFixupCheckExpandDown = off; 11023 off = iemNativeEmitJccToFixedEx(pbCodeBuf, off, off /*ASSUMES rel8 suffices*/, kIemNativeInstrCond_ne); 11024 } 11025 else 11026 { 11027 /* mov reg1, segattrs */ 11028 off = iemNativeEmitLoadGprFromGpr32Ex(pbCodeBuf, off, idxReg1, idxRegSegAttrib); 11029 /* and reg1, code | down */ 11030 off = iemNativeEmitAndGpr32ByImmEx(pbCodeBuf, off, idxReg1, X86_SEL_TYPE_CODE | X86_SEL_TYPE_DOWN); 11031 /* cmp reg1, down */ 11032 off = iemNativeEmitCmpGpr32WithImmEx(pbCodeBuf, off, idxReg1, X86_SEL_TYPE_DOWN); 11033 /* je check_expand_down */ 11034 offFixupCheckExpandDown = off; 11035 off = iemNativeEmitJccToFixedEx(pbCodeBuf, off, off /*ASSUMES rel8 suffices*/, kIemNativeInstrCond_e); 11036 } 11037 11038 /* expand_up: 11039 cmp seglim, regptr/reg2/imm */ 11040 if (idxRegPtr != UINT8_MAX) 11041 off = iemNativeEmitCmpGprWithGprEx(pbCodeBuf, off, idxRegSegLimit, cbMem > 1 ? idxReg2 : idxRegPtr); 11042 else 11043 off = iemNativeEmitCmpGpr32WithImmEx(pbCodeBuf, off, idxRegSegLimit, (uint32_t)uAbsPtr); 11044 /* jbe tlbmiss */ 11045 off = iemNativeEmitJccToLabelEx(pReNative, pbCodeBuf, off, idxLabelTlbMiss, kIemNativeInstrCond_be); 11046 /* jmp limitdone */ 11047 uint32_t const offFixupLimitDone = off; 11048 off = iemNativeEmitJmpToFixedEx(pbCodeBuf, off, off /*ASSUMES rel8 suffices*/); 11049 11050 /* check_expand_down: ; complicted! */ 11051 iemNativeFixupFixedJump(pReNative, offFixupCheckExpandDown, off); 11052 pbCodeBuf[off++] = 0xcc; 11053 /* cmp seglim, regptr */ 11054 if (idxRegPtr != UINT8_MAX) 11055 off = iemNativeEmitCmpGprWithGprEx(pbCodeBuf, off, idxRegSegLimit, idxRegPtr); 11056 else 11057 off = iemNativeEmitCmpGpr32WithImmEx(pbCodeBuf, off, idxRegSegLimit, (uint32_t)uAbsPtr); 11058 /* ja tlbmiss */ 11059 off = iemNativeEmitJccToLabelEx(pReNative, pbCodeBuf, off, idxLabelTlbMiss, kIemNativeInstrCond_nbe); 11060 /* mov reg1, X86DESCATTR_D (0x4000) */ 11061 off = iemNativeEmitCmpGpr32WithImmEx(pbCodeBuf, off, idxReg1, X86DESCATTR_D); 11062 /* and reg1, segattr */ 11063 off = iemNativeEmitAndGpr32ByGpr32Ex(pbCodeBuf, off, idxReg1, idxRegSegAttrib); 11064 /* xor reg1, X86DESCATTR_D */ 11065 off = iemNativeEmitXorGpr32ByImmEx(pbCodeBuf, off, idxReg1, X86DESCATTR_D); 11066 /* shl reg1, 2 (16 - 14) */ 11067 AssertCompile((X86DESCATTR_D << 2) == UINT32_C(0x10000)); 11068 off = iemNativeEmitShiftGpr32LeftEx(pbCodeBuf, off, idxReg1, 2); 11069 /* dec reg1 (=> 0xffff if D=0; 0xffffffff if D=1) */ 11070 off = iemNativeEmitSubGpr32ImmEx(pbCodeBuf, off, idxReg1, 1); 11071 /* cmp reg1, reg2 (64-bit) / imm (32-bit) */ 11072 if (idxRegPtr != UINT8_MAX) 11073 off = iemNativeEmitCmpGprWithGprEx(pbCodeBuf, off, idxReg1, idxReg2); 11074 else 11075 off = iemNativeEmitCmpGpr32WithImmEx(pbCodeBuf, off, idxReg1, (uint32_t)(uAbsPtr + cbMem - 1)); 11076 /* jbe tlbmiss */ 11077 off = iemNativeEmitJccToLabelEx(pReNative, pbCodeBuf, off, idxLabelTlbMiss, kIemNativeInstrCond_be); 11078 11079 /* limitdone: */ 11080 iemNativeFixupFixedJump(pReNative, offFixupLimitDone, off); 11081 } 11082 11083 /* 1b. Add the segment base. We use idxRegMemResult for the ptr register if this step is 11084 required or if the address is a constant (simplicity). */ 11085 uint8_t const idxRegFlatPtr = iSegReg != UINT8_MAX || idxRegPtr == UINT8_MAX ? idxRegMemResult : idxRegPtr; 11086 if (iSegReg != UINT8_MAX) 11087 { 11088 /** @todo this can be done using LEA as well. */ 11089 if ((pReNative->fExec & IEM_F_MODE_MASK) == IEM_F_MODE_X86_64BIT) 11090 { 11091 Assert(iSegReg >= X86_SREG_FS); 11092 /* mov regflat, regptr/imm */ 11093 if (idxRegPtr != UINT8_MAX) 11094 off = iemNativeEmitLoadGprFromGprEx(pbCodeBuf, off, idxRegFlatPtr, idxRegPtr); 11095 else 11096 off = iemNativeEmitLoadGprImmEx(pbCodeBuf, off, idxRegFlatPtr, uAbsPtr); 11097 /* add regflat, seg.base */ 11098 off = iemNativeEmitAddTwoGprsEx(pbCodeBuf, off, idxRegFlatPtr, idxRegSegBase); 11099 } 11100 else 11101 { 11102 /* mov regflat, regptr/imm */ 11103 if (idxRegPtr != UINT8_MAX) 11104 off = iemNativeEmitLoadGprFromGpr32Ex(pbCodeBuf, off, idxRegFlatPtr, idxRegPtr); 11105 else 11106 off = iemNativeEmitLoadGpr32ImmEx(pbCodeBuf, off, idxRegFlatPtr, uAbsPtr); 11107 /* add regflat, seg.base */ 11108 off = iemNativeEmitAddTwoGprs32Ex(pbCodeBuf, off, idxRegFlatPtr, idxRegSegBase); 11109 } 11110 } 11111 else if (idxRegPtr == UINT8_MAX) 11112 off = iemNativeEmitLoadGprImmEx(pbCodeBuf, off, idxRegFlatPtr, uAbsPtr); 11113 11114 /* 11115 * 2. Check that the address doesn't cross a page boundrary and doesn't have alignment issues. 11116 * 11117 * 2a. Alignment check using fAlignMask. 11118 */ 11119 if (fAlignMask) 11120 { 11121 Assert(RT_IS_POWER_OF_TWO(fAlignMask + 1)); 11122 Assert(fAlignMask < 128); 11123 /* test regflat, fAlignMask */ 11124 off = iemNativeEmitTestAnyBitsInGpr8Ex(pbCodeBuf, off, idxRegFlatPtr, fAlignMask); 11125 /* jnz tlbmiss */ 11126 off = iemNativeEmitJccToLabelEx(pReNative, pbCodeBuf, off, idxLabelTlbMiss, kIemNativeInstrCond_ne); 11127 } 11128 11129 /* 11130 * 2b. Check that it's not crossing page a boundrary. This is implicit in 11131 * the previous test if the alignment is same or larger than the type. 11132 */ 11133 if (cbMem > fAlignMask + 1) 11134 { 11135 /* mov reg1, 0xfff */ 11136 off = iemNativeEmitLoadGpr32ImmEx(pbCodeBuf, off, idxReg1, GUEST_PAGE_OFFSET_MASK); 11137 /* and reg1, regflat */ 11138 off = iemNativeEmitAndGpr32ByGpr32Ex(pbCodeBuf, off, idxReg1, idxRegFlatPtr); 11139 /* neg reg1 */ 11140 off = iemNativeEmitNegGpr32Ex(pbCodeBuf, off, idxReg1); 11141 /* add reg1, 0x1000 */ 11142 off = iemNativeEmitAddGpr32ImmEx(pbCodeBuf, off, idxReg1, GUEST_PAGE_SIZE); 11143 /* cmp reg1, cbMem */ 11144 off = iemNativeEmitCmpGpr32WithImmEx(pbCodeBuf, off, idxReg1, GUEST_PAGE_SIZE); 11145 /* ja tlbmiss */ 11146 off = iemNativeEmitJccToLabelEx(pReNative, pbCodeBuf, off, idxLabelTlbMiss, kIemNativeInstrCond_nbe); 11147 } 11148 11149 /* 11150 * 3. TLB lookup. 11151 * 11152 * 3a. Calculate the TLB tag value (IEMTLB_CALC_TAG). 11153 * In 64-bit mode we will also check for non-canonical addresses here. 11154 */ 11155 if ((pReNative->fExec & IEM_F_MODE_MASK) == IEM_F_MODE_X86_64BIT) 11156 { 11157 /* mov reg1, regflat */ 11158 off = iemNativeEmitLoadGprFromGprEx(pbCodeBuf, off, idxReg1, idxRegFlatPtr); 11159 /* rol reg1, 16 */ 11160 off = iemNativeEmitRotateGprLeftEx(pbCodeBuf, off, idxReg1, 16); 11161 /** @todo Would 'movsx reg2, word reg1' and working on reg2 in dwords be faster? */ 11162 /* inc word reg1 */ 11163 pbCodeBuf[off++] = X86_OP_PRF_SIZE_OP; 11164 if (idxReg1 >= 8) 11165 pbCodeBuf[off++] = X86_OP_REX_B; 11166 pbCodeBuf[off++] = 0xff; 11167 pbCodeBuf[off++] = X86_MODRM_MAKE(X86_MOD_REG, 0, idxReg1 & 7); 11168 /* cmp word reg1, 1 */ 11169 pbCodeBuf[off++] = X86_OP_PRF_SIZE_OP; 11170 if (idxReg1 >= 8) 11171 pbCodeBuf[off++] = X86_OP_REX_B; 11172 pbCodeBuf[off++] = 0x83; 11173 pbCodeBuf[off++] = X86_MODRM_MAKE(X86_MOD_REG, 7, idxReg1 & 7); 11174 pbCodeBuf[off++] = 1; 11175 /* ja tlbmiss */ 11176 off = iemNativeEmitJccToLabelEx(pReNative, pbCodeBuf, off, idxLabelTlbMiss, kIemNativeInstrCond_nbe); 11177 /* shr reg1, 16 + GUEST_PAGE_SHIFT */ 11178 off = iemNativeEmitShiftGprRightEx(pbCodeBuf, off, idxReg1, 16 + GUEST_PAGE_SHIFT); 11179 } 11180 else 11181 { 11182 /* mov reg1, regflat */ 11183 off = iemNativeEmitLoadGprFromGpr32Ex(pbCodeBuf, off, idxReg1, idxRegFlatPtr); 11184 /* shr reg1, GUEST_PAGE_SHIFT */ 11185 off = iemNativeEmitShiftGpr32RightEx(pbCodeBuf, off, idxReg1, GUEST_PAGE_SHIFT); 11186 } 11187 /* or reg1, [qword pVCpu->iem.s.DataTlb.uTlbRevision] */ 11188 pbCodeBuf[off++] = idxReg1 < 8 ? X86_OP_REX_W : X86_OP_REX_W | X86_OP_REX_R; 11189 pbCodeBuf[off++] = 0x0b; /* OR r64,r/m64 */ 11190 off = iemNativeEmitGprByVCpuDisp(pbCodeBuf, off, idxReg1, RT_UOFFSETOF(VMCPUCC, iem.s.DataTlb.uTlbRevision)); 11191 11192 /* 11193 * 3b. Calc pTlbe. 11194 */ 11195 /* movzx reg2, byte reg1 */ 11196 off = iemNativeEmitLoadGprFromGpr8Ex(pbCodeBuf, off, idxReg2, idxReg1); 11197 /* shl reg2, 5 ; reg2 *= sizeof(IEMTLBENTRY) */ 11198 AssertCompileSize(IEMTLBENTRY, 32); 11199 off = iemNativeEmitShiftGprLeftEx(pbCodeBuf, off, idxReg2, 5); 11200 /* lea reg2, [pVCpu->iem.s.DataTlb.aEntries + reg2] */ 11201 AssertCompile(IEMNATIVE_REG_FIXED_PVMCPU < 8); 11202 pbCodeBuf[off++] = idxReg2 < 8 ? X86_OP_REX_W : X86_OP_REX_W | X86_OP_REX_X | X86_OP_REX_R; 11203 pbCodeBuf[off++] = 0x8d; 11204 pbCodeBuf[off++] = X86_MODRM_MAKE(X86_MOD_MEM4, idxReg2 & 7, 4 /*SIB*/); 11205 pbCodeBuf[off++] = X86_SIB_MAKE(IEMNATIVE_REG_FIXED_PVMCPU & 7, idxReg2 & 7, 0); 11206 pbCodeBuf[off++] = RT_BYTE1(RT_UOFFSETOF(VMCPUCC, iem.s.DataTlb.aEntries)); 11207 pbCodeBuf[off++] = RT_BYTE2(RT_UOFFSETOF(VMCPUCC, iem.s.DataTlb.aEntries)); 11208 pbCodeBuf[off++] = RT_BYTE3(RT_UOFFSETOF(VMCPUCC, iem.s.DataTlb.aEntries)); 11209 pbCodeBuf[off++] = RT_BYTE4(RT_UOFFSETOF(VMCPUCC, iem.s.DataTlb.aEntries)); 11210 11211 /* 11212 * 3c. Compare the TLBE.uTag with the one from 2a (reg1). 11213 */ 11214 /* cmp reg1, [reg2] */ 11215 pbCodeBuf[off++] = X86_OP_REX_W | (idxReg1 < 8 ? 0 : X86_OP_REX_R) | (idxReg2 < 8 ? 0 : X86_OP_REX_B); 11216 pbCodeBuf[off++] = 0x3b; 11217 off = iemNativeEmitGprByGprDisp(pbCodeBuf, off, idxReg1, idxReg2, RT_UOFFSETOF(IEMTLBENTRY, uTag)); 11218 /* jne tlbmiss */ 11219 off = iemNativeEmitJccToLabelEx(pReNative, pbCodeBuf, off, idxLabelTlbMiss, kIemNativeInstrCond_ne); 11220 11221 /* 11222 * 4. Check TLB page table level access flags and physical page revision #. 11223 */ 11224 /* mov reg1, mask */ 11225 AssertCompile(IEMTLBE_F_PT_NO_USER == 4); 11226 uint64_t const fNoUser = (((pReNative->fExec >> IEM_F_X86_CPL_SHIFT) & IEM_F_X86_CPL_SMASK) + 1) & IEMTLBE_F_PT_NO_USER; 11227 off = iemNativeEmitLoadGprImmEx(pbCodeBuf, off, idxReg1, 11228 IEMTLBE_F_PHYS_REV | IEMTLBE_F_NO_MAPPINGR3 11229 | IEMTLBE_F_PG_UNASSIGNED | IEMTLBE_F_PG_NO_READ 11230 | IEMTLBE_F_PT_NO_ACCESSED | fNoUser); 11231 /* and reg1, [reg2->fFlagsAndPhysRev] */ 11232 pbCodeBuf[off++] = X86_OP_REX_W | (idxReg1 < 8 ? 0 : X86_OP_REX_R) | (idxReg2 < 8 ? 0 : X86_OP_REX_B); 11233 pbCodeBuf[off++] = 0x23; 11234 off = iemNativeEmitGprByGprDisp(pbCodeBuf, off, idxReg1, idxReg2, RT_UOFFSETOF(IEMTLBENTRY, fFlagsAndPhysRev)); 11235 11236 /* cmp reg1, [pVCpu->iem.s.DataTlb.uTlbPhysRev] */ 11237 pbCodeBuf[off++] = X86_OP_REX_W | (idxReg1 < 8 ? 0 : X86_OP_REX_R); 11238 pbCodeBuf[off++] = 0x3b; 11239 off = iemNativeEmitGprByGprDisp(pbCodeBuf, off, idxReg1, IEMNATIVE_REG_FIXED_PVMCPU, 11240 RT_UOFFSETOF(VMCPUCC, iem.s.DataTlb.uTlbPhysRev)); 11241 /* jne tlbmiss */ 11242 off = iemNativeEmitJccToLabelEx(pReNative, pbCodeBuf, off, idxLabelTlbMiss, kIemNativeInstrCond_ne); 11243 11244 /* 11245 * 5. Check that pbMappingR3 isn't NULL (paranoia) and calculate the 11246 * resulting pointer. 11247 */ 11248 /* mov reg1, [reg2->pbMappingR3] */ 11249 pbCodeBuf[off++] = X86_OP_REX_W | (idxReg1 < 8 ? 0 : X86_OP_REX_R) | (idxReg2 < 8 ? 0 : X86_OP_REX_B); 11250 pbCodeBuf[off++] = 0x8b; 11251 off = iemNativeEmitGprByGprDisp(pbCodeBuf, off, idxReg1, idxReg2, RT_UOFFSETOF(IEMTLBENTRY, pbMappingR3)); 11252 11253 /** @todo eliminate the need for this test? */ 11254 /* test reg1, reg1 */ 11255 pbCodeBuf[off++] = X86_OP_REX_W | (idxReg1 < 8 ? 0 : X86_OP_REX_R | X86_OP_REX_B); 11256 pbCodeBuf[off++] = 0x85; 11257 pbCodeBuf[off++] = X86_MODRM_MAKE(X86_MOD_REG, idxReg1 & 7, idxReg1 & 7); 11258 11259 /* jz tlbmiss */ 11260 off = iemNativeEmitJccToLabelEx(pReNative, pbCodeBuf, off, idxLabelTlbDone, kIemNativeInstrCond_e); 11261 11262 if (idxRegFlatPtr == idxRegMemResult) /* See step 1b. */ 11263 { 11264 /* and result, 0xfff */ 11265 off = iemNativeEmitAndGpr32ByImmEx(pbCodeBuf, off, idxRegMemResult, GUEST_PAGE_OFFSET_MASK); 11266 } 11267 else 11268 { 11269 Assert(idxRegFlatPtr == idxRegPtr); 11270 /* mov result, 0xfff */ 11271 off = iemNativeEmitLoadGpr32ImmEx(pbCodeBuf, off, idxRegMemResult, GUEST_PAGE_OFFSET_MASK); 11272 /* and result, regflat */ 11273 off = iemNativeEmitAndGpr32ByGpr32Ex(pbCodeBuf, off, idxRegMemResult, idxRegFlatPtr); 11274 } 11275 /* add result, reg1 */ 11276 off = iemNativeEmitAddTwoGprsEx(pbCodeBuf, off, idxRegMemResult, idxReg1); 11277 11278 if (idxRegPtr != UINT8_MAX) 11279 iemNativeVarRegisterRelease(pReNative, idxVarGCPtrMem); 11280 if (idxRegSegBase != UINT8_MAX) 11281 iemNativeRegFreeTmp(pReNative, idxRegSegBase); 11282 if (idxRegSegLimit != UINT8_MAX) 11283 { 11284 iemNativeRegFreeTmp(pReNative, idxRegSegLimit); 11285 iemNativeRegFreeTmp(pReNative, idxRegSegAttrib); 11286 } 11287 else 11288 Assert(idxRegSegAttrib == UINT8_MAX); 11289 iemNativeRegFreeTmp(pReNative, idxReg2); 11290 iemNativeRegFreeTmp(pReNative, idxReg1); 11291 IEMNATIVE_ASSERT_INSTR_BUF_ENSURE(pReNative, off); 11292 } 11293 /* Problematic absolute address, jmp to tlbmiss. Caller will be generate 11294 some unused tail code, but whatever. */ 11295 else 11296 off = iemNativeEmitJmpToLabel(pReNative, off, idxLabelTlbMiss); 11297 11298 /* 11299 * Tail code, specific to the MC when the above is moved into a separate function. 11300 */ 11301 uint8_t * const pbCodeBuf = iemNativeInstrBufEnsure(pReNative, off, 32); 11302 11303 /* [idxVarUnmapInfo] = 0 - allocate register for it. There must be free ones now, so no spilling required. */ 11304 uint32_t const offSaved = off; 11305 uint8_t const idxRegUnmapInfo = iemNativeVarRegisterAcquire(pReNative, idxVarUnmapInfo, &off); 11306 AssertStmt(off == offSaved, IEMNATIVE_DO_LONGJMP(pReNative, VERR_IEM_VAR_IPE_9)); 11307 off = iemNativeEmitLoadGpr32ImmEx(pbCodeBuf, off, idxRegUnmapInfo, 0); 11308 11309 /* jmp tlbdone */ 11310 off = iemNativeEmitJmpToLabelEx(pReNative, pbCodeBuf, off, idxLabelTlbDone); 11311 11312 IEMNATIVE_ASSERT_INSTR_BUF_ENSURE(pReNative, off); 11313 #else 11314 /** @todo arm64 TLB code */ 11315 RT_NOREF(fAccess, fAlignMask, cbMem); 11316 #endif 11317 11318 /* 11360 IEMNATIVEEMITTLBSTATE const TlbState(pReNative, &off, idxVarGCPtrMem, iSegReg, cbMem); 11361 11362 uint32_t const idxLabelTlbLookup = !TlbState.fSkip 11363 ? iemNativeLabelCreate(pReNative, kIemNativeLabelType_TlbLookup, UINT32_MAX, uTlbSeqNo) 11364 : UINT32_MAX; 11365 //off=iemNativeEmitBrk(pReNative, off, 0); 11366 /* 11367 * Jump to the TLB lookup code. 11368 */ 11369 if (!TlbState.fSkip) 11370 off = iemNativeEmitJmpToLabel(pReNative, off, idxLabelTlbLookup); /** @todo short jump */ 11371 11372 /* 11373 * tlbmiss: 11374 * 11319 11375 * Call helper to do the fetching. 11320 11376 * We flush all guest register shadow copies here. 11321 11377 */ 11322 iemNativeLabelDefine(pReNative, idxLabelTlbMiss, off);11378 uint32_t const idxLabelTlbMiss = iemNativeLabelCreate(pReNative, kIemNativeLabelType_TlbMiss, off, uTlbSeqNo); 11323 11379 11324 11380 #ifdef IEMNATIVE_WITH_INSTRUCTION_COUNTING … … 11358 11414 if (idxRegMemResult != IEMNATIVE_CALL_RET_GREG) 11359 11415 off = iemNativeEmitLoadGprFromGpr(pReNative, off, idxRegMemResult, IEMNATIVE_CALL_RET_GREG); 11360 iemNativeVarRegisterRelease(pReNative, idxVarMem); 11361 11362 #if defined(RT_ARCH_AMD64) 11416 11363 11417 Assert(pReNative->Core.aVars[idxVarUnmapInfo].idxReg == idxRegUnmapInfo); 11364 11418 off = iemNativeEmitLoadGprByBpU8(pReNative, off, idxRegUnmapInfo, offBpDispVarUnmapInfo); 11419 11420 #ifdef IEMNATIVE_WITH_TLB_LOOKUP 11421 if (!TlbState.fSkip) 11422 { 11423 /* end of tlbsmiss - Jump to the done label. */ 11424 uint32_t const idxLabelTlbDone = iemNativeLabelCreate(pReNative, kIemNativeLabelType_TlbDone, UINT32_MAX, uTlbSeqNo); 11425 off = iemNativeEmitJmpToLabel(pReNative, off, idxLabelTlbDone); 11426 11427 /* 11428 * tlblookup: 11429 */ 11430 off = iemNativeEmitTlbLookup(pReNative, off, &TlbState, iSegReg, cbMem, fAlignMask, fAccess, 11431 idxLabelTlbLookup, idxLabelTlbMiss, idxRegMemResult); 11432 TlbState.freeRegsAndReleaseVars(pReNative, idxVarGCPtrMem); 11433 11434 /* 11435 * Lookup tail code, specific to the MC when the above is moved into a separate function. 11436 */ 11437 /* [idxVarUnmapInfo] = 0 - allocate register for it. There must be free ones now, so no spilling required. */ 11438 off = iemNativeEmitLoadGprImm32(pReNative, off, idxRegUnmapInfo, 0); 11439 11440 /* 11441 * tlbdone: 11442 */ 11443 iemNativeLabelDefine(pReNative, idxLabelTlbDone, off); 11444 } 11445 #else 11446 RT_NOREF(fAccess, fAlignMask); 11447 #endif 11448 11365 11449 iemNativeVarRegisterRelease(pReNative, idxVarUnmapInfo); 11366 #endif 11367 11368 iemNativeLabelDefine(pReNative, idxLabelTlbDone, off); 11450 iemNativeVarRegisterRelease(pReNative, idxVarMem); 11369 11451 11370 11452 return off; … … 11873 11955 case kIemNativeLabelType_CheckIrq: 11874 11956 pszName = "CheckIrq_CheckVM"; 11957 fNumbered = true; 11958 break; 11959 case kIemNativeLabelType_TlbLookup: 11960 pszName = "TlbLookup"; 11875 11961 fNumbered = true; 11876 11962 break; -
trunk/src/VBox/VMM/include/IEMN8veRecompiler.h
r102717 r102724 319 319 kIemNativeLabelType_Endif, 320 320 kIemNativeLabelType_CheckIrq, 321 kIemNativeLabelType_TlbLookup, 321 322 kIemNativeLabelType_TlbMiss, 322 323 kIemNativeLabelType_TlbDone, -
trunk/src/VBox/VMM/include/IEMN8veRecompilerEmit.h
r102720 r102724 82 82 * Emit a breakpoint instruction. 83 83 */ 84 DECL_FORCE_INLINE(uint32_t) iemNativeEmitBrkEx(PIEMNATIVEINSTR pCodeBuf, uint32_t off, uint32_t uInfo) 85 { 86 #ifdef RT_ARCH_AMD64 87 pCodeBuf[off++] = 0xcc; 88 RT_NOREF(uInfo); /** @todo use multibyte nop for info? */ 89 90 #elif defined(RT_ARCH_ARM64) 91 pCodeBuf[off++] = Armv8A64MkInstrBrk(uInfo & UINT32_C(0xffff)); 92 93 #else 94 # error "error" 95 #endif 96 return off; 97 } 98 99 100 /** 101 * Emit a breakpoint instruction. 102 */ 84 103 DECL_INLINE_THROW(uint32_t) iemNativeEmitBrk(PIEMRECOMPILERSTATE pReNative, uint32_t off, uint32_t uInfo) 85 104 { 86 105 #ifdef RT_ARCH_AMD64 87 uint8_t * const pbCodeBuf = iemNativeInstrBufEnsure(pReNative, off, 1); 88 pbCodeBuf[off++] = 0xcc; 89 RT_NOREF(uInfo); 90 91 #elif defined(RT_ARCH_ARM64) 92 uint32_t * const pu32CodeBuf = iemNativeInstrBufEnsure(pReNative, off, 1); 93 pu32CodeBuf[off++] = Armv8A64MkInstrBrk(uInfo & UINT32_C(0xffff)); 94 106 off = iemNativeEmitBrkEx(iemNativeInstrBufEnsure(pReNative, off, 1), off, uInfo); 107 #elif defined(RT_ARCH_ARM64) 108 off = iemNativeEmitBrkEx(iemNativeInstrBufEnsure(pReNative, off, 1), off, uInfo); 95 109 #else 96 110 # error "error" … … 3321 3335 Assert(idxLabel < pReNative->cLabels); 3322 3336 3323 #ifdef RT_ARCH_AMD64 3324 /* jcc rel32 */ 3325 pCodeBuf[off++] = 0x0f; 3326 pCodeBuf[off++] = (uint8_t)enmCond | 0x80; 3327 iemNativeAddFixup(pReNative, off, idxLabel, kIemNativeFixupType_Rel32, -4); 3328 pCodeBuf[off++] = 0x00; 3329 pCodeBuf[off++] = 0x00; 3330 pCodeBuf[off++] = 0x00; 3331 pCodeBuf[off++] = 0x00; 3332 3333 #elif defined(RT_ARCH_ARM64) 3334 iemNativeAddFixup(pReNative, off, idxLabel, kIemNativeFixupType_RelImm19At5); 3335 pCodeBuf[off++] = Armv8A64MkInstrBCond(enmCond, -1); 3337 uint32_t const offLabel = pReNative->paLabels[idxLabel].off; 3338 #ifdef RT_ARCH_AMD64 3339 if (offLabel >= off) 3340 { 3341 /* jcc rel32 */ 3342 pCodeBuf[off++] = 0x0f; 3343 pCodeBuf[off++] = (uint8_t)enmCond | 0x80; 3344 iemNativeAddFixup(pReNative, off, idxLabel, kIemNativeFixupType_Rel32, -4); 3345 pCodeBuf[off++] = 0x00; 3346 pCodeBuf[off++] = 0x00; 3347 pCodeBuf[off++] = 0x00; 3348 pCodeBuf[off++] = 0x00; 3349 } 3350 else 3351 { 3352 int32_t offDisp = offLabel - (off + 2); 3353 if ((int8_t)offDisp == offDisp) 3354 { 3355 /* jcc rel8 */ 3356 pCodeBuf[off++] = (uint8_t)enmCond | 0x70; 3357 pCodeBuf[off++] = RT_BYTE1((uint32_t)offDisp); 3358 } 3359 else 3360 { 3361 /* jcc rel32 */ 3362 offDisp -= 4; 3363 pCodeBuf[off++] = 0x0f; 3364 pCodeBuf[off++] = (uint8_t)enmCond | 0x80; 3365 pCodeBuf[off++] = RT_BYTE1((uint32_t)offDisp); 3366 pCodeBuf[off++] = RT_BYTE2((uint32_t)offDisp); 3367 pCodeBuf[off++] = RT_BYTE3((uint32_t)offDisp); 3368 pCodeBuf[off++] = RT_BYTE4((uint32_t)offDisp); 3369 } 3370 } 3371 3372 #elif defined(RT_ARCH_ARM64) 3373 if (offLabel >= off) 3374 { 3375 iemNativeAddFixup(pReNative, off, idxLabel, kIemNativeFixupType_RelImm19At5); 3376 pCodeBuf[off++] = Armv8A64MkInstrBCond(enmCond, -1); 3377 } 3378 else 3379 { 3380 Assert(offLabel - off <= -0x3ffff); 3381 pCodeBuf[off++] = Armv8A64MkInstrBCond(enmCond, offLabel - off); 3382 } 3336 3383 3337 3384 #else
Note:
See TracChangeset
for help on using the changeset viewer.