Skip to content

Commit e6128a8

Browse files
ardbiesheuvelctmarinas
authored andcommitted
arm64: mm: Use 48-bit virtual addressing for the permanent ID map
Even though we support loading kernels anywhere in 48-bit addressable physical memory, we create the ID maps based on the number of levels that we happened to configure for the kernel VA and user VA spaces. The reason for this is that the PGD/PUD/PMD based classification of translation levels, along with the associated folding when the number of levels is less than 5, does not permit creating a page table hierarchy of a set number of levels. This means that, for instance, on 39-bit VA kernels we need to configure an additional level above PGD level on the fly, and 36-bit VA kernels still only support 47-bit virtual addressing with this trick applied. Now that we have a separate helper to populate page table hierarchies that does not define the levels in terms of PUDS/PMDS/etc at all, let's reuse it to create the permanent ID map with a fixed VA size of 48 bits. Signed-off-by: Ard Biesheuvel <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Catalin Marinas <[email protected]>
1 parent 97a6f43 commit e6128a8

File tree

5 files changed

+32
-32
lines changed

5 files changed

+32
-32
lines changed

arch/arm64/include/asm/kernel-pgtable.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@
3535
#define SWAPPER_PGTABLE_LEVELS (CONFIG_PGTABLE_LEVELS)
3636
#endif
3737

38+
#define IDMAP_VA_BITS 48
39+
#define IDMAP_LEVELS ARM64_HW_PGTABLE_LEVELS(IDMAP_VA_BITS)
40+
#define IDMAP_ROOT_LEVEL (4 - IDMAP_LEVELS)
3841

3942
/*
4043
* A relocatable kernel may execute from an address that differs from the one at

arch/arm64/kernel/head.S

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,11 @@ SYM_FUNC_START_LOCAL(__no_granule_support)
729729
SYM_FUNC_END(__no_granule_support)
730730

731731
SYM_FUNC_START_LOCAL(__primary_switch)
732+
mrs x1, tcr_el1
733+
mov x2, #64 - VA_BITS
734+
tcr_set_t0sz x1, x2
735+
msr tcr_el1, x1
736+
732737
adrp x1, reserved_pg_dir
733738
adrp x2, init_idmap_pg_dir
734739
bl __enable_mmu

arch/arm64/kvm/mmu.c

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1874,16 +1874,9 @@ int __init kvm_mmu_init(u32 *hyp_va_bits)
18741874
BUG_ON((hyp_idmap_start ^ (hyp_idmap_end - 1)) & PAGE_MASK);
18751875

18761876
/*
1877-
* The ID map may be configured to use an extended virtual address
1878-
* range. This is only the case if system RAM is out of range for the
1879-
* currently configured page size and VA_BITS_MIN, in which case we will
1880-
* also need the extended virtual range for the HYP ID map, or we won't
1881-
* be able to enable the EL2 MMU.
1882-
*
1883-
* However, in some cases the ID map may be configured for fewer than
1884-
* the number of VA bits used by the regular kernel stage 1. This
1885-
* happens when VA_BITS=52 and the kernel image is placed in PA space
1886-
* below 48 bits.
1877+
* The ID map is always configured for 48 bits of translation, which
1878+
* may be fewer than the number of VA bits used by the regular kernel
1879+
* stage 1, when VA_BITS=52.
18871880
*
18881881
* At EL2, there is only one TTBR register, and we can't switch between
18891882
* translation tables *and* update TCR_EL2.T0SZ at the same time. Bottom
@@ -1894,7 +1887,7 @@ int __init kvm_mmu_init(u32 *hyp_va_bits)
18941887
* 1 VA bits to assure that the hypervisor can both ID map its code page
18951888
* and map any kernel memory.
18961889
*/
1897-
idmap_bits = 64 - ((idmap_t0sz & TCR_T0SZ_MASK) >> TCR_T0SZ_OFFSET);
1890+
idmap_bits = IDMAP_VA_BITS;
18981891
kernel_bits = vabits_actual;
18991892
*hyp_va_bits = max(idmap_bits, kernel_bits);
19001893

arch/arm64/mm/mmu.c

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -757,22 +757,21 @@ static void __init map_kernel(pgd_t *pgdp)
757757
kasan_copy_shadow(pgdp);
758758
}
759759

