Guest LPCR depends on hardware type, and future changes will add
restrictions based on errata and guest MMU mode. Move this logic
to a common function and use it for the cases where the guest
wants to update its LPCR (or the LPCR of a nested guest).

This also adds a warning in other places that set or update LPCR
if we try to set something that would have been disallowed by
the filter, as a sanity check.

Reviewed-by: Fabiano Rosas <faro...@linux.ibm.com>
Signed-off-by: Nicholas Piggin <npig...@gmail.com>
---
 arch/powerpc/include/asm/kvm_book3s.h |  2 +
 arch/powerpc/kvm/book3s_hv.c          | 68 ++++++++++++++++++++-------
 arch/powerpc/kvm/book3s_hv_nested.c   |  8 +++-
 3 files changed, 59 insertions(+), 19 deletions(-)

diff --git a/arch/powerpc/include/asm/kvm_book3s.h 
b/arch/powerpc/include/asm/kvm_book3s.h
index 2f5f919f6cd3..c58121508157 100644
--- a/arch/powerpc/include/asm/kvm_book3s.h
+++ b/arch/powerpc/include/asm/kvm_book3s.h
@@ -258,6 +258,8 @@ extern long kvmppc_hv_get_dirty_log_hpt(struct kvm *kvm,
 extern void kvmppc_harvest_vpa_dirty(struct kvmppc_vpa *vpa,
                        struct kvm_memory_slot *memslot,
                        unsigned long *map);
+extern unsigned long kvmppc_filter_lpcr_hv(struct kvm *kvm,
+                       unsigned long lpcr);
 extern void kvmppc_update_lpcr(struct kvm *kvm, unsigned long lpcr,
                        unsigned long mask);
 extern void kvmppc_set_fscr(struct kvm_vcpu *vcpu, u64 fscr);
diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c
index 208a053c9adf..268e31c7e49c 100644
--- a/arch/powerpc/kvm/book3s_hv.c
+++ b/arch/powerpc/kvm/book3s_hv.c
@@ -1635,6 +1635,35 @@ static int kvm_arch_vcpu_ioctl_set_sregs_hv(struct 
kvm_vcpu *vcpu,
        return 0;
 }
 
+/*
+ * Enforce limits on guest LPCR values based on hardware availability,
+ * guest configuration, and possibly hypervisor support and security
+ * concerns.
+ */
+unsigned long kvmppc_filter_lpcr_hv(struct kvm *kvm, unsigned long lpcr)
+{
+       /* On POWER8 and above, userspace can modify AIL */
+       if (!cpu_has_feature(CPU_FTR_ARCH_207S))
+               lpcr &= ~LPCR_AIL;
+
+       /*
+        * On POWER9, allow userspace to enable large decrementer for the
+        * guest, whether or not the host has it enabled.
+        */
+       if (!cpu_has_feature(CPU_FTR_ARCH_300))
+               lpcr &= ~LPCR_LD;
+
+       return lpcr;
+}
+
+static void verify_lpcr(struct kvm *kvm, unsigned long lpcr)
+{
+       if (lpcr != kvmppc_filter_lpcr_hv(kvm, lpcr)) {
+               WARN_ONCE(1, "lpcr 0x%lx differs from filtered 0x%lx\n",
+                         lpcr, kvmppc_filter_lpcr_hv(kvm, lpcr));
+       }
+}
+
 static void kvmppc_set_lpcr(struct kvm_vcpu *vcpu, u64 new_lpcr,
                bool preserve_top32)
 {
@@ -1643,6 +1672,23 @@ static void kvmppc_set_lpcr(struct kvm_vcpu *vcpu, u64 
new_lpcr,
        u64 mask;
 
        spin_lock(&vc->lock);
+
+       /*
+        * Userspace can only modify
+        * DPFD (default prefetch depth), ILE (interrupt little-endian),
+        * TC (translation control), AIL (alternate interrupt location),
+        * LD (large decrementer).
+        * These are subject to restrictions from kvmppc_filter_lcpr_hv().
+        */
+       mask = LPCR_DPFD | LPCR_ILE | LPCR_TC | LPCR_AIL | LPCR_LD;
+
+       /* Broken 32-bit version of LPCR must not clear top bits */
+       if (preserve_top32)
+               mask &= 0xFFFFFFFF;
+
+       new_lpcr = kvmppc_filter_lpcr_hv(kvm,
+                       (vc->lpcr & ~mask) | (new_lpcr & mask));
+
        /*
         * If ILE (interrupt little-endian) has changed, update the
         * MSR_LE bit in the intr_msr for each vcpu in this vcore.
@@ -1661,25 +1707,8 @@ static void kvmppc_set_lpcr(struct kvm_vcpu *vcpu, u64 
new_lpcr,
                }
        }
 
-       /*
-        * Userspace can only modify DPFD (default prefetch depth),
-        * ILE (interrupt little-endian) and TC (translation control).
-        * On POWER8 and POWER9 userspace can also modify AIL (alt. interrupt 
loc.).
-        */
-       mask = LPCR_DPFD | LPCR_ILE | LPCR_TC;
-       if (cpu_has_feature(CPU_FTR_ARCH_207S))
-               mask |= LPCR_AIL;
-       /*
-        * On POWER9, allow userspace to enable large decrementer for the
-        * guest, whether or not the host has it enabled.
-        */
-       if (cpu_has_feature(CPU_FTR_ARCH_300))
-               mask |= LPCR_LD;
+       vc->lpcr = new_lpcr;
 
-       /* Broken 32-bit version of LPCR must not clear top bits */
-       if (preserve_top32)
-               mask &= 0xFFFFFFFF;
-       vc->lpcr = (vc->lpcr & ~mask) | (new_lpcr & mask);
        spin_unlock(&vc->lock);
 }
 
@@ -4644,8 +4673,10 @@ void kvmppc_update_lpcr(struct kvm *kvm, unsigned long 
lpcr, unsigned long mask)
                struct kvmppc_vcore *vc = kvm->arch.vcores[i];
                if (!vc)
                        continue;
+
                spin_lock(&vc->lock);
                vc->lpcr = (vc->lpcr & ~mask) | lpcr;
+               verify_lpcr(kvm, vc->lpcr);
                spin_unlock(&vc->lock);
                if (++cores_done >= kvm->arch.online_vcores)
                        break;
@@ -4973,6 +5004,7 @@ static int kvmppc_core_init_vm_hv(struct kvm *kvm)
                kvmppc_setup_partition_table(kvm);
        }
 
+       verify_lpcr(kvm, lpcr);
        kvm->arch.lpcr = lpcr;
 
        /* Initialization for future HPT resizes */
diff --git a/arch/powerpc/kvm/book3s_hv_nested.c 
b/arch/powerpc/kvm/book3s_hv_nested.c
index 3060e5deffc8..d14fe32f167b 100644
--- a/arch/powerpc/kvm/book3s_hv_nested.c
+++ b/arch/powerpc/kvm/book3s_hv_nested.c
@@ -151,7 +151,13 @@ static void sanitise_hv_regs(struct kvm_vcpu *vcpu, struct 
hv_guest_state *hr)
         */
        mask = LPCR_DPFD | LPCR_ILE | LPCR_TC | LPCR_AIL | LPCR_LD |
                LPCR_LPES | LPCR_MER;
-       hr->lpcr = (vc->lpcr & ~mask) | (hr->lpcr & mask);
+
+       /*
+        * Additional filtering is required depending on hardware
+        * and configuration.
+        */
+       hr->lpcr = kvmppc_filter_lpcr_hv(vcpu->kvm,
+                       (vc->lpcr & ~mask) | (hr->lpcr & mask));
 
        /*
         * Don't let L1 enable features for L2 which we've disabled for L1,
-- 
2.23.0

Reply via email to