1377
1420
pCache->uMagic = UINT64_C(0xDEADBEEFDEADBEEF);
1424
/* Clear all patch information. */
1425
pVM->hwaccm.s.pGuestPatchMem = 0;
1426
pVM->hwaccm.s.pFreeGuestPatchMem = 0;
1427
pVM->hwaccm.s.cbGuestPatchMem = 0;
1428
pVM->hwaccm.s.svm.cPatches = 0;
1429
pVM->hwaccm.s.svm.PatchTree = 0;
1430
pVM->hwaccm.s.svm.fTPRPatchingActive = false;
1431
ASMMemZero32(pVM->hwaccm.s.svm.aPatches, sizeof(pVM->hwaccm.s.svm.aPatches));
1435
* Callback to patch a TPR instruction (vmmcall or mov cr8)
1437
* @returns VBox status code.
1438
* @param pVM The VM handle.
1439
* @param pVCpu The VMCPU for the EMT we're being called on.
1440
* @param pvUser Unused
1443
DECLCALLBACK(int) hwaccmR3RemovePatches(PVM pVM, PVMCPU pVCpu, void *pvUser)
1445
VMCPUID idCpu = (VMCPUID)(uintptr_t)pvUser;
1447
/* Only execute the handler on the VCPU the original patch request was issued. */
1448
if (pVCpu->idCpu != idCpu)
1449
return VINF_SUCCESS;
1451
Log(("hwaccmR3RemovePatches\n"));
1452
for (unsigned i = 0; i < pVM->hwaccm.s.svm.cPatches; i++)
1454
uint8_t szInstr[15];
1455
PHWACCMTPRPATCH pPatch = &pVM->hwaccm.s.svm.aPatches[i];
1456
RTGCPTR pInstrGC = (RTGCPTR)pPatch->Core.Key;
1462
rc = DBGFR3DisasInstrEx(pVM, pVCpu->idCpu, CPUMGetGuestCS(pVCpu), pInstrGC, 0, szOutput, sizeof(szOutput), 0);
1463
if (VBOX_SUCCESS(rc))
1464
Log(("Patched instr: %s\n", szOutput));
1467
/* Check if the instruction is still the same. */
1468
rc = PGMPhysSimpleReadGCPtr(pVCpu, szInstr, pInstrGC, pPatch->cbNewOp);
1469
if (rc != VINF_SUCCESS)
1471
Log(("Patched code removed? (rc=%Rrc0\n", rc));
1472
continue; /* swapped out or otherwise removed; skip it. */
1475
if (memcmp(szInstr, pPatch->aNewOpcode, pPatch->cbNewOp))
1477
Log(("Patched instruction was changed! (rc=%Rrc0\n", rc));
1478
continue; /* skip it. */
1481
rc = PGMPhysSimpleWriteGCPtr(pVCpu, pInstrGC, pPatch->aOpcode, pPatch->cbOp);
1485
rc = DBGFR3DisasInstrEx(pVM, pVCpu->idCpu, CPUMGetGuestCS(pVCpu), pInstrGC, 0, szOutput, sizeof(szOutput), 0);
1486
if (VBOX_SUCCESS(rc))
1487
Log(("Original instr: %s\n", szOutput));
1490
pVM->hwaccm.s.svm.cPatches = 0;
1491
pVM->hwaccm.s.svm.PatchTree = 0;
1492
pVM->hwaccm.s.pFreeGuestPatchMem = pVM->hwaccm.s.pGuestPatchMem;
1493
pVM->hwaccm.s.svm.fTPRPatchingActive = false;
1494
return VINF_SUCCESS;
1498
* Enable patching in a VT-x/AMD-V guest
1500
* @returns VBox status code.
1501
* @param pVM The VM to operate on.
1502
* @param idCpu VCPU to execute hwaccmR3RemovePatches on
1503
* @param pPatchMem Patch memory range
1504
* @param cbPatchMem Size of the memory range
1506
int hwaccmR3EnablePatching(PVM pVM, VMCPUID idCpu, RTRCPTR pPatchMem, unsigned cbPatchMem)
1508
int rc = VMMR3EmtRendezvous(pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ONE_BY_ONE, hwaccmR3RemovePatches, (void *)idCpu);
1511
pVM->hwaccm.s.pGuestPatchMem = pPatchMem;
1512
pVM->hwaccm.s.pFreeGuestPatchMem = pPatchMem;
1513
pVM->hwaccm.s.cbGuestPatchMem = cbPatchMem;
1514
return VINF_SUCCESS;
1518
* Enable patching in a VT-x/AMD-V guest
1520
* @returns VBox status code.
1521
* @param pVM The VM to operate on.
1522
* @param pPatchMem Patch memory range
1523
* @param cbPatchMem Size of the memory range
1525
VMMR3DECL(int) HWACMMR3EnablePatching(PVM pVM, RTGCPTR pPatchMem, unsigned cbPatchMem)
1527
Log(("HWACMMR3EnablePatching %RGv size %x\n", pPatchMem, cbPatchMem));
1529
/* Current TPR patching only applies to AMD cpus.
1530
* Needs to be extended to Intel CPUs without the APIC TPR hardware optimization.
1532
if (CPUMGetCPUVendor(pVM) != CPUMCPUVENDOR_AMD)
1533
return VERR_NOT_SUPPORTED;
1537
/* We own the IOM lock here and could cause a deadlock by waiting for a VCPU that is blocking on the IOM lock. */
1539
int rc = VMR3ReqCallU(pVM->pUVM, VMCPUID_ANY_QUEUE, &pReq, 0, VMREQFLAGS_NO_WAIT,
1540
(PFNRT)hwaccmR3EnablePatching, 4, pVM, VMMGetCpuId(pVM), (RTRCPTR)pPatchMem, cbPatchMem);
1545
return hwaccmR3EnablePatching(pVM, VMMGetCpuId(pVM), (RTRCPTR)pPatchMem, cbPatchMem);
1549
* Disable patching in a VT-x/AMD-V guest
1551
* @returns VBox status code.
1552
* @param pVM The VM to operate on.
1553
* @param pPatchMem Patch memory range
1554
* @param cbPatchMem Size of the memory range
1556
VMMR3DECL(int) HWACMMR3DisablePatching(PVM pVM, RTGCPTR pPatchMem, unsigned cbPatchMem)
1558
Log(("HWACMMR3DisablePatching %RGv size %x\n", pPatchMem, cbPatchMem));
1560
Assert(pVM->hwaccm.s.pGuestPatchMem == pPatchMem);
1561
Assert(pVM->hwaccm.s.cbGuestPatchMem == cbPatchMem);
1563
/* @todo Potential deadlock when other VCPUs are waiting on the IOM lock (we own it)!! */
1564
int rc = VMMR3EmtRendezvous(pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ONE_BY_ONE, hwaccmR3RemovePatches, (void *)VMMGetCpuId(pVM));
1567
pVM->hwaccm.s.pGuestPatchMem = 0;
1568
pVM->hwaccm.s.pFreeGuestPatchMem = 0;
1569
pVM->hwaccm.s.cbGuestPatchMem = 0;
1570
pVM->hwaccm.s.svm.fTPRPatchingActive = false;
1571
return VINF_SUCCESS;
1576
* Callback to patch a TPR instruction (vmmcall or mov cr8)
1578
* @returns VBox status code.
1579
* @param pVM The VM handle.
1580
* @param pVCpu The VMCPU for the EMT we're being called on.
1581
* @param pvUser User specified CPU context
1584
DECLCALLBACK(int) hwaccmR3ReplaceTprInstr(PVM pVM, PVMCPU pVCpu, void *pvUser)
1586
VMCPUID idCpu = (VMCPUID)(uintptr_t)pvUser;
1587
PCPUMCTX pCtx = CPUMQueryGuestCtxPtr(pVCpu);
1588
RTGCPTR oldrip = pCtx->rip;
1589
PDISCPUSTATE pDis = &pVCpu->hwaccm.s.DisState;
1592
/* Only execute the handler on the VCPU the original patch request was issued. (the other CPU(s) might not yet have switched to protected mode) */
1593
if (pVCpu->idCpu != idCpu)
1594
return VINF_SUCCESS;
1596
Log(("hwaccmR3ReplaceTprInstr: %RGv\n", pCtx->rip));
1598
/* Two or more VCPUs were racing to patch this instruction. */
1599
PHWACCMTPRPATCH pPatch = (PHWACCMTPRPATCH)RTAvloU32Get(&pVM->hwaccm.s.svm.PatchTree, (AVLOU32KEY)pCtx->eip);
1601
return VINF_SUCCESS;
1603
Assert(pVM->hwaccm.s.svm.cPatches < RT_ELEMENTS(pVM->hwaccm.s.svm.aPatches));
1605
int rc = EMInterpretDisasOne(pVM, pVCpu, CPUMCTX2CORE(pCtx), pDis, &cbOp);
1607
if ( rc == VINF_SUCCESS
1608
&& pDis->pCurInstr->opcode == OP_MOV
1611
uint8_t aVMMCall[3] = { 0xf, 0x1, 0xd9};
1612
uint32_t idx = pVM->hwaccm.s.svm.cPatches;
1613
PHWACCMTPRPATCH pPatch = &pVM->hwaccm.s.svm.aPatches[idx];
1615
rc = PGMPhysSimpleReadGCPtr(pVCpu, pPatch->aOpcode, pCtx->rip, cbOp);
1618
pPatch->cbOp = cbOp;
1620
if (pDis->param1.flags == USE_DISPLACEMENT32)
1623
if (pDis->param2.flags == USE_REG_GEN32)
1625
pPatch->enmType = HWACCMTPRINSTR_WRITE_REG;
1626
pPatch->uSrcOperand = pDis->param2.base.reg_gen;
1630
Assert(pDis->param2.flags == USE_IMMEDIATE32);
1631
pPatch->enmType = HWACCMTPRINSTR_WRITE_IMM;
1632
pPatch->uSrcOperand = pDis->param2.parval;
1634
rc = PGMPhysSimpleWriteGCPtr(pVCpu, pCtx->rip, aVMMCall, sizeof(aVMMCall));
1637
memcpy(pPatch->aNewOpcode, aVMMCall, sizeof(aVMMCall));
1638
pPatch->cbNewOp = sizeof(aVMMCall);
1642
RTGCPTR oldrip = pCtx->rip;
1643
uint32_t oldcbOp = cbOp;
1644
uint32_t uMmioReg = pDis->param1.base.reg_gen;
1647
Assert(pDis->param1.flags == USE_REG_GEN32);
1650
* mov eax, dword [fffe0080] (5 bytes)
1651
* Check if next instruction is:
1655
rc = EMInterpretDisasOne(pVM, pVCpu, CPUMCTX2CORE(pCtx), pDis, &cbOp);
1657
if ( rc == VINF_SUCCESS
1658
&& pDis->pCurInstr->opcode == OP_SHR
1659
&& pDis->param1.flags == USE_REG_GEN32
1660
&& pDis->param1.base.reg_gen == uMmioReg
1661
&& pDis->param2.flags == USE_IMMEDIATE8
1662
&& pDis->param2.parval == 4
1663
&& oldcbOp + cbOp < sizeof(pVM->hwaccm.s.svm.aPatches[idx].aOpcode))
1665
uint8_t szInstr[15];
1667
/* Replacing two instructions now. */
1668
rc = PGMPhysSimpleReadGCPtr(pVCpu, &pPatch->aOpcode, pCtx->rip, oldcbOp + cbOp);
1671
pPatch->cbOp = oldcbOp + cbOp;
1673
/* 0xF0, 0x0F, 0x20, 0xC0 = mov eax, cr8 */
1677
szInstr[3] = 0xC0 | pDis->param1.base.reg_gen;
1678
for (unsigned i = 4; i < pPatch->cbOp; i++)
1679
szInstr[i] = 0x90; /* nop */
1681
rc = PGMPhysSimpleWriteGCPtr(pVCpu, pCtx->rip, szInstr, pPatch->cbOp);
1684
memcpy(pPatch->aNewOpcode, szInstr, pPatch->cbOp);
1685
pPatch->cbNewOp = pPatch->cbOp;
1687
Log(("Acceptable read/shr candidate!\n"));
1688
pPatch->enmType = HWACCMTPRINSTR_READ_SHR4;
1692
pPatch->enmType = HWACCMTPRINSTR_READ;
1693
pPatch->uDstOperand = pDis->param1.base.reg_gen;
1695
rc = PGMPhysSimpleWriteGCPtr(pVCpu, pCtx->rip, aVMMCall, sizeof(aVMMCall));
1698
memcpy(pPatch->aNewOpcode, aVMMCall, sizeof(aVMMCall));
1699
pPatch->cbNewOp = sizeof(aVMMCall);
1703
pPatch->Core.Key = pCtx->eip;
1704
rc = RTAvloU32Insert(&pVM->hwaccm.s.svm.PatchTree, &pPatch->Core);
1707
pVM->hwaccm.s.svm.cPatches++;
1708
STAM_COUNTER_INC(&pVM->hwaccm.s.StatTPRReplaceSuccess);
1709
return VINF_SUCCESS;
1712
/* Save invalid patch, so we will not try again. */
1713
uint32_t idx = pVM->hwaccm.s.svm.cPatches;
1717
rc = DBGFR3DisasInstrEx(pVM, pVCpu->idCpu, pCtx->cs, pCtx->rip, 0, szOutput, sizeof(szOutput), 0);
1718
if (VBOX_SUCCESS(rc))
1719
Log(("Failed to patch instr: %s\n", szOutput));
1722
pPatch = &pVM->hwaccm.s.svm.aPatches[idx];
1723
pPatch->Core.Key = pCtx->eip;
1724
pPatch->enmType = HWACCMTPRINSTR_INVALID;
1725
rc = RTAvloU32Insert(&pVM->hwaccm.s.svm.PatchTree, &pPatch->Core);
1727
pVM->hwaccm.s.svm.cPatches++;
1728
STAM_COUNTER_INC(&pVM->hwaccm.s.StatTPRReplaceFailure);
1729
return VINF_SUCCESS;
1733
* Callback to patch a TPR instruction (jump to generated code)
1735
* @returns VBox status code.
1736
* @param pVM The VM handle.
1737
* @param pVCpu The VMCPU for the EMT we're being called on.
1738
* @param pvUser User specified CPU context
1741
DECLCALLBACK(int) hwaccmR3PatchTprInstr(PVM pVM, PVMCPU pVCpu, void *pvUser)
1743
VMCPUID idCpu = (VMCPUID)(uintptr_t)pvUser;
1744
PCPUMCTX pCtx = CPUMQueryGuestCtxPtr(pVCpu);
1745
PDISCPUSTATE pDis = &pVCpu->hwaccm.s.DisState;
1753
/* Only execute the handler on the VCPU the original patch request was issued. (the other CPU(s) might not yet have switched to protected mode) */
1754
if (pVCpu->idCpu != idCpu)
1755
return VINF_SUCCESS;
1757
Assert(pVM->hwaccm.s.svm.cPatches < RT_ELEMENTS(pVM->hwaccm.s.svm.aPatches));
1759
/* Two or more VCPUs were racing to patch this instruction. */
1760
PHWACCMTPRPATCH pPatch = (PHWACCMTPRPATCH)RTAvloU32Get(&pVM->hwaccm.s.svm.PatchTree, (AVLOU32KEY)pCtx->eip);
1763
Log(("hwaccmR3PatchTprInstr: already patched %RGv\n", pCtx->rip));
1764
return VINF_SUCCESS;
1767
Log(("hwaccmR3PatchTprInstr %RGv\n", pCtx->rip));
1769
rc = EMInterpretDisasOne(pVM, pVCpu, CPUMCTX2CORE(pCtx), pDis, &cbOp);
1771
if ( rc == VINF_SUCCESS
1772
&& pDis->pCurInstr->opcode == OP_MOV
1775
uint32_t idx = pVM->hwaccm.s.svm.cPatches;
1776
PHWACCMTPRPATCH pPatch = &pVM->hwaccm.s.svm.aPatches[idx];
1781
rc = DBGFR3DisasInstrEx(pVM, pVCpu->idCpu, pCtx->cs, pCtx->rip, 0, szOutput, sizeof(szOutput), 0);
1782
if (VBOX_SUCCESS(rc))
1783
Log(("Original instr: %s\n", szOutput));
1786
rc = PGMPhysSimpleReadGCPtr(pVCpu, pPatch->aOpcode, pCtx->rip, cbOp);
1789
pPatch->cbOp = cbOp;
1790
pPatch->enmType = HWACCMTPRINSTR_JUMP_REPLACEMENT;
1792
if (pDis->param1.flags == USE_DISPLACEMENT32)
1800
* xor EDX,EDX [31 D2]
1801
* mov EAX,EAX [89 C0]
1803
* mov EAX,0000000CCh [B8 CC 00 00 00]
1804
* mov ECX,0C0000082h [B9 82 00 00 C0]
1809
* jmp return_address [E9 return_address]
1812
bool fUsesEax = (pDis->param2.flags == USE_REG_GEN32 && pDis->param2.base.reg_gen == USE_REG_EAX);
1814
aPatch[off++] = 0x51; /* push ecx */
1815
aPatch[off++] = 0x52; /* push edx */
1817
aPatch[off++] = 0x50; /* push eax */
1818
aPatch[off++] = 0x31; /* xor edx, edx */
1819
aPatch[off++] = 0xD2;
1820
if (pDis->param2.flags == USE_REG_GEN32)
1824
aPatch[off++] = 0x89; /* mov eax, src_reg */
1825
aPatch[off++] = MAKE_MODRM(3, pDis->param2.base.reg_gen, USE_REG_EAX);
1830
Assert(pDis->param2.flags == USE_IMMEDIATE32);
1831
aPatch[off++] = 0xB8; /* mov eax, immediate */
1832
*(uint32_t *)&aPatch[off] = pDis->param2.parval;
1833
off += sizeof(uint32_t);
1835
aPatch[off++] = 0xB9; /* mov ecx, 0xc0000082 */
1836
*(uint32_t *)&aPatch[off] = MSR_K8_LSTAR;
1837
off += sizeof(uint32_t);
1839
aPatch[off++] = 0x0F; /* wrmsr */
1840
aPatch[off++] = 0x30;
1842
aPatch[off++] = 0x58; /* pop eax */
1843
aPatch[off++] = 0x5A; /* pop edx */
1844
aPatch[off++] = 0x59; /* pop ecx */
1854
* mov ECX,0C0000082h [B9 82 00 00 C0]
1856
* mov EAX,EAX [89 C0]
1860
* jmp return_address [E9 return_address]
1863
Assert(pDis->param1.flags == USE_REG_GEN32);
1865
if (pDis->param1.base.reg_gen != USE_REG_ECX)
1866
aPatch[off++] = 0x51; /* push ecx */
1867
if (pDis->param1.base.reg_gen != USE_REG_EDX)
1868
aPatch[off++] = 0x52; /* push edx */
1869
if (pDis->param1.base.reg_gen != USE_REG_EAX)
1870
aPatch[off++] = 0x50; /* push eax */
1872
aPatch[off++] = 0x31; /* xor edx, edx */
1873
aPatch[off++] = 0xD2;
1875
aPatch[off++] = 0xB9; /* mov ecx, 0xc0000082 */
1876
*(uint32_t *)&aPatch[off] = MSR_K8_LSTAR;
1877
off += sizeof(uint32_t);
1879
aPatch[off++] = 0x0F; /* rdmsr */
1880
aPatch[off++] = 0x32;
1882
if (pDis->param1.base.reg_gen != USE_REG_EAX)
1884
aPatch[off++] = 0x89; /* mov dst_reg, eax */
1885
aPatch[off++] = MAKE_MODRM(3, USE_REG_EAX, pDis->param1.base.reg_gen);
1888
if (pDis->param1.base.reg_gen != USE_REG_EAX)
1889
aPatch[off++] = 0x58; /* pop eax */
1890
if (pDis->param1.base.reg_gen != USE_REG_EDX)
1891
aPatch[off++] = 0x5A; /* pop edx */
1892
if (pDis->param1.base.reg_gen != USE_REG_ECX)
1893
aPatch[off++] = 0x59; /* pop ecx */
1895
aPatch[off++] = 0xE9; /* jmp return_address */
1896
*(RTRCUINTPTR *)&aPatch[off] = ((RTRCUINTPTR)pCtx->eip + cbOp) - ((RTRCUINTPTR)pVM->hwaccm.s.pFreeGuestPatchMem + off + 4);
1897
off += sizeof(RTRCUINTPTR);
1899
if (pVM->hwaccm.s.pFreeGuestPatchMem + off <= pVM->hwaccm.s.pGuestPatchMem + pVM->hwaccm.s.cbGuestPatchMem)
1901
/* Write new code to the patch buffer. */
1902
rc = PGMPhysSimpleWriteGCPtr(pVCpu, pVM->hwaccm.s.pFreeGuestPatchMem, aPatch, off);
1906
pInstr = pVM->hwaccm.s.pFreeGuestPatchMem;
1911
rc = DBGFR3DisasInstrEx(pVM, pVCpu->idCpu, pCtx->cs, pInstr, 0, szOutput, sizeof(szOutput), &cb);
1912
if (VBOX_SUCCESS(rc))
1913
Log(("Patch instr %s\n", szOutput));
1917
if (pInstr >= pVM->hwaccm.s.pFreeGuestPatchMem + off)
1922
pPatch->aNewOpcode[0] = 0xE9;
1923
*(RTRCUINTPTR *)&pPatch->aNewOpcode[1] = ((RTRCUINTPTR)pVM->hwaccm.s.pFreeGuestPatchMem) - ((RTRCUINTPTR)pCtx->eip + 5);
1925
/* Overwrite the TPR instruction with a jump. */
1926
rc = PGMPhysSimpleWriteGCPtr(pVCpu, pCtx->eip, pPatch->aNewOpcode, 5);
1930
rc = DBGFR3DisasInstrEx(pVM, pVCpu->idCpu, pCtx->cs, pCtx->rip, 0, szOutput, sizeof(szOutput), 0);
1931
if (VBOX_SUCCESS(rc))
1932
Log(("Jump: %s\n", szOutput));
1934
pVM->hwaccm.s.pFreeGuestPatchMem += off;
1935
pPatch->cbNewOp = 5;
1937
pPatch->Core.Key = pCtx->eip;
1938
rc = RTAvloU32Insert(&pVM->hwaccm.s.svm.PatchTree, &pPatch->Core);
1941
pVM->hwaccm.s.svm.cPatches++;
1942
pVM->hwaccm.s.svm.fTPRPatchingActive = true;
1943
STAM_COUNTER_INC(&pVM->hwaccm.s.StatTPRPatchSuccess);
1944
return VINF_SUCCESS;
1947
Log(("Ran out of space in our patch buffer!\n"));
1950
/* Save invalid patch, so we will not try again. */
1951
uint32_t idx = pVM->hwaccm.s.svm.cPatches;
1954
rc = DBGFR3DisasInstrEx(pVM, pVCpu->idCpu, pCtx->cs, pCtx->rip, 0, szOutput, sizeof(szOutput), 0);
1955
if (VBOX_SUCCESS(rc))
1956
Log(("Failed to patch instr: %s\n", szOutput));
1959
pPatch = &pVM->hwaccm.s.svm.aPatches[idx];
1960
pPatch->Core.Key = pCtx->eip;
1961
pPatch->enmType = HWACCMTPRINSTR_INVALID;
1962
rc = RTAvloU32Insert(&pVM->hwaccm.s.svm.PatchTree, &pPatch->Core);
1964
pVM->hwaccm.s.svm.cPatches++;
1965
STAM_COUNTER_INC(&pVM->hwaccm.s.StatTPRPatchFailure);
1966
return VINF_SUCCESS;
1970
* Attempt to patch TPR mmio instructions
1972
* @returns VBox status code.
1973
* @param pVM The VM to operate on.
1974
* @param pVCpu The VM CPU to operate on.
1975
* @param pCtx CPU context
1977
VMMR3DECL(int) HWACCMR3PatchTprInstr(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx)
1979
int rc = VMMR3EmtRendezvous(pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ONE_BY_ONE, (pVM->hwaccm.s.pGuestPatchMem) ? hwaccmR3PatchTprInstr : hwaccmR3ReplaceTprInstr, (void *)pVCpu->idCpu);