VirtualBox

Changeset 55037 in vbox


Ignore:
Timestamp:
Mar 31, 2015 2:09:10 PM (9 years ago)
Author:
vboxsync
Message:

VMM/GIM: Add hypercall support and KVM spinlock support.

Location:
trunk
Files:
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/include/VBox/err.h

    r54903 r55037  
    26222622/** The GIM device is not registered with GIM when it ought to be. */
    26232623#define VERR_GIM_DEVICE_NOT_REGISTERED              (-6310)
    2624 /** An invalid Guest OS identifier was specified for the GIM provider. */
    2625 #define VERR_GIM_INVALID_GUESTOS_ID                 (-6311)
    26262624/** @} */
    26272625
  • trunk/include/VBox/vmm/gim.h

    r53615 r55037  
    9898AssertCompileMemberAlignment(GIMMMIO2REGION, pvPageR0, 8);
    9999
    100 
    101100#if 0
    102101/**
     
    175174VMM_INT_DECL(bool)          GIMAreHypercallsEnabled(PVMCPU pVCpu);
    176175VMM_INT_DECL(int)           GIMHypercall(PVMCPU pVCpu, PCPUMCTX pCtx);
     176VMM_INT_DECL(int)           GIMXcptUD(PVMCPU pVCpu, PCPUMCTX pCtx);
     177VMM_INT_DECL(bool)          GIMShouldTrapXcptUD(PVM pVM);
    177178VMM_INT_DECL(VBOXSTRICTRC)  GIMReadMsr(PVMCPU pVCpu, uint32_t idMsr, PCCPUMMSRRANGE pRange, uint64_t *puValue);
    178179VMM_INT_DECL(VBOXSTRICTRC)  GIMWriteMsr(PVMCPU pVCpu, uint32_t idMsr, PCCPUMMSRRANGE pRange, uint64_t uValue, uint64_t uRawValue);
    179 
    180180/** @} */
    181181
  • trunk/src/VBox/VMM/VMMAll/GIMAll.cpp

    r54819 r55037  
    7373            return gimHvAreHypercallsEnabled(pVCpu);
    7474
     75        case GIMPROVIDERID_KVM:
     76            return gimKvmAreHypercallsEnabled(pVCpu);
     77
    7578        default:
    7679            return false;
     
    99102            return gimHvHypercall(pVCpu, pCtx);
    100103
    101         default:
    102             AssertMsgFailed(("GIMHypercall: for unknown provider %u\n", pVM->gim.s.enmProviderId));
    103             return VERR_GIM_IPE_3;
     104        case GIMPROVIDERID_KVM:
     105            return gimKvmHypercall(pVCpu, pCtx);
     106
     107        default:
     108            AssertMsgFailed(("GIMHypercall: for provider %u not available/implemented\n", pVM->gim.s.enmProviderId));
     109            return VERR_GIM_HYPERCALLS_NOT_AVAILABLE;
    104110    }
    105111}
     
    127133    }
    128134    return false;
     135}
     136
     137
     138/**
     139 * Whether #UD exceptions in the guest needs to be intercepted by the GIM
     140 * provider.
     141 *
     142 * At the moment, the reason why this isn't a more generic interface wrt to
     143 * exceptions is because of performance (each VM-exit would have to manually
     144 * check whether or not GIM needs to be notified). Left as a todo for later if
     145 * really required.
     146 *
     147 * @returns true if needed, false otherwise.
     148 * @param   pVM         Pointer to the VM.
     149 */
     150VMM_INT_DECL(bool) GIMShouldTrapXcptUD(PVM pVM)
     151{
     152    if (!GIMIsEnabled(pVM))
     153        return 0;
     154
     155    switch (pVM->gim.s.enmProviderId)
     156    {
     157        case GIMPROVIDERID_KVM:
     158            return gimKvmShouldTrapXcptUD(pVM);
     159
     160        default:
     161            return 0;
     162    }
     163}
     164
     165
     166/**
     167 * Exception handler for #UD when requested by the GIM provider.
     168 *
     169 * @param   pVCpu       Pointer to the VMCPU.
     170 * @param   pCtx        Pointer to the guest-CPU context.
     171 */
     172VMM_INT_DECL(int) GIMXcptUD(PVMCPU pVCpu, PCPUMCTX pCtx)
     173{
     174    PVM pVM = pVCpu->CTX_SUFF(pVM);
     175    Assert(GIMIsEnabled(pVM));
     176
     177    switch (pVM->gim.s.enmProviderId)
     178    {
     179        case GIMPROVIDERID_KVM:
     180            return gimKvmXcptUD(pVCpu, pCtx);
     181
     182        default:
     183            return VERR_GIM_OPERATION_FAILED;
     184    }
    129185}
    130186
  • trunk/src/VBox/VMM/VMMAll/GIMAllKvm.cpp

    r54839 r55037  
    2424
    2525#include <VBox/err.h>
     26#include <VBox/dis.h>
    2627#include <VBox/vmm/hm.h>
     28#include <VBox/vmm/em.h>
    2729#include <VBox/vmm/tm.h>
    2830#include <VBox/vmm/vm.h>
     
    3032#include <VBox/vmm/pdmdev.h>
    3133#include <VBox/vmm/pdmapi.h>
     34#include <VBox/sup.h>
    3235
    3336#include <iprt/asm-amd64-x86.h>
     
    4346VMM_INT_DECL(int) gimKvmHypercall(PVMCPU pVCpu, PCPUMCTX pCtx)
    4447{
    45     PVM pVM = pVCpu->CTX_SUFF(pVM);
    46     /** @todo Handle hypercalls. Fail for now */
    47     return VERR_GIM_IPE_3;
     48    /*
     49     * Get the hypercall operation and arguments.
     50     */
     51    bool const fIs64BitMode = CPUMIsGuestIn64BitCodeEx(pCtx);
     52    uint64_t uHyperOp       = pCtx->rax;
     53    uint64_t uHyperArg0     = pCtx->rbx;
     54    uint64_t uHyperArg1     = pCtx->rcx;
     55    uint64_t uHyperArg2     = pCtx->rdi;
     56    uint64_t uHyperArg3     = pCtx->rsi;
     57    uint64_t uHyperRet      = KVM_HYPERCALL_RET_ENOSYS;
     58    uint64_t uAndMask       = UINT64_C(0xffffffffffffffff);
     59    if (!fIs64BitMode)
     60    {
     61        uAndMask    = UINT64_C(0xffffffff);
     62        uHyperOp   &= UINT64_C(0xffffffff);
     63        uHyperArg0 &= UINT64_C(0xffffffff);
     64        uHyperArg1 &= UINT64_C(0xffffffff);
     65        uHyperArg2 &= UINT64_C(0xffffffff);
     66        uHyperArg3 &= UINT64_C(0xffffffff);
     67        uHyperRet  &= UINT64_C(0xffffffff);
     68    }
     69
     70    /*
     71     * Verify that guest ring-0 is the one making the hypercall.
     72     */
     73    uint32_t uCpl = CPUMGetGuestCPL(pVCpu);
     74    if (uCpl)
     75    {
     76        pCtx->rax = KVM_HYPERCALL_RET_EPERM & uAndMask;
     77        return VINF_SUCCESS;
     78    }
     79
     80    /*
     81     * Do the work.
     82     */
     83    switch (uHyperOp)
     84    {
     85        case KVM_HYPERCALL_OP_KICK_CPU:
     86        {
     87            PVM pVM = pVCpu->CTX_SUFF(pVM);
     88            if (uHyperArg1 < pVM->cCpus)
     89            {
     90                PVMCPU pVCpuTarget = &pVM->aCpus[uHyperArg1];   /** ASSUMES pVCpu index == ApicId of the VCPU. */
     91                VMCPU_FF_SET(pVCpuTarget, VMCPU_FF_UNHALT);
     92#ifdef IN_RING0
     93                GVMMR0SchedWakeUp(pVM, pVCpuTarget->idCpu);
     94#elif defined(IN_RING3)
     95                int rc2 = SUPR3CallVMMR0(pVM->pVMR0, pVCpuTarget->idCpu, VMMR0_DO_GVMM_SCHED_WAKE_UP, NULL);
     96                AssertRC(rc2);
     97#endif
     98                uHyperRet = KVM_HYPERCALL_RET_SUCCESS;
     99            }
     100            break;
     101        }
     102
     103        case KVM_HYPERCALL_OP_VAPIC_POLL_IRQ:
     104            uHyperRet = KVM_HYPERCALL_RET_SUCCESS;
     105            break;
     106
     107        default:
     108            break;
     109    }
     110
     111    /*
     112     * Place the result in rax/eax.
     113     */
     114    if (fIs64BitMode)
     115        pCtx->rax = uHyperRet;
     116    else
     117        pCtx->eax = uHyperRet & uAndMask;
     118    return VINF_SUCCESS;
    48119}
    49120
     
    231302}
    232303
     304
     305/**
     306 * Whether we need to trap #UD exceptions in the guest.
     307 *
     308 * On AMD-V we need to trap them because paravirtualized Linux/KVM guests use
     309 * the Intel VMCALL instruction to make hypercalls and we need to trap and
     310 * optionally patch them to the AMD-V VMMCALL instruction and handle the
     311 * hypercall.
     312 *
     313 * I guess this was done so that guest teleporation between an AMD and an Intel
     314 * machine would working without any changes at the time of teleporation.
     315 * However, this also means we -always- need to intercept #UD exceptions on one
     316 * of the two CPU models (Intel or AMD). Hyper-V solves this problem more
     317 * elegantly by letting the hypervisor supply an opaque hypercall page.
     318 *
     319 * @param   pVM         Pointer to the VM.
     320 */
     321VMM_INT_DECL(bool) gimKvmShouldTrapXcptUD(PVM pVM)
     322{
     323    pVM->gim.s.u.Kvm.fTrapXcptUD = ASMIsAmdCpu();
     324    return pVM->gim.s.u.Kvm.fTrapXcptUD;
     325}
     326
     327
     328/**
     329 * Exception handler for #UD.
     330 *
     331 * @param   pVCpu       Pointer to the VMCPU.
     332 * @param   pCtx        Pointer to the guest-CPU context.
     333 */
     334VMM_INT_DECL(int) gimKvmXcptUD(PVMCPU pVCpu, PCPUMCTX pCtx)
     335{
     336    /*
     337     * If we didn't ask for #UD to be trapped, bail.
     338     */
     339    PVM pVM = pVCpu->CTX_SUFF(pVM);
     340    if (RT_UNLIKELY(!pVM->gim.s.u.Kvm.fTrapXcptUD))
     341        return VERR_GIM_OPERATION_FAILED;
     342
     343    /*
     344     * Disassemble the instruction at RIP to figure out if it's the Intel
     345     * VMCALL instruction and if so, handle it as a hypercall.
     346     */
     347    DISCPUSTATE Dis;
     348    unsigned    cbInstr;
     349    int rc = EMInterpretDisasCurrent(pVM, pVCpu, &Dis, &cbInstr);
     350    if (RT_SUCCESS(rc))
     351    {
     352        if (Dis.pCurInstr->uOpcode == OP_VMCALL)
     353        {
     354            Assert(cbInstr == 3);        /* paranoia. */
     355
     356            /*
     357             * Patch the instruction to so we don't have to spend time disassembling it each time.
     358             */
     359            static uint8_t s_abHypercall[3];
     360            size_t cbWritten;
     361            rc = HMPatchHypercall(pVM, &s_abHypercall, sizeof(s_abHypercall), &cbWritten);
     362            AssertRC(rc);
     363
     364            if (RT_LIKELY(cbWritten == cbInstr))
     365                rc = PGMPhysSimpleWriteGCPtr(pVCpu, pCtx->rip, &s_abHypercall, sizeof(s_abHypercall));
     366            if (RT_SUCCESS(rc))
     367                return gimKvmHypercall(pVCpu, pCtx);
     368        }
     369    }
     370
     371    return VERR_GIM_OPERATION_FAILED;
     372}
     373
  • trunk/src/VBox/VMM/VMMR0/HMSVMR0.cpp

    r55001 r55037  
    296296static FNSVMEXITHANDLER hmR0SvmExitXcptPF;
    297297static FNSVMEXITHANDLER hmR0SvmExitXcptNM;
     298static FNSVMEXITHANDLER hmR0SvmExitXcptUD;
    298299static FNSVMEXITHANDLER hmR0SvmExitXcptMF;
    299300static FNSVMEXITHANDLER hmR0SvmExitXcptDB;
     
    669670    Assert(pVM->hm.s.svm.fSupported);
    670671
     672    pVM->hm.s.fTrapXcptUD             = GIMShouldTrapXcptUD(pVM);
     673    uint32_t const fGimXcptIntercepts = pVM->hm.s.fTrapXcptUD ? RT_BIT(X86_XCPT_UD) : 0;
    671674    for (VMCPUID i = 0; i < pVM->cCpus; i++)
    672675    {
     
    783786        pVmcb->ctrl.u32InterceptCtrl1 |= SVM_CTRL1_INTERCEPT_TASK_SWITCH;
    784787#endif
     788
     789        /* Apply the exceptions intercepts needed by the GIM provider. */
     790        pVmcb->ctrl.u32InterceptException |= fGimXcptIntercepts;
    785791
    786792        /*
     
    34763482            return hmR0SvmExitXcptNM(pVCpu, pCtx, pSvmTransient);
    34773483
     3484        case SVM_EXIT_EXCEPTION_6:  /* X86_XCPT_UD */
     3485            return hmR0SvmExitXcptUD(pVCpu, pCtx, pSvmTransient);
     3486
    34783487        case SVM_EXIT_EXCEPTION_10:  /* X86_XCPT_MF */
    34793488            return hmR0SvmExitXcptMF(pVCpu, pCtx, pSvmTransient);
     
    35823591                case SVM_EXIT_EXCEPTION_4:             /* X86_XCPT_OF */
    35833592                case SVM_EXIT_EXCEPTION_5:             /* X86_XCPT_BR */
    3584                 case SVM_EXIT_EXCEPTION_6:             /* X86_XCPT_UD */
     3593                /* case SVM_EXIT_EXCEPTION_6: */       /* X86_XCPT_UD - Handled above. */
    35853594                /*   SVM_EXIT_EXCEPTION_7: */          /* X86_XCPT_NM - Handled above. */
    35863595                case SVM_EXIT_EXCEPTION_8:             /* X86_XCPT_DF */
     
    36193628                            /** @todo Investigate this later. */
    36203629                            STAM_COUNTER_INC(&pVCpu->hm.s.StatExitGuestBP);
    3621                             break;
    3622 
    3623                         case X86_XCPT_UD:
    3624                             STAM_COUNTER_INC(&pVCpu->hm.s.StatExitGuestUD);
    36253630                            break;
    36263631
     
    41524157    {
    41534158        PSVMVMCB pVmcb = (PSVMVMCB)pVCpu->hm.s.svm.pvVmcb;
     4159        Assert(pVmcb->ctrl.u64NextRIP - pCtx->rip == cb);
    41544160        pCtx->rip = pVmcb->ctrl.u64NextRIP;
    41554161    }
     
    50035009        if (GIMAreHypercallsEnabled(pVCpu))
    50045010            rc = GIMHypercall(pVCpu, pCtx);
    5005     }
    5006 
    5007     if (rc != VINF_SUCCESS)
     5011
     5012        /* If the hypercall changes anything other than guest general-purpose registers,
     5013           we would need to reload the guest changed bits on VM-reentry. */
     5014    }
     5015
     5016    if (RT_SUCCESS(rc))
     5017        hmR0SvmUpdateRip(pVCpu, pCtx, 3);
     5018    else
    50085019        hmR0SvmSetPendingXcptUD(pVCpu);
    50095020    return VINF_SUCCESS;
     
    52025213
    52035214/**
     5215 * #VMEXIT handler for undefined opcode (SVM_EXIT_EXCEPTION_6).
     5216 * Conditional #VMEXIT.
     5217 */
     5218HMSVM_EXIT_DECL hmR0SvmExitXcptUD(PVMCPU pVCpu, PCPUMCTX pCtx, PSVMTRANSIENT pSvmTransient)
     5219{
     5220    HMSVM_VALIDATE_EXIT_HANDLER_PARAMS();
     5221
     5222    HMSVM_CHECK_EXIT_DUE_TO_EVENT_DELIVERY();
     5223
     5224    STAM_COUNTER_INC(&pVCpu->hm.s.StatExitGuestUD);
     5225
     5226    PVM pVM = pVCpu->CTX_SUFF(pVM);
     5227    if (   pVM->hm.s.fTrapXcptUD
     5228        && GIMAreHypercallsEnabled(pVCpu))
     5229    {
     5230        int rc = GIMXcptUD(pVCpu, pCtx);
     5231        if (RT_SUCCESS(rc))
     5232        {
     5233            /* If the exception handler changes anything other than guest general-purpose registers,
     5234               we would need to reload the guest changed bits on VM-reentry. */
     5235            hmR0SvmUpdateRip(pVCpu, pCtx, 3);
     5236            return VINF_SUCCESS;
     5237        }
     5238    }
     5239
     5240    hmR0SvmSetPendingXcptUD(pVCpu);
     5241    return VINF_SUCCESS;
     5242}
     5243
     5244
     5245/**
    52045246 * #VMEXIT handler for math-fault exceptions (SVM_EXIT_EXCEPTION_10).
    52055247 * Conditional #VMEXIT.
  • trunk/src/VBox/VMM/VMMR0/HMVMXR0.cpp

    r54908 r55037  
    75327532        else if (uIntType == VMX_EXIT_INTERRUPTION_INFO_TYPE_NMI)
    75337533        {
    7534             bool fBlockNmi = RT_BOOL(uIntrState & VMX_VMCS_GUEST_INTERRUPTIBILITY_STATE_BLOCK_NMI);
     7534            bool const fBlockNmi = RT_BOOL(uIntrState & VMX_VMCS_GUEST_INTERRUPTIBILITY_STATE_BLOCK_NMI);
    75357535            Assert(!fBlockSti);
    75367536            Assert(!fBlockMovSS);
     
    1025010250        rc = GIMHypercall(pVCpu, pMixedCtx);
    1025110251    }
    10252     if (rc != VINF_SUCCESS)
     10252
     10253    if (RT_SUCCESS(rc))
     10254    {
     10255        rc = hmR0VmxAdvanceGuestRip(pVCpu, pMixedCtx, pVmxTransient);
     10256        Assert(pVmxTransient->cbInstr == 3);
     10257
     10258        /* If the hypercall changes anything other than guest general-purpose registers,
     10259           we would need to reload the guest changed bits on VM-reentry. */
     10260    }
     10261    else
    1025310262    {
    1025410263        hmR0VmxSetPendingXcptUD(pVCpu, pMixedCtx);
  • trunk/src/VBox/VMM/VMMR3/GIMKvm.cpp

    r54845 r55037  
    9898                        //| GIM_KVM_BASE_FEAT_STEAL_TIME
    9999                        //| GIM_KVM_BASE_FEAT_PV_EOI
    100                         //| GIM_KVM_BASE_FEAT_UNHALT
     100                        | GIM_KVM_BASE_FEAT_PV_UNHALT
    101101                        ;
    102102        /* Rest of the features are determined in gimR3KvmInitCompleted(). */
  • trunk/src/VBox/VMM/include/GIMInternal.h

    r54819 r55037  
    7373    union
    7474    {
    75         GIMHV Hv;
     75        GIMHV  Hv;
    7676        GIMKVM Kvm;
    7777    } u;
  • trunk/src/VBox/VMM/include/GIMKvmInternal.h

    r54839 r55037  
    117117 */
    118118/** Guest-physical address of the wall-clock struct. */
    119 #define MSR_GIM_KVM_WALL_CLOCK_GUEST_GPA(a)      (a)
    120 /** @} */
    121 
     119#define MSR_GIM_KVM_WALL_CLOCK_GUEST_GPA(a)        (a)
     120/** @} */
     121
     122
     123/** @name KVM Hypercall operations.
     124 *  @{ */
     125#define KVM_HYPERCALL_OP_VAPIC_POLL_IRQ            1
     126#define KVM_HYPERCALL_OP_MMU                       2
     127#define KVM_HYPERCALL_OP_FEATURES                  3
     128#define KVM_HYPERCALL_OP_KICK_CPU                  5
     129/** @} */
     130
     131/** @name KVM Hypercall return values.
     132 *  @{ */
     133/* Return values for hypercalls */
     134#define KVM_HYPERCALL_RET_SUCCESS                  0
     135#define KVM_HYPERCALL_RET_ENOSYS                   (uint64_t)(-1000)
     136#define KVM_HYPERCALL_RET_EFAULT                   (uint64_t)(-14)
     137#define KVM_HYPERCALL_RET_E2BIG                    (uint64_t)(-7)
     138#define KVM_HYPERCALL_RET_EPERM                    (uint64_t)(-1)
     139/** @} */
    122140
    123141/**
     
    184202    uint32_t                    uBaseFeat;
    185203    /** @} */
     204
     205    /** Whether we need to trap #UD exceptions. */
     206    bool                        fTrapXcptUD;
    186207} GIMKVM;
    187208/** Pointer to per-VM GIM KVM instance data. */
     
    242263VMM_INT_DECL(VBOXSTRICTRC)      gimKvmReadMsr(PVMCPU pVCpu, uint32_t idMsr, PCCPUMMSRRANGE pRange, uint64_t *puValue);
    243264VMM_INT_DECL(VBOXSTRICTRC)      gimKvmWriteMsr(PVMCPU pVCpu, uint32_t idMsr, PCCPUMMSRRANGE pRange, uint64_t uRawValue);
     265VMM_INT_DECL(bool)              gimKvmShouldTrapXcptUD(PVM pVM);
     266VMM_INT_DECL(int)               gimKvmXcptUD(PVMCPU pVCpu, PCPUMCTX pCtx);
     267
    244268
    245269RT_C_DECLS_END
  • trunk/src/VBox/VMM/include/HMInternal.h

    r54908 r55037  
    343343    /** Set when TPR patching is active. */
    344344    bool                        fTPRPatchingActive;
    345     bool                        u8Alignment[3];
     345    /** Whether #UD needs to be intercepted (required by certain GIM providers). */
     346    bool                        fTrapXcptUD;
     347    bool                        u8Alignment[2];
    346348
    347349    /** Host kernel flags that HM might need to know (SUPKERNELFEATURES_XXX). */
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette