Ejemplo n.º 1
0
/** @interface_method_impl{PDMAPICHLPR0,pfnSetInterruptFF} */
static DECLCALLBACK(void) pdmR0ApicHlp_SetInterruptFF(PPDMDEVINS pDevIns, PDMAPICIRQ enmType, VMCPUID idCpu)
{
    PDMDEV_ASSERT_DEVINS(pDevIns);
    PVM    pVM   = pDevIns->Internal.s.pVMR0;
    PVMCPU pVCpu = &pVM->aCpus[idCpu];

    AssertReturnVoid(idCpu < pVM->cCpus);

    LogFlow(("pdmR0ApicHlp_SetInterruptFF: CPU%d=caller=%p/%d: VM_FF_INTERRUPT %d -> 1 (CPU%d)\n",
             VMMGetCpuId(pVM), pDevIns, pDevIns->iInstance, VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_APIC), idCpu));

    switch (enmType)
    {
        case PDMAPICIRQ_UPDATE_PENDING:
            VMCPU_FF_SET(pVCpu, VMCPU_FF_UPDATE_APIC);
            break;
        case PDMAPICIRQ_HARDWARE:
#ifdef VBOX_WITH_NEW_APIC
            VMCPU_ASSERT_EMT_OR_NOT_RUNNING(pVCpu);
#endif
            VMCPU_FF_SET(pVCpu, VMCPU_FF_INTERRUPT_APIC);
            break;
        case PDMAPICIRQ_NMI:
            VMCPU_FF_SET(pVCpu, VMCPU_FF_INTERRUPT_NMI);
            break;
        case PDMAPICIRQ_SMI:
            VMCPU_FF_SET(pVCpu, VMCPU_FF_INTERRUPT_SMI);
            break;
        case PDMAPICIRQ_EXTINT:
            VMCPU_FF_SET(pVCpu, VMCPU_FF_INTERRUPT_PIC);
            break;
        default:
            AssertMsgFailed(("enmType=%d\n", enmType));
            break;
    }

    /* We need to wake up the target CPU. */
    if (
#ifdef VBOX_WITH_NEW_APIC
        /* We are already on EMT if enmType is PDMAPICIRQ_HARDWARE. Don't bother with poking! */
        enmType != PDMAPICIRQ_HARDWARE &&
#endif
        VMMGetCpuId(pVM) != idCpu)
    {
        switch (VMCPU_GET_STATE(pVCpu))
        {
            case VMCPUSTATE_STARTED_EXEC:
                GVMMR0SchedPokeEx(pVM, pVCpu->idCpu, false /* don't take the used lock */);
                break;

            case VMCPUSTATE_STARTED_HALTED:
                GVMMR0SchedWakeUpEx(pVM, pVCpu->idCpu, false /* don't take the used lock */);
                break;

            default:
                break; /* nothing to do in other states. */
        }
    }
}
/**
 * Handles the KVM hypercall.
 *
 * @returns VBox status code.
 * @param   pVCpu           Pointer to the VMCPU.
 * @param   pCtx            Pointer to the guest-CPU context.
 */
VMM_INT_DECL(int) gimKvmHypercall(PVMCPU pVCpu, PCPUMCTX pCtx)
{
    /*
     * Get the hypercall operation and arguments.
     */
    bool const fIs64BitMode = CPUMIsGuestIn64BitCodeEx(pCtx);
    uint64_t uHyperOp       = pCtx->rax;
    uint64_t uHyperArg0     = pCtx->rbx;
    uint64_t uHyperArg1     = pCtx->rcx;
    uint64_t uHyperArg2     = pCtx->rdi;
    uint64_t uHyperArg3     = pCtx->rsi;
    uint64_t uHyperRet      = KVM_HYPERCALL_RET_ENOSYS;
    uint64_t uAndMask       = UINT64_C(0xffffffffffffffff);
    if (!fIs64BitMode)
    {
        uAndMask    = UINT64_C(0xffffffff);
        uHyperOp   &= UINT64_C(0xffffffff);
        uHyperArg0 &= UINT64_C(0xffffffff);
        uHyperArg1 &= UINT64_C(0xffffffff);
        uHyperArg2 &= UINT64_C(0xffffffff);
        uHyperArg3 &= UINT64_C(0xffffffff);
        uHyperRet  &= UINT64_C(0xffffffff);
    }

    /*
     * Verify that guest ring-0 is the one making the hypercall.
     */
    uint32_t uCpl = CPUMGetGuestCPL(pVCpu);
    if (uCpl)
    {
        pCtx->rax = KVM_HYPERCALL_RET_EPERM & uAndMask;
        return VINF_SUCCESS;
    }

    /*
     * Do the work.
     */
    switch (uHyperOp)
    {
        case KVM_HYPERCALL_OP_KICK_CPU:
        {
            PVM pVM = pVCpu->CTX_SUFF(pVM);
            if (uHyperArg1 < pVM->cCpus)
            {
                PVMCPU pVCpuTarget = &pVM->aCpus[uHyperArg1];   /** ASSUMES pVCpu index == ApicId of the VCPU. */
                VMCPU_FF_SET(pVCpuTarget, VMCPU_FF_UNHALT);
#ifdef IN_RING0
                /*
                 * We might be here with preemption disabled or enabled (i.e. depending on thread-context hooks
                 * being used), so don't try obtaining the GVMMR0 used lock here. See @bugref{7270#c148}.
                 */
                GVMMR0SchedWakeUpEx(pVM, pVCpuTarget->idCpu, false /* fTakeUsedLock */);
#elif defined(IN_RING3)
                int rc2 = SUPR3CallVMMR0(pVM->pVMR0, pVCpuTarget->idCpu, VMMR0_DO_GVMM_SCHED_WAKE_UP, NULL);
                AssertRC(rc2);
#elif defined(IN_RC)
                /* Nothing to do for raw-mode, shouldn't really be used by raw-mode guests anyway. */
                Assert(pVM->cCpus == 1);
#endif
                uHyperRet = KVM_HYPERCALL_RET_SUCCESS;
            }
            break;
        }

        case KVM_HYPERCALL_OP_VAPIC_POLL_IRQ:
            uHyperRet = KVM_HYPERCALL_RET_SUCCESS;
            break;

        default:
            break;
    }

    /*
     * Place the result in rax/eax.
     */
    pCtx->rax = uHyperRet & uAndMask;
    return VINF_SUCCESS;
}