Changeset 71184 in vbox for trunk/src/VBox/VMM/VMMAll
- Timestamp:
- Mar 3, 2018 3:01:59 PM (7 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/VMM/VMMAll/NEMAllNativeTemplate-win.cpp.h
r71152 r71184 15 15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. 16 16 */ 17 18 19 /********************************************************************************************************************************* 20 * Defined Constants And Macros * 21 *********************************************************************************************************************************/ 22 /** Copy back a segment from hyper-V. */ 23 #define NEM_WIN_COPY_BACK_SEG(a_Dst, a_Src) \ 24 do { \ 25 (a_Dst).u64Base = (a_Src).Base; \ 26 (a_Dst).u32Limit = (a_Src).Limit; \ 27 (a_Dst).ValidSel = (a_Dst).Sel = (a_Src).Selector; \ 28 (a_Dst).Attr.u = (a_Src).Attributes; \ 29 (a_Dst).fFlags = CPUMSELREG_FLAGS_VALID; \ 30 } while (0) 31 32 /** The CPUMCTX_EXTRN_XXX mask for IEM. */ 33 #define NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM CPUMCTX_EXTRN_ALL 17 34 18 35 … … 91 108 #ifdef NEM_WIN_USE_HYPERCALLS_FOR_REGISTERS 92 109 NOREF(pCtx); 93 int rc = VMMR3CallR0Emt(pVM, pVCpu, VMMR0_DO_NEM_EXPORT_STATE, UINT64_MAX, NULL);110 int rc = VMMR3CallR0Emt(pVM, pVCpu, VMMR0_DO_NEM_EXPORT_STATE, 0, NULL); 94 111 AssertLogRelRCReturn(rc, rc); 95 112 return rc; … … 389 406 390 407 391 NEM_TMPL_STATIC int nemHCWinCopyStateFromHyperV(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx )408 NEM_TMPL_STATIC int nemHCWinCopyStateFromHyperV(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, uint64_t fWhat) 392 409 { 393 410 #ifdef NEM_WIN_USE_HYPERCALLS_FOR_REGISTERS 411 /* See NEMR0ImportState */ 394 412 NOREF(pCtx); 395 int rc = VMMR3CallR0Emt(pVM, pVCpu, VMMR0_DO_NEM_IMPORT_STATE, UINT64_MAX, NULL);413 int rc = VMMR3CallR0Emt(pVM, pVCpu, VMMR0_DO_NEM_IMPORT_STATE, fWhat, NULL); 396 414 if (RT_SUCCESS(rc)) 397 415 return rc; … … 552 570 do { \ 553 571 Assert(aenmNames[a_idx] == a_enmName); \ 554 (a_SReg).u64Base = aValues[a_idx].Segment.Base; \ 555 (a_SReg).u32Limit = aValues[a_idx].Segment.Limit; \ 556 (a_SReg).ValidSel = (a_SReg).Sel = aValues[a_idx].Segment.Selector; \ 557 (a_SReg).Attr.u = aValues[a_idx].Segment.Attributes; \ 558 (a_SReg).fFlags = CPUMSELREG_FLAGS_VALID; \ 572 NEM_WIN_COPY_BACK_SEG(a_SReg, aValues[a_idx]); \ 559 573 } while (0) 560 574 COPY_BACK_SEG(18, WHvX64RegisterEs, pCtx->es); … … 740 754 /// @todo WHvRegisterPendingEvent1 741 755 756 pCtx->fExtrn = 0; 742 757 743 758 if (fMaybeChangedMode) … … 792 807 793 808 #ifdef NEM_WIN_USE_OUR_OWN_RUN_API 794 795 809 # ifdef IN_RING3 /* hopefully not needed in ring-0, as we'd need KTHREADs and KeAlertThread. */ 796 810 /** … … 851 865 } 852 866 # endif /* IN_RING3 */ 853 854 855 /**856 * Fills in WHV_VP_EXIT_CONTEXT from HV_X64_INTERCEPT_MESSAGE_HEADER.857 */858 DECLINLINE(void) nemHCWinConvertX64MsgHdrToVpExitCtx(HV_X64_INTERCEPT_MESSAGE_HEADER const *pHdr, WHV_VP_EXIT_CONTEXT *pCtx)859 {860 pCtx->ExecutionState.AsUINT16 = pHdr->ExecutionState.AsUINT16;861 pCtx->InstructionLength = pHdr->InstructionLength;862 pCtx->Cs.Base = pHdr->CsSegment.Base;863 pCtx->Cs.Limit = pHdr->CsSegment.Limit;864 pCtx->Cs.Selector = pHdr->CsSegment.Selector;865 pCtx->Cs.Attributes = pHdr->CsSegment.Attributes;866 pCtx->Rip = pHdr->Rip;867 pCtx->Rflags = pHdr->Rflags;868 }869 870 871 /**872 * Convert hyper-V exit message to the WinHvPlatform structures.873 *874 * @returns VBox status code875 * @param pMsgHdr The message to convert.876 * @param pExitCtx The output structure. Assumes zeroed.877 */878 NEM_TMPL_STATIC int nemHCWinRunVirtualProcessorConvertPending(HV_MESSAGE_HEADER const *pMsgHdr, WHV_RUN_VP_EXIT_CONTEXT *pExitCtx)879 {880 switch (pMsgHdr->MessageType)881 {882 case HvMessageTypeUnmappedGpa:883 case HvMessageTypeGpaIntercept:884 {885 PCHV_X64_MEMORY_INTERCEPT_MESSAGE pMemMsg = (PCHV_X64_MEMORY_INTERCEPT_MESSAGE)(pMsgHdr + 1);886 Assert(pMsgHdr->PayloadSize == RT_UOFFSETOF(HV_X64_MEMORY_INTERCEPT_MESSAGE, DsSegment));887 888 pExitCtx->ExitReason = WHvRunVpExitReasonMemoryAccess;889 nemHCWinConvertX64MsgHdrToVpExitCtx(&pMemMsg->Header, &pExitCtx->MemoryAccess.VpContext);890 pExitCtx->MemoryAccess.InstructionByteCount = pMemMsg->InstructionByteCount;891 ((uint64_t *)pExitCtx->MemoryAccess.InstructionBytes)[0] = ((uint64_t const *)pMemMsg->InstructionBytes)[0];892 ((uint64_t *)pExitCtx->MemoryAccess.InstructionBytes)[1] = ((uint64_t const *)pMemMsg->InstructionBytes)[1];893 894 pExitCtx->MemoryAccess.AccessInfo.AccessType = pMemMsg->Header.InterceptAccessType;895 pExitCtx->MemoryAccess.AccessInfo.GpaUnmapped = pMsgHdr->MessageType == HvMessageTypeUnmappedGpa;896 pExitCtx->MemoryAccess.AccessInfo.GvaValid = pMemMsg->MemoryAccessInfo.GvaValid;897 pExitCtx->MemoryAccess.AccessInfo.Reserved = pMemMsg->MemoryAccessInfo.Reserved;898 pExitCtx->MemoryAccess.Gpa = pMemMsg->GuestPhysicalAddress;899 pExitCtx->MemoryAccess.Gva = pMemMsg->GuestVirtualAddress;900 return VINF_SUCCESS;901 }902 903 case HvMessageTypeX64IoPortIntercept:904 {905 PCHV_X64_IO_PORT_INTERCEPT_MESSAGE pPioMsg= (PCHV_X64_IO_PORT_INTERCEPT_MESSAGE)(pMsgHdr + 1);906 Assert(pMsgHdr->PayloadSize == sizeof(*pPioMsg));907 908 pExitCtx->ExitReason = WHvRunVpExitReasonX64IoPortAccess;909 nemHCWinConvertX64MsgHdrToVpExitCtx(&pPioMsg->Header, &pExitCtx->IoPortAccess.VpContext);910 pExitCtx->IoPortAccess.InstructionByteCount = pPioMsg->InstructionByteCount;911 ((uint64_t *)pExitCtx->IoPortAccess.InstructionBytes)[0] = ((uint64_t const *)pPioMsg->InstructionBytes)[0];912 ((uint64_t *)pExitCtx->IoPortAccess.InstructionBytes)[1] = ((uint64_t const *)pPioMsg->InstructionBytes)[1];913 914 pExitCtx->IoPortAccess.AccessInfo.IsWrite = pPioMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE;915 pExitCtx->IoPortAccess.AccessInfo.AccessSize = pPioMsg->AccessInfo.AccessSize;916 pExitCtx->IoPortAccess.AccessInfo.StringOp = pPioMsg->AccessInfo.StringOp;917 pExitCtx->IoPortAccess.AccessInfo.RepPrefix = pPioMsg->AccessInfo.RepPrefix;918 pExitCtx->IoPortAccess.AccessInfo.Reserved = pPioMsg->AccessInfo.Reserved;919 pExitCtx->IoPortAccess.PortNumber = pPioMsg->PortNumber;920 pExitCtx->IoPortAccess.Rax = pPioMsg->Rax;921 pExitCtx->IoPortAccess.Rcx = pPioMsg->Rcx;922 pExitCtx->IoPortAccess.Rsi = pPioMsg->Rsi;923 pExitCtx->IoPortAccess.Rdi = pPioMsg->Rdi;924 pExitCtx->IoPortAccess.Ds.Base = pPioMsg->DsSegment.Base;925 pExitCtx->IoPortAccess.Ds.Limit = pPioMsg->DsSegment.Limit;926 pExitCtx->IoPortAccess.Ds.Selector = pPioMsg->DsSegment.Selector;927 pExitCtx->IoPortAccess.Ds.Attributes = pPioMsg->DsSegment.Attributes;928 pExitCtx->IoPortAccess.Es.Base = pPioMsg->EsSegment.Base;929 pExitCtx->IoPortAccess.Es.Limit = pPioMsg->EsSegment.Limit;930 pExitCtx->IoPortAccess.Es.Selector = pPioMsg->EsSegment.Selector;931 pExitCtx->IoPortAccess.Es.Attributes = pPioMsg->EsSegment.Attributes;932 return VINF_SUCCESS;933 }934 935 case HvMessageTypeX64Halt:936 {937 PCHV_X64_HALT_MESSAGE pHaltMsg = (PCHV_X64_HALT_MESSAGE)(pMsgHdr + 1);938 AssertMsg(pHaltMsg->u64Reserved == 0, ("HALT reserved: %#RX64\n", pHaltMsg->u64Reserved));939 pExitCtx->ExitReason = WHvRunVpExitReasonX64Halt;940 return VINF_SUCCESS;941 }942 943 case HvMessageTypeX64InterruptWindow:944 AssertLogRelMsgFailedReturn(("Message type %#x not implemented!\n", pMsgHdr->MessageType), VERR_INTERNAL_ERROR_2);945 946 case HvMessageTypeInvalidVpRegisterValue:947 case HvMessageTypeUnrecoverableException:948 case HvMessageTypeUnsupportedFeature:949 case HvMessageTypeTlbPageSizeMismatch:950 AssertLogRelMsgFailedReturn(("Message type %#x not implemented!\n", pMsgHdr->MessageType), VERR_INTERNAL_ERROR_2);951 952 case HvMessageTypeX64MsrIntercept:953 case HvMessageTypeX64CpuidIntercept:954 case HvMessageTypeX64ExceptionIntercept:955 case HvMessageTypeX64ApicEoi:956 case HvMessageTypeX64LegacyFpError:957 case HvMessageTypeX64RegisterIntercept:958 case HvMessageTypeApicEoi:959 case HvMessageTypeFerrAsserted:960 case HvMessageTypeEventLogBufferComplete:961 case HvMessageTimerExpired:962 AssertLogRelMsgFailedReturn(("Unexpected message type #x!\n", pMsgHdr->MessageType), VERR_INTERNAL_ERROR_2);963 964 default:965 AssertLogRelMsgFailedReturn(("Unknown message type #x!\n", pMsgHdr->MessageType), VERR_INTERNAL_ERROR_2);966 }967 }968 969 970 /**971 * Our own WHvRunVirtualProcessor that can later be moved to ring-0.972 *973 * This is an experiment only.974 *975 * @returns VBox status code.976 * @param pVM The cross context VM structure.977 * @param pVCpu The cross context virtual CPU structure of the978 * calling EMT.979 * @param pExitCtx Where to return exit information.980 * @param cbExitCtx Size of the exit information area.981 */982 NEM_TMPL_STATIC int nemHCWinRunVirtualProcessor(PVM pVM, PVMCPU pVCpu, WHV_RUN_VP_EXIT_CONTEXT *pExitCtx, size_t cbExitCtx)983 {984 RT_BZERO(pExitCtx, cbExitCtx);985 986 /*987 * Tell the CPU to execute stuff if we haven't got a pending message.988 */989 VID_MESSAGE_MAPPING_HEADER volatile *pMappingHeader = (VID_MESSAGE_MAPPING_HEADER volatile *)pVCpu->nem.s.pvMsgSlotMapping;990 uint32_t fHandleAndGetFlags;991 if (VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED_EXEC_NEM, VMCPUSTATE_STARTED))992 {993 uint8_t const bMsgState = pVCpu->nem.s.bMsgState;994 if (bMsgState == NEM_WIN_MSG_STATE_PENDING_MSG)995 {996 Assert(pMappingHeader->enmVidMsgType == VidMessageHypervisorMessage);997 fHandleAndGetFlags = VID_MSHAGN_F_GET_NEXT_MESSAGE | VID_MSHAGN_F_HANDLE_MESSAGE;998 Log8(("nemHCWinRunVirtualProcessor: #1: msg pending, no need to start CPU (cpu state %u)\n", nemHCWinCpuGetRunningStatus(pVCpu) ));999 }1000 else if (bMsgState != NEM_WIN_MSG_STATE_STARTED)1001 {1002 if (bMsgState == NEM_WIN_MSG_STATE_PENDING_STOP_AND_MSG)1003 {1004 Log8(("nemHCWinRunVirtualProcessor: #0: pending stop+message (cpu status %u)\n", nemHCWinCpuGetRunningStatus(pVCpu) ));1005 /* ACK the pending message and get the stop message. */1006 BOOL fWait = g_pfnVidMessageSlotHandleAndGetNext(pVM->nem.s.hPartitionDevice, pVCpu->idCpu,1007 VID_MSHAGN_F_HANDLE_MESSAGE | VID_MSHAGN_F_GET_NEXT_MESSAGE, 5000);1008 AssertLogRelMsg(fWait, ("dwErr=%u (%#x) rcNt=%#x\n", RTNtLastErrorValue(), RTNtLastErrorValue(), RTNtLastStatusValue()));1009 1010 /* ACK the stop message. */1011 fWait = g_pfnVidMessageSlotHandleAndGetNext(pVM->nem.s.hPartitionDevice, pVCpu->idCpu,1012 VID_MSHAGN_F_HANDLE_MESSAGE, 5000);1013 AssertLogRelMsg(fWait, ("dwErr=%u (%#x) rcNt=%#x\n", RTNtLastErrorValue(), RTNtLastErrorValue(), RTNtLastStatusValue()));1014 1015 pVCpu->nem.s.bMsgState = NEM_WIN_MSG_STATE_STOPPED;1016 }1017 1018 Log8(("nemHCWinRunVirtualProcessor: #1: starting CPU (cpu status %u)\n", nemHCWinCpuGetRunningStatus(pVCpu) ));1019 if (g_pfnVidStartVirtualProcessor(pVM->nem.s.hPartitionDevice, pVCpu->idCpu))1020 pVCpu->nem.s.bMsgState = NEM_WIN_MSG_STATE_STARTED;1021 else1022 {1023 VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED, VMCPUSTATE_STARTED_EXEC_NEM);1024 AssertLogRelMsgFailedReturn(("VidStartVirtualProcessor failed for CPU #%u: rcNt=%#x dwErr=%u\n",1025 pVCpu->idCpu, RTNtLastStatusValue(), RTNtLastErrorValue()),1026 VERR_INTERNAL_ERROR_3);1027 }1028 fHandleAndGetFlags = VID_MSHAGN_F_GET_NEXT_MESSAGE;1029 }1030 else1031 {1032 /* This shouldn't happen. */1033 fHandleAndGetFlags = VID_MSHAGN_F_GET_NEXT_MESSAGE;1034 Log8(("nemHCWinRunVirtualProcessor: #1: NO MSG PENDING! No need to start CPU (cpu state %u)\n", nemHCWinCpuGetRunningStatus(pVCpu) ));1035 }1036 }1037 else1038 {1039 Log8(("nemHCWinRunVirtualProcessor: #1: state=%u -> canceled (cpu status %u)\n",1040 VMCPU_GET_STATE(pVCpu), nemHCWinCpuGetRunningStatus(pVCpu)));1041 pExitCtx->ExitReason = WHvRunVpExitReasonCanceled;1042 return VINF_SUCCESS;1043 }1044 1045 /*1046 * Wait for it to stop and give us a reason to work with.1047 */1048 uint32_t cMillies = 5000; // Starting low so we can experiment without getting stuck.1049 for (;;)1050 {1051 if (VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED_EXEC_NEM_WAIT, VMCPUSTATE_STARTED_EXEC_NEM))1052 {1053 Log8(("nemHCWinRunVirtualProcessor: #2: Waiting %#x (cpu status %u)...\n",1054 fHandleAndGetFlags, nemHCWinCpuGetRunningStatus(pVCpu)));1055 BOOL fWait = g_pfnVidMessageSlotHandleAndGetNext(pVM->nem.s.hPartitionDevice, pVCpu->idCpu,1056 fHandleAndGetFlags, cMillies);1057 if (fWait)1058 {1059 /* Not sure yet, but we have to check whether there is anything pending1060 and retry if there isn't. */1061 VID_MESSAGE_TYPE const enmVidMsgType = pMappingHeader->enmVidMsgType;1062 if (enmVidMsgType == VidMessageHypervisorMessage)1063 {1064 if (!VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED, VMCPUSTATE_STARTED_EXEC_NEM_WAIT))1065 VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED, VMCPUSTATE_STARTED_EXEC_NEM_CANCELED);1066 Log8(("nemHCWinRunVirtualProcessor: #3: wait succeeded: %#x / %#x (cpu status %u)\n",1067 enmVidMsgType, ((HV_MESSAGE_HEADER const *)(pMappingHeader + 1))->MessageType,1068 nemHCWinCpuGetRunningStatus(pVCpu) ));1069 pVCpu->nem.s.bMsgState = NEM_WIN_MSG_STATE_PENDING_MSG;1070 return nemHCWinRunVirtualProcessorConvertPending((HV_MESSAGE_HEADER const *)(pMappingHeader + 1), pExitCtx);1071 }1072 1073 /* This shouldn't happen, and I think its wrong. */1074 VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED_EXEC_NEM, VMCPUSTATE_STARTED_EXEC_NEM_WAIT);1075 #ifdef DEBUG_bird1076 __debugbreak();1077 #endif1078 Log8(("nemHCWinRunVirtualProcessor: #3: wait succeeded, but nothing pending: %#x / %#x (cpu status %u)\n",1079 enmVidMsgType, ((HV_MESSAGE_HEADER const *)(pMappingHeader + 1))->MessageType, nemHCWinCpuGetRunningStatus(pVCpu) ));1080 pVCpu->nem.s.bMsgState = NEM_WIN_MSG_STATE_STARTED;1081 AssertLogRelMsgReturnStmt(enmVidMsgType == VidMessageStopRequestComplete,1082 ("enmVidMsgType=%#x\n", enmVidMsgType),1083 g_pfnVidStopVirtualProcessor(pVM->nem.s.hPartitionDevice, pVCpu->idCpu),1084 VERR_INTERNAL_ERROR_3);1085 fHandleAndGetFlags &= ~VID_MSHAGN_F_HANDLE_MESSAGE;1086 }1087 else1088 {1089 VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED_EXEC_NEM, VMCPUSTATE_STARTED_EXEC_NEM_WAIT);1090 1091 /* Note! VID.SYS merges STATUS_ALERTED and STATUS_USER_APC into STATUS_TIMEOUT. */1092 DWORD const dwErr = RTNtLastErrorValue();1093 AssertLogRelMsgReturnStmt( dwErr == STATUS_TIMEOUT1094 || dwErr == STATUS_ALERTED || dwErr == STATUS_USER_APC, /* just in case */1095 ("dwErr=%u (%#x) (cpu status %u)\n", dwErr, dwErr, nemHCWinCpuGetRunningStatus(pVCpu)),1096 g_pfnVidStopVirtualProcessor(pVM->nem.s.hPartitionDevice, pVCpu->idCpu),1097 VERR_INTERNAL_ERROR_3);1098 Log8(("nemHCWinRunVirtualProcessor: #3: wait timed out (cpu status %u)\n", nemHCWinCpuGetRunningStatus(pVCpu) ));1099 pVCpu->nem.s.bMsgState = NEM_WIN_MSG_STATE_STARTED;1100 fHandleAndGetFlags &= ~VID_MSHAGN_F_HANDLE_MESSAGE;1101 }1102 }1103 else1104 {1105 /*1106 * State changed and we need to return.1107 *1108 * We must ensure that the processor is not running while we1109 * return, and that can be a bit complicated.1110 */1111 Log8(("nemHCWinRunVirtualProcessor: #4: state changed to %u (cpu status %u)\n",1112 VMCPU_GET_STATE(pVCpu), nemHCWinCpuGetRunningStatus(pVCpu) ));1113 VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED, VMCPUSTATE_STARTED_EXEC_NEM_CANCELED);1114 1115 /* If we haven't marked the pervious message as handled, simply return1116 without doing anything special. */1117 if (fHandleAndGetFlags & VID_MSHAGN_F_HANDLE_MESSAGE)1118 {1119 Log8(("nemHCWinRunVirtualProcessor: #5: Didn't resume previous message.\n"));1120 pVCpu->nem.s.bMsgState = NEM_WIN_MSG_STATE_PENDING_MSG;1121 pExitCtx->ExitReason = WHvRunVpExitReasonCanceled;1122 return VINF_SUCCESS;1123 }1124 1125 /* The processor is running, so try stop it. */1126 BOOL fStop = g_pfnVidStopVirtualProcessor(pVM->nem.s.hPartitionDevice, pVCpu->idCpu);1127 if (fStop)1128 {1129 Log8(("nemHCWinRunVirtualProcessor: #5: Stopping CPU succeeded (cpu status %u)\n", nemHCWinCpuGetRunningStatus(pVCpu) ));1130 pVCpu->nem.s.bMsgState = NEM_WIN_MSG_STATE_STOPPED;1131 pExitCtx->ExitReason = WHvRunVpExitReasonCanceled;1132 return VINF_SUCCESS;1133 }1134 1135 /* Dang, the CPU stopped by itself with a message pending. */1136 DWORD dwErr = RTNtLastErrorValue();1137 Log8(("nemHCWinRunVirtualProcessor: #5: Stopping CPU failed (%u/%#x) - cpu status %u\n",1138 dwErr, dwErr, nemHCWinCpuGetRunningStatus(pVCpu) ));1139 pExitCtx->ExitReason = WHvRunVpExitReasonCanceled;1140 AssertLogRelMsgReturn(dwErr == ERROR_VID_STOP_PENDING, ("dwErr=%#u\n", dwErr), VERR_INTERNAL_ERROR_3);1141 1142 /* Get the pending message. */1143 BOOL fWait = g_pfnVidMessageSlotHandleAndGetNext(pVM->nem.s.hPartitionDevice, pVCpu->idCpu,1144 VID_MSHAGN_F_GET_NEXT_MESSAGE, 5000);1145 AssertLogRelMsgReturn(fWait, ("error=%#u\n", RTNtLastErrorValue()), VERR_INTERNAL_ERROR_3);1146 1147 VID_MESSAGE_TYPE const enmVidMsgType = pMappingHeader->enmVidMsgType;1148 if (enmVidMsgType == VidMessageHypervisorMessage)1149 {1150 Log8(("nemHCWinRunVirtualProcessor: #6: wait succeeded: %#x / %#x (cpu status %u)\n", enmVidMsgType,1151 ((HV_MESSAGE_HEADER const *)(pMappingHeader + 1))->MessageType, nemHCWinCpuGetRunningStatus(pVCpu) ));1152 pVCpu->nem.s.bMsgState = NEM_WIN_MSG_STATE_PENDING_STOP_AND_MSG;1153 return nemHCWinRunVirtualProcessorConvertPending((HV_MESSAGE_HEADER const *)(pMappingHeader + 1), pExitCtx);1154 }1155 1156 /* ACK the stop message, if that's what it is. Don't think we'll ever get here. */1157 Log8(("nemHCWinRunVirtualProcessor: #6b: wait succeeded: %#x / %#x (cpu status %u)\n", enmVidMsgType,1158 ((HV_MESSAGE_HEADER const *)(pMappingHeader + 1))->MessageType, nemHCWinCpuGetRunningStatus(pVCpu) ));1159 AssertLogRelMsgReturn(enmVidMsgType == VidMessageStopRequestComplete, ("enmVidMsgType=%#x\n", enmVidMsgType),1160 VERR_INTERNAL_ERROR_3);1161 fWait = g_pfnVidMessageSlotHandleAndGetNext(pVM->nem.s.hPartitionDevice, pVCpu->idCpu,1162 VID_MSHAGN_F_HANDLE_MESSAGE, 5000);1163 AssertLogRelMsgReturn(fWait, ("dwErr=%#u\n", RTNtLastErrorValue()), VERR_INTERNAL_ERROR_3);1164 1165 pVCpu->nem.s.bMsgState = NEM_WIN_MSG_STATE_STOPPED;1166 pExitCtx->ExitReason = WHvRunVpExitReasonCanceled;1167 return VINF_SUCCESS;1168 }1169 1170 /** @todo check flags and stuff? */1171 }1172 }1173 1174 867 #endif /* NEM_WIN_USE_OUR_OWN_RUN_API */ 868 1175 869 1176 870 #ifdef LOG_ENABLED … … 1234 928 * @param pExitCtx The exit context. 1235 929 */ 1236 DECLINLINE(void) nemHCWinAdvanceGuestRipAndClearRF(PVMCPU pVCpu, PCPUMCTX pCtx, WHV_VP_EXIT_CONTEXT const *pExitCtx) 1237 { 930 DECLINLINE(void) nemHCWinAdvanceGuestRipAndClearRF(PVMCPU pVCpu, PCPUMCTX pCtx, HV_X64_INTERCEPT_MESSAGE_HEADER const *pMsgHdr) 931 { 932 Assert(!(pCtx->fExtrn & (CPUMCTX_EXTRN_RIP | CPUMCTX_EXTRN_RFLAGS))); 933 1238 934 /* Advance the RIP. */ 1239 Assert(p ExitCtx->InstructionLength > 0 && pExitCtx->InstructionLength < 16);1240 pCtx->rip += p ExitCtx->InstructionLength;935 Assert(pMsgHdr->InstructionLength > 0 && pMsgHdr->InstructionLength < 16); 936 pCtx->rip += pMsgHdr->InstructionLength; 1241 937 pCtx->rflags.Bits.u1RF = 0; 1242 938 … … 1476 1172 } 1477 1173 1478 1479 #if 0 /* later */ 1480 NEM_TMPL_STATIC nemHCWinRunGC(PVM pVM, PVMCPU pVCpu) 1481 { 1174 #ifdef IN_RING3 1175 1176 /** 1177 * Copies register state from the X64 intercept message header. 1178 * 1179 * ASSUMES no state copied yet. 1180 * 1181 * @param pCtx The registe rcontext. 1182 * @param pHdr The X64 intercept message header. 1183 */ 1184 DECLINLINE(void) nemHCWinCopyStateFromX64Header(PCPUMCTX pCtx, HV_X64_INTERCEPT_MESSAGE_HEADER const *pHdr) 1185 { 1186 Assert( (pCtx->fExtrn & (CPUMCTX_EXTRN_RIP | CPUMCTX_EXTRN_RFLAGS | CPUMCTX_EXTRN_CS)) 1187 == (CPUMCTX_EXTRN_RIP | CPUMCTX_EXTRN_RFLAGS | CPUMCTX_EXTRN_CS)); 1188 NEM_WIN_COPY_BACK_SEG(pCtx->cs, pHdr->CsSegment); 1189 pCtx->rip = pHdr->Rip; 1190 pCtx->rflags.u = pHdr->Rflags; 1191 pCtx->fExtrn &= ~(CPUMCTX_EXTRN_RIP | CPUMCTX_EXTRN_RFLAGS | CPUMCTX_EXTRN_CS); 1192 } 1193 1194 1195 /** 1196 * Deals with memory intercept message. 1197 * 1198 * @returns Strict VBox status code. 1199 * @param pVM The cross context VM structure. 1200 * @param pVCpu The cross context per CPU structure. 1201 * @param pMsg The message. 1202 * @param pCtx The register context. 1203 */ 1204 NEM_TMPL_STATIC VBOXSTRICTRC nemHCWinHandleMessageMemory(PVM pVM, PVMCPU pVCpu, HV_X64_MEMORY_INTERCEPT_MESSAGE const *pMsg, 1205 PCPUMCTX pCtx) 1206 { 1207 /* 1208 * Whatever we do, we must clear pending event ejection upon resume. 1209 */ 1210 if (pMsg->Header.ExecutionState.InterruptionPending) 1211 pCtx->fExtrn &= ~CPUMCTX_EXTRN_NEM_WIN_MASK; 1212 1213 /* 1214 * Ask PGM for information about the given GCPhys. We need to check if we're 1215 * out of sync first. 1216 */ 1217 NEMHCWINHMACPCCSTATE State = { pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE, false, false }; 1218 PGMPHYSNEMPAGEINFO Info; 1219 int rc = PGMPhysNemPageInfoChecker(pVM, pVCpu, pMsg->GuestPhysicalAddress, State.fWriteAccess, &Info, 1220 nemHCWinHandleMemoryAccessPageCheckerCallback, &State); 1221 if (RT_SUCCESS(rc)) 1222 { 1223 if (Info.fNemProt & ( pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE 1224 ? NEM_PAGE_PROT_WRITE : NEM_PAGE_PROT_READ)) 1225 { 1226 if (State.fCanResume) 1227 { 1228 Log4(("MemExit: %RGp (=>%RHp) %s fProt=%u%s%s%s; restarting (%s)\n", 1229 pMsg->GuestPhysicalAddress, Info.HCPhys, g_apszPageStates[Info.u2NemState], Info.fNemProt, 1230 Info.fHasHandlers ? " handlers" : "", Info.fZeroPage ? " zero-pg" : "", 1231 State.fDidSomething ? "" : " no-change", g_apszWHvMemAccesstypes[pMsg->Header.InterceptAccessType])); 1232 return VINF_SUCCESS; 1233 } 1234 } 1235 Log4(("MemExit: %RGp (=>%RHp) %s fProt=%u%s%s%s; emulating (%s)\n", 1236 pMsg->GuestPhysicalAddress, Info.HCPhys, g_apszPageStates[Info.u2NemState], Info.fNemProt, 1237 Info.fHasHandlers ? " handlers" : "", Info.fZeroPage ? " zero-pg" : "", 1238 State.fDidSomething ? "" : " no-change", g_apszWHvMemAccesstypes[pMsg->Header.InterceptAccessType])); 1239 } 1240 else 1241 Log4(("MemExit: %RGp rc=%Rrc%s; emulating (%s)\n", pMsg->GuestPhysicalAddress, rc, 1242 State.fDidSomething ? " modified-backing" : "", g_apszWHvMemAccesstypes[pMsg->Header.InterceptAccessType])); 1243 1244 /* 1245 * Emulate the memory access, either access handler or special memory. 1246 */ 1247 nemHCWinCopyStateFromX64Header(pCtx, &pMsg->Header); 1248 rc = nemHCWinCopyStateFromHyperV(pVM, pVCpu, pCtx, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM); 1249 AssertRCReturn(rc, rc); 1250 1251 VBOXSTRICTRC rcStrict; 1252 if (pMsg->InstructionByteCount > 0) 1253 rcStrict = IEMExecOneWithPrefetchedByPC(pVCpu, CPUMCTX2CORE(pCtx), pMsg->Header.Rip, 1254 pMsg->InstructionBytes, pMsg->InstructionByteCount); 1255 else 1256 rcStrict = IEMExecOne(pVCpu); 1257 /** @todo do we need to do anything wrt debugging here? */ 1258 return rcStrict; 1259 1260 } 1261 1262 1263 /** 1264 * Deals with I/O port intercept message. 1265 * 1266 * @returns Strict VBox status code. 1267 * @param pVM The cross context VM structure. 1268 * @param pVCpu The cross context per CPU structure. 1269 * @param pMsg The message. 1270 */ 1271 NEM_TMPL_STATIC VBOXSTRICTRC nemHCWinHandleMessageIoPort(PVM pVM, PVMCPU pVCpu, HV_X64_IO_PORT_INTERCEPT_MESSAGE const *pMsg, 1272 PCPUMCTX pCtx) 1273 { 1274 Assert( pMsg->AccessInfo.AccessSize == 1 1275 || pMsg->AccessInfo.AccessSize == 2 1276 || pMsg->AccessInfo.AccessSize == 4); 1277 1278 /* 1279 * Whatever we do, we must clear pending event ejection upon resume. 1280 */ 1281 if (pMsg->Header.ExecutionState.InterruptionPending) 1282 pCtx->fExtrn &= ~CPUMCTX_EXTRN_NEM_WIN_MASK; 1283 1284 VBOXSTRICTRC rcStrict; 1285 if (!pMsg->AccessInfo.StringOp) 1286 { 1287 /* 1288 * Simple port I/O. 1289 */ 1290 static uint32_t const s_fAndMask[8] = 1291 { UINT32_MAX, UINT32_C(0xff), UINT32_C(0xffff), UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX }; 1292 uint32_t const fAndMask = s_fAndMask[pMsg->AccessInfo.AccessSize]; 1293 if (pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE) 1294 { 1295 rcStrict = IOMIOPortWrite(pVM, pVCpu, pMsg->PortNumber, (uint32_t)pMsg->Rax & fAndMask, pMsg->AccessInfo.AccessSize); 1296 Log4(("IOExit: %04x:%08RX64: OUT %#x, %#x LB %u rcStrict=%Rrc\n", pMsg->Header.CsSegment.Selector, pMsg->Header.Rip, 1297 pMsg->PortNumber, (uint32_t)pMsg->Rax & fAndMask, pMsg->AccessInfo.AccessSize, VBOXSTRICTRC_VAL(rcStrict) )); 1298 if (IOM_SUCCESS(rcStrict)) 1299 { 1300 nemHCWinCopyStateFromX64Header(pCtx, &pMsg->Header); 1301 nemHCWinAdvanceGuestRipAndClearRF(pVCpu, pCtx, &pMsg->Header); 1302 } 1303 } 1304 else 1305 { 1306 uint32_t uValue = 0; 1307 rcStrict = IOMIOPortRead(pVM, pVCpu, pMsg->PortNumber, &uValue, pMsg->AccessInfo.AccessSize); 1308 Log4(("IOExit: %04x:%08RX64: IN %#x LB %u -> %#x, rcStrict=%Rrc\n", pMsg->Header.CsSegment.Selector, pMsg->Header.Rip, 1309 pMsg->PortNumber, pMsg->AccessInfo.AccessSize, uValue, VBOXSTRICTRC_VAL(rcStrict) )); 1310 if (IOM_SUCCESS(rcStrict)) 1311 { 1312 if (pMsg->AccessInfo.AccessSize != 4) 1313 pCtx->rax = (pMsg->Rax & ~(uint64_t)fAndMask) | (uValue & fAndMask); 1314 else 1315 pCtx->rax = uValue; 1316 pCtx->fExtrn &= ~CPUMCTX_EXTRN_RAX; 1317 Log4(("IOExit: RAX %#RX64 -> %#RX64\n", pMsg->Rax, pCtx->rax)); 1318 nemHCWinCopyStateFromX64Header(pCtx, &pMsg->Header); 1319 nemHCWinAdvanceGuestRipAndClearRF(pVCpu, pCtx, &pMsg->Header); 1320 } 1321 } 1322 } 1323 else 1324 { 1325 /* 1326 * String port I/O. 1327 */ 1328 /** @todo Someone at Microsoft please explain how we can get the address mode 1329 * from the IoPortAccess.VpContext. CS.Attributes is only sufficient for 1330 * getting the default mode, it can always be overridden by a prefix. This 1331 * forces us to interpret the instruction from opcodes, which is suboptimal. 1332 * Both AMD-V and VT-x includes the address size in the exit info, at least on 1333 * CPUs that are reasonably new. 1334 * 1335 * Of course, it's possible this is an undocumented and we just need to do some 1336 * experiments to figure out how it's communicated. Alternatively, we can scan 1337 * the opcode bytes for possible evil prefixes. 1338 */ 1339 nemHCWinCopyStateFromX64Header(pCtx, &pMsg->Header); 1340 pCtx->fExtrn &= ~( CPUMCTX_EXTRN_RAX | CPUMCTX_EXTRN_RCX | CPUMCTX_EXTRN_RDI | CPUMCTX_EXTRN_RSI 1341 | CPUMCTX_EXTRN_DS | CPUMCTX_EXTRN_ES); 1342 NEM_WIN_COPY_BACK_SEG(pCtx->ds, pMsg->DsSegment); 1343 NEM_WIN_COPY_BACK_SEG(pCtx->es, pMsg->EsSegment); 1344 pCtx->rax = pMsg->Rax; 1345 pCtx->rcx = pMsg->Rcx; 1346 pCtx->rdi = pMsg->Rdi; 1347 pCtx->rsi = pMsg->Rsi; 1348 int rc = nemHCWinCopyStateFromHyperV(pVM, pVCpu, pCtx, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM); 1349 AssertRCReturn(rc, rc); 1350 1351 Log4(("IOExit: %04x:%08RX64: %s%s %#x LB %u (emulating)\n", pMsg->Header.CsSegment.Selector, pMsg->Header.Rip, 1352 pMsg->AccessInfo.RepPrefix ? "REP " : "", 1353 pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE ? "OUTS" : "INS", 1354 pMsg->PortNumber, pMsg->AccessInfo.AccessSize )); 1355 rcStrict = IEMExecOne(pVCpu); 1356 } 1357 if (IOM_SUCCESS(rcStrict)) 1358 { 1359 /* 1360 * Do debug checks. 1361 */ 1362 if ( pMsg->Header.ExecutionState.DebugActive /** @todo Microsoft: Does DebugActive this only reflext DR7? */ 1363 || (pMsg->Header.Rflags & X86_EFL_TF) 1364 || DBGFBpIsHwIoArmed(pVM) ) 1365 { 1366 /** @todo Debugging. */ 1367 } 1368 } 1369 return rcStrict; 1370 } 1371 1372 1373 /** 1374 * Handles messages (VM exits). 1375 * 1376 * @returns Strict VBox status code. 1377 * @param pVM The cross context VM structure. 1378 * @param pVCpu The cross context per CPU structure. 1379 * @param pMappingHeader The message slot mapping. 1380 * @param pCtx The register context. 1381 */ 1382 NEM_TMPL_STATIC VBOXSTRICTRC nemHCWinHandleMessage(PVM pVM, PVMCPU pVCpu, VID_MESSAGE_MAPPING_HEADER volatile *pMappingHeader, 1383 PCPUMCTX pCtx) 1384 { 1385 if (pMappingHeader->enmVidMsgType == VidMessageHypervisorMessage) 1386 { 1387 AssertMsg(pMappingHeader->cbMessage == HV_MESSAGE_SIZE, ("%#x\n", pMappingHeader->cbMessage)); 1388 HV_MESSAGE const *pMsg = (HV_MESSAGE const *)(pMappingHeader + 1); 1389 switch (pMsg->Header.MessageType) 1390 { 1391 case HvMessageTypeUnmappedGpa: 1392 Assert(pMsg->Header.PayloadSize == RT_UOFFSETOF(HV_X64_MEMORY_INTERCEPT_MESSAGE, DsSegment)); 1393 return nemHCWinHandleMessageMemory(pVM, pVCpu, &pMsg->X64MemoryIntercept, pCtx); 1394 1395 case HvMessageTypeGpaIntercept: 1396 Assert(pMsg->Header.PayloadSize == RT_UOFFSETOF(HV_X64_MEMORY_INTERCEPT_MESSAGE, DsSegment)); 1397 return nemHCWinHandleMessageMemory(pVM, pVCpu, &pMsg->X64MemoryIntercept, pCtx); 1398 1399 case HvMessageTypeX64IoPortIntercept: 1400 Assert(pMsg->Header.PayloadSize == sizeof(pMsg->X64IoPortIntercept)); 1401 return nemHCWinHandleMessageIoPort(pVM, pVCpu, &pMsg->X64IoPortIntercept, pCtx); 1402 1403 case HvMessageTypeX64Halt: 1404 return VINF_EM_HALT; 1405 1406 case HvMessageTypeX64InterruptWindow: 1407 AssertLogRelMsgFailedReturn(("Message type %#x not implemented!\n", pMsg->Header.MessageType), 1408 VERR_INTERNAL_ERROR_2); 1409 1410 case HvMessageTypeInvalidVpRegisterValue: 1411 case HvMessageTypeUnrecoverableException: 1412 case HvMessageTypeUnsupportedFeature: 1413 case HvMessageTypeTlbPageSizeMismatch: 1414 AssertLogRelMsgFailedReturn(("Message type %#x not implemented!\n", pMsg->Header.MessageType), 1415 VERR_INTERNAL_ERROR_2); 1416 1417 case HvMessageTypeX64MsrIntercept: 1418 case HvMessageTypeX64CpuidIntercept: 1419 case HvMessageTypeX64ExceptionIntercept: 1420 case HvMessageTypeX64ApicEoi: 1421 case HvMessageTypeX64LegacyFpError: 1422 case HvMessageTypeX64RegisterIntercept: 1423 case HvMessageTypeApicEoi: 1424 case HvMessageTypeFerrAsserted: 1425 case HvMessageTypeEventLogBufferComplete: 1426 case HvMessageTimerExpired: 1427 AssertLogRelMsgFailedReturn(("Unexpected message on CPU #%u: #x\n", pVCpu->idCpu, pMsg->Header.MessageType), 1428 VERR_INTERNAL_ERROR_2); 1429 1430 default: 1431 AssertLogRelMsgFailedReturn(("Unknown message on CPU #%u: #x\n", pVCpu->idCpu, pMsg->Header.MessageType), 1432 VERR_INTERNAL_ERROR_2); 1433 } 1434 } 1435 else 1436 AssertLogRelMsgFailedReturn(("Unexpected VID message type on CPU #%u: %#x LB %u\n", 1437 pVCpu->idCpu, pMappingHeader->enmVidMsgType, pMappingHeader->cbMessage), 1438 VERR_INTERNAL_ERROR_3); 1439 } 1440 1441 1442 /** 1443 * Worker for nemHCWinRunGC that stops the execution on the way out. 1444 * 1445 * The CPU was running the last time we checked, no there are no messages that 1446 * needs being marked handled/whatever. Caller checks this. 1447 * 1448 * @returns rcStrict on success, error status on failure. 1449 * @param pVM The cross context VM structure. 1450 * @param pVCpu The cross context per CPU structure. 1451 * @param rcStrict The nemHCWinRunGC return status. This is a little 1452 * bit unnecessary, except in internal error cases, 1453 * since we won't need to stop the CPU if we took an 1454 * exit. 1455 * @param pMappingHeader The message slot mapping. 1456 */ 1457 NEM_TMPL_STATIC VBOXSTRICTRC nemHCWinStopCpu(PVM pVM, PVMCPU pVCpu, VBOXSTRICTRC rcStrict, 1458 VID_MESSAGE_MAPPING_HEADER volatile *pMappingHeader) 1459 { 1460 /* 1461 * Try stopping the processor. If we're lucky we manage to do this before it 1462 * does another VM exit. 1463 */ 1464 BOOL fRet = VidStopVirtualProcessor(pVM->nem.s.hPartitionDevice, pVCpu->idCpu); 1465 if (fRet) 1466 { 1467 Log8(("nemHCWinStopCpu: Stopping CPU succeeded (cpu status %u)\n", nemHCWinCpuGetRunningStatus(pVCpu) )); 1468 return rcStrict; 1469 } 1470 1471 /* 1472 * Dang. The CPU stopped by itself and we got a couple of message to deal with. 1473 */ 1474 DWORD dwErr = RTNtLastErrorValue(); 1475 AssertLogRelMsgReturn(dwErr == ERROR_VID_STOP_PENDING, ("dwErr=%#u\n", dwErr), 1476 RT_SUCCESS(rcStrict) ? VERR_INTERNAL_ERROR_3 : rcStrict); 1477 Log8(("nemHCWinStopCpu: Stopping CPU pending...\n")); 1478 1479 /* 1480 * First message: Exit or similar. 1481 * Note! We can safely ASSUME that rcStrict isn't an important information one. 1482 */ 1483 BOOL fWait = g_pfnVidMessageSlotHandleAndGetNext(pVM->nem.s.hPartitionDevice, pVCpu->idCpu, 1484 VID_MSHAGN_F_GET_NEXT_MESSAGE, 30000 /*ms*/); 1485 AssertLogRelMsgReturn(fWait, 1486 ("1st VidMessageSlotHandleAndGetNext after ERROR_VID_STOP_PENDING failed: %u\n", RTNtLastErrorValue()), 1487 RT_SUCCESS(rcStrict) ? VERR_INTERNAL_ERROR_3 : rcStrict); 1488 1489 /* It should be a hypervisor message and definitely not a stop request completed message. */ 1490 VID_MESSAGE_TYPE enmVidMsgType = pMappingHeader->enmVidMsgType; 1491 AssertLogRelMsgReturn(enmVidMsgType != VidMessageStopRequestComplete, 1492 ("Unexpected 1st message following ERROR_VID_STOP_PENDING: %#x LB %#x\n", 1493 enmVidMsgType, pMappingHeader->cbMessage), 1494 RT_SUCCESS(rcStrict) ? VERR_INTERNAL_ERROR_3 : rcStrict); 1495 1496 VBOXSTRICTRC rcStrict2 = nemHCWinHandleMessage(pVM, pVCpu, pMappingHeader, CPUMQueryGuestCtxPtr(pVCpu)); 1497 if (rcStrict2 != VINF_SUCCESS && RT_SUCCESS(rcStrict)) 1498 rcStrict = rcStrict2; 1499 1500 /* 1501 * Mark it as handled and get the stop request completed message, then mark 1502 * that as handled too. CPU is back into fully stopped stated then. 1503 */ 1504 fWait = g_pfnVidMessageSlotHandleAndGetNext(pVM->nem.s.hPartitionDevice, pVCpu->idCpu, 1505 VID_MSHAGN_F_HANDLE_MESSAGE | VID_MSHAGN_F_GET_NEXT_MESSAGE, 30000 /*ms*/); 1506 AssertLogRelMsgReturn(fWait, 1507 ("2nd VidMessageSlotHandleAndGetNext after ERROR_VID_STOP_PENDING failed: %u\n", RTNtLastErrorValue()), 1508 RT_SUCCESS(rcStrict) ? VERR_INTERNAL_ERROR_3 : rcStrict); 1509 1510 /* It should be a stop request completed message. */ 1511 enmVidMsgType = pMappingHeader->enmVidMsgType; 1512 AssertLogRelMsgReturn(enmVidMsgType == VidMessageStopRequestComplete, 1513 ("Unexpected 2nd message following ERROR_VID_STOP_PENDING: %#x LB %#x\n", 1514 enmVidMsgType, pMappingHeader->cbMessage), 1515 RT_SUCCESS(rcStrict) ? VERR_INTERNAL_ERROR_3 : rcStrict); 1516 1517 /* Mark this as handled. */ 1518 fWait = g_pfnVidMessageSlotHandleAndGetNext(pVM->nem.s.hPartitionDevice, pVCpu->idCpu, 1519 VID_MSHAGN_F_HANDLE_MESSAGE, 30000 /*ms*/); 1520 AssertLogRelMsgReturn(fWait, 1521 ("3rd VidMessageSlotHandleAndGetNext after ERROR_VID_STOP_PENDING failed: %u\n", RTNtLastErrorValue()), 1522 RT_SUCCESS(rcStrict) ? VERR_INTERNAL_ERROR_3 : rcStrict); 1523 Log8(("nemHCWinStopCpu: Stopped the CPU (rcStrict=%Rrc)\n", VBOXSTRICTRC_VAL(rcStrict) )); 1524 return rcStrict; 1525 } 1526 1527 1528 NEM_TMPL_STATIC VBOXSTRICTRC nemHCWinRunGC(PVM pVM, PVMCPU pVCpu) 1529 { 1530 PCPUMCTX pCtx = CPUMQueryGuestCtxPtr(pVCpu); 1531 LogFlow(("nemHCWinRunGC: Entering #%u cs:rip=%04x:%08RX64 efl=%#08RX64\n", pVCpu->idCpu, pCtx->cs.Sel, pCtx->rip, pCtx->rflags)); 1482 1532 #ifdef LOG_ENABLED 1483 1533 if (LogIs3Enabled()) 1484 { 1485 Log3(("nemR3NativeRunGC: Entering #%u\n", pVCpu->idCpu)); 1486 nemR3WinLogState(pVM, pVCpu); 1487 } 1534 nemHCWinLogState(pVM, pVCpu); 1488 1535 #endif 1489 1536 … … 1494 1541 * everything every time. This will be optimized later. 1495 1542 */ 1496 const bool fSingleStepping = false; /** @todo get this from somewhere. */ 1497 VBOXSTRICTRC rcStrict = VINF_SUCCESS; 1543 VID_MESSAGE_MAPPING_HEADER volatile *pMappingHeader = (VID_MESSAGE_MAPPING_HEADER volatile *)pVCpu->nem.s.pvMsgSlotMapping; 1544 uint32_t cMillies = 5000; /** @todo lower this later... */ 1545 const bool fSingleStepping = false; /** @todo get this from somewhere. */ 1546 VBOXSTRICTRC rcStrict = VINF_SUCCESS; 1498 1547 for (unsigned iLoop = 0;;iLoop++) 1499 1548 { 1500 1549 /* 1501 * Copy the state.1550 * Ensure that hyper-V has the whole state. 1502 1551 */ 1503 PCPUMCTX pCtx = CPUMQueryGuestCtxPtr(pVCpu); 1504 int rc2 = nemHCWinCopyStateToHyperV(pVM, pVCpu, pCtx); 1505 AssertRCBreakStmt(rc2, rcStrict = rc2); 1552 if ((pCtx->fExtrn & (CPUMCTX_EXTRN_ALL | CPUMCTX_EXTRN_NEM_WIN_MASK)) != (CPUMCTX_EXTRN_ALL | CPUMCTX_EXTRN_NEM_WIN_MASK)) 1553 { 1554 int rc2 = nemHCWinCopyStateToHyperV(pVM, pVCpu, pCtx); 1555 AssertRCReturn(rc2, rc2); 1556 } 1506 1557 1507 1558 /* 1508 1559 * Run a bit. 1509 1560 */ 1510 WHV_RUN_VP_EXIT_CONTEXT ExitReason;1511 RT_ZERO(ExitReason);1512 1561 if ( !VM_FF_IS_PENDING(pVM, VM_FF_EMT_RENDEZVOUS | VM_FF_TM_VIRTUAL_SYNC) 1513 1562 && !VMCPU_FF_IS_PENDING(pVCpu, VMCPU_FF_HM_TO_R3_MASK)) 1514 1563 { 1515 #ifdef NEM_WIN_USE_OUR_OWN_RUN_API 1516 int rc2 = nemR3WinRunVirtualProcessor(pVM, pVCpu, &ExitReason, sizeof(ExitReason)); 1517 AssertRCBreakStmt(rc2, rcStrict = rc2); 1518 #else 1519 Log8(("Calling WHvRunVirtualProcessor\n")); 1520 VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED_EXEC_NEM, VMCPUSTATE_STARTED); 1521 HRESULT hrc = WHvRunVirtualProcessor(pVM->nem.s.hPartition, pVCpu->idCpu, &ExitReason, sizeof(ExitReason)); 1522 VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED, VMCPUSTATE_STARTED_EXEC_NEM); 1523 AssertLogRelMsgBreakStmt(SUCCEEDED(hrc), 1524 ("WHvRunVirtualProcessor(%p, %u,,) -> %Rhrc (Last=%#x/%u)\n", pVM->nem.s.hPartition, pVCpu->idCpu, 1525 hrc, RTNtLastStatusValue(), RTNtLastErrorValue()), 1526 rcStrict = VERR_INTERNAL_ERROR); 1527 Log2(("WHvRunVirtualProcessor -> %#x; exit code %#x (%d) (cpu status %u)\n", 1528 hrc, ExitReason.ExitReason, ExitReason.ExitReason, nemR3WinCpuGetRunningStatus(pVCpu) )); 1564 if (pVCpu->nem.s.fHandleAndGetFlags) 1565 { /* Very likely that the CPU does NOT need starting (pending msg, running). */ } 1566 else 1567 { 1568 if (g_pfnVidStartVirtualProcessor(pVM->nem.s.hPartitionDevice, pVCpu->idCpu)) 1569 pVCpu->nem.s.fHandleAndGetFlags = VID_MSHAGN_F_GET_NEXT_MESSAGE; 1570 else 1571 AssertLogRelMsgFailedReturn(("VidStartVirtualProcessor failed for CPU #%u: %u (%#x, rcNt=%#x)\n", 1572 pVCpu->idCpu, RTNtLastErrorValue(), RTNtLastErrorValue(), RTNtLastStatusValue()), 1573 VERR_INTERNAL_ERROR_3); 1574 } 1575 1576 if (VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED_EXEC_NEM, VMCPUSTATE_STARTED)) 1577 { 1578 BOOL fRet = VidMessageSlotHandleAndGetNext(pVM->nem.s.hPartitionDevice, pVCpu->idCpu, 1579 pVCpu->nem.s.fHandleAndGetFlags, cMillies); 1580 VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED, VMCPUSTATE_STARTED_EXEC_NEM); 1581 if (fRet) 1582 { 1583 /* 1584 * Deal with the message. 1585 */ 1586 rcStrict = nemHCWinHandleMessage(pVM, pVCpu, pMappingHeader, pCtx); 1587 pVCpu->nem.s.fHandleAndGetFlags |= VID_MSHAGN_F_HANDLE_MESSAGE; 1588 } 1589 else 1590 { 1591 /* VID.SYS merges STATUS_ALERTED and STATUS_USER_APC into STATUS_TIMEOUT, 1592 so after NtAlertThread we end up here with a STATUS_TIMEOUT. And yeah, 1593 the error code conversion is into WAIT_XXX, i.e. NT status codes. */ 1594 DWORD dwErr = GetLastError(); 1595 if ( dwErr == STATUS_TIMEOUT 1596 || dwErr == STATUS_ALERTED /* just in case */ 1597 || dwErr == STATUS_USER_APC /* ditto */ ) 1598 pVCpu->nem.s.fHandleAndGetFlags = VID_MSHAGN_F_GET_NEXT_MESSAGE; /* exits are likely */ 1599 else 1600 AssertLogRelMsgFailedReturn(("VidMessageSlotHandleAndGetNext failed for CPU #%u: %u (%#x, rcNt=%#x)\n", 1601 pVCpu->idCpu, dwErr, dwErr, RTNtLastStatusValue()), 1602 VERR_INTERNAL_ERROR_3); 1603 } 1604 1605 /* 1606 * If no relevant FFs are pending, loop. 1607 */ 1608 if ( !VM_FF_IS_PENDING( pVM, !fSingleStepping ? VM_FF_HP_R0_PRE_HM_MASK : VM_FF_HP_R0_PRE_HM_STEP_MASK) 1609 && !VMCPU_FF_IS_PENDING(pVCpu, !fSingleStepping ? VMCPU_FF_HP_R0_PRE_HM_MASK : VMCPU_FF_HP_R0_PRE_HM_STEP_MASK) ) 1610 continue; 1611 1612 /** @todo Try handle pending flags, not just return to EM loops. Take care 1613 * not to set important RCs here unless we've handled a message. */ 1614 LogFlow(("nemHCWinRunGC: returning: pending FF (%#x / %#x)\n", pVM->fGlobalForcedActions, pVCpu->fLocalForcedActions)); 1615 } 1616 else 1617 LogFlow(("nemHCWinRunGC: returning: canceled %d (pre exec)\n", VMCPU_GET_STATE(pVCpu) )); 1618 } 1619 else 1620 LogFlow(("nemHCWinRunGC: returning: pending FF (pre exec)\n")); 1621 break; 1622 } /* the run loop */ 1623 1624 1625 /* 1626 * If the CPU is running, make sure to stop it before we try sync back the 1627 * state and return to EM. 1628 */ 1629 if (pVCpu->nem.s.fHandleAndGetFlags == VID_MSHAGN_F_GET_NEXT_MESSAGE) 1630 { 1631 pVCpu->nem.s.fHandleAndGetFlags = 0; 1632 rcStrict = nemHCWinStopCpu(pVM, pVCpu, rcStrict, pMappingHeader); 1633 } 1634 1635 VMCPU_CMPXCHG_STATE(pVCpu, VMCPUSTATE_STARTED, VMCPUSTATE_STARTED_EXEC_NEM_CANCELED); 1636 1637 if (pCtx->fExtrn & (CPUMCTX_EXTRN_ALL | (CPUMCTX_EXTRN_NEM_WIN_MASK & ~CPUMCTX_EXTRN_NEM_WIN_EVENT_INJECT))) 1638 { 1639 int rc2 = nemHCWinCopyStateFromHyperV(pVM, pVCpu, pCtx, CPUMCTX_EXTRN_ALL | CPUMCTX_EXTRN_NEM_WIN_MASK); 1640 if (RT_SUCCESS(rc2)) 1641 pCtx->fExtrn = 0; 1642 else if (RT_SUCCESS(rcStrict)) 1643 rcStrict = rc2; 1644 } 1645 else 1646 pCtx->fExtrn = 0; 1647 1648 LogFlow(("nemHCWinRunGC: Leaving #%u cs:rip=%04x:%08RX64 efl=%#08RX64\n", pVCpu->idCpu, pCtx->cs.Sel, pCtx->rip, pCtx->rflags)); 1649 return rcStrict; 1650 } 1529 1651 #endif 1530 }1531 else1532 {1533 LogFlow(("nemR3NativeRunGC: returning: pending FF (pre exec)\n"));1534 break;1535 }1536 1537 /*1538 * Copy back the state.1539 */1540 rc2 = nemR3WinCopyStateFromHyperV(pVM, pVCpu, pCtx);1541 AssertRCBreakStmt(rc2, rcStrict = rc2);1542 1543 #ifdef LOG_ENABLED1544 /*1545 * Do some logging.1546 */1547 if (LogIs2Enabled())1548 nemR3WinLogExitReason(&ExitReason);1549 if (LogIs3Enabled())1550 nemR3WinLogState(pVM, pVCpu);1551 #endif1552 1553 #ifdef VBOX_STRICT1554 /* Assert that the VpContext field makes sense. */1555 switch (ExitReason.ExitReason)1556 {1557 case WHvRunVpExitReasonMemoryAccess:1558 case WHvRunVpExitReasonX64IoPortAccess:1559 case WHvRunVpExitReasonX64MsrAccess:1560 case WHvRunVpExitReasonX64Cpuid:1561 case WHvRunVpExitReasonException:1562 case WHvRunVpExitReasonUnrecoverableException:1563 Assert( ExitReason.IoPortAccess.VpContext.InstructionLength > 01564 || ( ExitReason.ExitReason == WHvRunVpExitReasonMemoryAccess1565 && ExitReason.MemoryAccess.AccessInfo.AccessType == WHvMemoryAccessExecute));1566 Assert(ExitReason.IoPortAccess.VpContext.InstructionLength < 16);1567 Assert(ExitReason.IoPortAccess.VpContext.ExecutionState.Cpl == CPUMGetGuestCPL(pVCpu));1568 Assert(ExitReason.IoPortAccess.VpContext.ExecutionState.Cr0Pe == RT_BOOL(pCtx->cr0 & X86_CR0_PE));1569 Assert(ExitReason.IoPortAccess.VpContext.ExecutionState.Cr0Am == RT_BOOL(pCtx->cr0 & X86_CR0_AM));1570 Assert(ExitReason.IoPortAccess.VpContext.ExecutionState.EferLma == RT_BOOL(pCtx->msrEFER & MSR_K6_EFER_LMA));1571 Assert(ExitReason.IoPortAccess.VpContext.ExecutionState.DebugActive == RT_BOOL(pCtx->dr[7] & X86_DR7_ENABLED_MASK));1572 Assert(ExitReason.IoPortAccess.VpContext.ExecutionState.Reserved0 == 0);1573 Assert(ExitReason.IoPortAccess.VpContext.ExecutionState.Reserved1 == 0);1574 Assert(ExitReason.IoPortAccess.VpContext.Rip == pCtx->rip);1575 Assert(ExitReason.IoPortAccess.VpContext.Rflags == pCtx->rflags.u);1576 Assert( ExitReason.IoPortAccess.VpContext.Cs.Base == pCtx->cs.u64Base1577 && ExitReason.IoPortAccess.VpContext.Cs.Limit == pCtx->cs.u32Limit1578 && ExitReason.IoPortAccess.VpContext.Cs.Selector == pCtx->cs.Sel);1579 break;1580 default: break; /* shut up compiler. */1581 }1582 #endif1583 1584 /*1585 * Deal with the exit.1586 */1587 switch (ExitReason.ExitReason)1588 {1589 /* Frequent exits: */1590 case WHvRunVpExitReasonCanceled:1591 case WHvRunVpExitReasonAlerted:1592 rcStrict = VINF_SUCCESS;1593 break;1594 1595 case WHvRunVpExitReasonX64Halt:1596 rcStrict = nemR3WinHandleHalt(pVM, pVCpu, pCtx);1597 break;1598 1599 case WHvRunVpExitReasonMemoryAccess:1600 rcStrict = nemR3WinHandleMemoryAccess(pVM, pVCpu, pCtx, &ExitReason.MemoryAccess);1601 break;1602 1603 case WHvRunVpExitReasonX64IoPortAccess:1604 rcStrict = nemR3WinHandleIoPortAccess(pVM, pVCpu, pCtx, &ExitReason.IoPortAccess);1605 break;1606 1607 case WHvRunVpExitReasonX64InterruptWindow:1608 rcStrict = nemR3WinHandleInterruptWindow(pVM, pVCpu, pCtx, &ExitReason);1609 break;1610 1611 case WHvRunVpExitReasonX64MsrAccess: /* needs configuring */1612 rcStrict = nemR3WinHandleMsrAccess(pVM, pVCpu, pCtx, &ExitReason);1613 break;1614 1615 case WHvRunVpExitReasonX64Cpuid: /* needs configuring */1616 rcStrict = nemR3WinHandleCpuId(pVM, pVCpu, pCtx, &ExitReason);1617 break;1618 1619 case WHvRunVpExitReasonException: /* needs configuring */1620 rcStrict = nemR3WinHandleException(pVM, pVCpu, pCtx, &ExitReason);1621 break;1622 1623 /* Unlikely exits: */1624 case WHvRunVpExitReasonUnsupportedFeature:1625 rcStrict = nemR3WinHandleUD(pVM, pVCpu, pCtx, &ExitReason);1626 break;1627 1628 case WHvRunVpExitReasonUnrecoverableException:1629 rcStrict = nemR3WinHandleTripleFault(pVM, pVCpu, pCtx, &ExitReason);1630 break;1631 1632 case WHvRunVpExitReasonInvalidVpRegisterValue:1633 rcStrict = nemR3WinHandleInvalidState(pVM, pVCpu, pCtx, &ExitReason);1634 break;1635 1636 /* Undesired exits: */1637 case WHvRunVpExitReasonNone:1638 default:1639 AssertLogRelMsgFailed(("Unknown ExitReason: %#x\n", ExitReason.ExitReason));1640 rcStrict = VERR_INTERNAL_ERROR_3;1641 break;1642 }1643 if (rcStrict != VINF_SUCCESS)1644 {1645 LogFlow(("nemR3NativeRunGC: returning: %Rrc\n", VBOXSTRICTRC_VAL(rcStrict)));1646 break;1647 }1648 1649 #ifndef NEM_WIN_USE_HYPERCALLS_FOR_PAGES1650 /* Hack alert! */1651 uint32_t const cMappedPages = pVM->nem.s.cMappedPages;1652 if (cMappedPages < 4000)1653 { /* likely */ }1654 else1655 {1656 PGMPhysNemEnumPagesByState(pVM, pVCpu, NEM_WIN_PAGE_STATE_READABLE, nemR3WinUnmapOnePageCallback, NULL);1657 Log(("nemR3NativeRunGC: Unmapped all; cMappedPages=%u -> %u\n", cMappedPages, pVM->nem.s.cMappedPages));1658 }1659 #endif1660 1661 /* If any FF is pending, return to the EM loops. That's okay for the1662 current sledgehammer approach. */1663 if ( VM_FF_IS_PENDING( pVM, !fSingleStepping ? VM_FF_HP_R0_PRE_HM_MASK : VM_FF_HP_R0_PRE_HM_STEP_MASK)1664 || VMCPU_FF_IS_PENDING(pVCpu, !fSingleStepping ? VMCPU_FF_HP_R0_PRE_HM_MASK : VMCPU_FF_HP_R0_PRE_HM_STEP_MASK) )1665 {1666 LogFlow(("nemR3NativeRunGC: returning: pending FF (%#x / %#x)\n", pVM->fGlobalForcedActions, pVCpu->fLocalForcedActions));1667 break;1668 }1669 }1670 1671 return rcStrict;1672 }1673 #endif /* later */1674 1675 1652 1676 1653 #endif /* IN_RING0 */
Note:
See TracChangeset
for help on using the changeset viewer.