xref: /linux-6.15/arch/arm64/kernel/pi/map_kernel.c (revision 117c3b21)
197a6f43bSArd Biesheuvel // SPDX-License-Identifier: GPL-2.0-only
297a6f43bSArd Biesheuvel // Copyright 2023 Google LLC
397a6f43bSArd Biesheuvel // Author: Ard Biesheuvel <[email protected]>
497a6f43bSArd Biesheuvel 
597a6f43bSArd Biesheuvel #include <linux/init.h>
697a6f43bSArd Biesheuvel #include <linux/libfdt.h>
797a6f43bSArd Biesheuvel #include <linux/linkage.h>
897a6f43bSArd Biesheuvel #include <linux/types.h>
997a6f43bSArd Biesheuvel #include <linux/sizes.h>
1097a6f43bSArd Biesheuvel #include <linux/string.h>
1197a6f43bSArd Biesheuvel 
1297a6f43bSArd Biesheuvel #include <asm/memory.h>
1397a6f43bSArd Biesheuvel #include <asm/pgalloc.h>
1497a6f43bSArd Biesheuvel #include <asm/pgtable.h>
1597a6f43bSArd Biesheuvel #include <asm/tlbflush.h>
1697a6f43bSArd Biesheuvel 
1797a6f43bSArd Biesheuvel #include "pi.h"
1897a6f43bSArd Biesheuvel 
1997a6f43bSArd Biesheuvel extern const u8 __eh_frame_start[], __eh_frame_end[];
2097a6f43bSArd Biesheuvel 
2197a6f43bSArd Biesheuvel extern void idmap_cpu_replace_ttbr1(void *pgdir);
2297a6f43bSArd Biesheuvel 
map_segment(pgd_t * pg_dir,u64 * pgd,u64 va_offset,void * start,void * end,pgprot_t prot,bool may_use_cont,int root_level)2397a6f43bSArd Biesheuvel static void __init map_segment(pgd_t *pg_dir, u64 *pgd, u64 va_offset,
2497a6f43bSArd Biesheuvel 			       void *start, void *end, pgprot_t prot,
2597a6f43bSArd Biesheuvel 			       bool may_use_cont, int root_level)
2697a6f43bSArd Biesheuvel {
2797a6f43bSArd Biesheuvel 	map_range(pgd, ((u64)start + va_offset) & ~PAGE_OFFSET,
2897a6f43bSArd Biesheuvel 		  ((u64)end + va_offset) & ~PAGE_OFFSET, (u64)start,
2997a6f43bSArd Biesheuvel 		  prot, root_level, (pte_t *)pg_dir, may_use_cont, 0);
3097a6f43bSArd Biesheuvel }
3197a6f43bSArd Biesheuvel 
unmap_segment(pgd_t * pg_dir,u64 va_offset,void * start,void * end,int root_level)3297a6f43bSArd Biesheuvel static void __init unmap_segment(pgd_t *pg_dir, u64 va_offset, void *start,
3397a6f43bSArd Biesheuvel 				 void *end, int root_level)
3497a6f43bSArd Biesheuvel {
3597a6f43bSArd Biesheuvel 	map_segment(pg_dir, NULL, va_offset, start, end, __pgprot(0),
3697a6f43bSArd Biesheuvel 		    false, root_level);
3797a6f43bSArd Biesheuvel }
3897a6f43bSArd Biesheuvel 
map_kernel(u64 kaslr_offset,u64 va_offset,int root_level)3997a6f43bSArd Biesheuvel static void __init map_kernel(u64 kaslr_offset, u64 va_offset, int root_level)
4097a6f43bSArd Biesheuvel {
4197a6f43bSArd Biesheuvel 	bool enable_scs = IS_ENABLED(CONFIG_UNWIND_PATCH_PAC_INTO_SCS);
4297a6f43bSArd Biesheuvel 	bool twopass = IS_ENABLED(CONFIG_RELOCATABLE);
4397a6f43bSArd Biesheuvel 	u64 pgdp = (u64)init_pg_dir + PAGE_SIZE;
4497a6f43bSArd Biesheuvel 	pgprot_t text_prot = PAGE_KERNEL_ROX;
4597a6f43bSArd Biesheuvel 	pgprot_t data_prot = PAGE_KERNEL;
4697a6f43bSArd Biesheuvel 	pgprot_t prot;
4797a6f43bSArd Biesheuvel 
4897a6f43bSArd Biesheuvel 	/*
4997a6f43bSArd Biesheuvel 	 * External debuggers may need to write directly to the text mapping to
5097a6f43bSArd Biesheuvel 	 * install SW breakpoints. Allow this (only) when explicitly requested
5197a6f43bSArd Biesheuvel 	 * with rodata=off.
5297a6f43bSArd Biesheuvel 	 */
5397a6f43bSArd Biesheuvel 	if (arm64_test_sw_feature_override(ARM64_SW_FEATURE_OVERRIDE_RODATA_OFF))
5497a6f43bSArd Biesheuvel 		text_prot = PAGE_KERNEL_EXEC;
5597a6f43bSArd Biesheuvel 
5697a6f43bSArd Biesheuvel 	/*
5797a6f43bSArd Biesheuvel 	 * We only enable the shadow call stack dynamically if we are running
5897a6f43bSArd Biesheuvel 	 * on a system that does not implement PAC or BTI. PAC and SCS provide
5997a6f43bSArd Biesheuvel 	 * roughly the same level of protection, and BTI relies on the PACIASP
6097a6f43bSArd Biesheuvel 	 * instructions serving as landing pads, preventing us from patching
6197a6f43bSArd Biesheuvel 	 * those instructions into something else.
6297a6f43bSArd Biesheuvel 	 */
6397a6f43bSArd Biesheuvel 	if (IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL) && cpu_has_pac())
6497a6f43bSArd Biesheuvel 		enable_scs = false;
6597a6f43bSArd Biesheuvel 
6697a6f43bSArd Biesheuvel 	if (IS_ENABLED(CONFIG_ARM64_BTI_KERNEL) && cpu_has_bti()) {
6797a6f43bSArd Biesheuvel 		enable_scs = false;
6897a6f43bSArd Biesheuvel 
6997a6f43bSArd Biesheuvel 		/*
7097a6f43bSArd Biesheuvel 		 * If we have a CPU that supports BTI and a kernel built for
7197a6f43bSArd Biesheuvel 		 * BTI then mark the kernel executable text as guarded pages
7297a6f43bSArd Biesheuvel 		 * now so we don't have to rewrite the page tables later.
7397a6f43bSArd Biesheuvel 		 */
7497a6f43bSArd Biesheuvel 		text_prot = __pgprot_modify(text_prot, PTE_GP, PTE_GP);
7597a6f43bSArd Biesheuvel 	}
7697a6f43bSArd Biesheuvel 
7797a6f43bSArd Biesheuvel 	/* Map all code read-write on the first pass if needed */
7897a6f43bSArd Biesheuvel 	twopass |= enable_scs;
7997a6f43bSArd Biesheuvel 	prot = twopass ? data_prot : text_prot;
8097a6f43bSArd Biesheuvel 
8197a6f43bSArd Biesheuvel 	map_segment(init_pg_dir, &pgdp, va_offset, _stext, _etext, prot,
8297a6f43bSArd Biesheuvel 		    !twopass, root_level);
8397a6f43bSArd Biesheuvel 	map_segment(init_pg_dir, &pgdp, va_offset, __start_rodata,
8497a6f43bSArd Biesheuvel 		    __inittext_begin, data_prot, false, root_level);
8597a6f43bSArd Biesheuvel 	map_segment(init_pg_dir, &pgdp, va_offset, __inittext_begin,
8697a6f43bSArd Biesheuvel 		    __inittext_end, prot, false, root_level);
8797a6f43bSArd Biesheuvel 	map_segment(init_pg_dir, &pgdp, va_offset, __initdata_begin,
8897a6f43bSArd Biesheuvel 		    __initdata_end, data_prot, false, root_level);
8997a6f43bSArd Biesheuvel 	map_segment(init_pg_dir, &pgdp, va_offset, _data, _end, data_prot,
9097a6f43bSArd Biesheuvel 		    true, root_level);
9197a6f43bSArd Biesheuvel 	dsb(ishst);
9297a6f43bSArd Biesheuvel 
9397a6f43bSArd Biesheuvel 	idmap_cpu_replace_ttbr1(init_pg_dir);
9497a6f43bSArd Biesheuvel 
9597a6f43bSArd Biesheuvel 	if (twopass) {
9697a6f43bSArd Biesheuvel 		if (IS_ENABLED(CONFIG_RELOCATABLE))
9797a6f43bSArd Biesheuvel 			relocate_kernel(kaslr_offset);
9897a6f43bSArd Biesheuvel 
9997a6f43bSArd Biesheuvel 		if (enable_scs) {
10097a6f43bSArd Biesheuvel 			scs_patch(__eh_frame_start + va_offset,
10197a6f43bSArd Biesheuvel 				  __eh_frame_end - __eh_frame_start);
10297a6f43bSArd Biesheuvel 			asm("ic ialluis");
10397a6f43bSArd Biesheuvel 
10497a6f43bSArd Biesheuvel 			dynamic_scs_is_enabled = true;
10597a6f43bSArd Biesheuvel 		}
10697a6f43bSArd Biesheuvel 
10797a6f43bSArd Biesheuvel 		/*
10897a6f43bSArd Biesheuvel 		 * Unmap the text region before remapping it, to avoid
10997a6f43bSArd Biesheuvel 		 * potential TLB conflicts when creating the contiguous
11097a6f43bSArd Biesheuvel 		 * descriptors.
11197a6f43bSArd Biesheuvel 		 */
11297a6f43bSArd Biesheuvel 		unmap_segment(init_pg_dir, va_offset, _stext, _etext,
11397a6f43bSArd Biesheuvel 			      root_level);
11497a6f43bSArd Biesheuvel 		dsb(ishst);
11597a6f43bSArd Biesheuvel 		isb();
11697a6f43bSArd Biesheuvel 		__tlbi(vmalle1);
11797a6f43bSArd Biesheuvel 		isb();
11897a6f43bSArd Biesheuvel 
11997a6f43bSArd Biesheuvel 		/*
12097a6f43bSArd Biesheuvel 		 * Remap these segments with different permissions
12197a6f43bSArd Biesheuvel 		 * No new page table allocations should be needed
12297a6f43bSArd Biesheuvel 		 */
12397a6f43bSArd Biesheuvel 		map_segment(init_pg_dir, NULL, va_offset, _stext, _etext,
12497a6f43bSArd Biesheuvel 			    text_prot, true, root_level);
12597a6f43bSArd Biesheuvel 		map_segment(init_pg_dir, NULL, va_offset, __inittext_begin,
12697a6f43bSArd Biesheuvel 			    __inittext_end, text_prot, false, root_level);
12797a6f43bSArd Biesheuvel 	}
128ba5b0333SArd Biesheuvel 
129ba5b0333SArd Biesheuvel 	/* Copy the root page table to its final location */
1309684ec18SArd Biesheuvel 	memcpy((void *)swapper_pg_dir + va_offset, init_pg_dir, PAGE_SIZE);
131ba5b0333SArd Biesheuvel 	dsb(ishst);
132ba5b0333SArd Biesheuvel 	idmap_cpu_replace_ttbr1(swapper_pg_dir);
13397a6f43bSArd Biesheuvel }
13497a6f43bSArd Biesheuvel 
set_ttbr0_for_lpa2(u64 ttbr)1359684ec18SArd Biesheuvel static void noinline __section(".idmap.text") set_ttbr0_for_lpa2(u64 ttbr)
1369684ec18SArd Biesheuvel {
1379684ec18SArd Biesheuvel 	u64 sctlr = read_sysreg(sctlr_el1);
1389684ec18SArd Biesheuvel 	u64 tcr = read_sysreg(tcr_el1) | TCR_DS;
13962cffa49SArd Biesheuvel 	u64 mmfr0 = read_sysreg(id_aa64mmfr0_el1);
14062cffa49SArd Biesheuvel 	u64 parange = cpuid_feature_extract_unsigned_field(mmfr0,
14162cffa49SArd Biesheuvel 							   ID_AA64MMFR0_EL1_PARANGE_SHIFT);
14262cffa49SArd Biesheuvel 
14362cffa49SArd Biesheuvel 	tcr &= ~TCR_IPS_MASK;
14462cffa49SArd Biesheuvel 	tcr |= parange << TCR_IPS_SHIFT;
1459684ec18SArd Biesheuvel 
1469684ec18SArd Biesheuvel 	asm("	msr	sctlr_el1, %0		;"
1479684ec18SArd Biesheuvel 	    "	isb				;"
1489684ec18SArd Biesheuvel 	    "   msr     ttbr0_el1, %1		;"
1499684ec18SArd Biesheuvel 	    "   msr     tcr_el1, %2		;"
1509684ec18SArd Biesheuvel 	    "	isb				;"
1519684ec18SArd Biesheuvel 	    "	tlbi    vmalle1			;"
1529684ec18SArd Biesheuvel 	    "	dsb     nsh			;"
1539684ec18SArd Biesheuvel 	    "	isb				;"
1549684ec18SArd Biesheuvel 	    "	msr     sctlr_el1, %3		;"
1559684ec18SArd Biesheuvel 	    "	isb				;"
1569684ec18SArd Biesheuvel 	    ::	"r"(sctlr & ~SCTLR_ELx_M), "r"(ttbr), "r"(tcr), "r"(sctlr));
1579684ec18SArd Biesheuvel }
1589684ec18SArd Biesheuvel 
remap_idmap_for_lpa2(void)1599684ec18SArd Biesheuvel static void __init remap_idmap_for_lpa2(void)
1609684ec18SArd Biesheuvel {
1619684ec18SArd Biesheuvel 	/* clear the bits that change meaning once LPA2 is turned on */
1629684ec18SArd Biesheuvel 	pteval_t mask = PTE_SHARED;
1639684ec18SArd Biesheuvel 
1649684ec18SArd Biesheuvel 	/*
1659684ec18SArd Biesheuvel 	 * We have to clear bits [9:8] in all block or page descriptors in the
1669684ec18SArd Biesheuvel 	 * initial ID map, as otherwise they will be (mis)interpreted as
1679684ec18SArd Biesheuvel 	 * physical address bits once we flick the LPA2 switch (TCR.DS). Since
1689684ec18SArd Biesheuvel 	 * we cannot manipulate live descriptors in that way without creating
1699684ec18SArd Biesheuvel 	 * potential TLB conflicts, let's create another temporary ID map in a
1709684ec18SArd Biesheuvel 	 * LPA2 compatible fashion, and update the initial ID map while running
1719684ec18SArd Biesheuvel 	 * from that.
1729684ec18SArd Biesheuvel 	 */
1739684ec18SArd Biesheuvel 	create_init_idmap(init_pg_dir, mask);
1749684ec18SArd Biesheuvel 	dsb(ishst);
1759684ec18SArd Biesheuvel 	set_ttbr0_for_lpa2((u64)init_pg_dir);
1769684ec18SArd Biesheuvel 
1779684ec18SArd Biesheuvel 	/*
1789684ec18SArd Biesheuvel 	 * Recreate the initial ID map with the same granularity as before.
1799684ec18SArd Biesheuvel 	 * Don't bother with the FDT, we no longer need it after this.
1809684ec18SArd Biesheuvel 	 */
1819684ec18SArd Biesheuvel 	memset(init_idmap_pg_dir, 0,
182ecc54006SZenghui Yu 	       (u64)init_idmap_pg_end - (u64)init_idmap_pg_dir);
1839684ec18SArd Biesheuvel 
1849684ec18SArd Biesheuvel 	create_init_idmap(init_idmap_pg_dir, mask);
1859684ec18SArd Biesheuvel 	dsb(ishst);
1869684ec18SArd Biesheuvel 
1879684ec18SArd Biesheuvel 	/* switch back to the updated initial ID map */
1889684ec18SArd Biesheuvel 	set_ttbr0_for_lpa2((u64)init_idmap_pg_dir);
1899684ec18SArd Biesheuvel 
1909684ec18SArd Biesheuvel 	/* wipe the temporary ID map from memory */
1919684ec18SArd Biesheuvel 	memset(init_pg_dir, 0, (u64)init_pg_end - (u64)init_pg_dir);
1929684ec18SArd Biesheuvel }
1939684ec18SArd Biesheuvel 
map_fdt(u64 fdt)19484b04d3eSArd Biesheuvel static void __init map_fdt(u64 fdt)
19584b04d3eSArd Biesheuvel {
19684b04d3eSArd Biesheuvel 	static u8 ptes[INIT_IDMAP_FDT_SIZE] __initdata __aligned(PAGE_SIZE);
19784b04d3eSArd Biesheuvel 	u64 efdt = fdt + MAX_FDT_SIZE;
19884b04d3eSArd Biesheuvel 	u64 ptep = (u64)ptes;
19984b04d3eSArd Biesheuvel 
20084b04d3eSArd Biesheuvel 	/*
20184b04d3eSArd Biesheuvel 	 * Map up to MAX_FDT_SIZE bytes, but avoid overlap with
20284b04d3eSArd Biesheuvel 	 * the kernel image.
20384b04d3eSArd Biesheuvel 	 */
20484b04d3eSArd Biesheuvel 	map_range(&ptep, fdt, (u64)_text > fdt ? min((u64)_text, efdt) : efdt,
20584b04d3eSArd Biesheuvel 		  fdt, PAGE_KERNEL, IDMAP_ROOT_LEVEL,
20684b04d3eSArd Biesheuvel 		  (pte_t *)init_idmap_pg_dir, false, 0);
20784b04d3eSArd Biesheuvel 	dsb(ishst);
20884b04d3eSArd Biesheuvel }
20984b04d3eSArd Biesheuvel 
210*117c3b21SMarc Zyngier /*
211*117c3b21SMarc Zyngier  * PI version of the Cavium Eratum 27456 detection, which makes it
212*117c3b21SMarc Zyngier  * impossible to use non-global mappings.
213*117c3b21SMarc Zyngier  */
ng_mappings_allowed(void)214*117c3b21SMarc Zyngier static bool __init ng_mappings_allowed(void)
215*117c3b21SMarc Zyngier {
216*117c3b21SMarc Zyngier 	static const struct midr_range cavium_erratum_27456_cpus[] __initconst = {
217*117c3b21SMarc Zyngier 		/* Cavium ThunderX, T88 pass 1.x - 2.1 */
218*117c3b21SMarc Zyngier 		MIDR_RANGE(MIDR_THUNDERX, 0, 0, 1, 1),
219*117c3b21SMarc Zyngier 		/* Cavium ThunderX, T81 pass 1.0 */
220*117c3b21SMarc Zyngier 		MIDR_REV(MIDR_THUNDERX_81XX, 0, 0),
221*117c3b21SMarc Zyngier 		{},
222*117c3b21SMarc Zyngier 	};
223*117c3b21SMarc Zyngier 
224*117c3b21SMarc Zyngier 	for (const struct midr_range *r = cavium_erratum_27456_cpus; r->model; r++) {
225*117c3b21SMarc Zyngier 		if (midr_is_cpu_model_range(read_cpuid_id(), r->model,
226*117c3b21SMarc Zyngier 					    r->rv_min, r->rv_max))
227*117c3b21SMarc Zyngier 			return false;
228*117c3b21SMarc Zyngier 	}
229*117c3b21SMarc Zyngier 
230*117c3b21SMarc Zyngier 	return true;
231*117c3b21SMarc Zyngier }
232*117c3b21SMarc Zyngier 
early_map_kernel(u64 boot_status,void * fdt)23397a6f43bSArd Biesheuvel asmlinkage void __init early_map_kernel(u64 boot_status, void *fdt)
23497a6f43bSArd Biesheuvel {
23597a6f43bSArd Biesheuvel 	static char const chosen_str[] __initconst = "/chosen";
23697a6f43bSArd Biesheuvel 	u64 va_base, pa_base = (u64)&_text;
23797a6f43bSArd Biesheuvel 	u64 kaslr_offset = pa_base % MIN_KIMG_ALIGN;
23897a6f43bSArd Biesheuvel 	int root_level = 4 - CONFIG_PGTABLE_LEVELS;
2399684ec18SArd Biesheuvel 	int va_bits = VA_BITS;
24097a6f43bSArd Biesheuvel 	int chosen;
24197a6f43bSArd Biesheuvel 
24284b04d3eSArd Biesheuvel 	map_fdt((u64)fdt);
24384b04d3eSArd Biesheuvel 
24497a6f43bSArd Biesheuvel 	/* Clear BSS and the initial page tables */
24597a6f43bSArd Biesheuvel 	memset(__bss_start, 0, (u64)init_pg_end - (u64)__bss_start);
24697a6f43bSArd Biesheuvel 
24797a6f43bSArd Biesheuvel 	/* Parse the command line for CPU feature overrides */
24897a6f43bSArd Biesheuvel 	chosen = fdt_path_offset(fdt, chosen_str);
24997a6f43bSArd Biesheuvel 	init_feature_override(boot_status, fdt, chosen);
25097a6f43bSArd Biesheuvel 
2519684ec18SArd Biesheuvel 	if (IS_ENABLED(CONFIG_ARM64_64K_PAGES) && !cpu_has_lva()) {
2529684ec18SArd Biesheuvel 		va_bits = VA_BITS_MIN;
2539684ec18SArd Biesheuvel 	} else if (IS_ENABLED(CONFIG_ARM64_LPA2) && !cpu_has_lpa2()) {
2549684ec18SArd Biesheuvel 		va_bits = VA_BITS_MIN;
2559684ec18SArd Biesheuvel 		root_level++;
2569684ec18SArd Biesheuvel 	}
2579684ec18SArd Biesheuvel 
2589684ec18SArd Biesheuvel 	if (va_bits > VA_BITS_MIN)
2599684ec18SArd Biesheuvel 		sysreg_clear_set(tcr_el1, TCR_T1SZ_MASK, TCR_T1SZ(va_bits));
2609cce9c6cSArd Biesheuvel 
26197a6f43bSArd Biesheuvel 	/*
26297a6f43bSArd Biesheuvel 	 * The virtual KASLR displacement modulo 2MiB is decided by the
26397a6f43bSArd Biesheuvel 	 * physical placement of the image, as otherwise, we might not be able
26497a6f43bSArd Biesheuvel 	 * to create the early kernel mapping using 2 MiB block descriptors. So
26597a6f43bSArd Biesheuvel 	 * take the low bits of the KASLR offset from the physical address, and
26697a6f43bSArd Biesheuvel 	 * fill in the high bits from the seed.
26797a6f43bSArd Biesheuvel 	 */
26897a6f43bSArd Biesheuvel 	if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) {
26997a6f43bSArd Biesheuvel 		u64 kaslr_seed = kaslr_early_init(fdt, chosen);
27097a6f43bSArd Biesheuvel 
27197a6f43bSArd Biesheuvel 		if (kaslr_seed && kaslr_requires_kpti())
272*117c3b21SMarc Zyngier 			arm64_use_ng_mappings = ng_mappings_allowed();
27397a6f43bSArd Biesheuvel 
27497a6f43bSArd Biesheuvel 		kaslr_offset |= kaslr_seed & ~(MIN_KIMG_ALIGN - 1);
27597a6f43bSArd Biesheuvel 	}
27697a6f43bSArd Biesheuvel 
2779684ec18SArd Biesheuvel 	if (IS_ENABLED(CONFIG_ARM64_LPA2) && va_bits > VA_BITS_MIN)
2789684ec18SArd Biesheuvel 		remap_idmap_for_lpa2();
2799684ec18SArd Biesheuvel 
28097a6f43bSArd Biesheuvel 	va_base = KIMAGE_VADDR + kaslr_offset;
28197a6f43bSArd Biesheuvel 	map_kernel(kaslr_offset, va_base - pa_base, root_level);
28297a6f43bSArd Biesheuvel }
283