Exemple #1
0
/**
 * @callback_method_impl{PFNVMMEMTRENDEZVOUS,
 *      Worker for gimR3KvmEnableWallClock}
 */
static DECLCALLBACK(VBOXSTRICTRC) gimR3KvmEnableWallClockCallback(PVM pVM, PVMCPU pVCpu, void *pvData)
{
    Assert(pvData);
    PKVMWALLCLOCKINFO pWallClockInfo  = (PKVMWALLCLOCKINFO)pvData;
    RTGCPHYS          GCPhysWallClock = pWallClockInfo->GCPhysWallClock;

    /*
     * Read the wall-clock version (sequence) from the guest.
     */
    uint32_t uVersion;
    Assert(PGMPhysIsGCPhysNormal(pVM, GCPhysWallClock));
    int rc = PGMPhysSimpleReadGCPhys(pVM, &uVersion, GCPhysWallClock, sizeof(uVersion));
    if (RT_FAILURE(rc))
    {
        LogRel(("GIM: KVM: Failed to read wall-clock struct. version at %#RGp. rc=%Rrc\n", GCPhysWallClock, rc));
        return rc;
    }

    /*
     * Ensure the version is incrementally even.
     */
    if (!(uVersion & 1))
        ++uVersion;
    ++uVersion;

    /*
     * Update wall-clock guest struct. with UTC information.
     */
    RTTIMESPEC TimeSpec;
    int32_t    iSec;
    int32_t    iNano;
    TMR3UtcNow(pVM, &TimeSpec);
    RTTimeSpecGetSecondsAndNano(&TimeSpec, &iSec, &iNano);

    GIMKVMWALLCLOCK WallClock;
    RT_ZERO(WallClock);
    AssertCompile(sizeof(uVersion) == sizeof(WallClock.u32Version));
    WallClock.u32Version = uVersion;
    WallClock.u32Sec     = iSec;
    WallClock.u32Nano    = iNano;

    /*
     * Write out the wall-clock struct. to guest memory.
     */
    Assert(!(WallClock.u32Version & 1));
    rc = PGMPhysSimpleWriteGCPhys(pVM, GCPhysWallClock, &WallClock, sizeof(GIMKVMWALLCLOCK));
    if (RT_SUCCESS(rc))
    {
        LogRel(("GIM: KVM: Enabled wall-clock struct. at %#RGp - u32Sec=%u u32Nano=%u uVersion=%#RU32\n", GCPhysWallClock,
                WallClock.u32Sec, WallClock.u32Nano, WallClock.u32Version));
    }
    else
        LogRel(("GIM: KVM: Failed to write wall-clock struct. at %#RGp. rc=%Rrc\n", GCPhysWallClock, rc));
    return rc;
}
Exemple #2
0
/**
 * Read guest memory.
 *
 * @returns VBox status code.
 * @param   pUVM        The user mode VM handle.
 * @param   idCpu       The ID of the CPU context to read memory from.
 * @param   pAddress    Where to start reading.
 * @param   pvBuf       Where to store the data we've read.
 * @param   cbRead      The number of bytes to read.
 */
static DECLCALLBACK(int) dbgfR3MemRead(PUVM pUVM, VMCPUID idCpu, PCDBGFADDRESS pAddress, void *pvBuf, size_t cbRead)
{
    PVM pVM = pUVM->pVM;
    VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
    Assert(idCpu == VMMGetCpuId(pVM));

    /*
     * Validate the input we use, PGM does the rest.
     */
    if (!DBGFR3AddrIsValid(pUVM, pAddress))
        return VERR_INVALID_POINTER;
    if (!VALID_PTR(pvBuf))
        return VERR_INVALID_POINTER;

    /*
     * HMA is special
     */
    int rc;
    if (DBGFADDRESS_IS_HMA(pAddress))
    {
        if (DBGFADDRESS_IS_PHYS(pAddress))
            rc = VERR_INVALID_POINTER;
        else
            rc = MMR3HyperReadGCVirt(pVM, pvBuf, pAddress->FlatPtr, cbRead);
    }
    else
    {
        /*
         * Select DBGF worker by addressing mode.
         */
        PVMCPU  pVCpu   = VMMGetCpuById(pVM, idCpu);
        PGMMODE enmMode = PGMGetGuestMode(pVCpu);
        if (    enmMode == PGMMODE_REAL
            ||  enmMode == PGMMODE_PROTECTED
            ||  DBGFADDRESS_IS_PHYS(pAddress) )
            rc = PGMPhysSimpleReadGCPhys(pVM, pvBuf, pAddress->FlatPtr, cbRead);
        else
        {
#if GC_ARCH_BITS > 32
            if (    (   pAddress->FlatPtr >= _4G
                     || pAddress->FlatPtr + cbRead > _4G)
                &&  enmMode != PGMMODE_AMD64
                &&  enmMode != PGMMODE_AMD64_NX)
                return VERR_PAGE_TABLE_NOT_PRESENT;
#endif
            rc = PGMPhysSimpleReadGCPtr(pVCpu, pvBuf, pAddress->FlatPtr, cbRead);
        }
    }
    return rc;
}
Exemple #3
0
/**
 * KVM state-save operation.
 *
 * @returns VBox status code.
 * @param   pVM     Pointer to the VM.
 * @param   pSSM    Pointer to the SSM handle.
 */
