Skip to content

Commit

Permalink
whpx: Fixed incorrect CR8/TPR synchronization
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
sysprogs authored and bonzini committed Feb 28, 2022
1 parent 3e02040 commit 241a50e
Showing 1 changed file with 27 additions and 1 deletion.
28 changes: 27 additions & 1 deletion target/i386/whpx/whpx-all.c
Expand Up @@ -256,6 +256,21 @@ static int whpx_set_tsc(CPUState *cpu)
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)
{
struct whpx_state *whpx = &whpx_global;
Expand Down Expand Up @@ -284,7 +299,7 @@ static void whpx_set_registers(CPUState *cpu, int level)
v86 = (env->eflags & VM_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);

idx = 0;
Expand Down Expand Up @@ -475,6 +490,17 @@ static void whpx_get_registers(CPUState *cpu)
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;

/* Indexes for first 16 registers match between HV and QEMU definitions */
Expand Down

0 comments on commit 241a50e

Please sign in to comment.