Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

KVM: arm64: gic-v5: Add vgic-v5 save/restore hyp interface

Introduce the following hyp functions to save/restore GICv5 state:

* __vgic_v5_save_apr()
* __vgic_v5_restore_vmcr_apr()
* __vgic_v5_save_ppi_state() - no hypercall required
* __vgic_v5_restore_ppi_state() - no hypercall required
* __vgic_v5_save_state() - no hypercall required
* __vgic_v5_restore_state() - no hypercall required

Note that the functions tagged as not requiring hypercalls are always
called directly from the same context. They are either called via the
vgic_save_state()/vgic_restore_state() path when running with VHE, or
via __hyp_vgic_save_state()/__hyp_vgic_restore_state() otherwise. This
mimics how vgic_v3_save_state()/vgic_v3_restore_state() are
implemented.

Overall, the state of the following registers is saved/restored:

* ICC_ICSR_EL1
* ICH_APR_EL2
* ICH_PPI_ACTIVERx_EL2
* ICH_PPI_DVIRx_EL2
* ICH_PPI_ENABLERx_EL2
* ICH_PPI_PENDRx_EL2
* ICH_PPI_PRIORITYRx_EL2
* ICH_VMCR_EL2

All of these are saved/restored to/from the KVM vgic_v5 CPUIF shadow
state, with the exception of the PPI active, pending, and enable
state. The pending state is saved and restored from kvm_host_data as
any changes here need to be tracked and propagated back to the
vgic_irq shadow structures (coming in a future commit). Therefore, an
entry and an exit copy is required. The active and enable state is
restored from the vgic_v5 CPUIF, but is saved to kvm_host_data. Again,
this needs to by synced back into the shadow data structures.

The ICSR must be save/restored as this register is shared between host
and guest. Therefore, to avoid leaking host state to the guest, this
must be saved and restored. Moreover, as this can by used by the host
at any time, it must be save/restored eagerly. Note: the host state is
not preserved as the host should only use this register when
preemption is disabled.

As with GICv3, the VMCR is eagerly saved as this is required when
checking if interrupts can be injected or not, and therefore impacts
things such as WFI.

As part of restoring the ICH_VMCR_EL2 and ICH_APR_EL2, GICv3-compat
mode is also disabled by setting the ICH_VCTLR_EL2.V3 bit to 0. The
correspoinding GICv3-compat mode enable is part of the VMCR & APR
restore for a GICv3 guest as it only takes effect when actually
running a guest.

Co-authored-by: Timothy Hayes <timothy.hayes@arm.com>
Signed-off-by: Timothy Hayes <timothy.hayes@arm.com>
Signed-off-by: Sascha Bischoff <sascha.bischoff@arm.com>
Link: https://patch.msgid.link/20260319154937.3619520-17-sascha.bischoff@arm.com
Signed-off-by: Marc Zyngier <maz@kernel.org>

authored by

Sascha Bischoff
Timothy Hayes
and committed by
Marc Zyngier
af325e87 070543a8

