diff mbox series

[2/2] x86/emul: Emulate %cr8 accesses

Message ID 20250325174109.267974-3-andrew.cooper3@citrix.com (mailing list archive)
State New
Headers show
Series x86/emul: Emulate %cr8 accesses | expand

Commit Message

Andrew Cooper March 25, 2025, 5:41 p.m. UTC
Petr reports:

  (XEN) MMIO emulation failed (1): d12v1 64bit @ 0010:fffff8057ba7dfbf -> 45 0f 20 c2 ...

during introspection.

This is MOV %cr8, which is wired up for hvm_mov_{to,from}_cr(); the VMExit
fastpaths, but not for the full emulation slowpaths.

Xen's handling of %cr8 turns out to be quite wrong.  At a minimum, we need
storage for %cr8 separate to APIC_TPR, and to alter intercepts based on
whether the vLAPIC is enabled or not.  But that's more work than there is time
for in the short term, so make a stopgap fix.

Extend hvmemul_{read,write}_cr() with %cr8 cases.  Unlike hvm_mov_to_cr(),
hardware hasn't filtered out invalid values (#GP checks are ahead of
intercepts), so introduce X86_CR8_VALID_MASK.

Reported-by: Petr Beneš <w1benny@gmail.com>
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
---
CC: Jan Beulich <JBeulich@suse.com>
CC: Roger Pau Monné <roger.pau@citrix.com>
CC: Petr Beneš <w1benny@gmail.com>

v2:
 * Check for reserved bits

In CPUs, CR8 has has 4 bits of storage, and can explicitly get out of sync of
APIC_TPR if the the LAPIC is hardware-disabled in MSR_APIC_BASE.  Writes to
APIC_TPR don't get reflected back into CR8 (according to AMD at least).  Both
Intel and AMD accelerate TPR differently; SVM in the base architecture, and
VT-x as the ~first optional extension.
---
 xen/arch/x86/hvm/emulate.c           | 15 +++++++++++++++
 xen/arch/x86/include/asm/x86-defns.h |  2 ++
 2 files changed, 17 insertions(+)

Comments

Jan Beulich March 26, 2025, 11:19 a.m. UTC | #1
On 25.03.2025 18:41, Andrew Cooper wrote:
> Petr reports:
> 
>   (XEN) MMIO emulation failed (1): d12v1 64bit @ 0010:fffff8057ba7dfbf -> 45 0f 20 c2 ...
> 
> during introspection.
> 
> This is MOV %cr8, which is wired up for hvm_mov_{to,from}_cr(); the VMExit
> fastpaths, but not for the full emulation slowpaths.
> 
> Xen's handling of %cr8 turns out to be quite wrong.  At a minimum, we need
> storage for %cr8 separate to APIC_TPR, and to alter intercepts based on
> whether the vLAPIC is enabled or not.  But that's more work than there is time
> for in the short term, so make a stopgap fix.
> 
> Extend hvmemul_{read,write}_cr() with %cr8 cases.  Unlike hvm_mov_to_cr(),
> hardware hasn't filtered out invalid values (#GP checks are ahead of
> intercepts), so introduce X86_CR8_VALID_MASK.
> 
> Reported-by: Petr Beneš <w1benny@gmail.com>
> Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>

Reviewed-by: Jan Beulich <jbeulich@suse.com>