VMMR3_INT_DECL(int) gimR3KvmSave(PVM pVM, PSSMHANDLE pSSM)
{
    PCGIMKVM pcKvm = &pVM->gim.s.u.Kvm;

    /*
     * Save the KVM SSM version.
     */
    SSMR3PutU32(pSSM, GIM_KVM_SAVED_STATE_VERSION);

    /*
     * Save per-VCPU data.
     */
    for (uint32_t i = 0; i < pVM->cCpus; i++)
    {
        PCGIMKVMCPU pcKvmCpu = &pVM->aCpus[i].gim.s.u.KvmCpu;

        /* Guest may alter flags (namely GIM_KVM_SYSTEM_TIME_FLAGS_GUEST_PAUSED bit). So re-read them from guest-memory. */
        GIMKVMSYSTEMTIME SystemTime;
        RT_ZERO(SystemTime);
        if (MSR_GIM_KVM_SYSTEM_TIME_IS_ENABLED(pcKvmCpu->u64SystemTimeMsr))
        {
            int rc = PGMPhysSimpleReadGCPhys(pVM, &SystemTime, pcKvmCpu->GCPhysSystemTime, sizeof(GIMKVMSYSTEMTIME));
            AssertRCReturn(rc, rc);
        }

        SSMR3PutU64(pSSM, pcKvmCpu->u64SystemTimeMsr);
        SSMR3PutU64(pSSM, pcKvmCpu->uTsc);
        SSMR3PutU64(pSSM, pcKvmCpu->uVirtNanoTS);
        SSMR3PutGCPhys(pSSM, pcKvmCpu->GCPhysSystemTime);
        SSMR3PutU32(pSSM, pcKvmCpu->u32SystemTimeVersion);
        SSMR3PutU8(pSSM, SystemTime.fFlags);
    }

    /*
     * Save per-VM data.
     */
    SSMR3PutU64(pSSM, pcKvm->u64WallClockMsr);
    return SSMR3PutU32(pSSM, pcKvm->uBaseFeat);
}
/**
 * Worker function for dbgfR3CoreWrite() which does the writing.
 *
 * @returns VBox status code
 * @param   pVM                 Pointer to the VM.
 * @param   hFile               The file to write to.  Caller closes this.
 */