760+
void __pi_map_range(u64 *pgd, u64 start, u64 end, u64 pa, pgprot_t prot,
761+
int level, pte_t *tbl, bool may_use_cont, u64 va_offset);
762+
763+
static u8 idmap_ptes[IDMAP_LEVELS - 1][PAGE_SIZE] __aligned(PAGE_SIZE) __ro_after_init,
764+
kpti_ptes[IDMAP_LEVELS - 1][PAGE_SIZE] __aligned(PAGE_SIZE) __ro_after_init;
765+
760766
static void __init create_idmap(void)
761767
{
762768
u64 start = __pa_symbol(__idmap_text_start);
763-
u64 size = __pa_symbol(__idmap_text_end) - start;
764-
pgd_t *pgd = idmap_pg_dir;
765-
u64 pgd_phys;
766-
767-
/* check if we need an additional level of translation */
768-
if (VA_BITS < 48 && idmap_t0sz < (64 - VA_BITS_MIN)) {
769-
pgd_phys = early_pgtable_alloc(PAGE_SHIFT);
770-
set_pgd(&idmap_pg_dir[start >> VA_BITS],
771-
__pgd(pgd_phys | P4D_TYPE_TABLE));
772-
pgd = __va(pgd_phys);
773-
}
774-
__create_pgd_mapping(pgd, start, start, size, PAGE_KERNEL_ROX,
775-
early_pgtable_alloc, 0);
769+
u64 end = __pa_symbol(__idmap_text_end);
770+
u64 ptep = __pa_symbol(idmap_ptes);
771+
772+
__pi_map_range(&ptep, start, end, start, PAGE_KERNEL_ROX,
773+
IDMAP_ROOT_LEVEL, (pte_t *)idmap_pg_dir, false,
774+
__phys_to_virt(ptep) - ptep);
776775

777776
if (IS_ENABLED(CONFIG_UNMAP_KERNEL_AT_EL0)) {
778777
extern u32 __idmap_kpti_flag;
@@ -782,8 +781,10 @@ static void __init create_idmap(void)
782781
* The KPTI G-to-nG conversion code needs a read-write mapping
783782
* of its synchronization flag in the ID map.
784783
*/
785-
__create_pgd_mapping(pgd, pa, pa, sizeof(u32), PAGE_KERNEL,
786-
early_pgtable_alloc, 0);
784+
ptep = __pa_symbol(kpti_ptes);
785+
__pi_map_range(&ptep, pa, pa + sizeof(u32), pa, PAGE_KERNEL,
786+
IDMAP_ROOT_LEVEL, (pte_t *)idmap_pg_dir, false,
787+
__phys_to_virt(ptep) - ptep);
787788
}
788789
}
789790

@@ -808,6 +809,7 @@ void __init paging_init(void)
808809
memblock_allow_resize();
809810

810811
create_idmap();
812+
idmap_t0sz = TCR_T0SZ(IDMAP_VA_BITS);
811813
}
812814

813815
#ifdef CONFIG_MEMORY_HOTPLUG

arch/arm64/mm/proc.S

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -421,20 +421,17 @@ SYM_FUNC_START(__cpu_setup)
421421
mair .req x17
422422
tcr .req x16
423423
mov_q mair, MAIR_EL1_SET
424-
mov_q tcr, TCR_TxSZ(VA_BITS) | TCR_CACHE_FLAGS | TCR_SMP_FLAGS | \
425-
TCR_TG_FLAGS | TCR_KASLR_FLAGS | TCR_ASID16 | \
426-
TCR_TBI0 | TCR_A1 | TCR_KASAN_SW_FLAGS | TCR_MTE_FLAGS
424+
mov_q tcr, TCR_T0SZ(IDMAP_VA_BITS) | TCR_T1SZ(VA_BITS) | TCR_CACHE_FLAGS | \
425+
TCR_SMP_FLAGS | TCR_TG_FLAGS | TCR_KASLR_FLAGS | TCR_ASID16 | \
426+
TCR_TBI0 | TCR_A1 | TCR_KASAN_SW_FLAGS | TCR_MTE_FLAGS
427427

428428
tcr_clear_errata_bits tcr, x9, x5
429429

430430
#ifdef CONFIG_ARM64_VA_BITS_52
431431
sub x9, xzr, x0
432432
add x9, x9, #64
433433
tcr_set_t1sz tcr, x9
434-
#else
435-
idmap_get_t0sz x9
436434
#endif
437-
tcr_set_t0sz tcr, x9
438435

439436
/*
440437
* Set the IPS bits in TCR_EL1.

0 commit comments

Comments
 (0)