whpx: Fixed incorrect CR8/TPR synchronization

This fixes the following error triggered when stopping and resuming a 64-bit
Linux kernel via gdb:

qemu-system-x86_64.exe: WHPX: Failed to set virtual processor context, hr=c0350005

The previous logic for synchronizing the values did not take into account
that the lower 4 bits of the CR8 register, containing the priority level,
mapped to bits 7:4 of the APIC.TPR register (see section 10.8.6.1 of
Volume 3 of Intel 64 and IA-32 Architectures Software Developer's Manual).
The caused WHvSetVirtualProcessorRegisters() to fail with an error,
effectively preventing GDB from changing the guest context.

Signed-off-by: Ivan Shcherbakov <ivan@sysprogs.com>
Message-Id: <010b01d82874$bb4ef160$31ecd420$@sysprogs.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Ivan Shcherbakov 2022-02-22 21:18:00 -08:00 committed by Paolo Bonzini
parent e561890841
commit 5ad93fd351

View File

@ -256,6 +256,21 @@ static int whpx_set_tsc(CPUState *cpu)
return 0; return 0;
} }
/*
* The CR8 register in the CPU is mapped to the TPR register of the APIC,
* however, they use a slightly different encoding. Specifically:
*
* APIC.TPR[bits 7:4] = CR8[bits 3:0]
*
* This mechanism is described in section 10.8.6.1 of Volume 3 of Intel 64
* and IA-32 Architectures Software Developer's Manual.
*/
static uint64_t whpx_apic_tpr_to_cr8(uint64_t tpr)
{
return tpr >> 4;
}
static void whpx_set_registers(CPUState *cpu, int level) static void whpx_set_registers(CPUState *cpu, int level)
{ {
struct whpx_state *whpx = &whpx_global; struct whpx_state *whpx = &whpx_global;
@ -284,7 +299,7 @@ static void whpx_set_registers(CPUState *cpu, int level)
v86 = (env->eflags & VM_MASK); v86 = (env->eflags & VM_MASK);
r86 = !(env->cr[0] & CR0_PE_MASK); r86 = !(env->cr[0] & CR0_PE_MASK);
vcpu->tpr = cpu_get_apic_tpr(x86_cpu->apic_state); vcpu->tpr = whpx_apic_tpr_to_cr8(cpu_get_apic_tpr(x86_cpu->apic_state));
vcpu->apic_base = cpu_get_apic_base(x86_cpu->apic_state); vcpu->apic_base = cpu_get_apic_base(x86_cpu->apic_state);
idx = 0; idx = 0;
@ -475,6 +490,17 @@ static void whpx_get_registers(CPUState *cpu)
hr); hr);
} }
if (whpx_apic_in_platform()) {
/*
* Fetch the TPR value from the emulated APIC. It may get overwritten
* below with the value from CR8 returned by
* WHvGetVirtualProcessorRegisters().
*/
whpx_apic_get(x86_cpu->apic_state);
vcpu->tpr = whpx_apic_tpr_to_cr8(
cpu_get_apic_tpr(x86_cpu->apic_state));
}
idx = 0; idx = 0;
/* Indexes for first 16 registers match between HV and QEMU definitions */ /* Indexes for first 16 registers match between HV and QEMU definitions */