static int dbgfR3CoreWriteWorker(PVM pVM, RTFILE hFile)
{
    /*
     * Collect core information.
     */
    uint32_t const cu32MemRanges = dbgfR3GetRamRangeCount(pVM);
    uint16_t const cMemRanges    = cu32MemRanges < UINT16_MAX - 1 ? cu32MemRanges : UINT16_MAX - 1; /* One PT_NOTE Program header */
    uint16_t const cProgHdrs     = cMemRanges + 1;

    DBGFCOREDESCRIPTOR CoreDescriptor;
    RT_ZERO(CoreDescriptor);
    CoreDescriptor.u32Magic           = DBGFCORE_MAGIC;
    CoreDescriptor.u32FmtVersion      = DBGFCORE_FMT_VERSION;
    CoreDescriptor.cbSelf             = sizeof(CoreDescriptor);
    CoreDescriptor.u32VBoxVersion     = VBOX_FULL_VERSION;
    CoreDescriptor.u32VBoxRevision    = VMMGetSvnRev();
    CoreDescriptor.cCpus              = pVM->cCpus;

    Log((DBGFLOG_NAME ": CoreDescriptor Version=%u Revision=%u\n", CoreDescriptor.u32VBoxVersion, CoreDescriptor.u32VBoxRevision));

    /*
     * Compute the file layout (see pg_dbgf_vmcore).
     */
    uint64_t const offElfHdr          = RTFileTell(hFile);
    uint64_t const offNoteSection     = offElfHdr         + sizeof(Elf64_Ehdr);
    uint64_t const offLoadSections    = offNoteSection    + sizeof(Elf64_Phdr);
    uint64_t const cbLoadSections     = cMemRanges * sizeof(Elf64_Phdr);
    uint64_t const offCoreDescriptor  = offLoadSections   + cbLoadSections;
    uint64_t const cbCoreDescriptor   = Elf64NoteSectionSize(g_pcszCoreVBoxCore, sizeof(CoreDescriptor));
    uint64_t const offCpuDumps        = offCoreDescriptor + cbCoreDescriptor;
    uint64_t const cbCpuDumps         = pVM->cCpus * Elf64NoteSectionSize(g_pcszCoreVBoxCpu, sizeof(DBGFCORECPU));
    uint64_t const offMemory          = offCpuDumps       + cbCpuDumps;

    uint64_t const offNoteSectionData = offCoreDescriptor;
    uint64_t const cbNoteSectionData  = cbCoreDescriptor + cbCpuDumps;

    /*
     * Write ELF header.
     */
    int rc = Elf64WriteElfHdr(hFile, cProgHdrs, 0 /* cSecHdrs */);
    if (RT_FAILURE(rc))
    {
        LogRel((DBGFLOG_NAME ": Elf64WriteElfHdr failed. rc=%Rrc\n", rc));
        return rc;
    }

    /*
     * Write PT_NOTE program header.
     */
    Assert(RTFileTell(hFile) == offNoteSection);
    rc = Elf64WriteProgHdr(hFile, PT_NOTE, PF_R,
                           offNoteSectionData,  /* file offset to contents */
                           cbNoteSectionData,   /* size in core file */
                           cbNoteSectionData,   /* size in memory */
                           0);                  /* physical address */
    if (RT_FAILURE(rc))
    {
        LogRel((DBGFLOG_NAME ": Elf64WritreProgHdr failed for PT_NOTE. rc=%Rrc\n", rc));
        return rc;
    }

    /*
     * Write PT_LOAD program header for each memory range.
     */
    Assert(RTFileTell(hFile) == offLoadSections);
    uint64_t offMemRange = offMemory;
    for (uint16_t iRange = 0; iRange < cMemRanges; iRange++)
    {
        RTGCPHYS    GCPhysStart;
        RTGCPHYS    GCPhysEnd;
        bool        fIsMmio;
        rc = PGMR3PhysGetRange(pVM, iRange, &GCPhysStart, &GCPhysEnd, NULL /* pszDesc */, &fIsMmio);
        if (RT_FAILURE(rc))
        {
            LogRel((DBGFLOG_NAME ": PGMR3PhysGetRange failed for iRange(%u) rc=%Rrc\n", iRange, rc));
            return rc;
        }

        uint64_t cbMemRange  = GCPhysEnd - GCPhysStart + 1;
        uint64_t cbFileRange = fIsMmio ? 0 : cbMemRange;

        Log((DBGFLOG_NAME ": PGMR3PhysGetRange iRange=%u GCPhysStart=%#x GCPhysEnd=%#x cbMemRange=%u\n",
             iRange, GCPhysStart, GCPhysEnd, cbMemRange));

        rc = Elf64WriteProgHdr(hFile, PT_LOAD, PF_R,
                               offMemRange,                         /* file offset to contents */
                               cbFileRange,                         /* size in core file */
                               cbMemRange,                          /* size in memory */
                               GCPhysStart);                        /* physical address */
        if (RT_FAILURE(rc))
        {
            LogRel((DBGFLOG_NAME ": Elf64WriteProgHdr failed for memory range(%u) cbFileRange=%u cbMemRange=%u rc=%Rrc\n",
                    iRange, cbFileRange, cbMemRange, rc));
            return rc;
        }

        offMemRange += cbFileRange;
    }

    /*
     * Write the Core descriptor note header and data.
     */
    Assert(RTFileTell(hFile) == offCoreDescriptor);
    rc = Elf64WriteNoteHdr(hFile, NT_VBOXCORE, g_pcszCoreVBoxCore, &CoreDescriptor, sizeof(CoreDescriptor));
    if (RT_FAILURE(rc))
    {
        LogRel((DBGFLOG_NAME ": Elf64WriteNoteHdr failed for Note '%s' rc=%Rrc\n", g_pcszCoreVBoxCore, rc));
        return rc;
    }

    /*
     * Write the CPU context note headers and data.
     */
    Assert(RTFileTell(hFile) == offCpuDumps);
    PDBGFCORECPU pDbgfCoreCpu = (PDBGFCORECPU)RTMemAlloc(sizeof(*pDbgfCoreCpu));
    if (RT_UNLIKELY(!pDbgfCoreCpu))
    {
        LogRel((DBGFLOG_NAME ": failed to alloc %u bytes for DBGFCORECPU\n", sizeof(*pDbgfCoreCpu)));
        return VERR_NO_MEMORY;
    }

    for (uint32_t iCpu = 0; iCpu < pVM->cCpus; iCpu++)
    {
        PVMCPU      pVCpu = &pVM->aCpus[iCpu];
        PCPUMCTX    pCtx  = CPUMQueryGuestCtxPtr(pVCpu);
        if (RT_UNLIKELY(!pCtx))
        {
            LogRel((DBGFLOG_NAME ": CPUMQueryGuestCtxPtr failed for vCPU[%u]\n", iCpu));
            RTMemFree(pDbgfCoreCpu);
            return VERR_INVALID_POINTER;
        }

        RT_BZERO(pDbgfCoreCpu, sizeof(*pDbgfCoreCpu));
        dbgfR3GetCoreCpu(pCtx, pDbgfCoreCpu);
        rc = Elf64WriteNoteHdr(hFile, NT_VBOXCPU, g_pcszCoreVBoxCpu, pDbgfCoreCpu, sizeof(*pDbgfCoreCpu));
        if (RT_FAILURE(rc))
        {
            LogRel((DBGFLOG_NAME ": Elf64WriteNoteHdr failed for vCPU[%u] rc=%Rrc\n", iCpu, rc));
            RTMemFree(pDbgfCoreCpu);
            return rc;
        }
    }
    RTMemFree(pDbgfCoreCpu);
    pDbgfCoreCpu = NULL;

    /*
     * Write memory ranges.
     */
    Assert(RTFileTell(hFile) == offMemory);
    for (uint16_t iRange = 0; iRange < cMemRanges; iRange++)
    {
        RTGCPHYS GCPhysStart;
        RTGCPHYS GCPhysEnd;
        bool     fIsMmio;
        rc = PGMR3PhysGetRange(pVM, iRange, &GCPhysStart, &GCPhysEnd, NULL /* pszDesc */, &fIsMmio);
        if (RT_FAILURE(rc))
        {
            LogRel((DBGFLOG_NAME ": PGMR3PhysGetRange(2) failed for iRange(%u) rc=%Rrc\n", iRange, rc));
            return rc;
        }

        if (fIsMmio)
            continue;

        /*
         * Write page-by-page of this memory range.
         *
         * The read function may fail on MMIO ranges, we write these as zero
         * pages for now (would be nice to have the VGA bits there though).
         */
        uint64_t cbMemRange  = GCPhysEnd - GCPhysStart + 1;
        uint64_t cPages      = cbMemRange >> PAGE_SHIFT;
        for (uint64_t iPage = 0; iPage < cPages; iPage++)
        {
            uint8_t abPage[PAGE_SIZE];
            rc = PGMPhysSimpleReadGCPhys(pVM, abPage, GCPhysStart + (iPage << PAGE_SHIFT),  sizeof(abPage));
            if (RT_FAILURE(rc))
            {
                if (rc != VERR_PGM_PHYS_PAGE_RESERVED)
                    LogRel((DBGFLOG_NAME ": PGMPhysRead failed for iRange=%u iPage=%u. rc=%Rrc. Ignoring...\n", iRange, iPage, rc));
                RT_ZERO(abPage);
            }

            rc = RTFileWrite(hFile, abPage, sizeof(abPage), NULL /* all */);
            if (RT_FAILURE(rc))
            {
                LogRel((DBGFLOG_NAME ": RTFileWrite failed. iRange=%u iPage=%u rc=%Rrc\n", iRange, iPage, rc));
                return rc;
            }
        }
    }

    return rc;
}
/**
 * MSR write handler for KVM.
 *
 * @returns Strict VBox status code like CPUMSetGuestMsr().
 * @retval  VINF_CPUM_R3_MSR_WRITE
 * @retval  VERR_CPUM_RAISE_GP_0
 *
 * @param   pVCpu       Pointer to the VMCPU.
 * @param   idMsr       The MSR being written.
 * @param   pRange      The range this MSR belongs to.
 * @param   uRawValue   The raw value with the ignored bits not masked.
 */
