]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
KVM: arm64: Don't feed uninitialised data to HCR_EL2
authorMarc Zyngier <maz@kernel.org>
Tue, 22 Apr 2025 12:26:12 +0000 (13:26 +0100)
committerMarc Zyngier <maz@kernel.org>
Wed, 14 May 2025 09:43:50 +0000 (10:43 +0100)
When the guest executes an AT S1E{0,1} from EL2, and that its
HCR_EL2.{E2H,TGE}=={1,1}, then this is a pure S1 translation
that doesn't involve a guest-supplied S2, and the full S1
context is already in place. This allows us to take a shortcut
and avoid save/restoring a bunch of registers.

However, we set HCR_EL2 to a value suitable for the use of AT
in guest context. And we do so by using the value that we saved.
Or not. In the case described above, we restore whatever junk
was on the stack, and carry on with it until the next entry.

Needless to say, this is completely broken.

But this also triggers the realisation that saving HCR_EL2 is
a bit pointless. We are always in host context at the point where
reach this code, and what we program to enter the guest is a known
value (vcpu->arch.hcr_el2).

Drop the pointless save/restore, and wrap the AT operations with
writes that switch between guest and host values for HCR_EL2.

Reported-by: D Scott Phillips <scott@os.amperecomputing.com>
Link: https://lore.kernel.org/r/20250422122612.2675672-4-maz@kernel.org
Signed-off-by: Marc Zyngier <maz@kernel.org>
arch/arm64/kvm/at.c

index c40583edebc4f13a9e9fdeb002cfccfe446c62c3..7a5267f43b51f83108c0e6fe2d421e9bcb2b477a 100644 (file)
@@ -492,7 +492,6 @@ struct mmu_config {
        u64     sctlr;
        u64     vttbr;
        u64     vtcr;
-       u64     hcr;
 };
 
 static void __mmu_config_save(struct mmu_config *config)
@@ -515,13 +514,10 @@ static void __mmu_config_save(struct mmu_config *config)
        config->sctlr   = read_sysreg_el1(SYS_SCTLR);
        config->vttbr   = read_sysreg(vttbr_el2);
        config->vtcr    = read_sysreg(vtcr_el2);
-       config->hcr     = read_sysreg(hcr_el2);
 }
 
 static void __mmu_config_restore(struct mmu_config *config)
 {
-       write_sysreg(config->hcr,       hcr_el2);
-
        /*
         * ARM errata 1165522 and 1530923 require TGE to be 1 before
         * we update the guest state.
@@ -1271,8 +1267,8 @@ static u64 __kvm_at_s1e01_fast(struct kvm_vcpu *vcpu, u32 op, u64 vaddr)
        __load_stage2(mmu, mmu->arch);
 
 skip_mmu_switch:
-       /* Clear TGE, enable S2 translation, we're rolling */
-       write_sysreg((config.hcr & ~HCR_TGE) | HCR_VM,  hcr_el2);
+       /* Temporarily switch back to guest context */
+       write_sysreg(vcpu->arch.hcr_el2, hcr_el2);
        isb();
 
        switch (op) {
@@ -1304,6 +1300,8 @@ skip_mmu_switch:
        if (!fail)
                par = read_sysreg_par();
 
+       write_sysreg(HCR_HOST_VHE_FLAGS, hcr_el2);
+
        if (!(vcpu_el2_e2h_is_set(vcpu) && vcpu_el2_tge_is_set(vcpu)))
                __mmu_config_restore(&config);