diff options
Diffstat (limited to 'arch/arm64/mm/mmu.c')
| -rw-r--r-- | arch/arm64/mm/mmu.c | 78 | 
1 files changed, 63 insertions, 15 deletions
| diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index 626ec32873c6..db7c4e6ae57b 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -43,15 +43,27 @@  #define NO_CONT_MAPPINGS	BIT(1)  #define NO_EXEC_MAPPINGS	BIT(2)	/* assumes FEAT_HPDS is not used */ -u64 idmap_t0sz = TCR_T0SZ(VA_BITS_MIN); -u64 idmap_ptrs_per_pgd = PTRS_PER_PGD; +int idmap_t0sz __ro_after_init; -u64 __section(".mmuoff.data.write") vabits_actual; +#if VA_BITS > 48 +u64 vabits_actual __ro_after_init = VA_BITS_MIN;  EXPORT_SYMBOL(vabits_actual); +#endif + +u64 kimage_vaddr __ro_after_init = (u64)&_text; +EXPORT_SYMBOL(kimage_vaddr);  u64 kimage_voffset __ro_after_init;  EXPORT_SYMBOL(kimage_voffset); +u32 __boot_cpu_mode[] = { BOOT_CPU_MODE_EL2, BOOT_CPU_MODE_EL1 }; + +/* + * The booting CPU updates the failed status @__early_cpu_boot_status, + * with MMU turned off. + */ +long __section(".mmuoff.data.write") __early_cpu_boot_status; +  /*   * Empty_zero_page is a special page that is used for zero-initialized data   * and COW. @@ -388,6 +400,13 @@ static void __create_pgd_mapping(pgd_t *pgdir, phys_addr_t phys,  	} while (pgdp++, addr = next, addr != end);  } +#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 +extern __alias(__create_pgd_mapping) +void create_kpti_ng_temp_pgd(pgd_t *pgdir, phys_addr_t phys, unsigned long virt, +			     phys_addr_t size, pgprot_t prot, +			     phys_addr_t (*pgtable_alloc)(int), int flags); +#endif +  static phys_addr_t __pgd_pgtable_alloc(int shift)  {  	void *ptr = (void *)__get_free_page(GFP_PGTABLE_KERNEL); @@ -529,8 +548,7 @@ static void __init map_mem(pgd_t *pgdp)  #ifdef CONFIG_KEXEC_CORE  	if (crash_mem_map) { -		if (IS_ENABLED(CONFIG_ZONE_DMA) || -		    IS_ENABLED(CONFIG_ZONE_DMA32)) +		if (defer_reserve_crashkernel())  			flags |= NO_BLOCK_MAPPINGS | NO_CONT_MAPPINGS;  		else if (crashk_res.end)  			memblock_mark_nomap(crashk_res.start, @@ -571,8 +589,7 @@ static void __init map_mem(pgd_t *pgdp)  	 * through /sys/kernel/kexec_crash_size interface.  	 */  #ifdef CONFIG_KEXEC_CORE -	if (crash_mem_map && -	    !IS_ENABLED(CONFIG_ZONE_DMA) && !IS_ENABLED(CONFIG_ZONE_DMA32)) { +	if (crash_mem_map && !defer_reserve_crashkernel()) {  		if (crashk_res.end) {  			__map_memblock(pgdp, crashk_res.start,  				       crashk_res.end + 1, @@ -665,13 +682,9 @@ static int __init map_entry_trampoline(void)  		__set_fixmap(FIX_ENTRY_TRAMP_TEXT1 - i,  			     pa_start + i * PAGE_SIZE, prot); -	if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) { -		extern char __entry_tramp_data_start[]; - -		__set_fixmap(FIX_ENTRY_TRAMP_DATA, -			     __pa_symbol(__entry_tramp_data_start), -			     PAGE_KERNEL_RO); -	} +	if (IS_ENABLED(CONFIG_RELOCATABLE)) +		__set_fixmap(FIX_ENTRY_TRAMP_TEXT1 - i, +			     pa_start + i * PAGE_SIZE, PAGE_KERNEL_RO);  	return 0;  } @@ -762,22 +775,57 @@ static void __init map_kernel(pgd_t *pgdp)  	kasan_copy_shadow(pgdp);  } +static void __init create_idmap(void) +{ +	u64 start = __pa_symbol(__idmap_text_start); +	u64 size = __pa_symbol(__idmap_text_end) - start; +	pgd_t *pgd = idmap_pg_dir; +	u64 pgd_phys; + +	/* check if we need an additional level of translation */ +	if (VA_BITS < 48 && idmap_t0sz < (64 - VA_BITS_MIN)) { +		pgd_phys = early_pgtable_alloc(PAGE_SHIFT); +		set_pgd(&idmap_pg_dir[start >> VA_BITS], +			__pgd(pgd_phys | P4D_TYPE_TABLE)); +		pgd = __va(pgd_phys); +	} +	__create_pgd_mapping(pgd, start, start, size, PAGE_KERNEL_ROX, +			     early_pgtable_alloc, 0); + +	if (IS_ENABLED(CONFIG_UNMAP_KERNEL_AT_EL0)) { +		extern u32 __idmap_kpti_flag; +		u64 pa = __pa_symbol(&__idmap_kpti_flag); + +		/* +		 * The KPTI G-to-nG conversion code needs a read-write mapping +		 * of its synchronization flag in the ID map. +		 */ +		__create_pgd_mapping(pgd, pa, pa, sizeof(u32), PAGE_KERNEL, +				     early_pgtable_alloc, 0); +	} +} +  void __init paging_init(void)  {  	pgd_t *pgdp = pgd_set_fixmap(__pa_symbol(swapper_pg_dir)); +	extern pgd_t init_idmap_pg_dir[]; + +	idmap_t0sz = 63UL - __fls(__pa_symbol(_end) | GENMASK(VA_BITS_MIN - 1, 0));  	map_kernel(pgdp);  	map_mem(pgdp);  	pgd_clear_fixmap(); -	cpu_replace_ttbr1(lm_alias(swapper_pg_dir)); +	cpu_replace_ttbr1(lm_alias(swapper_pg_dir), init_idmap_pg_dir);  	init_mm.pgd = swapper_pg_dir;  	memblock_phys_free(__pa_symbol(init_pg_dir),  			   __pa_symbol(init_pg_end) - __pa_symbol(init_pg_dir));  	memblock_allow_resize(); + +	create_idmap();  }  /* |