VMM_INT_DECL(VBOXSTRICTRC) gimKvmWriteMsr(PVMCPU pVCpu, uint32_t idMsr, PCCPUMMSRRANGE pRange, uint64_t uRawValue)
{
    NOREF(pRange);
    PVM     pVM  = pVCpu->CTX_SUFF(pVM);
    PGIMKVM pKvm = &pVM->gim.s.u.Kvm;
    PGIMKVMCPU pKvmCpu = &pVCpu->gim.s.u.KvmCpu;

    switch (idMsr)
    {
        case MSR_GIM_KVM_SYSTEM_TIME:
        case MSR_GIM_KVM_SYSTEM_TIME_OLD:
        {
            bool fEnable = RT_BOOL(uRawValue & MSR_GIM_KVM_SYSTEM_TIME_ENABLE_BIT);
#ifdef IN_RING0
            gimR0KvmUpdateSystemTime(pVM, pVCpu);
            return VINF_CPUM_R3_MSR_WRITE;
#elif defined(IN_RC)
            Assert(pVM->cCpus == 1);
            if (fEnable)
            {
                RTCCUINTREG fEFlags  = ASMIntDisableFlags();
                pKvmCpu->uTsc        = TMCpuTickGetNoCheck(pVCpu) | UINT64_C(1);
                pKvmCpu->uVirtNanoTS = TMVirtualGetNoCheck(pVM)   | UINT64_C(1);
                ASMSetFlags(fEFlags);
            }
            return VINF_CPUM_R3_MSR_WRITE;
#else /* IN_RING3 */
            if (!fEnable)
            {
                gimR3KvmDisableSystemTime(pVM);
                pKvmCpu->u64SystemTimeMsr = uRawValue;
                return VINF_SUCCESS;
            }

            /* Is the system-time struct. already enabled? If so, get flags that need preserving. */
            uint8_t fFlags = 0;
            GIMKVMSYSTEMTIME SystemTime;
            RT_ZERO(SystemTime);
            if (   MSR_GIM_KVM_SYSTEM_TIME_IS_ENABLED(pKvmCpu->u64SystemTimeMsr)
                && MSR_GIM_KVM_SYSTEM_TIME_GUEST_GPA(uRawValue) == pKvmCpu->GCPhysSystemTime)
            {
                int rc2 = PGMPhysSimpleReadGCPhys(pVM, &SystemTime, pKvmCpu->GCPhysSystemTime, sizeof(GIMKVMSYSTEMTIME));
                if (RT_SUCCESS(rc2))
                    pKvmCpu->fSystemTimeFlags = (SystemTime.fFlags & GIM_KVM_SYSTEM_TIME_FLAGS_GUEST_PAUSED);
            }

            /* Enable and populate the system-time struct. */
            pKvmCpu->u64SystemTimeMsr      = uRawValue;
            pKvmCpu->GCPhysSystemTime      = MSR_GIM_KVM_SYSTEM_TIME_GUEST_GPA(uRawValue);
            pKvmCpu->u32SystemTimeVersion += 2;
            int rc = gimR3KvmEnableSystemTime(pVM, pVCpu);
            if (RT_FAILURE(rc))
            {
                pKvmCpu->u64SystemTimeMsr = 0;
                return VERR_CPUM_RAISE_GP_0;
            }
            return VINF_SUCCESS;
#endif
        }

        case MSR_GIM_KVM_WALL_CLOCK:
        case MSR_GIM_KVM_WALL_CLOCK_OLD:
        {
#ifndef IN_RING3
            return VINF_CPUM_R3_MSR_WRITE;
#else
            /* Enable the wall-clock struct. */
            RTGCPHYS GCPhysWallClock = MSR_GIM_KVM_WALL_CLOCK_GUEST_GPA(uRawValue);
            if (RT_LIKELY(RT_ALIGN_64(GCPhysWallClock, 4) == GCPhysWallClock))
            {
                int rc = gimR3KvmEnableWallClock(pVM, GCPhysWallClock);
                if (RT_SUCCESS(rc))
                {
                    pKvm->u64WallClockMsr = uRawValue;
                    return VINF_SUCCESS;
                }
            }
            return VERR_CPUM_RAISE_GP_0;
#endif /* IN_RING3 */
        }

        default:
        {
#ifdef IN_RING3
            static uint32_t s_cTimes = 0;
            if (s_cTimes++ < 20)
                LogRel(("GIM: KVM: Unknown/invalid WrMsr (%#x,%#x`%08x) -> #GP(0)\n", idMsr,
                        uRawValue & UINT64_C(0xffffffff00000000), uRawValue & UINT64_C(0xffffffff)));
#endif
            LogFunc(("Unknown/invalid WrMsr (%#RX32,%#RX64) -> #GP(0)\n", idMsr, uRawValue));
            break;
        }
    }

    return VERR_CPUM_RAISE_GP_0;
}