162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/sched/task.h>
362306a36Sopenharmony_ci#include <linux/pgtable.h>
462306a36Sopenharmony_ci#include <linux/kasan.h>
562306a36Sopenharmony_ci#include <asm/pgalloc.h>
662306a36Sopenharmony_ci#include <asm/facility.h>
762306a36Sopenharmony_ci#include <asm/sections.h>
862306a36Sopenharmony_ci#include <asm/physmem_info.h>
962306a36Sopenharmony_ci#include <asm/maccess.h>
1062306a36Sopenharmony_ci#include <asm/abs_lowcore.h>
1162306a36Sopenharmony_ci#include "decompressor.h"
1262306a36Sopenharmony_ci#include "boot.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ciunsigned long __bootdata_preserved(s390_invalid_asce);
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS
1762306a36Sopenharmony_ciatomic_long_t __bootdata_preserved(direct_pages_count[PG_DIRECT_MAP_MAX]);
1862306a36Sopenharmony_ci#endif
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define init_mm			(*(struct mm_struct *)vmlinux.init_mm_off)
2162306a36Sopenharmony_ci#define swapper_pg_dir		vmlinux.swapper_pg_dir_off
2262306a36Sopenharmony_ci#define invalid_pg_dir		vmlinux.invalid_pg_dir_off
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cienum populate_mode {
2562306a36Sopenharmony_ci	POPULATE_NONE,
2662306a36Sopenharmony_ci	POPULATE_DIRECT,
2762306a36Sopenharmony_ci	POPULATE_ABS_LOWCORE,
2862306a36Sopenharmony_ci#ifdef CONFIG_KASAN
2962306a36Sopenharmony_ci	POPULATE_KASAN_MAP_SHADOW,
3062306a36Sopenharmony_ci	POPULATE_KASAN_ZERO_SHADOW,
3162306a36Sopenharmony_ci	POPULATE_KASAN_SHALLOW
3262306a36Sopenharmony_ci#endif
3362306a36Sopenharmony_ci};
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic void pgtable_populate(unsigned long addr, unsigned long end, enum populate_mode mode);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#ifdef CONFIG_KASAN
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define kasan_early_shadow_page	vmlinux.kasan_early_shadow_page_off
4062306a36Sopenharmony_ci#define kasan_early_shadow_pte	((pte_t *)vmlinux.kasan_early_shadow_pte_off)
4162306a36Sopenharmony_ci#define kasan_early_shadow_pmd	((pmd_t *)vmlinux.kasan_early_shadow_pmd_off)
4262306a36Sopenharmony_ci#define kasan_early_shadow_pud	((pud_t *)vmlinux.kasan_early_shadow_pud_off)
4362306a36Sopenharmony_ci#define kasan_early_shadow_p4d	((p4d_t *)vmlinux.kasan_early_shadow_p4d_off)
4462306a36Sopenharmony_ci#define __sha(x)		((unsigned long)kasan_mem_to_shadow((void *)x))
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic pte_t pte_z;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic inline void kasan_populate(unsigned long start, unsigned long end, enum populate_mode mode)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	start = PAGE_ALIGN_DOWN(__sha(start));
5162306a36Sopenharmony_ci	end = PAGE_ALIGN(__sha(end));
5262306a36Sopenharmony_ci	pgtable_populate(start, end, mode);
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic void kasan_populate_shadow(void)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	pmd_t pmd_z = __pmd(__pa(kasan_early_shadow_pte) | _SEGMENT_ENTRY);
5862306a36Sopenharmony_ci	pud_t pud_z = __pud(__pa(kasan_early_shadow_pmd) | _REGION3_ENTRY);
5962306a36Sopenharmony_ci	p4d_t p4d_z = __p4d(__pa(kasan_early_shadow_pud) | _REGION2_ENTRY);
6062306a36Sopenharmony_ci	unsigned long memgap_start = 0;
6162306a36Sopenharmony_ci	unsigned long untracked_end;
6262306a36Sopenharmony_ci	unsigned long start, end;
6362306a36Sopenharmony_ci	int i;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	pte_z = __pte(__pa(kasan_early_shadow_page) | pgprot_val(PAGE_KERNEL_RO));
6662306a36Sopenharmony_ci	if (!machine.has_nx)
6762306a36Sopenharmony_ci		pte_z = clear_pte_bit(pte_z, __pgprot(_PAGE_NOEXEC));
6862306a36Sopenharmony_ci	crst_table_init((unsigned long *)kasan_early_shadow_p4d, p4d_val(p4d_z));
6962306a36Sopenharmony_ci	crst_table_init((unsigned long *)kasan_early_shadow_pud, pud_val(pud_z));
7062306a36Sopenharmony_ci	crst_table_init((unsigned long *)kasan_early_shadow_pmd, pmd_val(pmd_z));
7162306a36Sopenharmony_ci	memset64((u64 *)kasan_early_shadow_pte, pte_val(pte_z), PTRS_PER_PTE);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	/*
7462306a36Sopenharmony_ci	 * Current memory layout:
7562306a36Sopenharmony_ci	 * +- 0 -------------+	       +- shadow start -+
7662306a36Sopenharmony_ci	 * |1:1 ident mapping|	      /|1/8 of ident map|
7762306a36Sopenharmony_ci	 * |		     |	     / |		|
7862306a36Sopenharmony_ci	 * +-end of ident map+	    /  +----------------+
7962306a36Sopenharmony_ci	 * | ... gap ...     |	   /   |    kasan	|
8062306a36Sopenharmony_ci	 * |		     |	  /    |  zero page	|
8162306a36Sopenharmony_ci	 * +- vmalloc area  -+	 /     |   mapping	|
8262306a36Sopenharmony_ci	 * | vmalloc_size    |	/      | (untracked)	|
8362306a36Sopenharmony_ci	 * +- modules vaddr -+ /       +----------------+
8462306a36Sopenharmony_ci	 * | 2Gb	     |/        |    unmapped	| allocated per module
8562306a36Sopenharmony_ci	 * +- shadow start  -+	       +----------------+
8662306a36Sopenharmony_ci	 * | 1/8 addr space  |	       | zero pg mapping| (untracked)
8762306a36Sopenharmony_ci	 * +- shadow end ----+---------+- shadow end ---+
8862306a36Sopenharmony_ci	 *
8962306a36Sopenharmony_ci	 * Current memory layout (KASAN_VMALLOC):
9062306a36Sopenharmony_ci	 * +- 0 -------------+	       +- shadow start -+
9162306a36Sopenharmony_ci	 * |1:1 ident mapping|	      /|1/8 of ident map|
9262306a36Sopenharmony_ci	 * |		     |	     / |		|
9362306a36Sopenharmony_ci	 * +-end of ident map+	    /  +----------------+
9462306a36Sopenharmony_ci	 * | ... gap ...     |	   /   | kasan zero page| (untracked)
9562306a36Sopenharmony_ci	 * |		     |	  /    | mapping	|
9662306a36Sopenharmony_ci	 * +- vmalloc area  -+	 /     +----------------+
9762306a36Sopenharmony_ci	 * | vmalloc_size    |	/      |shallow populate|
9862306a36Sopenharmony_ci	 * +- modules vaddr -+ /       +----------------+
9962306a36Sopenharmony_ci	 * | 2Gb	     |/        |shallow populate|
10062306a36Sopenharmony_ci	 * +- shadow start  -+	       +----------------+
10162306a36Sopenharmony_ci	 * | 1/8 addr space  |	       | zero pg mapping| (untracked)
10262306a36Sopenharmony_ci	 * +- shadow end ----+---------+- shadow end ---+
10362306a36Sopenharmony_ci	 */
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	for_each_physmem_usable_range(i, &start, &end) {
10662306a36Sopenharmony_ci		kasan_populate(start, end, POPULATE_KASAN_MAP_SHADOW);
10762306a36Sopenharmony_ci		if (memgap_start && physmem_info.info_source == MEM_DETECT_DIAG260)
10862306a36Sopenharmony_ci			kasan_populate(memgap_start, start, POPULATE_KASAN_ZERO_SHADOW);
10962306a36Sopenharmony_ci		memgap_start = end;
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_KASAN_VMALLOC)) {
11262306a36Sopenharmony_ci		untracked_end = VMALLOC_START;
11362306a36Sopenharmony_ci		/* shallowly populate kasan shadow for vmalloc and modules */
11462306a36Sopenharmony_ci		kasan_populate(VMALLOC_START, MODULES_END, POPULATE_KASAN_SHALLOW);
11562306a36Sopenharmony_ci	} else {
11662306a36Sopenharmony_ci		untracked_end = MODULES_VADDR;
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci	/* populate kasan shadow for untracked memory */
11962306a36Sopenharmony_ci	kasan_populate(ident_map_size, untracked_end, POPULATE_KASAN_ZERO_SHADOW);
12062306a36Sopenharmony_ci	kasan_populate(MODULES_END, _REGION1_SIZE, POPULATE_KASAN_ZERO_SHADOW);
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic bool kasan_pgd_populate_zero_shadow(pgd_t *pgd, unsigned long addr,
12462306a36Sopenharmony_ci					   unsigned long end, enum populate_mode mode)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	if (mode == POPULATE_KASAN_ZERO_SHADOW &&
12762306a36Sopenharmony_ci	    IS_ALIGNED(addr, PGDIR_SIZE) && end - addr >= PGDIR_SIZE) {
12862306a36Sopenharmony_ci		pgd_populate(&init_mm, pgd, kasan_early_shadow_p4d);
12962306a36Sopenharmony_ci		return true;
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci	return false;
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistatic bool kasan_p4d_populate_zero_shadow(p4d_t *p4d, unsigned long addr,
13562306a36Sopenharmony_ci					   unsigned long end, enum populate_mode mode)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	if (mode == POPULATE_KASAN_ZERO_SHADOW &&
13862306a36Sopenharmony_ci	    IS_ALIGNED(addr, P4D_SIZE) && end - addr >= P4D_SIZE) {
13962306a36Sopenharmony_ci		p4d_populate(&init_mm, p4d, kasan_early_shadow_pud);
14062306a36Sopenharmony_ci		return true;
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci	return false;
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic bool kasan_pud_populate_zero_shadow(pud_t *pud, unsigned long addr,
14662306a36Sopenharmony_ci					   unsigned long end, enum populate_mode mode)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	if (mode == POPULATE_KASAN_ZERO_SHADOW &&
14962306a36Sopenharmony_ci	    IS_ALIGNED(addr, PUD_SIZE) && end - addr >= PUD_SIZE) {
15062306a36Sopenharmony_ci		pud_populate(&init_mm, pud, kasan_early_shadow_pmd);
15162306a36Sopenharmony_ci		return true;
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci	return false;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic bool kasan_pmd_populate_zero_shadow(pmd_t *pmd, unsigned long addr,
15762306a36Sopenharmony_ci					   unsigned long end, enum populate_mode mode)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	if (mode == POPULATE_KASAN_ZERO_SHADOW &&
16062306a36Sopenharmony_ci	    IS_ALIGNED(addr, PMD_SIZE) && end - addr >= PMD_SIZE) {
16162306a36Sopenharmony_ci		pmd_populate(&init_mm, pmd, kasan_early_shadow_pte);
16262306a36Sopenharmony_ci		return true;
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci	return false;
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistatic bool kasan_pte_populate_zero_shadow(pte_t *pte, enum populate_mode mode)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	pte_t entry;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	if (mode == POPULATE_KASAN_ZERO_SHADOW) {
17262306a36Sopenharmony_ci		set_pte(pte, pte_z);
17362306a36Sopenharmony_ci		return true;
17462306a36Sopenharmony_ci	}
17562306a36Sopenharmony_ci	return false;
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci#else
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic inline void kasan_populate_shadow(void) {}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic inline bool kasan_pgd_populate_zero_shadow(pgd_t *pgd, unsigned long addr,
18262306a36Sopenharmony_ci						  unsigned long end, enum populate_mode mode)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	return false;
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic inline bool kasan_p4d_populate_zero_shadow(p4d_t *p4d, unsigned long addr,
18862306a36Sopenharmony_ci						  unsigned long end, enum populate_mode mode)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	return false;
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistatic inline bool kasan_pud_populate_zero_shadow(pud_t *pud, unsigned long addr,
19462306a36Sopenharmony_ci						  unsigned long end, enum populate_mode mode)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	return false;
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic inline bool kasan_pmd_populate_zero_shadow(pmd_t *pmd, unsigned long addr,
20062306a36Sopenharmony_ci						  unsigned long end, enum populate_mode mode)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	return false;
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic bool kasan_pte_populate_zero_shadow(pte_t *pte, enum populate_mode mode)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	return false;
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci#endif
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci/*
21362306a36Sopenharmony_ci * Mimic virt_to_kpte() in lack of init_mm symbol. Skip pmd NULL check though.
21462306a36Sopenharmony_ci */
21562306a36Sopenharmony_cistatic inline pte_t *__virt_to_kpte(unsigned long va)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	return pte_offset_kernel(pmd_offset(pud_offset(p4d_offset(pgd_offset_k(va), va), va), va), va);
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic void *boot_crst_alloc(unsigned long val)
22162306a36Sopenharmony_ci{
22262306a36Sopenharmony_ci	unsigned long size = PAGE_SIZE << CRST_ALLOC_ORDER;
22362306a36Sopenharmony_ci	unsigned long *table;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	table = (unsigned long *)physmem_alloc_top_down(RR_VMEM, size, size);
22662306a36Sopenharmony_ci	crst_table_init(table, val);
22762306a36Sopenharmony_ci	return table;
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic pte_t *boot_pte_alloc(void)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	static void *pte_leftover;
23362306a36Sopenharmony_ci	pte_t *pte;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	/*
23662306a36Sopenharmony_ci	 * handling pte_leftovers this way helps to avoid memory fragmentation
23762306a36Sopenharmony_ci	 * during POPULATE_KASAN_MAP_SHADOW when EDAT is off
23862306a36Sopenharmony_ci	 */
23962306a36Sopenharmony_ci	if (!pte_leftover) {
24062306a36Sopenharmony_ci		pte_leftover = (void *)physmem_alloc_top_down(RR_VMEM, PAGE_SIZE, PAGE_SIZE);
24162306a36Sopenharmony_ci		pte = pte_leftover + _PAGE_TABLE_SIZE;
24262306a36Sopenharmony_ci	} else {
24362306a36Sopenharmony_ci		pte = pte_leftover;
24462306a36Sopenharmony_ci		pte_leftover = NULL;
24562306a36Sopenharmony_ci	}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	memset64((u64 *)pte, _PAGE_INVALID, PTRS_PER_PTE);
24862306a36Sopenharmony_ci	return pte;
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic unsigned long _pa(unsigned long addr, unsigned long size, enum populate_mode mode)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	switch (mode) {
25462306a36Sopenharmony_ci	case POPULATE_NONE:
25562306a36Sopenharmony_ci		return -1;
25662306a36Sopenharmony_ci	case POPULATE_DIRECT:
25762306a36Sopenharmony_ci		return addr;
25862306a36Sopenharmony_ci	case POPULATE_ABS_LOWCORE:
25962306a36Sopenharmony_ci		return __abs_lowcore_pa(addr);
26062306a36Sopenharmony_ci#ifdef CONFIG_KASAN
26162306a36Sopenharmony_ci	case POPULATE_KASAN_MAP_SHADOW:
26262306a36Sopenharmony_ci		addr = physmem_alloc_top_down(RR_VMEM, size, size);
26362306a36Sopenharmony_ci		memset((void *)addr, 0, size);
26462306a36Sopenharmony_ci		return addr;
26562306a36Sopenharmony_ci#endif
26662306a36Sopenharmony_ci	default:
26762306a36Sopenharmony_ci		return -1;
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic bool can_large_pud(pud_t *pu_dir, unsigned long addr, unsigned long end)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	return machine.has_edat2 &&
27462306a36Sopenharmony_ci	       IS_ALIGNED(addr, PUD_SIZE) && (end - addr) >= PUD_SIZE;
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic bool can_large_pmd(pmd_t *pm_dir, unsigned long addr, unsigned long end)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	return machine.has_edat1 &&
28062306a36Sopenharmony_ci	       IS_ALIGNED(addr, PMD_SIZE) && (end - addr) >= PMD_SIZE;
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic void pgtable_pte_populate(pmd_t *pmd, unsigned long addr, unsigned long end,
28462306a36Sopenharmony_ci				 enum populate_mode mode)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	unsigned long pages = 0;
28762306a36Sopenharmony_ci	pte_t *pte, entry;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	pte = pte_offset_kernel(pmd, addr);
29062306a36Sopenharmony_ci	for (; addr < end; addr += PAGE_SIZE, pte++) {
29162306a36Sopenharmony_ci		if (pte_none(*pte)) {
29262306a36Sopenharmony_ci			if (kasan_pte_populate_zero_shadow(pte, mode))
29362306a36Sopenharmony_ci				continue;
29462306a36Sopenharmony_ci			entry = __pte(_pa(addr, PAGE_SIZE, mode));
29562306a36Sopenharmony_ci			entry = set_pte_bit(entry, PAGE_KERNEL);
29662306a36Sopenharmony_ci			if (!machine.has_nx)
29762306a36Sopenharmony_ci				entry = clear_pte_bit(entry, __pgprot(_PAGE_NOEXEC));
29862306a36Sopenharmony_ci			set_pte(pte, entry);
29962306a36Sopenharmony_ci			pages++;
30062306a36Sopenharmony_ci		}
30162306a36Sopenharmony_ci	}
30262306a36Sopenharmony_ci	if (mode == POPULATE_DIRECT)
30362306a36Sopenharmony_ci		update_page_count(PG_DIRECT_MAP_4K, pages);
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_cistatic void pgtable_pmd_populate(pud_t *pud, unsigned long addr, unsigned long end,
30762306a36Sopenharmony_ci				 enum populate_mode mode)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	unsigned long next, pages = 0;
31062306a36Sopenharmony_ci	pmd_t *pmd, entry;
31162306a36Sopenharmony_ci	pte_t *pte;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	pmd = pmd_offset(pud, addr);
31462306a36Sopenharmony_ci	for (; addr < end; addr = next, pmd++) {
31562306a36Sopenharmony_ci		next = pmd_addr_end(addr, end);
31662306a36Sopenharmony_ci		if (pmd_none(*pmd)) {
31762306a36Sopenharmony_ci			if (kasan_pmd_populate_zero_shadow(pmd, addr, next, mode))
31862306a36Sopenharmony_ci				continue;
31962306a36Sopenharmony_ci			if (can_large_pmd(pmd, addr, next)) {
32062306a36Sopenharmony_ci				entry = __pmd(_pa(addr, _SEGMENT_SIZE, mode));
32162306a36Sopenharmony_ci				entry = set_pmd_bit(entry, SEGMENT_KERNEL);
32262306a36Sopenharmony_ci				if (!machine.has_nx)
32362306a36Sopenharmony_ci					entry = clear_pmd_bit(entry, __pgprot(_SEGMENT_ENTRY_NOEXEC));
32462306a36Sopenharmony_ci				set_pmd(pmd, entry);
32562306a36Sopenharmony_ci				pages++;
32662306a36Sopenharmony_ci				continue;
32762306a36Sopenharmony_ci			}
32862306a36Sopenharmony_ci			pte = boot_pte_alloc();
32962306a36Sopenharmony_ci			pmd_populate(&init_mm, pmd, pte);
33062306a36Sopenharmony_ci		} else if (pmd_large(*pmd)) {
33162306a36Sopenharmony_ci			continue;
33262306a36Sopenharmony_ci		}
33362306a36Sopenharmony_ci		pgtable_pte_populate(pmd, addr, next, mode);
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci	if (mode == POPULATE_DIRECT)
33662306a36Sopenharmony_ci		update_page_count(PG_DIRECT_MAP_1M, pages);
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_cistatic void pgtable_pud_populate(p4d_t *p4d, unsigned long addr, unsigned long end,
34062306a36Sopenharmony_ci				 enum populate_mode mode)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	unsigned long next, pages = 0;
34362306a36Sopenharmony_ci	pud_t *pud, entry;
34462306a36Sopenharmony_ci	pmd_t *pmd;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	pud = pud_offset(p4d, addr);
34762306a36Sopenharmony_ci	for (; addr < end; addr = next, pud++) {
34862306a36Sopenharmony_ci		next = pud_addr_end(addr, end);
34962306a36Sopenharmony_ci		if (pud_none(*pud)) {
35062306a36Sopenharmony_ci			if (kasan_pud_populate_zero_shadow(pud, addr, next, mode))
35162306a36Sopenharmony_ci				continue;
35262306a36Sopenharmony_ci			if (can_large_pud(pud, addr, next)) {
35362306a36Sopenharmony_ci				entry = __pud(_pa(addr, _REGION3_SIZE, mode));
35462306a36Sopenharmony_ci				entry = set_pud_bit(entry, REGION3_KERNEL);
35562306a36Sopenharmony_ci				if (!machine.has_nx)
35662306a36Sopenharmony_ci					entry = clear_pud_bit(entry, __pgprot(_REGION_ENTRY_NOEXEC));
35762306a36Sopenharmony_ci				set_pud(pud, entry);
35862306a36Sopenharmony_ci				pages++;
35962306a36Sopenharmony_ci				continue;
36062306a36Sopenharmony_ci			}
36162306a36Sopenharmony_ci			pmd = boot_crst_alloc(_SEGMENT_ENTRY_EMPTY);
36262306a36Sopenharmony_ci			pud_populate(&init_mm, pud, pmd);
36362306a36Sopenharmony_ci		} else if (pud_large(*pud)) {
36462306a36Sopenharmony_ci			continue;
36562306a36Sopenharmony_ci		}
36662306a36Sopenharmony_ci		pgtable_pmd_populate(pud, addr, next, mode);
36762306a36Sopenharmony_ci	}
36862306a36Sopenharmony_ci	if (mode == POPULATE_DIRECT)
36962306a36Sopenharmony_ci		update_page_count(PG_DIRECT_MAP_2G, pages);
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cistatic void pgtable_p4d_populate(pgd_t *pgd, unsigned long addr, unsigned long end,
37362306a36Sopenharmony_ci				 enum populate_mode mode)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci	unsigned long next;
37662306a36Sopenharmony_ci	p4d_t *p4d;
37762306a36Sopenharmony_ci	pud_t *pud;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	p4d = p4d_offset(pgd, addr);
38062306a36Sopenharmony_ci	for (; addr < end; addr = next, p4d++) {
38162306a36Sopenharmony_ci		next = p4d_addr_end(addr, end);
38262306a36Sopenharmony_ci		if (p4d_none(*p4d)) {
38362306a36Sopenharmony_ci			if (kasan_p4d_populate_zero_shadow(p4d, addr, next, mode))
38462306a36Sopenharmony_ci				continue;
38562306a36Sopenharmony_ci			pud = boot_crst_alloc(_REGION3_ENTRY_EMPTY);
38662306a36Sopenharmony_ci			p4d_populate(&init_mm, p4d, pud);
38762306a36Sopenharmony_ci		}
38862306a36Sopenharmony_ci		pgtable_pud_populate(p4d, addr, next, mode);
38962306a36Sopenharmony_ci	}
39062306a36Sopenharmony_ci}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_cistatic void pgtable_populate(unsigned long addr, unsigned long end, enum populate_mode mode)
39362306a36Sopenharmony_ci{
39462306a36Sopenharmony_ci	unsigned long next;
39562306a36Sopenharmony_ci	pgd_t *pgd;
39662306a36Sopenharmony_ci	p4d_t *p4d;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	pgd = pgd_offset(&init_mm, addr);
39962306a36Sopenharmony_ci	for (; addr < end; addr = next, pgd++) {
40062306a36Sopenharmony_ci		next = pgd_addr_end(addr, end);
40162306a36Sopenharmony_ci		if (pgd_none(*pgd)) {
40262306a36Sopenharmony_ci			if (kasan_pgd_populate_zero_shadow(pgd, addr, next, mode))
40362306a36Sopenharmony_ci				continue;
40462306a36Sopenharmony_ci			p4d = boot_crst_alloc(_REGION2_ENTRY_EMPTY);
40562306a36Sopenharmony_ci			pgd_populate(&init_mm, pgd, p4d);
40662306a36Sopenharmony_ci		}
40762306a36Sopenharmony_ci#ifdef CONFIG_KASAN
40862306a36Sopenharmony_ci		if (mode == POPULATE_KASAN_SHALLOW)
40962306a36Sopenharmony_ci			continue;
41062306a36Sopenharmony_ci#endif
41162306a36Sopenharmony_ci		pgtable_p4d_populate(pgd, addr, next, mode);
41262306a36Sopenharmony_ci	}
41362306a36Sopenharmony_ci}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_civoid setup_vmem(unsigned long asce_limit)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	unsigned long start, end;
41862306a36Sopenharmony_ci	unsigned long asce_type;
41962306a36Sopenharmony_ci	unsigned long asce_bits;
42062306a36Sopenharmony_ci	int i;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	if (asce_limit == _REGION1_SIZE) {
42362306a36Sopenharmony_ci		asce_type = _REGION2_ENTRY_EMPTY;
42462306a36Sopenharmony_ci		asce_bits = _ASCE_TYPE_REGION2 | _ASCE_TABLE_LENGTH;
42562306a36Sopenharmony_ci	} else {
42662306a36Sopenharmony_ci		asce_type = _REGION3_ENTRY_EMPTY;
42762306a36Sopenharmony_ci		asce_bits = _ASCE_TYPE_REGION3 | _ASCE_TABLE_LENGTH;
42862306a36Sopenharmony_ci	}
42962306a36Sopenharmony_ci	s390_invalid_asce = invalid_pg_dir | _ASCE_TYPE_REGION3 | _ASCE_TABLE_LENGTH;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	crst_table_init((unsigned long *)swapper_pg_dir, asce_type);
43262306a36Sopenharmony_ci	crst_table_init((unsigned long *)invalid_pg_dir, _REGION3_ENTRY_EMPTY);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	/*
43562306a36Sopenharmony_ci	 * To allow prefixing the lowcore must be mapped with 4KB pages.
43662306a36Sopenharmony_ci	 * To prevent creation of a large page at address 0 first map
43762306a36Sopenharmony_ci	 * the lowcore and create the identity mapping only afterwards.
43862306a36Sopenharmony_ci	 */
43962306a36Sopenharmony_ci	pgtable_populate(0, sizeof(struct lowcore), POPULATE_DIRECT);
44062306a36Sopenharmony_ci	for_each_physmem_usable_range(i, &start, &end)
44162306a36Sopenharmony_ci		pgtable_populate(start, end, POPULATE_DIRECT);
44262306a36Sopenharmony_ci	pgtable_populate(__abs_lowcore, __abs_lowcore + sizeof(struct lowcore),
44362306a36Sopenharmony_ci			 POPULATE_ABS_LOWCORE);
44462306a36Sopenharmony_ci	pgtable_populate(__memcpy_real_area, __memcpy_real_area + PAGE_SIZE,
44562306a36Sopenharmony_ci			 POPULATE_NONE);
44662306a36Sopenharmony_ci	memcpy_real_ptep = __virt_to_kpte(__memcpy_real_area);
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	kasan_populate_shadow();
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	S390_lowcore.kernel_asce = swapper_pg_dir | asce_bits;
45162306a36Sopenharmony_ci	S390_lowcore.user_asce = s390_invalid_asce;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	__ctl_load(S390_lowcore.kernel_asce, 1, 1);
45462306a36Sopenharmony_ci	__ctl_load(S390_lowcore.user_asce, 7, 7);
45562306a36Sopenharmony_ci	__ctl_load(S390_lowcore.kernel_asce, 13, 13);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	init_mm.context.asce = S390_lowcore.kernel_asce;
45862306a36Sopenharmony_ci}
459