OvmfPkg/VmgExitLib: Add support for DR7 Read/Write NAE events
BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198 Under SEV-ES, a DR7 read or write intercept generates a #VC exception. The #VC handler must provide special support to the guest for this. On a DR7 write, the #VC handler must cache the value and issue a VMGEXIT to notify the hypervisor of the write. However, the #VC handler must not actually set the value of the DR7 register. On a DR7 read, the #VC handler must return the cached value of the DR7 register to the guest. VMGEXIT is not invoked for a DR7 register read. The caching of the DR7 values will make use of the per-CPU data pages that are allocated along with the GHCB pages. The per-CPU page for a vCPU is the page that immediately follows the vCPU's GHCB page. Since each GHCB page is unique for a vCPU, the page that follows becomes unique for that vCPU. The SEC phase will reserves an area of memory for a single GHCB and per-CPU page for use by the BSP. After transitioning to the PEI phase, new GHCB and per-CPU pages are allocated for the BSP and all APs. Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com> Acked-by: Laszlo Ersek <lersek@redhat.com> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com> Regression-tested-by: Laszlo Ersek <lersek@redhat.com>
This commit is contained in:
committed by
mergify[bot]
parent
9f7e0d0ade
commit
fefcf90c33
@ -126,6 +126,14 @@ UINT64
|
|||||||
SEV_ES_INSTRUCTION_DATA *InstructionData
|
SEV_ES_INSTRUCTION_DATA *InstructionData
|
||||||
);
|
);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Per-CPU data mapping structure
|
||||||
|
//
|
||||||
|
typedef struct {
|
||||||
|
BOOLEAN Dr7Cached;
|
||||||
|
UINT64 Dr7;
|
||||||
|
} SEV_ES_PER_CPU_DATA;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Checks the GHCB to determine if the specified register has been marked valid.
|
Checks the GHCB to determine if the specified register has been marked valid.
|
||||||
@ -1482,6 +1490,104 @@ RdtscExit (
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Handle a DR7 register write event.
|
||||||
|
|
||||||
|
Use the VMGEXIT instruction to handle a DR7 write event.
|
||||||
|
|
||||||
|
@param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
|
||||||
|
Block
|
||||||
|
@param[in, out] Regs x64 processor context
|
||||||
|
@param[in] InstructionData Instruction parsing context
|
||||||
|
|
||||||
|
@retval 0 Event handled successfully
|
||||||
|
@return New exception value to propagate
|
||||||
|
|
||||||
|
**/
|
||||||
|
STATIC
|
||||||
|
UINT64
|
||||||
|
Dr7WriteExit (
|
||||||
|
IN OUT GHCB *Ghcb,
|
||||||
|
IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
|
||||||
|
IN SEV_ES_INSTRUCTION_DATA *InstructionData
|
||||||
|
)
|
||||||
|
{
|
||||||
|
SEV_ES_INSTRUCTION_OPCODE_EXT *Ext;
|
||||||
|
SEV_ES_PER_CPU_DATA *SevEsData;
|
||||||
|
UINT64 *Register;
|
||||||
|
UINT64 Status;
|
||||||
|
|
||||||
|
Ext = &InstructionData->Ext;
|
||||||
|
SevEsData = (SEV_ES_PER_CPU_DATA *) (Ghcb + 1);
|
||||||
|
|
||||||
|
DecodeModRm (Regs, InstructionData);
|
||||||
|
|
||||||
|
//
|
||||||
|
// MOV DRn always treats MOD == 3 no matter how encoded
|
||||||
|
//
|
||||||
|
Register = GetRegisterPointer (Regs, Ext->ModRm.Rm);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Using a value of 0 for ExitInfo1 means RAX holds the value
|
||||||
|
//
|
||||||
|
Ghcb->SaveArea.Rax = *Register;
|
||||||
|
GhcbSetRegValid (Ghcb, GhcbRax);
|
||||||
|
|
||||||
|
Status = VmgExit (Ghcb, SVM_EXIT_DR7_WRITE, 0, 0);
|
||||||
|
if (Status != 0) {
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
SevEsData->Dr7 = *Register;
|
||||||
|
SevEsData->Dr7Cached = TRUE;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Handle a DR7 register read event.
|
||||||
|
|
||||||
|
Use the VMGEXIT instruction to handle a DR7 read event.
|
||||||
|
|
||||||
|
@param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
|
||||||
|
Block
|
||||||
|
@param[in, out] Regs x64 processor context
|
||||||
|
@param[in] InstructionData Instruction parsing context
|
||||||
|
|
||||||
|
@retval 0 Event handled successfully
|
||||||
|
|
||||||
|
**/
|
||||||
|
STATIC
|
||||||
|
UINT64
|
||||||
|
Dr7ReadExit (
|
||||||
|
IN OUT GHCB *Ghcb,
|
||||||
|
IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
|
||||||
|
IN SEV_ES_INSTRUCTION_DATA *InstructionData
|
||||||
|
)
|
||||||
|
{
|
||||||
|
SEV_ES_INSTRUCTION_OPCODE_EXT *Ext;
|
||||||
|
SEV_ES_PER_CPU_DATA *SevEsData;
|
||||||
|
UINT64 *Register;
|
||||||
|
|
||||||
|
Ext = &InstructionData->Ext;
|
||||||
|
SevEsData = (SEV_ES_PER_CPU_DATA *) (Ghcb + 1);
|
||||||
|
|
||||||
|
DecodeModRm (Regs, InstructionData);
|
||||||
|
|
||||||
|
//
|
||||||
|
// MOV DRn always treats MOD == 3 no matter how encoded
|
||||||
|
//
|
||||||
|
Register = GetRegisterPointer (Regs, Ext->ModRm.Rm);
|
||||||
|
|
||||||
|
//
|
||||||
|
// If there is a cached valued for DR7, return that. Otherwise return the
|
||||||
|
// DR7 standard reset value of 0x400 (no debug breakpoints set).
|
||||||
|
//
|
||||||
|
*Register = (SevEsData->Dr7Cached) ? SevEsData->Dr7 : 0x400;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Handle a #VC exception.
|
Handle a #VC exception.
|
||||||
|
|
||||||
@ -1526,6 +1632,14 @@ VmgExitHandleVc (
|
|||||||
|
|
||||||
ExitCode = Regs->ExceptionData;
|
ExitCode = Regs->ExceptionData;
|
||||||
switch (ExitCode) {
|
switch (ExitCode) {
|
||||||
|
case SVM_EXIT_DR7_READ:
|
||||||
|
NaeExit = Dr7ReadExit;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SVM_EXIT_DR7_WRITE:
|
||||||
|
NaeExit = Dr7WriteExit;
|
||||||
|
break;
|
||||||
|
|
||||||
case SVM_EXIT_RDTSC:
|
case SVM_EXIT_RDTSC:
|
||||||
NaeExit = RdtscExit;
|
NaeExit = RdtscExit;
|
||||||
break;
|
break;
|
||||||
|
Reference in New Issue
Block a user