Skip to content

Commit bd412e2

Browse files
ryanhrobMarc Zyngier
authored andcommitted
KVM: arm64: Use LPA2 page-tables for stage2 and hyp stage1
Implement a simple policy whereby if the HW supports FEAT_LPA2 for the page size we are using, always use LPA2-style page-tables for stage 2 and hyp stage 1 (assuming an nvhe hyp), regardless of the VMM-requested IPA size or HW-implemented PA size. When in use we can now support up to 52-bit IPA and PA sizes. We use the previously created cpu feature to track whether LPA2 is supported for deciding whether to use the LPA2 or classic pte format. Note that FEAT_LPA2 brings support for bigger block mappings (512GB with 4KB, 64GB with 16KB). We explicitly don't enable these in the library because stage2_apply_range() works on batch sizes of the largest used block mapping, and increasing the size of the batch would lead to soft lockups. See commit 5994bc9 ("KVM: arm64: Limit stage2_apply_range() batch size to largest block"). With the addition of LPA2 support in the hypervisor, the PA size supported by the HW must be capped with a runtime decision, rather than simply using a compile-time decision based on PA_BITS. For example, on a system that advertises 52 bit PA but does not support FEAT_LPA2, A 4KB or 16KB kernel compiled with LPA2 support must still limit the PA size to 48 bits. Therefore, move the insertion of the PS field into TCR_EL2 out of __kvm_hyp_init assembly code and instead do it in cpu_prepare_hyp_mode() where the rest of TCR_EL2 is prepared. This allows us to figure out PS with kvm_get_parange(), which has the appropriate logic to ensure the above requirement. (and the PS field of VTCR_EL2 is already populated this way). Reviewed-by: Oliver Upton <oliver.upton@linux.dev> Signed-off-by: Ryan Roberts <ryan.roberts@arm.com> Signed-off-by: Marc Zyngier <maz@kernel.org> Link: https://lore.kernel.org/r/20231127111737.1897081-8-ryan.roberts@arm.com
1 parent d4fbbb2 commit bd412e2

4 files changed

Lines changed: 54 additions & 19 deletions

File tree

arch/arm64/include/asm/kvm_pgtable.h

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,24 @@
2525
#define KVM_PGTABLE_MIN_BLOCK_LEVEL 2U
2626
#endif
2727

28-
#define kvm_lpa2_is_enabled() false
28+
#define kvm_lpa2_is_enabled() system_supports_lpa2()
29+
30+
static inline u64 kvm_get_parange_max(void)
31+
{
32+
if (kvm_lpa2_is_enabled() ||
33+
(IS_ENABLED(CONFIG_ARM64_PA_BITS_52) && PAGE_SHIFT == 16))
34+
return ID_AA64MMFR0_EL1_PARANGE_52;
35+
else
36+
return ID_AA64MMFR0_EL1_PARANGE_48;
37+
}
2938

3039
static inline u64 kvm_get_parange(u64 mmfr0)
3140
{
41+
u64 parange_max = kvm_get_parange_max();
3242
u64 parange = cpuid_feature_extract_unsigned_field(mmfr0,
3343
ID_AA64MMFR0_EL1_PARANGE_SHIFT);
34-
if (parange > ID_AA64MMFR0_EL1_PARANGE_MAX)
35-
parange = ID_AA64MMFR0_EL1_PARANGE_MAX;
44+
if (parange > parange_max)
45+
parange = parange_max;
3646

3747
return parange;
3848
}
@@ -43,6 +53,8 @@ typedef u64 kvm_pte_t;
4353

4454
#define KVM_PTE_ADDR_MASK GENMASK(47, PAGE_SHIFT)
4555
#define KVM_PTE_ADDR_51_48 GENMASK(15, 12)
56+
#define KVM_PTE_ADDR_MASK_LPA2 GENMASK(49, PAGE_SHIFT)
57+
#define KVM_PTE_ADDR_51_50_LPA2 GENMASK(9, 8)
4658

4759
#define KVM_PHYS_INVALID (-1ULL)
4860

@@ -53,21 +65,34 @@ static inline bool kvm_pte_valid(kvm_pte_t pte)
5365

5466
static inline u64 kvm_pte_to_phys(kvm_pte_t pte)
5567
{
56-
u64 pa = pte & KVM_PTE_ADDR_MASK;
57-
58-
if (PAGE_SHIFT == 16)
59-
pa |= FIELD_GET(KVM_PTE_ADDR_51_48, pte) << 48;
68+
u64 pa;
69+
70+
if (kvm_lpa2_is_enabled()) {
71+
pa = pte & KVM_PTE_ADDR_MASK_LPA2;
72+
pa |= FIELD_GET(KVM_PTE_ADDR_51_50_LPA2, pte) << 50;
73+
} else {
74+
pa = pte & KVM_PTE_ADDR_MASK;
75+
if (PAGE_SHIFT == 16)
76+
pa |= FIELD_GET(KVM_PTE_ADDR_51_48, pte) << 48;
77+
}
6078

6179
return pa;
6280
}
6381

