|
Pablo Greco |
cb7c71 |
From 5b7a08e0ea5bb51f886f2f5e2373de2bf5981dd2 Mon Sep 17 00:00:00 2001
|
|
Pablo Greco |
cb7c71 |
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
|
Pablo Greco |
cb7c71 |
Date: Thu, 28 Nov 2019 20:58:05 +0100
|
|
Pablo Greco |
cb7c71 |
Subject: [PATCH 1/2] arm64: KVM: Invoke compute_layout() before alternatives
|
|
Pablo Greco |
cb7c71 |
are applied
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
compute_layout() is invoked as part of an alternative fixup under
|
|
Pablo Greco |
cb7c71 |
stop_machine(). This function invokes get_random_long() which acquires a
|
|
Pablo Greco |
cb7c71 |
sleeping lock on -RT which can not be acquired in this context.
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
Rename compute_layout() to kvm_compute_layout() and invoke it before
|
|
Pablo Greco |
cb7c71 |
stop_machine() applies the alternatives. Add a __init prefix to
|
|
Pablo Greco |
cb7c71 |
kvm_compute_layout() because the caller has it, too (and so the code can be
|
|
Pablo Greco |
cb7c71 |
discarded after boot).
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
Reviewed-by: James Morse <james.morse@arm.com>
|
|
Pablo Greco |
cb7c71 |
Acked-by: Marc Zyngier <maz@kernel.org>
|
|
Pablo Greco |
cb7c71 |
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
|
Pablo Greco |
cb7c71 |
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
|
|
Pablo Greco |
cb7c71 |
---
|
|
Pablo Greco |
cb7c71 |
arch/arm64/include/asm/kvm_mmu.h | 1 +
|
|
Pablo Greco |
cb7c71 |
arch/arm64/kernel/smp.c | 4 ++++
|
|
Pablo Greco |
cb7c71 |
arch/arm64/kvm/va_layout.c | 8 +-------
|
|
Pablo Greco |
cb7c71 |
3 files changed, 6 insertions(+), 7 deletions(-)
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h
|
|
Pablo Greco |
cb7c71 |
index befe37d..53d846f 100644
|
|
Pablo Greco |
cb7c71 |
--- a/arch/arm64/include/asm/kvm_mmu.h
|
|
Pablo Greco |
cb7c71 |
+++ b/arch/arm64/include/asm/kvm_mmu.h
|
|
Pablo Greco |
cb7c71 |
@@ -91,6 +91,7 @@
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
void kvm_update_va_mask(struct alt_instr *alt,
|
|
Pablo Greco |
cb7c71 |
__le32 *origptr, __le32 *updptr, int nr_inst);
|
|
Pablo Greco |
cb7c71 |
+void kvm_compute_layout(void);
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
static inline unsigned long __kern_hyp_va(unsigned long v)
|
|
Pablo Greco |
cb7c71 |
{
|
|
Pablo Greco |
cb7c71 |
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
|
|
Pablo Greco |
cb7c71 |
index dc9fe87..02d41ea 100644
|
|
Pablo Greco |
cb7c71 |
--- a/arch/arm64/kernel/smp.c
|
|
Pablo Greco |
cb7c71 |
+++ b/arch/arm64/kernel/smp.c
|
|
Pablo Greco |
cb7c71 |
@@ -31,6 +31,7 @@
|
|
Pablo Greco |
cb7c71 |
#include <linux/of.h>
|
|
Pablo Greco |
cb7c71 |
#include <linux/irq_work.h>
|
|
Pablo Greco |
cb7c71 |
#include <linux/kexec.h>
|
|
Pablo Greco |
cb7c71 |
+#include <linux/kvm_host.h>
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
#include <asm/alternative.h>
|
|
Pablo Greco |
cb7c71 |
#include <asm/atomic.h>
|
|
Pablo Greco |
cb7c71 |
@@ -39,6 +40,7 @@
|
|
Pablo Greco |
cb7c71 |
#include <asm/cputype.h>
|
|
Pablo Greco |
cb7c71 |
#include <asm/cpu_ops.h>
|
|
Pablo Greco |
cb7c71 |
#include <asm/daifflags.h>
|
|
Pablo Greco |
cb7c71 |
+#include <asm/kvm_mmu.h>
|
|
Pablo Greco |
cb7c71 |
#include <asm/mmu_context.h>
|
|
Pablo Greco |
cb7c71 |
#include <asm/numa.h>
|
|
Pablo Greco |
cb7c71 |
#include <asm/pgtable.h>
|
|
Pablo Greco |
cb7c71 |
@@ -408,6 +410,8 @@ static void __init hyp_mode_check(void)
|
|
Pablo Greco |
cb7c71 |
"CPU: CPUs started in inconsistent modes");
|
|
Pablo Greco |
cb7c71 |
else
|
|
Pablo Greco |
cb7c71 |
pr_info("CPU: All CPU(s) started at EL1\n");
|
|
Pablo Greco |
cb7c71 |
+ if (IS_ENABLED(CONFIG_KVM_ARM_HOST))
|
|
Pablo Greco |
cb7c71 |
+ kvm_compute_layout();
|
|
Pablo Greco |
cb7c71 |
}
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
void __init smp_cpus_done(unsigned int max_cpus)
|
|
Pablo Greco |
cb7c71 |
diff --git a/arch/arm64/kvm/va_layout.c b/arch/arm64/kvm/va_layout.c
|
|
Pablo Greco |
cb7c71 |
index 2cf7d4b..dab1fea 100644
|
|
Pablo Greco |
cb7c71 |
--- a/arch/arm64/kvm/va_layout.c
|
|
Pablo Greco |
cb7c71 |
+++ b/arch/arm64/kvm/va_layout.c
|
|
Pablo Greco |
cb7c71 |
@@ -22,7 +22,7 @@
|
|
Pablo Greco |
cb7c71 |
static u64 tag_val;
|
|
Pablo Greco |
cb7c71 |
static u64 va_mask;
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
-static void compute_layout(void)
|
|
Pablo Greco |
cb7c71 |
+__init void kvm_compute_layout(void)
|
|
Pablo Greco |
cb7c71 |
{
|
|
Pablo Greco |
cb7c71 |
phys_addr_t idmap_addr = __pa_symbol(__hyp_idmap_text_start);
|
|
Pablo Greco |
cb7c71 |
u64 hyp_va_msb;
|
|
Pablo Greco |
cb7c71 |
@@ -110,9 +110,6 @@ void __init kvm_update_va_mask(struct alt_instr *alt,
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
BUG_ON(nr_inst != 5);
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
- if (!has_vhe() && !va_mask)
|
|
Pablo Greco |
cb7c71 |
- compute_layout();
|
|
Pablo Greco |
cb7c71 |
-
|
|
Pablo Greco |
cb7c71 |
for (i = 0; i < nr_inst; i++) {
|
|
Pablo Greco |
cb7c71 |
u32 rd, rn, insn, oinsn;
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
@@ -156,9 +153,6 @@ void kvm_patch_vector_branch(struct alt_instr *alt,
|
|
Pablo Greco |
cb7c71 |
return;
|
|
Pablo Greco |
cb7c71 |
}
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
- if (!va_mask)
|
|
Pablo Greco |
cb7c71 |
- compute_layout();
|
|
Pablo Greco |
cb7c71 |
-
|
|
Pablo Greco |
cb7c71 |
/*
|
|
Pablo Greco |
cb7c71 |
* Compute HYP VA by using the same computation as kern_hyp_va()
|
|
Pablo Greco |
cb7c71 |
*/
|
|
Pablo Greco |
cb7c71 |
--
|
|
Pablo Greco |
cb7c71 |
1.8.3.1
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
From d2d093ca412b7fd66acd745d00e3ebd4764fe127 Mon Sep 17 00:00:00 2001
|
|
Pablo Greco |
cb7c71 |
From: Russell King <rmk+kernel@armlinux.org.uk>
|
|
Pablo Greco |
cb7c71 |
Date: Sat, 28 Dec 2019 11:57:14 +0000
|
|
Pablo Greco |
cb7c71 |
Subject: [PATCH 2/2] arm64: kvm: Fix IDMAP overlap with HYP VA
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
Booting 5.4 on LX2160A reveals that KVM is non-functional:
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
kvm: Limiting the IPA size due to kernel Virtual Address limit
|
|
Pablo Greco |
cb7c71 |
kvm [1]: IPA Size Limit: 43bits
|
|
Pablo Greco |
cb7c71 |
kvm [1]: IDMAP intersecting with HYP VA, unable to continue
|
|
Pablo Greco |
cb7c71 |
kvm [1]: error initializing Hyp mode: -22
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
Debugging shows:
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
kvm [1]: IDMAP page: 81a26000
|
|
Pablo Greco |
cb7c71 |
kvm [1]: HYP VA range: 0:22ffffffff
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
as RAM is located at:
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
80000000-fbdfffff : System RAM
|
|
Pablo Greco |
cb7c71 |
2080000000-237fffffff : System RAM
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
Comparing this with the same kernel on Armada 8040 shows:
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
kvm: Limiting the IPA size due to kernel Virtual Address limit
|
|
Pablo Greco |
cb7c71 |
kvm [1]: IPA Size Limit: 43bits
|
|
Pablo Greco |
cb7c71 |
kvm [1]: IDMAP page: 2a26000
|
|
Pablo Greco |
cb7c71 |
kvm [1]: HYP VA range: 4800000000:493fffffff
|
|
Pablo Greco |
cb7c71 |
...
|
|
Pablo Greco |
cb7c71 |
kvm [1]: Hyp mode initialized successfully
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
which indicates that hyp_va_msb is set, and is always set to the
|
|
Pablo Greco |
cb7c71 |
opposite value of the idmap page to avoid the overlap. This does not
|
|
Pablo Greco |
cb7c71 |
happen with the LX2160A.
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
Further debugging shows vabits_actual = 39, kva_msb = 38 on LX2160A and
|
|
Pablo Greco |
cb7c71 |
kva_msb = 33 on Armada 8040. Looking at the bit layout of the HYP VA,
|
|
Pablo Greco |
cb7c71 |
there is still one bit available for hyp_va_msb. Set this bit
|
|
Pablo Greco |
cb7c71 |
appropriately. This allows KVM to be functional on the LX2160A, but
|
|
Pablo Greco |
cb7c71 |
without any HYP VA randomisation:
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
kvm: Limiting the IPA size due to kernel Virtual Address limit
|
|
Pablo Greco |
cb7c71 |
kvm [1]: IPA Size Limit: 43bits
|
|
Pablo Greco |
cb7c71 |
kvm [1]: IDMAP page: 81a24000
|
|
Pablo Greco |
cb7c71 |
kvm [1]: HYP VA range: 4000000000:62ffffffff
|
|
Pablo Greco |
cb7c71 |
...
|
|
Pablo Greco |
cb7c71 |
kvm [1]: Hyp mode initialized successfully
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
Fixes: ed57cac83e05 ("arm64: KVM: Introduce EL2 VA randomisation")
|
|
Pablo Greco |
cb7c71 |
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
|
|
Pablo Greco |
cb7c71 |
[maz: small additional cleanups, preserved case where the tag
|
|
Pablo Greco |
cb7c71 |
is legitimately 0 and we can just use the mask, Fixes tag]
|
|
Pablo Greco |
cb7c71 |
Signed-off-by: Marc Zyngier <maz@kernel.org>
|
|
Pablo Greco |
cb7c71 |
Link: https://lore.kernel.org/r/E1ilAiY-0000MA-RG@rmk-PC.armlinux.org.uk
|
|
Pablo Greco |
cb7c71 |
---
|
|
Pablo Greco |
cb7c71 |
arch/arm64/kvm/va_layout.c | 56 +++++++++++++++++++++-------------------------
|
|
Pablo Greco |
cb7c71 |
1 file changed, 25 insertions(+), 31 deletions(-)
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
diff --git a/arch/arm64/kvm/va_layout.c b/arch/arm64/kvm/va_layout.c
|
|
Pablo Greco |
cb7c71 |
index dab1fea..a4f48c1 100644
|
|
Pablo Greco |
cb7c71 |
--- a/arch/arm64/kvm/va_layout.c
|
|
Pablo Greco |
cb7c71 |
+++ b/arch/arm64/kvm/va_layout.c
|
|
Pablo Greco |
cb7c71 |
@@ -13,52 +13,46 @@
|
|
Pablo Greco |
cb7c71 |
#include <asm/kvm_mmu.h>
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
/*
|
|
Pablo Greco |
cb7c71 |
- * The LSB of the random hyp VA tag or 0 if no randomization is used.
|
|
Pablo Greco |
cb7c71 |
+ * The LSB of the HYP VA tag
|
|
Pablo Greco |
cb7c71 |
*/
|
|
Pablo Greco |
cb7c71 |
static u8 tag_lsb;
|
|
Pablo Greco |
cb7c71 |
/*
|
|
Pablo Greco |
cb7c71 |
- * The random hyp VA tag value with the region bit if hyp randomization is used
|
|
Pablo Greco |
cb7c71 |
+ * The HYP VA tag value with the region bit
|
|
Pablo Greco |
cb7c71 |
*/
|
|
Pablo Greco |
cb7c71 |
static u64 tag_val;
|
|
Pablo Greco |
cb7c71 |
static u64 va_mask;
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
+/*
|
|
Pablo Greco |
cb7c71 |
+ * We want to generate a hyp VA with the following format (with V ==
|
|
Pablo Greco |
cb7c71 |
+ * vabits_actual):
|
|
Pablo Greco |
cb7c71 |
+ *
|
|
Pablo Greco |
cb7c71 |
+ * 63 ... V | V-1 | V-2 .. tag_lsb | tag_lsb - 1 .. 0
|
|
Pablo Greco |
cb7c71 |
+ * ---------------------------------------------------------
|
|
Pablo Greco |
cb7c71 |
+ * | 0000000 | hyp_va_msb | random tag | kern linear VA |
|
|
Pablo Greco |
cb7c71 |
+ * |--------- tag_val -----------|----- va_mask ---|
|
|
Pablo Greco |
cb7c71 |
+ *
|
|
Pablo Greco |
cb7c71 |
+ * which does not conflict with the idmap regions.
|
|
Pablo Greco |
cb7c71 |
+ */
|
|
Pablo Greco |
cb7c71 |
__init void kvm_compute_layout(void)
|
|
Pablo Greco |
cb7c71 |
{
|
|
Pablo Greco |
cb7c71 |
phys_addr_t idmap_addr = __pa_symbol(__hyp_idmap_text_start);
|
|
Pablo Greco |
cb7c71 |
u64 hyp_va_msb;
|
|
Pablo Greco |
cb7c71 |
- int kva_msb;
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
/* Where is my RAM region? */
|
|
Pablo Greco |
cb7c71 |
hyp_va_msb = idmap_addr & BIT(vabits_actual - 1);
|
|
Pablo Greco |
cb7c71 |
hyp_va_msb ^= BIT(vabits_actual - 1);
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
- kva_msb = fls64((u64)phys_to_virt(memblock_start_of_DRAM()) ^
|
|
Pablo Greco |
cb7c71 |
+ tag_lsb = fls64((u64)phys_to_virt(memblock_start_of_DRAM()) ^
|
|
Pablo Greco |
cb7c71 |
(u64)(high_memory - 1));
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
- if (kva_msb == (vabits_actual - 1)) {
|
|
Pablo Greco |
cb7c71 |
- /*
|
|
Pablo Greco |
cb7c71 |
- * No space in the address, let's compute the mask so
|
|
Pablo Greco |
cb7c71 |
- * that it covers (vabits_actual - 1) bits, and the region
|
|
Pablo Greco |
cb7c71 |
- * bit. The tag stays set to zero.
|
|
Pablo Greco |
cb7c71 |
- */
|
|
Pablo Greco |
cb7c71 |
- va_mask = BIT(vabits_actual - 1) - 1;
|
|
Pablo Greco |
cb7c71 |
- va_mask |= hyp_va_msb;
|
|
Pablo Greco |
cb7c71 |
- } else {
|
|
Pablo Greco |
cb7c71 |
- /*
|
|
Pablo Greco |
cb7c71 |
- * We do have some free bits to insert a random tag.
|
|
Pablo Greco |
cb7c71 |
- * Hyp VAs are now created from kernel linear map VAs
|
|
Pablo Greco |
cb7c71 |
- * using the following formula (with V == vabits_actual):
|
|
Pablo Greco |
cb7c71 |
- *
|
|
Pablo Greco |
cb7c71 |
- * 63 ... V | V-1 | V-2 .. tag_lsb | tag_lsb - 1 .. 0
|
|
Pablo Greco |
cb7c71 |
- * ---------------------------------------------------------
|
|
Pablo Greco |
cb7c71 |
- * | 0000000 | hyp_va_msb | random tag | kern linear VA |
|
|
Pablo Greco |
cb7c71 |
- */
|
|
Pablo Greco |
cb7c71 |
- tag_lsb = kva_msb;
|
|
Pablo Greco |
cb7c71 |
- va_mask = GENMASK_ULL(tag_lsb - 1, 0);
|
|
Pablo Greco |
cb7c71 |
- tag_val = get_random_long() & GENMASK_ULL(vabits_actual - 2, tag_lsb);
|
|
Pablo Greco |
cb7c71 |
- tag_val |= hyp_va_msb;
|
|
Pablo Greco |
cb7c71 |
- tag_val >>= tag_lsb;
|
|
Pablo Greco |
cb7c71 |
+ va_mask = GENMASK_ULL(tag_lsb - 1, 0);
|
|
Pablo Greco |
cb7c71 |
+ tag_val = hyp_va_msb;
|
|
Pablo Greco |
cb7c71 |
+
|
|
Pablo Greco |
cb7c71 |
+ if (tag_lsb != (vabits_actual - 1)) {
|
|
Pablo Greco |
cb7c71 |
+ /* We have some free bits to insert a random tag. */
|
|
Pablo Greco |
cb7c71 |
+ tag_val |= get_random_long() & GENMASK_ULL(vabits_actual - 2, tag_lsb);
|
|
Pablo Greco |
cb7c71 |
}
|
|
Pablo Greco |
cb7c71 |
+ tag_val >>= tag_lsb;
|
|
Pablo Greco |
cb7c71 |
}
|
|
Pablo Greco |
cb7c71 |
|
|
Pablo Greco |
cb7c71 |
static u32 compute_instruction(int n, u32 rd, u32 rn)
|
|
Pablo Greco |
cb7c71 |
@@ -117,11 +111,11 @@ void __init kvm_update_va_mask(struct alt_instr *alt,
|
|
Pablo Greco |
cb7c71 |
* VHE doesn't need any address translation, let's NOP
|
|
Pablo Greco |
cb7c71 |
* everything.
|
|
Pablo Greco |
cb7c71 |
*
|
|
Pablo Greco |
cb7c71 |
- * Alternatively, if we don't have any spare bits in
|
|
Pablo Greco |
cb7c71 |
- * the address, NOP everything after masking that
|
|
Pablo Greco |
cb7c71 |
- * kernel VA.
|
|
Pablo Greco |
cb7c71 |
+ * Alternatively, if the tag is zero (because the layout
|
|
Pablo Greco |
cb7c71 |
+ * dictates it and we don't have any spare bits in the
|
|
Pablo Greco |
cb7c71 |
+ * address), NOP everything after masking the kernel VA.
|
|
Pablo Greco |
cb7c71 |
*/
|
|
Pablo Greco |
cb7c71 |
- if (has_vhe() || (!tag_lsb && i > 0)) {
|
|
Pablo Greco |
cb7c71 |
+ if (has_vhe() || (!tag_val && i > 0)) {
|
|
Pablo Greco |
cb7c71 |
updptr[i] = cpu_to_le32(aarch64_insn_gen_nop());
|
|
Pablo Greco |
cb7c71 |
continue;
|
|
Pablo Greco |
cb7c71 |
}
|
|
Pablo Greco |
cb7c71 |
--
|
|
Pablo Greco |
cb7c71 |
1.8.3.1
|
|
Pablo Greco |
cb7c71 |
|