> --- a/xen/arch/x86/hvm/emulate.c
> +++ b/xen/arch/x86/hvm/emulate.c
> @@ -2288,6 +2288,10 @@ static int cf_check hvmemul_read_cr(
>          val = curr->arch.hvm.guest_cr[reg];
>          break;
>  
> +    case 8:
> +        val = (vlapic_get_reg(vcpu_vlapic(curr), APIC_TASKPRI) & 0xf0) >> 4;

No new #define then to use MASK_EXTR() here and ...

> @@ -2333,6 +2337,17 @@ static int cf_check hvmemul_write_cr(
>          rc = hvm_set_cr4(val, true);
>          break;
>  
> +    case 8:
> +        if ( val & ~X86_CR8_VALID_MASK )
> +        {
> +            rc = X86EMUL_EXCEPTION;
> +            break;
> +        }
> +
> +        vlapic_set_reg(vcpu_vlapic(curr), APIC_TASKPRI, val << 4);

... MASK_INSR() here?

Jan
Andrew Cooper March 26, 2025, 11:33 a.m. UTC | #2
On 26/03/2025 11:19 am, Jan Beulich wrote:
> On 25.03.2025 18:41, Andrew Cooper wrote:
>> Petr reports:
>>
>>   (XEN) MMIO emulation failed (1): d12v1 64bit @ 0010:fffff8057ba7dfbf -> 45 0f 20 c2 ...
>>
>> during introspection.
>>
>> This is MOV %cr8, which is wired up for hvm_mov_{to,from}_cr(); the VMExit
>> fastpaths, but not for the full emulation slowpaths.
>>
>> Xen's handling of %cr8 turns out to be quite wrong.  At a minimum, we need
>> storage for %cr8 separate to APIC_TPR, and to alter intercepts based on
>> whether the vLAPIC is enabled or not.  But that's more work than there is time
>> for in the short term, so make a stopgap fix.
>>
>> Extend hvmemul_{read,write}_cr() with %cr8 cases.  Unlike hvm_mov_to_cr(),
>> hardware hasn't filtered out invalid values (#GP checks are ahead of
>> intercepts), so introduce X86_CR8_VALID_MASK.
>>
>> Reported-by: Petr Beneš <w1benny@gmail.com>
>> Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
> Reviewed-by: Jan Beulich <jbeulich@suse.com>

Thanks.

>
>> --- a/xen/arch/x86/hvm/emulate.c
>> +++ b/xen/arch/x86/hvm/emulate.c
>> @@ -2288,6 +2288,10 @@ static int cf_check hvmemul_read_cr(
>>          val = curr->arch.hvm.guest_cr[reg];
>>          break;
>>  
>> +    case 8:
>> +        val = (vlapic_get_reg(vcpu_vlapic(curr), APIC_TASKPRI) & 0xf0) >> 4;
> No new #define then to use MASK_EXTR() here and ...
>
>> @@ -2333,6 +2337,17 @@ static int cf_check hvmemul_write_cr(
>>          rc = hvm_set_cr4(val, true);
>>          break;
>>  
>> +    case 8:
>> +        if ( val & ~X86_CR8_VALID_MASK )
>> +        {
>> +            rc = X86EMUL_EXCEPTION;
>> +            break;
>> +        }
>> +
>> +        vlapic_set_reg(vcpu_vlapic(curr), APIC_TASKPRI, val << 4);
> ... MASK_INSR() here?

No.  The logic wont survive fixing cr8 I don't think.

AFAICT, what we need is plain storage, and vlapic_{get,set}_tpr()
accessors which account for hw-disable, and then call back into
hvm_set_reg() to adjust intercepts.

~Andrew
diff mbox series

Patch

diff --git a/xen/arch/x86/hvm/emulate.c b/xen/arch/x86/hvm/emulate.c
index 172ed55bd5e7..61aa356d5736 100644
--- a/xen/arch/x86/hvm/emulate.c
+++ b/xen/arch/x86/hvm/emulate.c
@@ -2288,6 +2288,10 @@  static int cf_check hvmemul_read_cr(
         val = curr->arch.hvm.guest_cr[reg];
         break;
 
+    case 8:
+        val = (vlapic_get_reg(vcpu_vlapic(curr), APIC_TASKPRI) & 0xf0) >> 4;
+        break;
+
     default:
         return X86EMUL_UNHANDLEABLE;
     }
@@ -2333,6 +2337,17 @@  static int cf_check hvmemul_write_cr(
         rc = hvm_set_cr4(val, true);
         break;
 
+    case 8:
+        if ( val & ~X86_CR8_VALID_MASK )
+        {
+            rc = X86EMUL_EXCEPTION;
+            break;
+        }
+
+        vlapic_set_reg(vcpu_vlapic(curr), APIC_TASKPRI, val << 4);
+        rc = X86EMUL_OKAY;
+        break;
+
     default:
         rc = X86EMUL_UNHANDLEABLE;
         break;
diff --git a/xen/arch/x86/include/asm/x86-defns.h b/xen/arch/x86/include/asm/x86-defns.h
index 61b0cea8f37c..23579c471f4a 100644
--- a/xen/arch/x86/include/asm/x86-defns.h
+++ b/xen/arch/x86/include/asm/x86-defns.h
@@ -76,6 +76,8 @@ 
 #define X86_CR4_CET        0x00800000 /* Control-flow Enforcement Technology */
 #define X86_CR4_PKS        0x01000000 /* Protection Key Supervisor */
 
+#define X86_CR8_VALID_MASK 0xf
+
 /*
  * XSTATE component flags in XCR0 | MSR_XSS
  */