6482
static inline kvm_pte_t kvm_phys_to_pte(u64 pa)
6583
{
66-
kvm_pte_t pte = pa & KVM_PTE_ADDR_MASK;
67-
68-
if (PAGE_SHIFT == 16) {
69-
pa &= GENMASK(51, 48);
70-
pte |= FIELD_PREP(KVM_PTE_ADDR_51_48, pa >> 48);
84+
kvm_pte_t pte;
85+
86+
if (kvm_lpa2_is_enabled()) {
87+
pte = pa & KVM_PTE_ADDR_MASK_LPA2;
88+
pa &= GENMASK(51, 50);
89+
pte |= FIELD_PREP(KVM_PTE_ADDR_51_50_LPA2, pa >> 50);
90+
} else {
91+
pte = pa & KVM_PTE_ADDR_MASK;
92+
if (PAGE_SHIFT == 16) {
93+
pa &= GENMASK(51, 48);
94+
pte |= FIELD_PREP(KVM_PTE_ADDR_51_48, pa >> 48);
95+
}
7196
}
7297

7398
return pte;

arch/arm64/kvm/arm.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1837,6 +1837,7 @@ static int kvm_init_vector_slots(void)
18371837
static void __init cpu_prepare_hyp_mode(int cpu, u32 hyp_va_bits)
18381838
{
18391839
struct kvm_nvhe_init_params *params = per_cpu_ptr_nvhe_sym(kvm_init_params, cpu);
1840+
u64 mmfr0 = read_sanitised_ftr_reg(SYS_ID_AA64MMFR0_EL1);
18401841
unsigned long tcr;
18411842

18421843
/*
@@ -1859,6 +1860,10 @@ static void __init cpu_prepare_hyp_mode(int cpu, u32 hyp_va_bits)
18591860
}
18601861
tcr &= ~TCR_T0SZ_MASK;
18611862
tcr |= TCR_T0SZ(hyp_va_bits);
1863+
tcr &= ~TCR_EL2_PS_MASK;
1864+
tcr |= FIELD_PREP(TCR_EL2_PS_MASK, kvm_get_parange(mmfr0));
1865+
if (kvm_lpa2_is_enabled())
1866+
tcr |= TCR_EL2_DS;
18621867
params->tcr_el2 = tcr;
18631868

18641869
params->pgd_pa = kvm_mmu_get_httbr();

arch/arm64/kvm/hyp/nvhe/hyp-init.S

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,7 @@ alternative_if ARM64_HAS_CNP
122122
alternative_else_nop_endif
123123
msr ttbr0_el2, x2
124124

125-
/*
126-
* Set the PS bits in TCR_EL2.
127-
*/
128125
ldr x0, [x0, #NVHE_INIT_TCR_EL2]
129-
tcr_compute_pa_size x0, #TCR_EL2_PS_SHIFT, x1, x2
130126
msr tcr_el2, x0
131127

132128
isb

arch/arm64/kvm/hyp/pgtable.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,10 @@ static bool kvm_pgtable_walk_skip_cmo(const struct kvm_pgtable_visit_ctx *ctx)
7979

8080
static bool kvm_phys_is_valid(u64 phys)
8181
{
82-
return phys < BIT(id_aa64mmfr0_parange_to_phys_shift(ID_AA64MMFR0_EL1_PARANGE_MAX));
82+
u64 parange_max = kvm_get_parange_max();
83+
u8 shift = id_aa64mmfr0_parange_to_phys_shift(parange_max);
84+
85+
return phys < BIT(shift);
8386
}
8487

8588
static bool kvm_block_mapping_supported(const struct kvm_pgtable_visit_ctx *ctx, u64 phys)
@@ -408,7 +411,8 @@ static int hyp_set_prot_attr(enum kvm_pgtable_prot prot, kvm_pte_t *ptep)
408411
}
409412

410413
attr |= FIELD_PREP(KVM_PTE_LEAF_ATTR_LO_S1_AP, ap);
411-
attr |= FIELD_PREP(KVM_PTE_LEAF_ATTR_LO_S1_SH, sh);
414+
if (!kvm_lpa2_is_enabled())
415+
attr |= FIELD_PREP(KVM_PTE_LEAF_ATTR_LO_S1_SH, sh);
412416
attr |= KVM_PTE_LEAF_ATTR_LO_S1_AF;
413417
attr |= prot & KVM_PTE_LEAF_ATTR_HI_SW;
414418
*ptep = attr;
@@ -654,6 +658,9 @@ u64 kvm_get_vtcr(u64 mmfr0, u64 mmfr1, u32 phys_shift)
654658
vtcr |= VTCR_EL2_HA;
655659
#endif /* CONFIG_ARM64_HW_AFDBM */
656660

661+
if (kvm_lpa2_is_enabled())
662+
vtcr |= VTCR_EL2_DS;
663+
657664
/* Set the vmid bits */
658665
vtcr |= (get_vmid_bits(mmfr1) == 16) ?
659666
VTCR_EL2_VS_16BIT :
@@ -711,7 +718,9 @@ static int stage2_set_prot_attr(struct kvm_pgtable *pgt, enum kvm_pgtable_prot p
711718
if (prot & KVM_PGTABLE_PROT_W)
712719
attr |= KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W;
713720

714-
attr |= FIELD_PREP(KVM_PTE_LEAF_ATTR_LO_S2_SH, sh);
721+
if (!kvm_lpa2_is_enabled())
722+
attr |= FIELD_PREP(KVM_PTE_LEAF_ATTR_LO_S2_SH, sh);
723+
715724
attr |= KVM_PTE_LEAF_ATTR_LO_S2_AF;
716725
attr |= prot & KVM_PTE_LEAF_ATTR_HI_SW;
717726
*ptep = attr;

0 commit comments

Comments
 (0)