Skip to content

Commit 814ad8f

Browse files
author
Marc Zyngier
committed
KVM: arm64: Drop trapping of PAuth instructions/keys
We currently insist on disabling PAuth on vcpu_load(), and get to enable it on first guest use of an instruction or a key (ignoring the NV case for now). It isn't clear at all what this is trying to achieve: guests tend to use PAuth when available, and nothing forces you to expose it to the guest if you don't want to. This also isn't totally free: we take a full GPR save/restore between host and guest, only to write ten 64bit registers. The "value proposition" escapes me. So let's forget this stuff and enable PAuth eagerly if exposed to the guest. This results in much simpler code. Performance wise, that's not bad either (tested on M2 Pro running a fully automated Debian installer as the workload): - On a non-NV guest, I can see reduction of 0.24% in the number of cycles (measured with perf over 10 consecutive runs) - On a NV guest (L2), I see a 2% reduction in wall-clock time (measured with 'time', as M2 doesn't have a PMUv3 and NV doesn't support it either) So overall, a much reduced complexity and a (small) performance improvement. Reviewed-by: Oliver Upton <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Marc Zyngier <[email protected]>
1 parent f4f6a95 commit 814ad8f

File tree

7 files changed

+70
-99
lines changed

7 files changed

+70
-99
lines changed

arch/arm64/include/asm/kvm_emulate.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,6 @@ static inline void vcpu_set_wfx_traps(struct kvm_vcpu *vcpu)
125125
vcpu->arch.hcr_el2 |= HCR_TWI;
126126
}
127127