+237 -2
+2
arch/arm64/include/asm/kvm_asm.h
··· 89 89 __KVM_HOST_SMCCC_FUNC___pkvm_vcpu_load, 90 90 __KVM_HOST_SMCCC_FUNC___pkvm_vcpu_put, 91 91 __KVM_HOST_SMCCC_FUNC___pkvm_tlb_flush_vmid, 92 + __KVM_HOST_SMCCC_FUNC___vgic_v5_save_apr, 93 + __KVM_HOST_SMCCC_FUNC___vgic_v5_restore_vmcr_apr, 92 94 }; 93 95 94 96 #define DECLARE_KVM_VHE_SYM(sym) extern char sym[]
+16
arch/arm64/include/asm/kvm_host.h
··· 800 800 801 801 /* Last vgic_irq part of the AP list recorded in an LR */ 802 802 struct vgic_irq *last_lr_irq; 803 + 804 + /* PPI state tracking for GICv5-based guests */ 805 + struct { 806 + /* 807 + * For tracking the PPI pending state, we need both the entry 808 + * state and exit state to correctly detect edges as it is 809 + * possible that an interrupt has been injected in software in 810 + * the interim. 811 + */ 812 + DECLARE_BITMAP(pendr_entry, VGIC_V5_NR_PRIVATE_IRQS); 813 + DECLARE_BITMAP(pendr_exit, VGIC_V5_NR_PRIVATE_IRQS); 814 + 815 + /* The saved state of the regs when leaving the guest */ 816 + DECLARE_BITMAP(activer_exit, VGIC_V5_NR_PRIVATE_IRQS); 817 + DECLARE_BITMAP(enabler_exit, VGIC_V5_NR_PRIVATE_IRQS); 818 + } vgic_v5_ppi_state; 803 819 }; 804 820 805 821 struct kvm_host_psci_config {
+9
arch/arm64/include/asm/kvm_hyp.h
··· 87 87 void __vgic_v3_restore_vmcr_aprs(struct vgic_v3_cpu_if *cpu_if); 88 88 int __vgic_v3_perform_cpuif_access(struct kvm_vcpu *vcpu); 89 89 90 + /* GICv5 */ 91 + void __vgic_v5_save_apr(struct vgic_v5_cpu_if *cpu_if); 92 + void __vgic_v5_restore_vmcr_apr(struct vgic_v5_cpu_if *cpu_if); 93 + /* No hypercalls for the following */ 94 + void __vgic_v5_save_ppi_state(struct vgic_v5_cpu_if *cpu_if); 95 + void __vgic_v5_restore_ppi_state(struct vgic_v5_cpu_if *cpu_if); 96 + void __vgic_v5_save_state(struct vgic_v5_cpu_if *cpu_if); 97 + void __vgic_v5_restore_state(struct vgic_v5_cpu_if *cpu_if); 98 + 90 99 #ifdef __KVM_NVHE_HYPERVISOR__ 91 100 void __timer_enable_traps(struct kvm_vcpu *vcpu); 92 101 void __timer_disable_traps(struct kvm_vcpu *vcpu);
+1 -1
arch/arm64/kvm/hyp/nvhe/Makefile
··· 26 26 hyp-main.o hyp-smp.o psci-relay.o early_alloc.o page_alloc.o \ 27 27 cache.o setup.o mm.o mem_protect.o sys_regs.o pkvm.o stacktrace.o ffa.o 28 28 hyp-obj-y += ../vgic-v3-sr.o ../aarch32.o ../vgic-v2-cpuif-proxy.o ../entry.o \ 29 - ../fpsimd.o ../hyp-entry.o ../exception.o ../pgtable.o 29 + ../fpsimd.o ../hyp-entry.o ../exception.o ../pgtable.o ../vgic-v5-sr.o 30 30 hyp-obj-y += ../../../kernel/smccc-call.o 31 31 hyp-obj-$(CONFIG_LIST_HARDENED) += list_debug.o 32 32 hyp-obj-y += $(lib-objs)
+16
arch/arm64/kvm/hyp/nvhe/hyp-main.c
··· 589 589 cpu_reg(host_ctxt, 1) = __pkvm_teardown_vm(handle); 590 590 } 591 591 592 + static void handle___vgic_v5_save_apr(struct kvm_cpu_context *host_ctxt) 593 + { 594 + DECLARE_REG(struct vgic_v5_cpu_if *, cpu_if, host_ctxt, 1); 595 + 596 + __vgic_v5_save_apr(kern_hyp_va(cpu_if)); 597 + } 598 + 599 + static void handle___vgic_v5_restore_vmcr_apr(struct kvm_cpu_context *host_ctxt) 600 + { 601 + DECLARE_REG(struct vgic_v5_cpu_if *, cpu_if, host_ctxt, 1); 602 + 603 + __vgic_v5_restore_vmcr_apr(kern_hyp_va(cpu_if)); 604 + } 605 + 592 606 typedef void (*hcall_t)(struct kvm_cpu_context *); 593 607 594 608 #define HANDLE_FUNC(x) [__KVM_HOST_SMCCC_FUNC_##x] = (hcall_t)handle_##x ··· 644 630 HANDLE_FUNC(__pkvm_vcpu_load), 645 631 HANDLE_FUNC(__pkvm_vcpu_put), 646 632 HANDLE_FUNC(__pkvm_tlb_flush_vmid), 633 + HANDLE_FUNC(__vgic_v5_save_apr), 634 + HANDLE_FUNC(__vgic_v5_restore_vmcr_apr), 647 635 }; 648 636 649 637 static void handle_host_hcall(struct kvm_cpu_context *host_ctxt)
+170
arch/arm64/kvm/hyp/vgic-v5-sr.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (C) 2025, 2026 - Arm Ltd 4 + */ 5 + 6 + #include <linux/irqchip/arm-gic-v5.h> 7 + 8 + #include <asm/kvm_hyp.h> 9 + 10 + void __vgic_v5_save_apr(struct vgic_v5_cpu_if *cpu_if) 11 + { 12 + cpu_if->vgic_apr = read_sysreg_s(SYS_ICH_APR_EL2); 13 + } 14 + 15 + static void __vgic_v5_compat_mode_disable(void) 16 + { 17 + sysreg_clear_set_s(SYS_ICH_VCTLR_EL2, ICH_VCTLR_EL2_V3, 0); 18 + isb(); 19 + } 20 + 21 + void __vgic_v5_restore_vmcr_apr(struct vgic_v5_cpu_if *cpu_if) 22 + { 23 + __vgic_v5_compat_mode_disable(); 24 + 25 + write_sysreg_s(cpu_if->vgic_vmcr, SYS_ICH_VMCR_EL2); 26 + write_sysreg_s(cpu_if->vgic_apr, SYS_ICH_APR_EL2); 27 + } 28 + 29 + void __vgic_v5_save_ppi_state(struct vgic_v5_cpu_if *cpu_if) 30 + { 31 + /* 32 + * The following code assumes that the bitmap storage that we have for 33 + * PPIs is either 64 (architected PPIs, only) or 128 bits (architected & 34 + * impdef PPIs). 35 + */ 36 + BUILD_BUG_ON(VGIC_V5_NR_PRIVATE_IRQS % 64); 37 + 38 + bitmap_write(host_data_ptr(vgic_v5_ppi_state)->activer_exit, 39 + read_sysreg_s(SYS_ICH_PPI_ACTIVER0_EL2), 0, 64); 40 + bitmap_write(host_data_ptr(vgic_v5_ppi_state)->enabler_exit, 41 + read_sysreg_s(SYS_ICH_PPI_ENABLER0_EL2), 0, 64); 42 + bitmap_write(host_data_ptr(vgic_v5_ppi_state)->pendr_exit, 43 + read_sysreg_s(SYS_ICH_PPI_PENDR0_EL2), 0, 64); 44 + 45 + cpu_if->vgic_ppi_priorityr[0] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR0_EL2); 46 + cpu_if->vgic_ppi_priorityr[1] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR1_EL2); 47 + cpu_if->vgic_ppi_priorityr[2] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR2_EL2); 48 + cpu_if->vgic_ppi_priorityr[3] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR3_EL2); 49 + cpu_if->vgic_ppi_priorityr[4] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR4_EL2); 50 + cpu_if->vgic_ppi_priorityr[5] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR5_EL2); 51 + cpu_if->vgic_ppi_priorityr[6] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR6_EL2); 52 + cpu_if->vgic_ppi_priorityr[7] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR7_EL2); 53 + 54 + if (VGIC_V5_NR_PRIVATE_IRQS == 128) { 55 + bitmap_write(host_data_ptr(vgic_v5_ppi_state)->activer_exit, 56 + read_sysreg_s(SYS_ICH_PPI_ACTIVER1_EL2), 64, 64); 57 + bitmap_write(host_data_ptr(vgic_v5_ppi_state)->enabler_exit, 58 + read_sysreg_s(SYS_ICH_PPI_ENABLER1_EL2), 64, 64); 59 + bitmap_write(host_data_ptr(vgic_v5_ppi_state)->pendr_exit, 60 + read_sysreg_s(SYS_ICH_PPI_PENDR1_EL2), 64, 64); 61 + 62 + cpu_if->vgic_ppi_priorityr[8] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR8_EL2); 63 + cpu_if->vgic_ppi_priorityr[9] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR9_EL2); 64 + cpu_if->vgic_ppi_priorityr[10] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR10_EL2); 65 + cpu_if->vgic_ppi_priorityr[11] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR11_EL2); 66 + cpu_if->vgic_ppi_priorityr[12] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR12_EL2); 67 + cpu_if->vgic_ppi_priorityr[13] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR13_EL2); 68 + cpu_if->vgic_ppi_priorityr[14] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR14_EL2); 69 + cpu_if->vgic_ppi_priorityr[15] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR15_EL2); 70 + } 71 + 72 + /* Now that we are done, disable DVI */ 73 + write_sysreg_s(0, SYS_ICH_PPI_DVIR0_EL2); 74 + write_sysreg_s(0, SYS_ICH_PPI_DVIR1_EL2); 75 + } 76 + 77 + void __vgic_v5_restore_ppi_state(struct vgic_v5_cpu_if *cpu_if) 78 + { 79 + DECLARE_BITMAP(pendr, VGIC_V5_NR_PRIVATE_IRQS); 80 + 81 + /* We assume 64 or 128 PPIs - see above comment */ 82 + BUILD_BUG_ON(VGIC_V5_NR_PRIVATE_IRQS % 64); 83 + 84 + /* Enable DVI so that the guest's interrupt config takes over */ 85 + write_sysreg_s(bitmap_read(cpu_if->vgic_ppi_dvir, 0, 64), 86 + SYS_ICH_PPI_DVIR0_EL2); 87 + 88 + write_sysreg_s(bitmap_read(cpu_if->vgic_ppi_activer, 0, 64), 89 + SYS_ICH_PPI_ACTIVER0_EL2); 90 + write_sysreg_s(bitmap_read(cpu_if->vgic_ppi_enabler, 0, 64), 91 + SYS_ICH_PPI_ENABLER0_EL2); 92 + 93 + /* Update the pending state of the NON-DVI'd PPIs, only */ 94 + bitmap_andnot(pendr, host_data_ptr(vgic_v5_ppi_state)->pendr_entry, 95 + cpu_if->vgic_ppi_dvir, VGIC_V5_NR_PRIVATE_IRQS); 96 + write_sysreg_s(bitmap_read(pendr, 0, 64), SYS_ICH_PPI_PENDR0_EL2); 97 + 98 + write_sysreg_s(cpu_if->vgic_ppi_priorityr[0], 99 + SYS_ICH_PPI_PRIORITYR0_EL2); 100 + write_sysreg_s(cpu_if->vgic_ppi_priorityr[1], 101 + SYS_ICH_PPI_PRIORITYR1_EL2); 102 + write_sysreg_s(cpu_if->vgic_ppi_priorityr[2], 103 + SYS_ICH_PPI_PRIORITYR2_EL2); 104 + write_sysreg_s(cpu_if->vgic_ppi_priorityr[3], 105 + SYS_ICH_PPI_PRIORITYR3_EL2); 106 + write_sysreg_s(cpu_if->vgic_ppi_priorityr[4], 107 + SYS_ICH_PPI_PRIORITYR4_EL2); 108 + write_sysreg_s(cpu_if->vgic_ppi_priorityr[5], 109 + SYS_ICH_PPI_PRIORITYR5_EL2); 110 + write_sysreg_s(cpu_if->vgic_ppi_priorityr[6], 111 + SYS_ICH_PPI_PRIORITYR6_EL2); 112 + write_sysreg_s(cpu_if->vgic_ppi_priorityr[7], 113 + SYS_ICH_PPI_PRIORITYR7_EL2); 114 + 115 + if (VGIC_V5_NR_PRIVATE_IRQS == 128) { 116 + /* Enable DVI so that the guest's interrupt config takes over */ 117 + write_sysreg_s(bitmap_read(cpu_if->vgic_ppi_dvir, 64, 64), 118 + SYS_ICH_PPI_DVIR1_EL2); 119 + 120 + write_sysreg_s(bitmap_read(cpu_if->vgic_ppi_activer, 64, 64), 121 + SYS_ICH_PPI_ACTIVER1_EL2); 122 + write_sysreg_s(bitmap_read(cpu_if->vgic_ppi_enabler, 64, 64), 123 + SYS_ICH_PPI_ENABLER1_EL2); 124 + write_sysreg_s(bitmap_read(pendr, 64, 64), 125 + SYS_ICH_PPI_PENDR1_EL2); 126 + 127 + write_sysreg_s(cpu_if->vgic_ppi_priorityr[8], 128 + SYS_ICH_PPI_PRIORITYR8_EL2); 129 + write_sysreg_s(cpu_if->vgic_ppi_priorityr[9], 130 + SYS_ICH_PPI_PRIORITYR9_EL2); 131 + write_sysreg_s(cpu_if->vgic_ppi_priorityr[10], 132 + SYS_ICH_PPI_PRIORITYR10_EL2); 133 + write_sysreg_s(cpu_if->vgic_ppi_priorityr[11], 134 + SYS_ICH_PPI_PRIORITYR11_EL2); 135 + write_sysreg_s(cpu_if->vgic_ppi_priorityr[12], 136 + SYS_ICH_PPI_PRIORITYR12_EL2); 137 + write_sysreg_s(cpu_if->vgic_ppi_priorityr[13], 138 + SYS_ICH_PPI_PRIORITYR13_EL2); 139 + write_sysreg_s(cpu_if->vgic_ppi_priorityr[14], 140 + SYS_ICH_PPI_PRIORITYR14_EL2); 141 + write_sysreg_s(cpu_if->vgic_ppi_priorityr[15], 142 + SYS_ICH_PPI_PRIORITYR15_EL2); 143 + } else { 144 + write_sysreg_s(0, SYS_ICH_PPI_DVIR1_EL2); 145 + 146 + write_sysreg_s(0, SYS_ICH_PPI_ACTIVER1_EL2); 147 + write_sysreg_s(0, SYS_ICH_PPI_ENABLER1_EL2); 148 + write_sysreg_s(0, SYS_ICH_PPI_PENDR1_EL2); 149 + 150 + write_sysreg_s(0, SYS_ICH_PPI_PRIORITYR8_EL2); 151 + write_sysreg_s(0, SYS_ICH_PPI_PRIORITYR9_EL2); 152 + write_sysreg_s(0, SYS_ICH_PPI_PRIORITYR10_EL2); 153 + write_sysreg_s(0, SYS_ICH_PPI_PRIORITYR11_EL2); 154 + write_sysreg_s(0, SYS_ICH_PPI_PRIORITYR12_EL2); 155 + write_sysreg_s(0, SYS_ICH_PPI_PRIORITYR13_EL2); 156 + write_sysreg_s(0, SYS_ICH_PPI_PRIORITYR14_EL2); 157 + write_sysreg_s(0, SYS_ICH_PPI_PRIORITYR15_EL2); 158 + } 159 + } 160 + 161 + void __vgic_v5_save_state(struct vgic_v5_cpu_if *cpu_if) 162 + { 163 + cpu_if->vgic_vmcr = read_sysreg_s(SYS_ICH_VMCR_EL2); 164 + cpu_if->vgic_icsr = read_sysreg_s(SYS_ICC_ICSR_EL1); 165 + } 166 + 167 + void __vgic_v5_restore_state(struct vgic_v5_cpu_if *cpu_if) 168 + { 169 + write_sysreg_s(cpu_if->vgic_icsr, SYS_ICC_ICSR_EL1); 170 + }
+1 -1
arch/arm64/kvm/hyp/vhe/Makefile
··· 10 10 11 11 obj-y := timer-sr.o sysreg-sr.o debug-sr.o switch.o tlb.o 12 12 obj-y += ../vgic-v3-sr.o ../aarch32.o ../vgic-v2-cpuif-proxy.o ../entry.o \ 13 - ../fpsimd.o ../hyp-entry.o ../exception.o 13 + ../fpsimd.o ../hyp-entry.o ../exception.o ../vgic-v5-sr.o
+22
include/kvm/arm_vgic.h
··· 428 428 unsigned int used_lrs; 429 429 }; 430 430 431 + struct vgic_v5_cpu_if { 432 + u64 vgic_apr; 433 + u64 vgic_vmcr; 434 + 435 + /* PPI register state */ 436 + DECLARE_BITMAP(vgic_ppi_dvir, VGIC_V5_NR_PRIVATE_IRQS); 437 + DECLARE_BITMAP(vgic_ppi_activer, VGIC_V5_NR_PRIVATE_IRQS); 438 + DECLARE_BITMAP(vgic_ppi_enabler, VGIC_V5_NR_PRIVATE_IRQS); 439 + /* We have one byte (of which 5 bits are used) per PPI for priority */ 440 + u64 vgic_ppi_priorityr[VGIC_V5_NR_PRIVATE_IRQS / 8]; 441 + 442 + /* 443 + * The ICSR is re-used across host and guest, and hence it needs to be 444 + * saved/restored. Only one copy is required as the host should block 445 + * preemption between executing GIC CDRCFG and acccessing the 446 + * ICC_ICSR_EL1. A guest, of course, can never guarantee this, and hence 447 + * it is the hyp's responsibility to keep the state constistent. 448 + */ 449 + u64 vgic_icsr; 450 + }; 451 + 431 452 /* What PPI capabilities does a GICv5 host have */ 432 453 struct vgic_v5_ppi_caps { 433 454 DECLARE_BITMAP(impl_ppi_mask, VGIC_V5_NR_PRIVATE_IRQS); ··· 459 438 union { 460 439 struct vgic_v2_cpu_if vgic_v2; 461 440 struct vgic_v3_cpu_if vgic_v3; 441 + struct vgic_v5_cpu_if vgic_v5; 462 442 }; 463 443 464 444 struct vgic_irq *private_irqs;