/** * Implements a GIM hypercall with the provider configured for the VM. * * @returns Strict VBox status code. * @retval VINF_SUCCESS if the hypercall succeeded (even if its operation * failed). * @retval VINF_GIM_R3_HYPERCALL re-start the hypercall from ring-3. * @retval VERR_GIM_HYPERCALL_ACCESS_DENIED CPL is insufficient. * @retval VERR_GIM_HYPERCALLS_NOT_AVAILABLE hypercalls unavailable. * @retval VERR_GIM_NOT_ENABLED GIM is not enabled (shouldn't really happen) * @retval VERR_GIM_HYPERCALL_MEMORY_READ_FAILED hypercall failed while reading * memory. * @retval VERR_GIM_HYPERCALL_MEMORY_WRITE_FAILED hypercall failed while * writing memory. * * @param pVCpu The cross context virtual CPU structure. * @param pCtx Pointer to the guest-CPU context. * * @thread EMT. */ VMM_INT_DECL(VBOXSTRICTRC) GIMHypercall(PVMCPU pVCpu, PCPUMCTX pCtx) { PVM pVM = pVCpu->CTX_SUFF(pVM); VMCPU_ASSERT_EMT(pVCpu); if (RT_UNLIKELY(!GIMIsEnabled(pVM))) return VERR_GIM_NOT_ENABLED; switch (pVM->gim.s.enmProviderId) { case GIMPROVIDERID_HYPERV: return gimHvHypercall(pVCpu, pCtx); case GIMPROVIDERID_KVM: return gimKvmHypercall(pVCpu, pCtx); default: AssertMsgFailed(("GIMHypercall: for provider %u not available/implemented\n", pVM->gim.s.enmProviderId)); return VERR_GIM_HYPERCALLS_NOT_AVAILABLE; } }
/** * Exception handler for #UD. * * @param pVCpu Pointer to the VMCPU. * @param pCtx Pointer to the guest-CPU context. * @param pDis Pointer to the disassembled instruction state at RIP. * Optional, can be NULL. */ VMM_INT_DECL(int) gimKvmXcptUD(PVMCPU pVCpu, PCPUMCTX pCtx, PDISCPUSTATE pDis) { /* * If we didn't ask for #UD to be trapped, bail. */ PVM pVM = pVCpu->CTX_SUFF(pVM); PGIMKVM pKvm = &pVM->gim.s.u.Kvm; if (RT_UNLIKELY(!pVM->gim.s.u.Kvm.fTrapXcptUD)) return VERR_GIM_OPERATION_FAILED; /* * Make sure guest ring-0 is the one making the hypercall. */ if (CPUMGetGuestCPL(pVCpu)) return VERR_GIM_HYPERCALL_ACCESS_DENIED; int rc = VINF_SUCCESS; if (!pDis) { /* * Disassemble the instruction at RIP to figure out if it's the Intel VMCALL instruction * or the AMD VMMCALL instruction and if so, handle it as a hypercall. */ DISCPUSTATE Dis; rc = EMInterpretDisasCurrent(pVM, pVCpu, &Dis, NULL /* pcbInstr */); pDis = &Dis; } if (RT_SUCCESS(rc)) { /* * Patch the instruction to so we don't have to spend time disassembling it each time. * Makes sense only for HM as with raw-mode we will be getting a #UD regardless. */ if ( pDis->pCurInstr->uOpcode == OP_VMCALL || pDis->pCurInstr->uOpcode == OP_VMMCALL) { if ( pDis->pCurInstr->uOpcode != pKvm->uOpCodeNative && HMIsEnabled(pVM)) { uint8_t abHypercall[3]; size_t cbWritten = 0; rc = VMMPatchHypercall(pVM, &abHypercall, sizeof(abHypercall), &cbWritten); AssertRC(rc); Assert(sizeof(abHypercall) == pDis->cbInstr); Assert(sizeof(abHypercall) == cbWritten); rc = PGMPhysSimpleWriteGCPtr(pVCpu, pCtx->rip, &abHypercall, sizeof(abHypercall)); } /* * Perform the hypercall and update RIP. * * For HM, we can simply resume guest execution without performing the hypercall now and * do it on the next VMCALL/VMMCALL exit handler on the patched instruction. * * For raw-mode we need to do this now anyway. So we do it here regardless with an added * advantage is that it saves one world-switch for the HM case. */ if (RT_SUCCESS(rc)) { int rc2 = gimKvmHypercall(pVCpu, pCtx); AssertRC(rc2); pCtx->rip += pDis->cbInstr; } return rc; } } return VERR_GIM_OPERATION_FAILED; }