128-
static inline void vcpu_ptrauth_disable(struct kvm_vcpu *vcpu)
129-
{
130-
vcpu->arch.hcr_el2 &= ~(HCR_API | HCR_APK);
131-
}
132-
133128
static inline unsigned long vcpu_get_vsesr(struct kvm_vcpu *vcpu)
134129
{
135130
return vcpu->arch.vsesr_el2;

arch/arm64/include/asm/kvm_ptrauth.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,5 +99,26 @@ alternative_else_nop_endif
9999
.macro ptrauth_switch_to_hyp g_ctxt, h_ctxt, reg1, reg2, reg3
100100
.endm
101101
#endif /* CONFIG_ARM64_PTR_AUTH */
102+
103+
#else /* !__ASSEMBLY */
104+
105+
#define __ptrauth_save_key(ctxt, key) \
106+
do { \
107+
u64 __val; \
108+
__val = read_sysreg_s(SYS_ ## key ## KEYLO_EL1); \
109+
ctxt_sys_reg(ctxt, key ## KEYLO_EL1) = __val; \
110+
__val = read_sysreg_s(SYS_ ## key ## KEYHI_EL1); \
111+
ctxt_sys_reg(ctxt, key ## KEYHI_EL1) = __val; \
112+
} while(0)
113+
114+
#define ptrauth_save_keys(ctxt) \
115+
do { \
116+
__ptrauth_save_key(ctxt, APIA); \
117+
__ptrauth_save_key(ctxt, APIB); \
118+
__ptrauth_save_key(ctxt, APDA); \
119+
__ptrauth_save_key(ctxt, APDB); \
120+
__ptrauth_save_key(ctxt, APGA); \
121+
} while(0)
122+
102123
#endif /* __ASSEMBLY__ */
103124
#endif /* __ASM_KVM_PTRAUTH_H */

arch/arm64/kvm/arm.c

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,11 @@
3535
#include <asm/virt.h>
3636
#include <asm/kvm_arm.h>
3737
#include <asm/kvm_asm.h>
38+
#include <asm/kvm_emulate.h>
3839
#include <asm/kvm_mmu.h>
3940
#include <asm/kvm_nested.h>
4041
#include <asm/kvm_pkvm.h>
41-
#include <asm/kvm_emulate.h>
42+
#include <asm/kvm_ptrauth.h>
4243
#include <asm/sections.h>
4344

4445
#include <kvm/arm_hypercalls.h>
@@ -462,6 +463,44 @@ void kvm_arch_vcpu_unblocking(struct kvm_vcpu *vcpu)
462463

463464
}
464465

466+
static void vcpu_set_pauth_traps(struct kvm_vcpu *vcpu)
467+
{
468+
if (vcpu_has_ptrauth(vcpu)) {
469+
/*
470+
* Either we're running running an L2 guest, and the API/APK
471+
* bits come from L1's HCR_EL2, or API/APK are both set.
472+
*/
473+
if (unlikely(vcpu_has_nv(vcpu) && !is_hyp_ctxt(vcpu))) {
474+
u64 val;
475+
476+
val = __vcpu_sys_reg(vcpu, HCR_EL2);
477+
val &= (HCR_API | HCR_APK);
478+
vcpu->arch.hcr_el2 &= ~(HCR_API | HCR_APK);
479+
vcpu->arch.hcr_el2 |= val;
480+
} else {
481+
vcpu->arch.hcr_el2 |= (HCR_API | HCR_APK);
482+
}
483+
484+
/*
485+
* Save the host keys if there is any chance for the guest
486+
* to use pauth, as the entry code will reload the guest
487+
* keys in that case.
488+
* Protected mode is the exception to that rule, as the
489+
* entry into the EL2 code eagerly switch back and forth
490+
* between host and hyp keys (and kvm_hyp_ctxt is out of
491+
* reach anyway).
492+
*/
493+
if (is_protected_kvm_enabled())
494+
return;
495+
496+
if (vcpu->arch.hcr_el2 & (HCR_API | HCR_APK)) {
497+
struct kvm_cpu_context *ctxt;
498+
ctxt = this_cpu_ptr_hyp_sym(kvm_hyp_ctxt);
499+
ptrauth_save_keys(ctxt);
500+
}
501+
}
502+
}
503+
465504
void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
466505
{
467506
struct kvm_s2_mmu *mmu;
@@ -500,8 +539,8 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
500539
else
501540
vcpu_set_wfx_traps(vcpu);
502541

503-
if (vcpu_has_ptrauth(vcpu))
504-
vcpu_ptrauth_disable(vcpu);
542+
vcpu_set_pauth_traps(vcpu);
543+
505544
kvm_arch_vcpu_load_debug_state_flags(vcpu);
506545

507546
if (!cpumask_test_cpu(cpu, vcpu->kvm->arch.supported_cpus))

arch/arm64/kvm/handle_exit.c

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -217,14 +217,12 @@ static int handle_sve(struct kvm_vcpu *vcpu)
217217
* Two possibilities to handle a trapping ptrauth instruction:
218218
*
219219
* - Guest usage of a ptrauth instruction (which the guest EL1 did not
220-
* turn into a NOP). If we get here, it is that we didn't fixup
221-
* ptrauth on exit, and all that we can do is give the guest an
222-
* UNDEF (as the guest isn't supposed to use ptrauth without being
223-
* told it could).
220+
* turn into a NOP). If we get here, it is because we didn't enable
221+
* ptrauth for the guest. This results in an UNDEF, as it isn't
222+
* supposed to use ptrauth without being told it could.
224223
*
225224
* - Running an L2 NV guest while L1 has left HCR_EL2.API==0, and for
226-
* which we reinject the exception into L1. API==1 is handled as a
227-
* fixup so the only way to get here is when API==0.
225+
* which we reinject the exception into L1.
228226
*
229227
* Anything else is an emulation bug (hence the WARN_ON + UNDEF).
230228
*/

arch/arm64/kvm/hyp/include/hyp/switch.h

Lines changed: 1 addition & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <asm/kvm_hyp.h>
2828
#include <asm/kvm_mmu.h>
2929
#include <asm/kvm_nested.h>
30+
#include <asm/kvm_ptrauth.h>
3031
#include <asm/fpsimd.h>
3132
#include <asm/debug-monitors.h>
3233
#include <asm/processor.h>
@@ -447,82 +448,6 @@ static inline bool handle_tx2_tvm(struct kvm_vcpu *vcpu)
447448
return true;
448449
}
449450

450-
static inline bool esr_is_ptrauth_trap(u64 esr)
451-
{
452-
switch (esr_sys64_to_sysreg(esr)) {
453-
case SYS_APIAKEYLO_EL1:
454-
case SYS_APIAKEYHI_EL1:
455-
case SYS_APIBKEYLO_EL1:
456-
case SYS_APIBKEYHI_EL1:
457-
case SYS_APDAKEYLO_EL1:
458-
case SYS_APDAKEYHI_EL1:
459-
case SYS_APDBKEYLO_EL1:
460-
case SYS_APDBKEYHI_EL1:
461-
case SYS_APGAKEYLO_EL1:
462-
case SYS_APGAKEYHI_EL1:
463-
return true;
464-
}
465-
466-
return false;
467-
}
468-
469-
#define __ptrauth_save_key(ctxt, key) \
470-
do { \
471-
u64 __val; \
472-
__val = read_sysreg_s(SYS_ ## key ## KEYLO_EL1); \
473-
ctxt_sys_reg(ctxt, key ## KEYLO_EL1) = __val; \
474-
__val = read_sysreg_s(SYS_ ## key ## KEYHI_EL1); \
475-
ctxt_sys_reg(ctxt, key ## KEYHI_EL1) = __val; \
476-
} while(0)
477-
478-
DECLARE_PER_CPU(struct kvm_cpu_context, kvm_hyp_ctxt);
479-
480-
static bool kvm_hyp_handle_ptrauth(struct kvm_vcpu *vcpu, u64 *exit_code)
481-
{
482-
struct kvm_cpu_context *ctxt;
483-
u64 enable = 0;
484-
485-
if (!vcpu_has_ptrauth(vcpu))
486-
return false;
487-
488-
/*
489-
* NV requires us to handle API and APK independently, just in
490-
* case the hypervisor is totally nuts. Please barf >here<.
491-
*/
492-
if (vcpu_has_nv(vcpu) && !is_hyp_ctxt(vcpu)) {
493-
switch (ESR_ELx_EC(kvm_vcpu_get_esr(vcpu))) {
494-
case ESR_ELx_EC_PAC:
495-
if (!(__vcpu_sys_reg(vcpu, HCR_EL2) & HCR_API))
496-
return false;
497-
498-
enable |= HCR_API;
499-
break;
500-
501-
case ESR_ELx_EC_SYS64:
502-
if (!(__vcpu_sys_reg(vcpu, HCR_EL2) & HCR_APK))
503-
return false;
504-
505-
enable |= HCR_APK;
506-
break;
507-
}
508-
} else {
509-
enable = HCR_API | HCR_APK;
510-
}
511-
512-
ctxt = this_cpu_ptr(&kvm_hyp_ctxt);
513-
__ptrauth_save_key(ctxt, APIA);
514-
__ptrauth_save_key(ctxt, APIB);
515-
__ptrauth_save_key(ctxt, APDA);
516-
__ptrauth_save_key(ctxt, APDB);
517-
__ptrauth_save_key(ctxt, APGA);
518-
519-
520-
vcpu->arch.hcr_el2 |= enable;
521-
sysreg_clear_set(hcr_el2, 0, enable);
522-
523-
return true;
524-
}
525-
526451
static bool kvm_hyp_handle_cntpct(struct kvm_vcpu *vcpu)
527452
{
528453
struct arch_timer_context *ctxt;
@@ -610,9 +535,6 @@ static bool kvm_hyp_handle_sysreg(struct kvm_vcpu *vcpu, u64 *exit_code)
610535
__vgic_v3_perform_cpuif_access(vcpu) == 1)
611536
return true;
612537

613-
if (esr_is_ptrauth_trap(kvm_vcpu_get_esr(vcpu)))
614-
return kvm_hyp_handle_ptrauth(vcpu, exit_code);
615-
616538
if (kvm_hyp_handle_cntpct(vcpu))
617539
return true;
618540

arch/arm64/kvm/hyp/nvhe/switch.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,6 @@ static const exit_handler_fn hyp_exit_handlers[] = {
191191
[ESR_ELx_EC_IABT_LOW] = kvm_hyp_handle_iabt_low,
192192
[ESR_ELx_EC_DABT_LOW] = kvm_hyp_handle_dabt_low,
193193
[ESR_ELx_EC_WATCHPT_LOW] = kvm_hyp_handle_watchpt_low,
194-
[ESR_ELx_EC_PAC] = kvm_hyp_handle_ptrauth,
195194
[ESR_ELx_EC_MOPS] = kvm_hyp_handle_mops,
196195
};
197196

@@ -203,7 +202,6 @@ static const exit_handler_fn pvm_exit_handlers[] = {
203202
[ESR_ELx_EC_IABT_LOW] = kvm_hyp_handle_iabt_low,
204203
[ESR_ELx_EC_DABT_LOW] = kvm_hyp_handle_dabt_low,
205204
[ESR_ELx_EC_WATCHPT_LOW] = kvm_hyp_handle_watchpt_low,
206-
[ESR_ELx_EC_PAC] = kvm_hyp_handle_ptrauth,
207205
[ESR_ELx_EC_MOPS] = kvm_hyp_handle_mops,
208206
};
209207

arch/arm64/kvm/hyp/vhe/switch.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,8 @@ DEFINE_PER_CPU(unsigned long, kvm_hyp_vector);
4141
* - TGE: we want the guest to use EL1, which is incompatible with
4242
* this bit being set
4343
*
44-
* - API/APK: for hysterical raisins, we enable PAuth lazily, which
45-
* means that the guest's bits cannot be directly applied (we really
46-
* want to see the traps). Revisit this at some point.
44+
* - API/APK: they are already accounted for by vcpu_load(), and can
45+
* only take effect across a load/put cycle (such as ERET)
4746
*/
4847
#define NV_HCR_GUEST_EXCLUDE (HCR_TGE | HCR_API | HCR_APK)
4948

@@ -268,7 +267,6 @@ static const exit_handler_fn hyp_exit_handlers[] = {
268267
[ESR_ELx_EC_IABT_LOW] = kvm_hyp_handle_iabt_low,
269268
[ESR_ELx_EC_DABT_LOW] = kvm_hyp_handle_dabt_low,
270269
[ESR_ELx_EC_WATCHPT_LOW] = kvm_hyp_handle_watchpt_low,
271-
[ESR_ELx_EC_PAC] = kvm_hyp_handle_ptrauth,
272270
[ESR_ELx_EC_ERET] = kvm_hyp_handle_eret,
273271
[ESR_ELx_EC_MOPS] = kvm_hyp_handle_mops,
274272
};

0 commit comments

Comments
 (0)