18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright 2002 Andi Kleen, SuSE Labs.
48c2ecf20Sopenharmony_ci * Thanks to Ben LaHaise for precious feedback.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci#include <linux/highmem.h>
78c2ecf20Sopenharmony_ci#include <linux/memblock.h>
88c2ecf20Sopenharmony_ci#include <linux/sched.h>
98c2ecf20Sopenharmony_ci#include <linux/mm.h>
108c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
118c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
128c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
138c2ecf20Sopenharmony_ci#include <linux/pfn.h>
148c2ecf20Sopenharmony_ci#include <linux/percpu.h>
158c2ecf20Sopenharmony_ci#include <linux/gfp.h>
168c2ecf20Sopenharmony_ci#include <linux/pci.h>
178c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
188c2ecf20Sopenharmony_ci#include <linux/libnvdimm.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include <asm/e820/api.h>
218c2ecf20Sopenharmony_ci#include <asm/processor.h>
228c2ecf20Sopenharmony_ci#include <asm/tlbflush.h>
238c2ecf20Sopenharmony_ci#include <asm/sections.h>
248c2ecf20Sopenharmony_ci#include <asm/setup.h>
258c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
268c2ecf20Sopenharmony_ci#include <asm/pgalloc.h>
278c2ecf20Sopenharmony_ci#include <asm/proto.h>
288c2ecf20Sopenharmony_ci#include <asm/memtype.h>
298c2ecf20Sopenharmony_ci#include <asm/set_memory.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#include "../mm_internal.h"
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/*
348c2ecf20Sopenharmony_ci * The current flushing context - we pass it instead of 5 arguments:
358c2ecf20Sopenharmony_ci */
368c2ecf20Sopenharmony_cistruct cpa_data {
378c2ecf20Sopenharmony_ci	unsigned long	*vaddr;
388c2ecf20Sopenharmony_ci	pgd_t		*pgd;
398c2ecf20Sopenharmony_ci	pgprot_t	mask_set;
408c2ecf20Sopenharmony_ci	pgprot_t	mask_clr;
418c2ecf20Sopenharmony_ci	unsigned long	numpages;
428c2ecf20Sopenharmony_ci	unsigned long	curpage;
438c2ecf20Sopenharmony_ci	unsigned long	pfn;
448c2ecf20Sopenharmony_ci	unsigned int	flags;
458c2ecf20Sopenharmony_ci	unsigned int	force_split		: 1,
468c2ecf20Sopenharmony_ci			force_static_prot	: 1,
478c2ecf20Sopenharmony_ci			force_flush_all		: 1;
488c2ecf20Sopenharmony_ci	struct page	**pages;
498c2ecf20Sopenharmony_ci};
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cienum cpa_warn {
528c2ecf20Sopenharmony_ci	CPA_CONFLICT,
538c2ecf20Sopenharmony_ci	CPA_PROTECT,
548c2ecf20Sopenharmony_ci	CPA_DETECT,
558c2ecf20Sopenharmony_ci};
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic const int cpa_warn_level = CPA_PROTECT;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci/*
608c2ecf20Sopenharmony_ci * Serialize cpa() (for !DEBUG_PAGEALLOC which uses large identity mappings)
618c2ecf20Sopenharmony_ci * using cpa_lock. So that we don't allow any other cpu, with stale large tlb
628c2ecf20Sopenharmony_ci * entries change the page attribute in parallel to some other cpu
638c2ecf20Sopenharmony_ci * splitting a large page entry along with changing the attribute.
648c2ecf20Sopenharmony_ci */
658c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(cpa_lock);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci#define CPA_FLUSHTLB 1
688c2ecf20Sopenharmony_ci#define CPA_ARRAY 2
698c2ecf20Sopenharmony_ci#define CPA_PAGES_ARRAY 4
708c2ecf20Sopenharmony_ci#define CPA_NO_CHECK_ALIAS 8 /* Do not search for aliases */
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic inline pgprot_t cachemode2pgprot(enum page_cache_mode pcm)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	return __pgprot(cachemode2protval(pcm));
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS
788c2ecf20Sopenharmony_cistatic unsigned long direct_pages_count[PG_LEVEL_NUM];
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_civoid update_page_count(int level, unsigned long pages)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	/* Protect against CPA */
838c2ecf20Sopenharmony_ci	spin_lock(&pgd_lock);
848c2ecf20Sopenharmony_ci	direct_pages_count[level] += pages;
858c2ecf20Sopenharmony_ci	spin_unlock(&pgd_lock);
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic void split_page_count(int level)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	if (direct_pages_count[level] == 0)
918c2ecf20Sopenharmony_ci		return;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	direct_pages_count[level]--;
948c2ecf20Sopenharmony_ci	direct_pages_count[level - 1] += PTRS_PER_PTE;
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_civoid arch_report_meminfo(struct seq_file *m)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	seq_printf(m, "DirectMap4k:    %8lu kB\n",
1008c2ecf20Sopenharmony_ci			direct_pages_count[PG_LEVEL_4K] << 2);
1018c2ecf20Sopenharmony_ci#if defined(CONFIG_X86_64) || defined(CONFIG_X86_PAE)
1028c2ecf20Sopenharmony_ci	seq_printf(m, "DirectMap2M:    %8lu kB\n",
1038c2ecf20Sopenharmony_ci			direct_pages_count[PG_LEVEL_2M] << 11);
1048c2ecf20Sopenharmony_ci#else
1058c2ecf20Sopenharmony_ci	seq_printf(m, "DirectMap4M:    %8lu kB\n",
1068c2ecf20Sopenharmony_ci			direct_pages_count[PG_LEVEL_2M] << 12);
1078c2ecf20Sopenharmony_ci#endif
1088c2ecf20Sopenharmony_ci	if (direct_gbpages)
1098c2ecf20Sopenharmony_ci		seq_printf(m, "DirectMap1G:    %8lu kB\n",
1108c2ecf20Sopenharmony_ci			direct_pages_count[PG_LEVEL_1G] << 20);
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci#else
1138c2ecf20Sopenharmony_cistatic inline void split_page_count(int level) { }
1148c2ecf20Sopenharmony_ci#endif
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_CPA_STATISTICS
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic unsigned long cpa_1g_checked;
1198c2ecf20Sopenharmony_cistatic unsigned long cpa_1g_sameprot;
1208c2ecf20Sopenharmony_cistatic unsigned long cpa_1g_preserved;
1218c2ecf20Sopenharmony_cistatic unsigned long cpa_2m_checked;
1228c2ecf20Sopenharmony_cistatic unsigned long cpa_2m_sameprot;
1238c2ecf20Sopenharmony_cistatic unsigned long cpa_2m_preserved;
1248c2ecf20Sopenharmony_cistatic unsigned long cpa_4k_install;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic inline void cpa_inc_1g_checked(void)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	cpa_1g_checked++;
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic inline void cpa_inc_2m_checked(void)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	cpa_2m_checked++;
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic inline void cpa_inc_4k_install(void)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	data_race(cpa_4k_install++);
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic inline void cpa_inc_lp_sameprot(int level)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	if (level == PG_LEVEL_1G)
1448c2ecf20Sopenharmony_ci		cpa_1g_sameprot++;
1458c2ecf20Sopenharmony_ci	else
1468c2ecf20Sopenharmony_ci		cpa_2m_sameprot++;
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic inline void cpa_inc_lp_preserved(int level)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	if (level == PG_LEVEL_1G)
1528c2ecf20Sopenharmony_ci		cpa_1g_preserved++;
1538c2ecf20Sopenharmony_ci	else
1548c2ecf20Sopenharmony_ci		cpa_2m_preserved++;
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic int cpastats_show(struct seq_file *m, void *p)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	seq_printf(m, "1G pages checked:     %16lu\n", cpa_1g_checked);
1608c2ecf20Sopenharmony_ci	seq_printf(m, "1G pages sameprot:    %16lu\n", cpa_1g_sameprot);
1618c2ecf20Sopenharmony_ci	seq_printf(m, "1G pages preserved:   %16lu\n", cpa_1g_preserved);
1628c2ecf20Sopenharmony_ci	seq_printf(m, "2M pages checked:     %16lu\n", cpa_2m_checked);
1638c2ecf20Sopenharmony_ci	seq_printf(m, "2M pages sameprot:    %16lu\n", cpa_2m_sameprot);
1648c2ecf20Sopenharmony_ci	seq_printf(m, "2M pages preserved:   %16lu\n", cpa_2m_preserved);
1658c2ecf20Sopenharmony_ci	seq_printf(m, "4K pages set-checked: %16lu\n", cpa_4k_install);
1668c2ecf20Sopenharmony_ci	return 0;
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic int cpastats_open(struct inode *inode, struct file *file)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	return single_open(file, cpastats_show, NULL);
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistatic const struct file_operations cpastats_fops = {
1758c2ecf20Sopenharmony_ci	.open		= cpastats_open,
1768c2ecf20Sopenharmony_ci	.read		= seq_read,
1778c2ecf20Sopenharmony_ci	.llseek		= seq_lseek,
1788c2ecf20Sopenharmony_ci	.release	= single_release,
1798c2ecf20Sopenharmony_ci};
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic int __init cpa_stats_init(void)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	debugfs_create_file("cpa_stats", S_IRUSR, arch_debugfs_dir, NULL,
1848c2ecf20Sopenharmony_ci			    &cpastats_fops);
1858c2ecf20Sopenharmony_ci	return 0;
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_cilate_initcall(cpa_stats_init);
1888c2ecf20Sopenharmony_ci#else
1898c2ecf20Sopenharmony_cistatic inline void cpa_inc_1g_checked(void) { }
1908c2ecf20Sopenharmony_cistatic inline void cpa_inc_2m_checked(void) { }
1918c2ecf20Sopenharmony_cistatic inline void cpa_inc_4k_install(void) { }
1928c2ecf20Sopenharmony_cistatic inline void cpa_inc_lp_sameprot(int level) { }
1938c2ecf20Sopenharmony_cistatic inline void cpa_inc_lp_preserved(int level) { }
1948c2ecf20Sopenharmony_ci#endif
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic inline int
1988c2ecf20Sopenharmony_ciwithin(unsigned long addr, unsigned long start, unsigned long end)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	return addr >= start && addr < end;
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic inline int
2048c2ecf20Sopenharmony_ciwithin_inclusive(unsigned long addr, unsigned long start, unsigned long end)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	return addr >= start && addr <= end;
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_64
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic inline unsigned long highmap_start_pfn(void)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	return __pa_symbol(_text) >> PAGE_SHIFT;
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cistatic inline unsigned long highmap_end_pfn(void)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	/* Do not reference physical address outside the kernel. */
2198c2ecf20Sopenharmony_ci	return __pa_symbol(roundup(_brk_end, PMD_SIZE) - 1) >> PAGE_SHIFT;
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic bool __cpa_pfn_in_highmap(unsigned long pfn)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	/*
2258c2ecf20Sopenharmony_ci	 * Kernel text has an alias mapping at a high address, known
2268c2ecf20Sopenharmony_ci	 * here as "highmap".
2278c2ecf20Sopenharmony_ci	 */
2288c2ecf20Sopenharmony_ci	return within_inclusive(pfn, highmap_start_pfn(), highmap_end_pfn());
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci#else
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_cistatic bool __cpa_pfn_in_highmap(unsigned long pfn)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	/* There is no highmap on 32-bit */
2368c2ecf20Sopenharmony_ci	return false;
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci#endif
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci/*
2428c2ecf20Sopenharmony_ci * See set_mce_nospec().
2438c2ecf20Sopenharmony_ci *
2448c2ecf20Sopenharmony_ci * Machine check recovery code needs to change cache mode of poisoned pages to
2458c2ecf20Sopenharmony_ci * UC to avoid speculative access logging another error. But passing the
2468c2ecf20Sopenharmony_ci * address of the 1:1 mapping to set_memory_uc() is a fine way to encourage a
2478c2ecf20Sopenharmony_ci * speculative access. So we cheat and flip the top bit of the address. This
2488c2ecf20Sopenharmony_ci * works fine for the code that updates the page tables. But at the end of the
2498c2ecf20Sopenharmony_ci * process we need to flush the TLB and cache and the non-canonical address
2508c2ecf20Sopenharmony_ci * causes a #GP fault when used by the INVLPG and CLFLUSH instructions.
2518c2ecf20Sopenharmony_ci *
2528c2ecf20Sopenharmony_ci * But in the common case we already have a canonical address. This code
2538c2ecf20Sopenharmony_ci * will fix the top bit if needed and is a no-op otherwise.
2548c2ecf20Sopenharmony_ci */
2558c2ecf20Sopenharmony_cistatic inline unsigned long fix_addr(unsigned long addr)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_64
2588c2ecf20Sopenharmony_ci	return (long)(addr << 1) >> 1;
2598c2ecf20Sopenharmony_ci#else
2608c2ecf20Sopenharmony_ci	return addr;
2618c2ecf20Sopenharmony_ci#endif
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic unsigned long __cpa_addr(struct cpa_data *cpa, unsigned long idx)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	if (cpa->flags & CPA_PAGES_ARRAY) {
2678c2ecf20Sopenharmony_ci		struct page *page = cpa->pages[idx];
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci		if (unlikely(PageHighMem(page)))
2708c2ecf20Sopenharmony_ci			return 0;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci		return (unsigned long)page_address(page);
2738c2ecf20Sopenharmony_ci	}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	if (cpa->flags & CPA_ARRAY)
2768c2ecf20Sopenharmony_ci		return cpa->vaddr[idx];
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	return *cpa->vaddr + idx * PAGE_SIZE;
2798c2ecf20Sopenharmony_ci}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci/*
2828c2ecf20Sopenharmony_ci * Flushing functions
2838c2ecf20Sopenharmony_ci */
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic void clflush_cache_range_opt(void *vaddr, unsigned int size)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	const unsigned long clflush_size = boot_cpu_data.x86_clflush_size;
2888c2ecf20Sopenharmony_ci	void *p = (void *)((unsigned long)vaddr & ~(clflush_size - 1));
2898c2ecf20Sopenharmony_ci	void *vend = vaddr + size;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	if (p >= vend)
2928c2ecf20Sopenharmony_ci		return;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	for (; p < vend; p += clflush_size)
2958c2ecf20Sopenharmony_ci		clflushopt(p);
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci/**
2998c2ecf20Sopenharmony_ci * clflush_cache_range - flush a cache range with clflush
3008c2ecf20Sopenharmony_ci * @vaddr:	virtual start address
3018c2ecf20Sopenharmony_ci * @size:	number of bytes to flush
3028c2ecf20Sopenharmony_ci *
3038c2ecf20Sopenharmony_ci * CLFLUSHOPT is an unordered instruction which needs fencing with MFENCE or
3048c2ecf20Sopenharmony_ci * SFENCE to avoid ordering issues.
3058c2ecf20Sopenharmony_ci */
3068c2ecf20Sopenharmony_civoid clflush_cache_range(void *vaddr, unsigned int size)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	mb();
3098c2ecf20Sopenharmony_ci	clflush_cache_range_opt(vaddr, size);
3108c2ecf20Sopenharmony_ci	mb();
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(clflush_cache_range);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_HAS_PMEM_API
3158c2ecf20Sopenharmony_civoid arch_invalidate_pmem(void *addr, size_t size)
3168c2ecf20Sopenharmony_ci{
3178c2ecf20Sopenharmony_ci	clflush_cache_range(addr, size);
3188c2ecf20Sopenharmony_ci}
3198c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(arch_invalidate_pmem);
3208c2ecf20Sopenharmony_ci#endif
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_cistatic void __cpa_flush_all(void *arg)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	unsigned long cache = (unsigned long)arg;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	/*
3278c2ecf20Sopenharmony_ci	 * Flush all to work around Errata in early athlons regarding
3288c2ecf20Sopenharmony_ci	 * large page flushing.
3298c2ecf20Sopenharmony_ci	 */
3308c2ecf20Sopenharmony_ci	__flush_tlb_all();
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	if (cache && boot_cpu_data.x86 >= 4)
3338c2ecf20Sopenharmony_ci		wbinvd();
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_cistatic void cpa_flush_all(unsigned long cache)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	BUG_ON(irqs_disabled() && !early_boot_irqs_disabled);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	on_each_cpu(__cpa_flush_all, (void *) cache, 1);
3418c2ecf20Sopenharmony_ci}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_cistatic void __cpa_flush_tlb(void *data)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	struct cpa_data *cpa = data;
3468c2ecf20Sopenharmony_ci	unsigned int i;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	for (i = 0; i < cpa->numpages; i++)
3498c2ecf20Sopenharmony_ci		flush_tlb_one_kernel(fix_addr(__cpa_addr(cpa, i)));
3508c2ecf20Sopenharmony_ci}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_cistatic void cpa_flush(struct cpa_data *data, int cache)
3538c2ecf20Sopenharmony_ci{
3548c2ecf20Sopenharmony_ci	struct cpa_data *cpa = data;
3558c2ecf20Sopenharmony_ci	unsigned int i;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	BUG_ON(irqs_disabled() && !early_boot_irqs_disabled);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	if (cache && !static_cpu_has(X86_FEATURE_CLFLUSH)) {
3608c2ecf20Sopenharmony_ci		cpa_flush_all(cache);
3618c2ecf20Sopenharmony_ci		return;
3628c2ecf20Sopenharmony_ci	}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	if (cpa->force_flush_all || cpa->numpages > tlb_single_page_flush_ceiling)
3658c2ecf20Sopenharmony_ci		flush_tlb_all();
3668c2ecf20Sopenharmony_ci	else
3678c2ecf20Sopenharmony_ci		on_each_cpu(__cpa_flush_tlb, cpa, 1);
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	if (!cache)
3708c2ecf20Sopenharmony_ci		return;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	mb();
3738c2ecf20Sopenharmony_ci	for (i = 0; i < cpa->numpages; i++) {
3748c2ecf20Sopenharmony_ci		unsigned long addr = __cpa_addr(cpa, i);
3758c2ecf20Sopenharmony_ci		unsigned int level;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci		pte_t *pte = lookup_address(addr, &level);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci		/*
3808c2ecf20Sopenharmony_ci		 * Only flush present addresses:
3818c2ecf20Sopenharmony_ci		 */
3828c2ecf20Sopenharmony_ci		if (pte && (pte_val(*pte) & _PAGE_PRESENT))
3838c2ecf20Sopenharmony_ci			clflush_cache_range_opt((void *)fix_addr(addr), PAGE_SIZE);
3848c2ecf20Sopenharmony_ci	}
3858c2ecf20Sopenharmony_ci	mb();
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_cistatic bool overlaps(unsigned long r1_start, unsigned long r1_end,
3898c2ecf20Sopenharmony_ci		     unsigned long r2_start, unsigned long r2_end)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	return (r1_start <= r2_end && r1_end >= r2_start) ||
3928c2ecf20Sopenharmony_ci		(r2_start <= r1_end && r2_end >= r1_start);
3938c2ecf20Sopenharmony_ci}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_BIOS
3968c2ecf20Sopenharmony_ci/*
3978c2ecf20Sopenharmony_ci * The BIOS area between 640k and 1Mb needs to be executable for PCI BIOS
3988c2ecf20Sopenharmony_ci * based config access (CONFIG_PCI_GOBIOS) support.
3998c2ecf20Sopenharmony_ci */
4008c2ecf20Sopenharmony_ci#define BIOS_PFN	PFN_DOWN(BIOS_BEGIN)
4018c2ecf20Sopenharmony_ci#define BIOS_PFN_END	PFN_DOWN(BIOS_END - 1)
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_cistatic pgprotval_t protect_pci_bios(unsigned long spfn, unsigned long epfn)
4048c2ecf20Sopenharmony_ci{
4058c2ecf20Sopenharmony_ci	if (pcibios_enabled && overlaps(spfn, epfn, BIOS_PFN, BIOS_PFN_END))
4068c2ecf20Sopenharmony_ci		return _PAGE_NX;
4078c2ecf20Sopenharmony_ci	return 0;
4088c2ecf20Sopenharmony_ci}
4098c2ecf20Sopenharmony_ci#else
4108c2ecf20Sopenharmony_cistatic pgprotval_t protect_pci_bios(unsigned long spfn, unsigned long epfn)
4118c2ecf20Sopenharmony_ci{
4128c2ecf20Sopenharmony_ci	return 0;
4138c2ecf20Sopenharmony_ci}
4148c2ecf20Sopenharmony_ci#endif
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci/*
4178c2ecf20Sopenharmony_ci * The .rodata section needs to be read-only. Using the pfn catches all
4188c2ecf20Sopenharmony_ci * aliases.  This also includes __ro_after_init, so do not enforce until
4198c2ecf20Sopenharmony_ci * kernel_set_to_readonly is true.
4208c2ecf20Sopenharmony_ci */
4218c2ecf20Sopenharmony_cistatic pgprotval_t protect_rodata(unsigned long spfn, unsigned long epfn)
4228c2ecf20Sopenharmony_ci{
4238c2ecf20Sopenharmony_ci	unsigned long epfn_ro, spfn_ro = PFN_DOWN(__pa_symbol(__start_rodata));
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	/*
4268c2ecf20Sopenharmony_ci	 * Note: __end_rodata is at page aligned and not inclusive, so
4278c2ecf20Sopenharmony_ci	 * subtract 1 to get the last enforced PFN in the rodata area.
4288c2ecf20Sopenharmony_ci	 */
4298c2ecf20Sopenharmony_ci	epfn_ro = PFN_DOWN(__pa_symbol(__end_rodata)) - 1;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	if (kernel_set_to_readonly && overlaps(spfn, epfn, spfn_ro, epfn_ro))
4328c2ecf20Sopenharmony_ci		return _PAGE_RW;
4338c2ecf20Sopenharmony_ci	return 0;
4348c2ecf20Sopenharmony_ci}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci/*
4378c2ecf20Sopenharmony_ci * Protect kernel text against becoming non executable by forbidding
4388c2ecf20Sopenharmony_ci * _PAGE_NX.  This protects only the high kernel mapping (_text -> _etext)
4398c2ecf20Sopenharmony_ci * out of which the kernel actually executes.  Do not protect the low
4408c2ecf20Sopenharmony_ci * mapping.
4418c2ecf20Sopenharmony_ci *
4428c2ecf20Sopenharmony_ci * This does not cover __inittext since that is gone after boot.
4438c2ecf20Sopenharmony_ci */
4448c2ecf20Sopenharmony_cistatic pgprotval_t protect_kernel_text(unsigned long start, unsigned long end)
4458c2ecf20Sopenharmony_ci{
4468c2ecf20Sopenharmony_ci	unsigned long t_end = (unsigned long)_etext - 1;
4478c2ecf20Sopenharmony_ci	unsigned long t_start = (unsigned long)_text;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	if (overlaps(start, end, t_start, t_end))
4508c2ecf20Sopenharmony_ci		return _PAGE_NX;
4518c2ecf20Sopenharmony_ci	return 0;
4528c2ecf20Sopenharmony_ci}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci#if defined(CONFIG_X86_64)
4558c2ecf20Sopenharmony_ci/*
4568c2ecf20Sopenharmony_ci * Once the kernel maps the text as RO (kernel_set_to_readonly is set),
4578c2ecf20Sopenharmony_ci * kernel text mappings for the large page aligned text, rodata sections
4588c2ecf20Sopenharmony_ci * will be always read-only. For the kernel identity mappings covering the
4598c2ecf20Sopenharmony_ci * holes caused by this alignment can be anything that user asks.
4608c2ecf20Sopenharmony_ci *
4618c2ecf20Sopenharmony_ci * This will preserve the large page mappings for kernel text/data at no
4628c2ecf20Sopenharmony_ci * extra cost.
4638c2ecf20Sopenharmony_ci */
4648c2ecf20Sopenharmony_cistatic pgprotval_t protect_kernel_text_ro(unsigned long start,
4658c2ecf20Sopenharmony_ci					  unsigned long end)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	unsigned long t_end = (unsigned long)__end_rodata_hpage_align - 1;
4688c2ecf20Sopenharmony_ci	unsigned long t_start = (unsigned long)_text;
4698c2ecf20Sopenharmony_ci	unsigned int level;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	if (!kernel_set_to_readonly || !overlaps(start, end, t_start, t_end))
4728c2ecf20Sopenharmony_ci		return 0;
4738c2ecf20Sopenharmony_ci	/*
4748c2ecf20Sopenharmony_ci	 * Don't enforce the !RW mapping for the kernel text mapping, if
4758c2ecf20Sopenharmony_ci	 * the current mapping is already using small page mapping.  No
4768c2ecf20Sopenharmony_ci	 * need to work hard to preserve large page mappings in this case.
4778c2ecf20Sopenharmony_ci	 *
4788c2ecf20Sopenharmony_ci	 * This also fixes the Linux Xen paravirt guest boot failure caused
4798c2ecf20Sopenharmony_ci	 * by unexpected read-only mappings for kernel identity
4808c2ecf20Sopenharmony_ci	 * mappings. In this paravirt guest case, the kernel text mapping
4818c2ecf20Sopenharmony_ci	 * and the kernel identity mapping share the same page-table pages,
4828c2ecf20Sopenharmony_ci	 * so the protections for kernel text and identity mappings have to
4838c2ecf20Sopenharmony_ci	 * be the same.
4848c2ecf20Sopenharmony_ci	 */
4858c2ecf20Sopenharmony_ci	if (lookup_address(start, &level) && (level != PG_LEVEL_4K))
4868c2ecf20Sopenharmony_ci		return _PAGE_RW;
4878c2ecf20Sopenharmony_ci	return 0;
4888c2ecf20Sopenharmony_ci}
4898c2ecf20Sopenharmony_ci#else
4908c2ecf20Sopenharmony_cistatic pgprotval_t protect_kernel_text_ro(unsigned long start,
4918c2ecf20Sopenharmony_ci					  unsigned long end)
4928c2ecf20Sopenharmony_ci{
4938c2ecf20Sopenharmony_ci	return 0;
4948c2ecf20Sopenharmony_ci}
4958c2ecf20Sopenharmony_ci#endif
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_cistatic inline bool conflicts(pgprot_t prot, pgprotval_t val)
4988c2ecf20Sopenharmony_ci{
4998c2ecf20Sopenharmony_ci	return (pgprot_val(prot) & ~val) != pgprot_val(prot);
5008c2ecf20Sopenharmony_ci}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_cistatic inline void check_conflict(int warnlvl, pgprot_t prot, pgprotval_t val,
5038c2ecf20Sopenharmony_ci				  unsigned long start, unsigned long end,
5048c2ecf20Sopenharmony_ci				  unsigned long pfn, const char *txt)
5058c2ecf20Sopenharmony_ci{
5068c2ecf20Sopenharmony_ci	static const char *lvltxt[] = {
5078c2ecf20Sopenharmony_ci		[CPA_CONFLICT]	= "conflict",
5088c2ecf20Sopenharmony_ci		[CPA_PROTECT]	= "protect",
5098c2ecf20Sopenharmony_ci		[CPA_DETECT]	= "detect",
5108c2ecf20Sopenharmony_ci	};
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	if (warnlvl > cpa_warn_level || !conflicts(prot, val))
5138c2ecf20Sopenharmony_ci		return;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	pr_warn("CPA %8s %10s: 0x%016lx - 0x%016lx PFN %lx req %016llx prevent %016llx\n",
5168c2ecf20Sopenharmony_ci		lvltxt[warnlvl], txt, start, end, pfn, (unsigned long long)pgprot_val(prot),
5178c2ecf20Sopenharmony_ci		(unsigned long long)val);
5188c2ecf20Sopenharmony_ci}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci/*
5218c2ecf20Sopenharmony_ci * Certain areas of memory on x86 require very specific protection flags,
5228c2ecf20Sopenharmony_ci * for example the BIOS area or kernel text. Callers don't always get this
5238c2ecf20Sopenharmony_ci * right (again, ioremap() on BIOS memory is not uncommon) so this function
5248c2ecf20Sopenharmony_ci * checks and fixes these known static required protection bits.
5258c2ecf20Sopenharmony_ci */
5268c2ecf20Sopenharmony_cistatic inline pgprot_t static_protections(pgprot_t prot, unsigned long start,
5278c2ecf20Sopenharmony_ci					  unsigned long pfn, unsigned long npg,
5288c2ecf20Sopenharmony_ci					  unsigned long lpsize, int warnlvl)
5298c2ecf20Sopenharmony_ci{
5308c2ecf20Sopenharmony_ci	pgprotval_t forbidden, res;
5318c2ecf20Sopenharmony_ci	unsigned long end;
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	/*
5348c2ecf20Sopenharmony_ci	 * There is no point in checking RW/NX conflicts when the requested
5358c2ecf20Sopenharmony_ci	 * mapping is setting the page !PRESENT.
5368c2ecf20Sopenharmony_ci	 */
5378c2ecf20Sopenharmony_ci	if (!(pgprot_val(prot) & _PAGE_PRESENT))
5388c2ecf20Sopenharmony_ci		return prot;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	/* Operate on the virtual address */
5418c2ecf20Sopenharmony_ci	end = start + npg * PAGE_SIZE - 1;
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	res = protect_kernel_text(start, end);
5448c2ecf20Sopenharmony_ci	check_conflict(warnlvl, prot, res, start, end, pfn, "Text NX");
5458c2ecf20Sopenharmony_ci	forbidden = res;
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	/*
5488c2ecf20Sopenharmony_ci	 * Special case to preserve a large page. If the change spawns the
5498c2ecf20Sopenharmony_ci	 * full large page mapping then there is no point to split it
5508c2ecf20Sopenharmony_ci	 * up. Happens with ftrace and is going to be removed once ftrace
5518c2ecf20Sopenharmony_ci	 * switched to text_poke().
5528c2ecf20Sopenharmony_ci	 */
5538c2ecf20Sopenharmony_ci	if (lpsize != (npg * PAGE_SIZE) || (start & (lpsize - 1))) {
5548c2ecf20Sopenharmony_ci		res = protect_kernel_text_ro(start, end);
5558c2ecf20Sopenharmony_ci		check_conflict(warnlvl, prot, res, start, end, pfn, "Text RO");
5568c2ecf20Sopenharmony_ci		forbidden |= res;
5578c2ecf20Sopenharmony_ci	}
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	/* Check the PFN directly */
5608c2ecf20Sopenharmony_ci	res = protect_pci_bios(pfn, pfn + npg - 1);
5618c2ecf20Sopenharmony_ci	check_conflict(warnlvl, prot, res, start, end, pfn, "PCIBIOS NX");
5628c2ecf20Sopenharmony_ci	forbidden |= res;
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	res = protect_rodata(pfn, pfn + npg - 1);
5658c2ecf20Sopenharmony_ci	check_conflict(warnlvl, prot, res, start, end, pfn, "Rodata RO");
5668c2ecf20Sopenharmony_ci	forbidden |= res;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	return __pgprot(pgprot_val(prot) & ~forbidden);
5698c2ecf20Sopenharmony_ci}
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci/*
5728c2ecf20Sopenharmony_ci * Lookup the page table entry for a virtual address in a specific pgd.
5738c2ecf20Sopenharmony_ci * Return a pointer to the entry and the level of the mapping.
5748c2ecf20Sopenharmony_ci */
5758c2ecf20Sopenharmony_cipte_t *lookup_address_in_pgd(pgd_t *pgd, unsigned long address,
5768c2ecf20Sopenharmony_ci			     unsigned int *level)
5778c2ecf20Sopenharmony_ci{
5788c2ecf20Sopenharmony_ci	p4d_t *p4d;
5798c2ecf20Sopenharmony_ci	pud_t *pud;
5808c2ecf20Sopenharmony_ci	pmd_t *pmd;
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	*level = PG_LEVEL_NONE;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	if (pgd_none(*pgd))
5858c2ecf20Sopenharmony_ci		return NULL;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	p4d = p4d_offset(pgd, address);
5888c2ecf20Sopenharmony_ci	if (p4d_none(*p4d))
5898c2ecf20Sopenharmony_ci		return NULL;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	*level = PG_LEVEL_512G;
5928c2ecf20Sopenharmony_ci	if (p4d_large(*p4d) || !p4d_present(*p4d))
5938c2ecf20Sopenharmony_ci		return (pte_t *)p4d;
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	pud = pud_offset(p4d, address);
5968c2ecf20Sopenharmony_ci	if (pud_none(*pud))
5978c2ecf20Sopenharmony_ci		return NULL;
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	*level = PG_LEVEL_1G;
6008c2ecf20Sopenharmony_ci	if (pud_large(*pud) || !pud_present(*pud))
6018c2ecf20Sopenharmony_ci		return (pte_t *)pud;
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	pmd = pmd_offset(pud, address);
6048c2ecf20Sopenharmony_ci	if (pmd_none(*pmd))
6058c2ecf20Sopenharmony_ci		return NULL;
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	*level = PG_LEVEL_2M;
6088c2ecf20Sopenharmony_ci	if (pmd_large(*pmd) || !pmd_present(*pmd))
6098c2ecf20Sopenharmony_ci		return (pte_t *)pmd;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	*level = PG_LEVEL_4K;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	return pte_offset_kernel(pmd, address);
6148c2ecf20Sopenharmony_ci}
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci/*
6178c2ecf20Sopenharmony_ci * Lookup the page table entry for a virtual address. Return a pointer
6188c2ecf20Sopenharmony_ci * to the entry and the level of the mapping.
6198c2ecf20Sopenharmony_ci *
6208c2ecf20Sopenharmony_ci * Note: We return pud and pmd either when the entry is marked large
6218c2ecf20Sopenharmony_ci * or when the present bit is not set. Otherwise we would return a
6228c2ecf20Sopenharmony_ci * pointer to a nonexisting mapping.
6238c2ecf20Sopenharmony_ci */
6248c2ecf20Sopenharmony_cipte_t *lookup_address(unsigned long address, unsigned int *level)
6258c2ecf20Sopenharmony_ci{
6268c2ecf20Sopenharmony_ci	return lookup_address_in_pgd(pgd_offset_k(address), address, level);
6278c2ecf20Sopenharmony_ci}
6288c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(lookup_address);
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci/*
6318c2ecf20Sopenharmony_ci * Lookup the page table entry for a virtual address in a given mm. Return a
6328c2ecf20Sopenharmony_ci * pointer to the entry and the level of the mapping.
6338c2ecf20Sopenharmony_ci */
6348c2ecf20Sopenharmony_cipte_t *lookup_address_in_mm(struct mm_struct *mm, unsigned long address,
6358c2ecf20Sopenharmony_ci			    unsigned int *level)
6368c2ecf20Sopenharmony_ci{
6378c2ecf20Sopenharmony_ci	return lookup_address_in_pgd(pgd_offset(mm, address), address, level);
6388c2ecf20Sopenharmony_ci}
6398c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(lookup_address_in_mm);
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_cistatic pte_t *_lookup_address_cpa(struct cpa_data *cpa, unsigned long address,
6428c2ecf20Sopenharmony_ci				  unsigned int *level)
6438c2ecf20Sopenharmony_ci{
6448c2ecf20Sopenharmony_ci	if (cpa->pgd)
6458c2ecf20Sopenharmony_ci		return lookup_address_in_pgd(cpa->pgd + pgd_index(address),
6468c2ecf20Sopenharmony_ci					       address, level);
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	return lookup_address(address, level);
6498c2ecf20Sopenharmony_ci}
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci/*
6528c2ecf20Sopenharmony_ci * Lookup the PMD entry for a virtual address. Return a pointer to the entry
6538c2ecf20Sopenharmony_ci * or NULL if not present.
6548c2ecf20Sopenharmony_ci */
6558c2ecf20Sopenharmony_cipmd_t *lookup_pmd_address(unsigned long address)
6568c2ecf20Sopenharmony_ci{
6578c2ecf20Sopenharmony_ci	pgd_t *pgd;
6588c2ecf20Sopenharmony_ci	p4d_t *p4d;
6598c2ecf20Sopenharmony_ci	pud_t *pud;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	pgd = pgd_offset_k(address);
6628c2ecf20Sopenharmony_ci	if (pgd_none(*pgd))
6638c2ecf20Sopenharmony_ci		return NULL;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	p4d = p4d_offset(pgd, address);
6668c2ecf20Sopenharmony_ci	if (p4d_none(*p4d) || p4d_large(*p4d) || !p4d_present(*p4d))
6678c2ecf20Sopenharmony_ci		return NULL;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	pud = pud_offset(p4d, address);
6708c2ecf20Sopenharmony_ci	if (pud_none(*pud) || pud_large(*pud) || !pud_present(*pud))
6718c2ecf20Sopenharmony_ci		return NULL;
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	return pmd_offset(pud, address);
6748c2ecf20Sopenharmony_ci}
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci/*
6778c2ecf20Sopenharmony_ci * This is necessary because __pa() does not work on some
6788c2ecf20Sopenharmony_ci * kinds of memory, like vmalloc() or the alloc_remap()
6798c2ecf20Sopenharmony_ci * areas on 32-bit NUMA systems.  The percpu areas can
6808c2ecf20Sopenharmony_ci * end up in this kind of memory, for instance.
6818c2ecf20Sopenharmony_ci *
6828c2ecf20Sopenharmony_ci * This could be optimized, but it is only intended to be
6838c2ecf20Sopenharmony_ci * used at inititalization time, and keeping it
6848c2ecf20Sopenharmony_ci * unoptimized should increase the testing coverage for
6858c2ecf20Sopenharmony_ci * the more obscure platforms.
6868c2ecf20Sopenharmony_ci */
6878c2ecf20Sopenharmony_ciphys_addr_t slow_virt_to_phys(void *__virt_addr)
6888c2ecf20Sopenharmony_ci{
6898c2ecf20Sopenharmony_ci	unsigned long virt_addr = (unsigned long)__virt_addr;
6908c2ecf20Sopenharmony_ci	phys_addr_t phys_addr;
6918c2ecf20Sopenharmony_ci	unsigned long offset;
6928c2ecf20Sopenharmony_ci	enum pg_level level;
6938c2ecf20Sopenharmony_ci	pte_t *pte;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	pte = lookup_address(virt_addr, &level);
6968c2ecf20Sopenharmony_ci	BUG_ON(!pte);
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	/*
6998c2ecf20Sopenharmony_ci	 * pXX_pfn() returns unsigned long, which must be cast to phys_addr_t
7008c2ecf20Sopenharmony_ci	 * before being left-shifted PAGE_SHIFT bits -- this trick is to
7018c2ecf20Sopenharmony_ci	 * make 32-PAE kernel work correctly.
7028c2ecf20Sopenharmony_ci	 */
7038c2ecf20Sopenharmony_ci	switch (level) {
7048c2ecf20Sopenharmony_ci	case PG_LEVEL_1G:
7058c2ecf20Sopenharmony_ci		phys_addr = (phys_addr_t)pud_pfn(*(pud_t *)pte) << PAGE_SHIFT;
7068c2ecf20Sopenharmony_ci		offset = virt_addr & ~PUD_PAGE_MASK;
7078c2ecf20Sopenharmony_ci		break;
7088c2ecf20Sopenharmony_ci	case PG_LEVEL_2M:
7098c2ecf20Sopenharmony_ci		phys_addr = (phys_addr_t)pmd_pfn(*(pmd_t *)pte) << PAGE_SHIFT;
7108c2ecf20Sopenharmony_ci		offset = virt_addr & ~PMD_PAGE_MASK;
7118c2ecf20Sopenharmony_ci		break;
7128c2ecf20Sopenharmony_ci	default:
7138c2ecf20Sopenharmony_ci		phys_addr = (phys_addr_t)pte_pfn(*pte) << PAGE_SHIFT;
7148c2ecf20Sopenharmony_ci		offset = virt_addr & ~PAGE_MASK;
7158c2ecf20Sopenharmony_ci	}
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	return (phys_addr_t)(phys_addr | offset);
7188c2ecf20Sopenharmony_ci}
7198c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(slow_virt_to_phys);
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci/*
7228c2ecf20Sopenharmony_ci * Set the new pmd in all the pgds we know about:
7238c2ecf20Sopenharmony_ci */
7248c2ecf20Sopenharmony_cistatic void __set_pmd_pte(pte_t *kpte, unsigned long address, pte_t pte)
7258c2ecf20Sopenharmony_ci{
7268c2ecf20Sopenharmony_ci	/* change init_mm */
7278c2ecf20Sopenharmony_ci	set_pte_atomic(kpte, pte);
7288c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_32
7298c2ecf20Sopenharmony_ci	if (!SHARED_KERNEL_PMD) {
7308c2ecf20Sopenharmony_ci		struct page *page;
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci		list_for_each_entry(page, &pgd_list, lru) {
7338c2ecf20Sopenharmony_ci			pgd_t *pgd;
7348c2ecf20Sopenharmony_ci			p4d_t *p4d;
7358c2ecf20Sopenharmony_ci			pud_t *pud;
7368c2ecf20Sopenharmony_ci			pmd_t *pmd;
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci			pgd = (pgd_t *)page_address(page) + pgd_index(address);
7398c2ecf20Sopenharmony_ci			p4d = p4d_offset(pgd, address);
7408c2ecf20Sopenharmony_ci			pud = pud_offset(p4d, address);
7418c2ecf20Sopenharmony_ci			pmd = pmd_offset(pud, address);
7428c2ecf20Sopenharmony_ci			set_pte_atomic((pte_t *)pmd, pte);
7438c2ecf20Sopenharmony_ci		}
7448c2ecf20Sopenharmony_ci	}
7458c2ecf20Sopenharmony_ci#endif
7468c2ecf20Sopenharmony_ci}
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_cistatic pgprot_t pgprot_clear_protnone_bits(pgprot_t prot)
7498c2ecf20Sopenharmony_ci{
7508c2ecf20Sopenharmony_ci	/*
7518c2ecf20Sopenharmony_ci	 * _PAGE_GLOBAL means "global page" for present PTEs.
7528c2ecf20Sopenharmony_ci	 * But, it is also used to indicate _PAGE_PROTNONE
7538c2ecf20Sopenharmony_ci	 * for non-present PTEs.
7548c2ecf20Sopenharmony_ci	 *
7558c2ecf20Sopenharmony_ci	 * This ensures that a _PAGE_GLOBAL PTE going from
7568c2ecf20Sopenharmony_ci	 * present to non-present is not confused as
7578c2ecf20Sopenharmony_ci	 * _PAGE_PROTNONE.
7588c2ecf20Sopenharmony_ci	 */
7598c2ecf20Sopenharmony_ci	if (!(pgprot_val(prot) & _PAGE_PRESENT))
7608c2ecf20Sopenharmony_ci		pgprot_val(prot) &= ~_PAGE_GLOBAL;
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	return prot;
7638c2ecf20Sopenharmony_ci}
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_cistatic int __should_split_large_page(pte_t *kpte, unsigned long address,
7668c2ecf20Sopenharmony_ci				     struct cpa_data *cpa)
7678c2ecf20Sopenharmony_ci{
7688c2ecf20Sopenharmony_ci	unsigned long numpages, pmask, psize, lpaddr, pfn, old_pfn;
7698c2ecf20Sopenharmony_ci	pgprot_t old_prot, new_prot, req_prot, chk_prot;
7708c2ecf20Sopenharmony_ci	pte_t new_pte, *tmp;
7718c2ecf20Sopenharmony_ci	enum pg_level level;
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	/*
7748c2ecf20Sopenharmony_ci	 * Check for races, another CPU might have split this page
7758c2ecf20Sopenharmony_ci	 * up already:
7768c2ecf20Sopenharmony_ci	 */
7778c2ecf20Sopenharmony_ci	tmp = _lookup_address_cpa(cpa, address, &level);
7788c2ecf20Sopenharmony_ci	if (tmp != kpte)
7798c2ecf20Sopenharmony_ci		return 1;
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	switch (level) {
7828c2ecf20Sopenharmony_ci	case PG_LEVEL_2M:
7838c2ecf20Sopenharmony_ci		old_prot = pmd_pgprot(*(pmd_t *)kpte);
7848c2ecf20Sopenharmony_ci		old_pfn = pmd_pfn(*(pmd_t *)kpte);
7858c2ecf20Sopenharmony_ci		cpa_inc_2m_checked();
7868c2ecf20Sopenharmony_ci		break;
7878c2ecf20Sopenharmony_ci	case PG_LEVEL_1G:
7888c2ecf20Sopenharmony_ci		old_prot = pud_pgprot(*(pud_t *)kpte);
7898c2ecf20Sopenharmony_ci		old_pfn = pud_pfn(*(pud_t *)kpte);
7908c2ecf20Sopenharmony_ci		cpa_inc_1g_checked();
7918c2ecf20Sopenharmony_ci		break;
7928c2ecf20Sopenharmony_ci	default:
7938c2ecf20Sopenharmony_ci		return -EINVAL;
7948c2ecf20Sopenharmony_ci	}
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	psize = page_level_size(level);
7978c2ecf20Sopenharmony_ci	pmask = page_level_mask(level);
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	/*
8008c2ecf20Sopenharmony_ci	 * Calculate the number of pages, which fit into this large
8018c2ecf20Sopenharmony_ci	 * page starting at address:
8028c2ecf20Sopenharmony_ci	 */
8038c2ecf20Sopenharmony_ci	lpaddr = (address + psize) & pmask;
8048c2ecf20Sopenharmony_ci	numpages = (lpaddr - address) >> PAGE_SHIFT;
8058c2ecf20Sopenharmony_ci	if (numpages < cpa->numpages)
8068c2ecf20Sopenharmony_ci		cpa->numpages = numpages;
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	/*
8098c2ecf20Sopenharmony_ci	 * We are safe now. Check whether the new pgprot is the same:
8108c2ecf20Sopenharmony_ci	 * Convert protection attributes to 4k-format, as cpa->mask* are set
8118c2ecf20Sopenharmony_ci	 * up accordingly.
8128c2ecf20Sopenharmony_ci	 */
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	/* Clear PSE (aka _PAGE_PAT) and move PAT bit to correct position */
8158c2ecf20Sopenharmony_ci	req_prot = pgprot_large_2_4k(old_prot);
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	pgprot_val(req_prot) &= ~pgprot_val(cpa->mask_clr);
8188c2ecf20Sopenharmony_ci	pgprot_val(req_prot) |= pgprot_val(cpa->mask_set);
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	/*
8218c2ecf20Sopenharmony_ci	 * req_prot is in format of 4k pages. It must be converted to large
8228c2ecf20Sopenharmony_ci	 * page format: the caching mode includes the PAT bit located at
8238c2ecf20Sopenharmony_ci	 * different bit positions in the two formats.
8248c2ecf20Sopenharmony_ci	 */
8258c2ecf20Sopenharmony_ci	req_prot = pgprot_4k_2_large(req_prot);
8268c2ecf20Sopenharmony_ci	req_prot = pgprot_clear_protnone_bits(req_prot);
8278c2ecf20Sopenharmony_ci	if (pgprot_val(req_prot) & _PAGE_PRESENT)
8288c2ecf20Sopenharmony_ci		pgprot_val(req_prot) |= _PAGE_PSE;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	/*
8318c2ecf20Sopenharmony_ci	 * old_pfn points to the large page base pfn. So we need to add the
8328c2ecf20Sopenharmony_ci	 * offset of the virtual address:
8338c2ecf20Sopenharmony_ci	 */
8348c2ecf20Sopenharmony_ci	pfn = old_pfn + ((address & (psize - 1)) >> PAGE_SHIFT);
8358c2ecf20Sopenharmony_ci	cpa->pfn = pfn;
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	/*
8388c2ecf20Sopenharmony_ci	 * Calculate the large page base address and the number of 4K pages
8398c2ecf20Sopenharmony_ci	 * in the large page
8408c2ecf20Sopenharmony_ci	 */
8418c2ecf20Sopenharmony_ci	lpaddr = address & pmask;
8428c2ecf20Sopenharmony_ci	numpages = psize >> PAGE_SHIFT;
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	/*
8458c2ecf20Sopenharmony_ci	 * Sanity check that the existing mapping is correct versus the static
8468c2ecf20Sopenharmony_ci	 * protections. static_protections() guards against !PRESENT, so no
8478c2ecf20Sopenharmony_ci	 * extra conditional required here.
8488c2ecf20Sopenharmony_ci	 */
8498c2ecf20Sopenharmony_ci	chk_prot = static_protections(old_prot, lpaddr, old_pfn, numpages,
8508c2ecf20Sopenharmony_ci				      psize, CPA_CONFLICT);
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(pgprot_val(chk_prot) != pgprot_val(old_prot))) {
8538c2ecf20Sopenharmony_ci		/*
8548c2ecf20Sopenharmony_ci		 * Split the large page and tell the split code to
8558c2ecf20Sopenharmony_ci		 * enforce static protections.
8568c2ecf20Sopenharmony_ci		 */
8578c2ecf20Sopenharmony_ci		cpa->force_static_prot = 1;
8588c2ecf20Sopenharmony_ci		return 1;
8598c2ecf20Sopenharmony_ci	}
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	/*
8628c2ecf20Sopenharmony_ci	 * Optimization: If the requested pgprot is the same as the current
8638c2ecf20Sopenharmony_ci	 * pgprot, then the large page can be preserved and no updates are
8648c2ecf20Sopenharmony_ci	 * required independent of alignment and length of the requested
8658c2ecf20Sopenharmony_ci	 * range. The above already established that the current pgprot is
8668c2ecf20Sopenharmony_ci	 * correct, which in consequence makes the requested pgprot correct
8678c2ecf20Sopenharmony_ci	 * as well if it is the same. The static protection scan below will
8688c2ecf20Sopenharmony_ci	 * not come to a different conclusion.
8698c2ecf20Sopenharmony_ci	 */
8708c2ecf20Sopenharmony_ci	if (pgprot_val(req_prot) == pgprot_val(old_prot)) {
8718c2ecf20Sopenharmony_ci		cpa_inc_lp_sameprot(level);
8728c2ecf20Sopenharmony_ci		return 0;
8738c2ecf20Sopenharmony_ci	}
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci	/*
8768c2ecf20Sopenharmony_ci	 * If the requested range does not cover the full page, split it up
8778c2ecf20Sopenharmony_ci	 */
8788c2ecf20Sopenharmony_ci	if (address != lpaddr || cpa->numpages != numpages)
8798c2ecf20Sopenharmony_ci		return 1;
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	/*
8828c2ecf20Sopenharmony_ci	 * Check whether the requested pgprot is conflicting with a static
8838c2ecf20Sopenharmony_ci	 * protection requirement in the large page.
8848c2ecf20Sopenharmony_ci	 */
8858c2ecf20Sopenharmony_ci	new_prot = static_protections(req_prot, lpaddr, old_pfn, numpages,
8868c2ecf20Sopenharmony_ci				      psize, CPA_DETECT);
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	/*
8898c2ecf20Sopenharmony_ci	 * If there is a conflict, split the large page.
8908c2ecf20Sopenharmony_ci	 *
8918c2ecf20Sopenharmony_ci	 * There used to be a 4k wise evaluation trying really hard to
8928c2ecf20Sopenharmony_ci	 * preserve the large pages, but experimentation has shown, that this
8938c2ecf20Sopenharmony_ci	 * does not help at all. There might be corner cases which would
8948c2ecf20Sopenharmony_ci	 * preserve one large page occasionally, but it's really not worth the
8958c2ecf20Sopenharmony_ci	 * extra code and cycles for the common case.
8968c2ecf20Sopenharmony_ci	 */
8978c2ecf20Sopenharmony_ci	if (pgprot_val(req_prot) != pgprot_val(new_prot))
8988c2ecf20Sopenharmony_ci		return 1;
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci	/* All checks passed. Update the large page mapping. */
9018c2ecf20Sopenharmony_ci	new_pte = pfn_pte(old_pfn, new_prot);
9028c2ecf20Sopenharmony_ci	__set_pmd_pte(kpte, address, new_pte);
9038c2ecf20Sopenharmony_ci	cpa->flags |= CPA_FLUSHTLB;
9048c2ecf20Sopenharmony_ci	cpa_inc_lp_preserved(level);
9058c2ecf20Sopenharmony_ci	return 0;
9068c2ecf20Sopenharmony_ci}
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_cistatic int should_split_large_page(pte_t *kpte, unsigned long address,
9098c2ecf20Sopenharmony_ci				   struct cpa_data *cpa)
9108c2ecf20Sopenharmony_ci{
9118c2ecf20Sopenharmony_ci	int do_split;
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci	if (cpa->force_split)
9148c2ecf20Sopenharmony_ci		return 1;
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	spin_lock(&pgd_lock);
9178c2ecf20Sopenharmony_ci	do_split = __should_split_large_page(kpte, address, cpa);
9188c2ecf20Sopenharmony_ci	spin_unlock(&pgd_lock);
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci	return do_split;
9218c2ecf20Sopenharmony_ci}
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_cistatic void split_set_pte(struct cpa_data *cpa, pte_t *pte, unsigned long pfn,
9248c2ecf20Sopenharmony_ci			  pgprot_t ref_prot, unsigned long address,
9258c2ecf20Sopenharmony_ci			  unsigned long size)
9268c2ecf20Sopenharmony_ci{
9278c2ecf20Sopenharmony_ci	unsigned int npg = PFN_DOWN(size);
9288c2ecf20Sopenharmony_ci	pgprot_t prot;
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci	/*
9318c2ecf20Sopenharmony_ci	 * If should_split_large_page() discovered an inconsistent mapping,
9328c2ecf20Sopenharmony_ci	 * remove the invalid protection in the split mapping.
9338c2ecf20Sopenharmony_ci	 */
9348c2ecf20Sopenharmony_ci	if (!cpa->force_static_prot)
9358c2ecf20Sopenharmony_ci		goto set;
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci	/* Hand in lpsize = 0 to enforce the protection mechanism */
9388c2ecf20Sopenharmony_ci	prot = static_protections(ref_prot, address, pfn, npg, 0, CPA_PROTECT);
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	if (pgprot_val(prot) == pgprot_val(ref_prot))
9418c2ecf20Sopenharmony_ci		goto set;
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	/*
9448c2ecf20Sopenharmony_ci	 * If this is splitting a PMD, fix it up. PUD splits cannot be
9458c2ecf20Sopenharmony_ci	 * fixed trivially as that would require to rescan the newly
9468c2ecf20Sopenharmony_ci	 * installed PMD mappings after returning from split_large_page()
9478c2ecf20Sopenharmony_ci	 * so an eventual further split can allocate the necessary PTE
9488c2ecf20Sopenharmony_ci	 * pages. Warn for now and revisit it in case this actually
9498c2ecf20Sopenharmony_ci	 * happens.
9508c2ecf20Sopenharmony_ci	 */
9518c2ecf20Sopenharmony_ci	if (size == PAGE_SIZE)
9528c2ecf20Sopenharmony_ci		ref_prot = prot;
9538c2ecf20Sopenharmony_ci	else
9548c2ecf20Sopenharmony_ci		pr_warn_once("CPA: Cannot fixup static protections for PUD split\n");
9558c2ecf20Sopenharmony_ciset:
9568c2ecf20Sopenharmony_ci	set_pte(pte, pfn_pte(pfn, ref_prot));
9578c2ecf20Sopenharmony_ci}
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_cistatic int
9608c2ecf20Sopenharmony_ci__split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address,
9618c2ecf20Sopenharmony_ci		   struct page *base)
9628c2ecf20Sopenharmony_ci{
9638c2ecf20Sopenharmony_ci	unsigned long lpaddr, lpinc, ref_pfn, pfn, pfninc = 1;
9648c2ecf20Sopenharmony_ci	pte_t *pbase = (pte_t *)page_address(base);
9658c2ecf20Sopenharmony_ci	unsigned int i, level;
9668c2ecf20Sopenharmony_ci	pgprot_t ref_prot;
9678c2ecf20Sopenharmony_ci	pte_t *tmp;
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci	spin_lock(&pgd_lock);
9708c2ecf20Sopenharmony_ci	/*
9718c2ecf20Sopenharmony_ci	 * Check for races, another CPU might have split this page
9728c2ecf20Sopenharmony_ci	 * up for us already:
9738c2ecf20Sopenharmony_ci	 */
9748c2ecf20Sopenharmony_ci	tmp = _lookup_address_cpa(cpa, address, &level);
9758c2ecf20Sopenharmony_ci	if (tmp != kpte) {
9768c2ecf20Sopenharmony_ci		spin_unlock(&pgd_lock);
9778c2ecf20Sopenharmony_ci		return 1;
9788c2ecf20Sopenharmony_ci	}
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci	paravirt_alloc_pte(&init_mm, page_to_pfn(base));
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	switch (level) {
9838c2ecf20Sopenharmony_ci	case PG_LEVEL_2M:
9848c2ecf20Sopenharmony_ci		ref_prot = pmd_pgprot(*(pmd_t *)kpte);
9858c2ecf20Sopenharmony_ci		/*
9868c2ecf20Sopenharmony_ci		 * Clear PSE (aka _PAGE_PAT) and move
9878c2ecf20Sopenharmony_ci		 * PAT bit to correct position.
9888c2ecf20Sopenharmony_ci		 */
9898c2ecf20Sopenharmony_ci		ref_prot = pgprot_large_2_4k(ref_prot);
9908c2ecf20Sopenharmony_ci		ref_pfn = pmd_pfn(*(pmd_t *)kpte);
9918c2ecf20Sopenharmony_ci		lpaddr = address & PMD_MASK;
9928c2ecf20Sopenharmony_ci		lpinc = PAGE_SIZE;
9938c2ecf20Sopenharmony_ci		break;
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	case PG_LEVEL_1G:
9968c2ecf20Sopenharmony_ci		ref_prot = pud_pgprot(*(pud_t *)kpte);
9978c2ecf20Sopenharmony_ci		ref_pfn = pud_pfn(*(pud_t *)kpte);
9988c2ecf20Sopenharmony_ci		pfninc = PMD_PAGE_SIZE >> PAGE_SHIFT;
9998c2ecf20Sopenharmony_ci		lpaddr = address & PUD_MASK;
10008c2ecf20Sopenharmony_ci		lpinc = PMD_SIZE;
10018c2ecf20Sopenharmony_ci		/*
10028c2ecf20Sopenharmony_ci		 * Clear the PSE flags if the PRESENT flag is not set
10038c2ecf20Sopenharmony_ci		 * otherwise pmd_present/pmd_huge will return true
10048c2ecf20Sopenharmony_ci		 * even on a non present pmd.
10058c2ecf20Sopenharmony_ci		 */
10068c2ecf20Sopenharmony_ci		if (!(pgprot_val(ref_prot) & _PAGE_PRESENT))
10078c2ecf20Sopenharmony_ci			pgprot_val(ref_prot) &= ~_PAGE_PSE;
10088c2ecf20Sopenharmony_ci		break;
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	default:
10118c2ecf20Sopenharmony_ci		spin_unlock(&pgd_lock);
10128c2ecf20Sopenharmony_ci		return 1;
10138c2ecf20Sopenharmony_ci	}
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ci	ref_prot = pgprot_clear_protnone_bits(ref_prot);
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	/*
10188c2ecf20Sopenharmony_ci	 * Get the target pfn from the original entry:
10198c2ecf20Sopenharmony_ci	 */
10208c2ecf20Sopenharmony_ci	pfn = ref_pfn;
10218c2ecf20Sopenharmony_ci	for (i = 0; i < PTRS_PER_PTE; i++, pfn += pfninc, lpaddr += lpinc)
10228c2ecf20Sopenharmony_ci		split_set_pte(cpa, pbase + i, pfn, ref_prot, lpaddr, lpinc);
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	if (virt_addr_valid(address)) {
10258c2ecf20Sopenharmony_ci		unsigned long pfn = PFN_DOWN(__pa(address));
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci		if (pfn_range_is_mapped(pfn, pfn + 1))
10288c2ecf20Sopenharmony_ci			split_page_count(level);
10298c2ecf20Sopenharmony_ci	}
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	/*
10328c2ecf20Sopenharmony_ci	 * Install the new, split up pagetable.
10338c2ecf20Sopenharmony_ci	 *
10348c2ecf20Sopenharmony_ci	 * We use the standard kernel pagetable protections for the new
10358c2ecf20Sopenharmony_ci	 * pagetable protections, the actual ptes set above control the
10368c2ecf20Sopenharmony_ci	 * primary protection behavior:
10378c2ecf20Sopenharmony_ci	 */
10388c2ecf20Sopenharmony_ci	__set_pmd_pte(kpte, address, mk_pte(base, __pgprot(_KERNPG_TABLE)));
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci	/*
10418c2ecf20Sopenharmony_ci	 * Do a global flush tlb after splitting the large page
10428c2ecf20Sopenharmony_ci	 * and before we do the actual change page attribute in the PTE.
10438c2ecf20Sopenharmony_ci	 *
10448c2ecf20Sopenharmony_ci	 * Without this, we violate the TLB application note, that says:
10458c2ecf20Sopenharmony_ci	 * "The TLBs may contain both ordinary and large-page
10468c2ecf20Sopenharmony_ci	 *  translations for a 4-KByte range of linear addresses. This
10478c2ecf20Sopenharmony_ci	 *  may occur if software modifies the paging structures so that
10488c2ecf20Sopenharmony_ci	 *  the page size used for the address range changes. If the two
10498c2ecf20Sopenharmony_ci	 *  translations differ with respect to page frame or attributes
10508c2ecf20Sopenharmony_ci	 *  (e.g., permissions), processor behavior is undefined and may
10518c2ecf20Sopenharmony_ci	 *  be implementation-specific."
10528c2ecf20Sopenharmony_ci	 *
10538c2ecf20Sopenharmony_ci	 * We do this global tlb flush inside the cpa_lock, so that we
10548c2ecf20Sopenharmony_ci	 * don't allow any other cpu, with stale tlb entries change the
10558c2ecf20Sopenharmony_ci	 * page attribute in parallel, that also falls into the
10568c2ecf20Sopenharmony_ci	 * just split large page entry.
10578c2ecf20Sopenharmony_ci	 */
10588c2ecf20Sopenharmony_ci	flush_tlb_all();
10598c2ecf20Sopenharmony_ci	spin_unlock(&pgd_lock);
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	return 0;
10628c2ecf20Sopenharmony_ci}
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_cistatic int split_large_page(struct cpa_data *cpa, pte_t *kpte,
10658c2ecf20Sopenharmony_ci			    unsigned long address)
10668c2ecf20Sopenharmony_ci{
10678c2ecf20Sopenharmony_ci	struct page *base;
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci	if (!debug_pagealloc_enabled())
10708c2ecf20Sopenharmony_ci		spin_unlock(&cpa_lock);
10718c2ecf20Sopenharmony_ci	base = alloc_pages(GFP_KERNEL, 0);
10728c2ecf20Sopenharmony_ci	if (!debug_pagealloc_enabled())
10738c2ecf20Sopenharmony_ci		spin_lock(&cpa_lock);
10748c2ecf20Sopenharmony_ci	if (!base)
10758c2ecf20Sopenharmony_ci		return -ENOMEM;
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	if (__split_large_page(cpa, kpte, address, base))
10788c2ecf20Sopenharmony_ci		__free_page(base);
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci	return 0;
10818c2ecf20Sopenharmony_ci}
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_cistatic bool try_to_free_pte_page(pte_t *pte)
10848c2ecf20Sopenharmony_ci{
10858c2ecf20Sopenharmony_ci	int i;
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci	for (i = 0; i < PTRS_PER_PTE; i++)
10888c2ecf20Sopenharmony_ci		if (!pte_none(pte[i]))
10898c2ecf20Sopenharmony_ci			return false;
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci	free_page((unsigned long)pte);
10928c2ecf20Sopenharmony_ci	return true;
10938c2ecf20Sopenharmony_ci}
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_cistatic bool try_to_free_pmd_page(pmd_t *pmd)
10968c2ecf20Sopenharmony_ci{
10978c2ecf20Sopenharmony_ci	int i;
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_ci	for (i = 0; i < PTRS_PER_PMD; i++)
11008c2ecf20Sopenharmony_ci		if (!pmd_none(pmd[i]))
11018c2ecf20Sopenharmony_ci			return false;
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci	free_page((unsigned long)pmd);
11048c2ecf20Sopenharmony_ci	return true;
11058c2ecf20Sopenharmony_ci}
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_cistatic bool unmap_pte_range(pmd_t *pmd, unsigned long start, unsigned long end)
11088c2ecf20Sopenharmony_ci{
11098c2ecf20Sopenharmony_ci	pte_t *pte = pte_offset_kernel(pmd, start);
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci	while (start < end) {
11128c2ecf20Sopenharmony_ci		set_pte(pte, __pte(0));
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci		start += PAGE_SIZE;
11158c2ecf20Sopenharmony_ci		pte++;
11168c2ecf20Sopenharmony_ci	}
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci	if (try_to_free_pte_page((pte_t *)pmd_page_vaddr(*pmd))) {
11198c2ecf20Sopenharmony_ci		pmd_clear(pmd);
11208c2ecf20Sopenharmony_ci		return true;
11218c2ecf20Sopenharmony_ci	}
11228c2ecf20Sopenharmony_ci	return false;
11238c2ecf20Sopenharmony_ci}
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_cistatic void __unmap_pmd_range(pud_t *pud, pmd_t *pmd,
11268c2ecf20Sopenharmony_ci			      unsigned long start, unsigned long end)
11278c2ecf20Sopenharmony_ci{
11288c2ecf20Sopenharmony_ci	if (unmap_pte_range(pmd, start, end))
11298c2ecf20Sopenharmony_ci		if (try_to_free_pmd_page(pud_pgtable(*pud)))
11308c2ecf20Sopenharmony_ci			pud_clear(pud);
11318c2ecf20Sopenharmony_ci}
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_cistatic void unmap_pmd_range(pud_t *pud, unsigned long start, unsigned long end)
11348c2ecf20Sopenharmony_ci{
11358c2ecf20Sopenharmony_ci	pmd_t *pmd = pmd_offset(pud, start);
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci	/*
11388c2ecf20Sopenharmony_ci	 * Not on a 2MB page boundary?
11398c2ecf20Sopenharmony_ci	 */
11408c2ecf20Sopenharmony_ci	if (start & (PMD_SIZE - 1)) {
11418c2ecf20Sopenharmony_ci		unsigned long next_page = (start + PMD_SIZE) & PMD_MASK;
11428c2ecf20Sopenharmony_ci		unsigned long pre_end = min_t(unsigned long, end, next_page);
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci		__unmap_pmd_range(pud, pmd, start, pre_end);
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci		start = pre_end;
11478c2ecf20Sopenharmony_ci		pmd++;
11488c2ecf20Sopenharmony_ci	}
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci	/*
11518c2ecf20Sopenharmony_ci	 * Try to unmap in 2M chunks.
11528c2ecf20Sopenharmony_ci	 */
11538c2ecf20Sopenharmony_ci	while (end - start >= PMD_SIZE) {
11548c2ecf20Sopenharmony_ci		if (pmd_large(*pmd))
11558c2ecf20Sopenharmony_ci			pmd_clear(pmd);
11568c2ecf20Sopenharmony_ci		else
11578c2ecf20Sopenharmony_ci			__unmap_pmd_range(pud, pmd, start, start + PMD_SIZE);
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci		start += PMD_SIZE;
11608c2ecf20Sopenharmony_ci		pmd++;
11618c2ecf20Sopenharmony_ci	}
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	/*
11648c2ecf20Sopenharmony_ci	 * 4K leftovers?
11658c2ecf20Sopenharmony_ci	 */
11668c2ecf20Sopenharmony_ci	if (start < end)
11678c2ecf20Sopenharmony_ci		return __unmap_pmd_range(pud, pmd, start, end);
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci	/*
11708c2ecf20Sopenharmony_ci	 * Try again to free the PMD page if haven't succeeded above.
11718c2ecf20Sopenharmony_ci	 */
11728c2ecf20Sopenharmony_ci	if (!pud_none(*pud))
11738c2ecf20Sopenharmony_ci		if (try_to_free_pmd_page(pud_pgtable(*pud)))
11748c2ecf20Sopenharmony_ci			pud_clear(pud);
11758c2ecf20Sopenharmony_ci}
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_cistatic void unmap_pud_range(p4d_t *p4d, unsigned long start, unsigned long end)
11788c2ecf20Sopenharmony_ci{
11798c2ecf20Sopenharmony_ci	pud_t *pud = pud_offset(p4d, start);
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci	/*
11828c2ecf20Sopenharmony_ci	 * Not on a GB page boundary?
11838c2ecf20Sopenharmony_ci	 */
11848c2ecf20Sopenharmony_ci	if (start & (PUD_SIZE - 1)) {
11858c2ecf20Sopenharmony_ci		unsigned long next_page = (start + PUD_SIZE) & PUD_MASK;
11868c2ecf20Sopenharmony_ci		unsigned long pre_end	= min_t(unsigned long, end, next_page);
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_ci		unmap_pmd_range(pud, start, pre_end);
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci		start = pre_end;
11918c2ecf20Sopenharmony_ci		pud++;
11928c2ecf20Sopenharmony_ci	}
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci	/*
11958c2ecf20Sopenharmony_ci	 * Try to unmap in 1G chunks?
11968c2ecf20Sopenharmony_ci	 */
11978c2ecf20Sopenharmony_ci	while (end - start >= PUD_SIZE) {
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci		if (pud_large(*pud))
12008c2ecf20Sopenharmony_ci			pud_clear(pud);
12018c2ecf20Sopenharmony_ci		else
12028c2ecf20Sopenharmony_ci			unmap_pmd_range(pud, start, start + PUD_SIZE);
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci		start += PUD_SIZE;
12058c2ecf20Sopenharmony_ci		pud++;
12068c2ecf20Sopenharmony_ci	}
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_ci	/*
12098c2ecf20Sopenharmony_ci	 * 2M leftovers?
12108c2ecf20Sopenharmony_ci	 */
12118c2ecf20Sopenharmony_ci	if (start < end)
12128c2ecf20Sopenharmony_ci		unmap_pmd_range(pud, start, end);
12138c2ecf20Sopenharmony_ci
12148c2ecf20Sopenharmony_ci	/*
12158c2ecf20Sopenharmony_ci	 * No need to try to free the PUD page because we'll free it in
12168c2ecf20Sopenharmony_ci	 * populate_pgd's error path
12178c2ecf20Sopenharmony_ci	 */
12188c2ecf20Sopenharmony_ci}
12198c2ecf20Sopenharmony_ci
12208c2ecf20Sopenharmony_cistatic int alloc_pte_page(pmd_t *pmd)
12218c2ecf20Sopenharmony_ci{
12228c2ecf20Sopenharmony_ci	pte_t *pte = (pte_t *)get_zeroed_page(GFP_KERNEL);
12238c2ecf20Sopenharmony_ci	if (!pte)
12248c2ecf20Sopenharmony_ci		return -1;
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	set_pmd(pmd, __pmd(__pa(pte) | _KERNPG_TABLE));
12278c2ecf20Sopenharmony_ci	return 0;
12288c2ecf20Sopenharmony_ci}
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_cistatic int alloc_pmd_page(pud_t *pud)
12318c2ecf20Sopenharmony_ci{
12328c2ecf20Sopenharmony_ci	pmd_t *pmd = (pmd_t *)get_zeroed_page(GFP_KERNEL);
12338c2ecf20Sopenharmony_ci	if (!pmd)
12348c2ecf20Sopenharmony_ci		return -1;
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_ci	set_pud(pud, __pud(__pa(pmd) | _KERNPG_TABLE));
12378c2ecf20Sopenharmony_ci	return 0;
12388c2ecf20Sopenharmony_ci}
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_cistatic void populate_pte(struct cpa_data *cpa,
12418c2ecf20Sopenharmony_ci			 unsigned long start, unsigned long end,
12428c2ecf20Sopenharmony_ci			 unsigned num_pages, pmd_t *pmd, pgprot_t pgprot)
12438c2ecf20Sopenharmony_ci{
12448c2ecf20Sopenharmony_ci	pte_t *pte;
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ci	pte = pte_offset_kernel(pmd, start);
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci	pgprot = pgprot_clear_protnone_bits(pgprot);
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_ci	while (num_pages-- && start < end) {
12518c2ecf20Sopenharmony_ci		set_pte(pte, pfn_pte(cpa->pfn, pgprot));
12528c2ecf20Sopenharmony_ci
12538c2ecf20Sopenharmony_ci		start	 += PAGE_SIZE;
12548c2ecf20Sopenharmony_ci		cpa->pfn++;
12558c2ecf20Sopenharmony_ci		pte++;
12568c2ecf20Sopenharmony_ci	}
12578c2ecf20Sopenharmony_ci}
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_cistatic long populate_pmd(struct cpa_data *cpa,
12608c2ecf20Sopenharmony_ci			 unsigned long start, unsigned long end,
12618c2ecf20Sopenharmony_ci			 unsigned num_pages, pud_t *pud, pgprot_t pgprot)
12628c2ecf20Sopenharmony_ci{
12638c2ecf20Sopenharmony_ci	long cur_pages = 0;
12648c2ecf20Sopenharmony_ci	pmd_t *pmd;
12658c2ecf20Sopenharmony_ci	pgprot_t pmd_pgprot;
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_ci	/*
12688c2ecf20Sopenharmony_ci	 * Not on a 2M boundary?
12698c2ecf20Sopenharmony_ci	 */
12708c2ecf20Sopenharmony_ci	if (start & (PMD_SIZE - 1)) {
12718c2ecf20Sopenharmony_ci		unsigned long pre_end = start + (num_pages << PAGE_SHIFT);
12728c2ecf20Sopenharmony_ci		unsigned long next_page = (start + PMD_SIZE) & PMD_MASK;
12738c2ecf20Sopenharmony_ci
12748c2ecf20Sopenharmony_ci		pre_end   = min_t(unsigned long, pre_end, next_page);
12758c2ecf20Sopenharmony_ci		cur_pages = (pre_end - start) >> PAGE_SHIFT;
12768c2ecf20Sopenharmony_ci		cur_pages = min_t(unsigned int, num_pages, cur_pages);
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci		/*
12798c2ecf20Sopenharmony_ci		 * Need a PTE page?
12808c2ecf20Sopenharmony_ci		 */
12818c2ecf20Sopenharmony_ci		pmd = pmd_offset(pud, start);
12828c2ecf20Sopenharmony_ci		if (pmd_none(*pmd))
12838c2ecf20Sopenharmony_ci			if (alloc_pte_page(pmd))
12848c2ecf20Sopenharmony_ci				return -1;
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci		populate_pte(cpa, start, pre_end, cur_pages, pmd, pgprot);
12878c2ecf20Sopenharmony_ci
12888c2ecf20Sopenharmony_ci		start = pre_end;
12898c2ecf20Sopenharmony_ci	}
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci	/*
12928c2ecf20Sopenharmony_ci	 * We mapped them all?
12938c2ecf20Sopenharmony_ci	 */
12948c2ecf20Sopenharmony_ci	if (num_pages == cur_pages)
12958c2ecf20Sopenharmony_ci		return cur_pages;
12968c2ecf20Sopenharmony_ci
12978c2ecf20Sopenharmony_ci	pmd_pgprot = pgprot_4k_2_large(pgprot);
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_ci	while (end - start >= PMD_SIZE) {
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_ci		/*
13028c2ecf20Sopenharmony_ci		 * We cannot use a 1G page so allocate a PMD page if needed.
13038c2ecf20Sopenharmony_ci		 */
13048c2ecf20Sopenharmony_ci		if (pud_none(*pud))
13058c2ecf20Sopenharmony_ci			if (alloc_pmd_page(pud))
13068c2ecf20Sopenharmony_ci				return -1;
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_ci		pmd = pmd_offset(pud, start);
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ci		set_pmd(pmd, pmd_mkhuge(pfn_pmd(cpa->pfn,
13118c2ecf20Sopenharmony_ci					canon_pgprot(pmd_pgprot))));
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci		start	  += PMD_SIZE;
13148c2ecf20Sopenharmony_ci		cpa->pfn  += PMD_SIZE >> PAGE_SHIFT;
13158c2ecf20Sopenharmony_ci		cur_pages += PMD_SIZE >> PAGE_SHIFT;
13168c2ecf20Sopenharmony_ci	}
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_ci	/*
13198c2ecf20Sopenharmony_ci	 * Map trailing 4K pages.
13208c2ecf20Sopenharmony_ci	 */
13218c2ecf20Sopenharmony_ci	if (start < end) {
13228c2ecf20Sopenharmony_ci		pmd = pmd_offset(pud, start);
13238c2ecf20Sopenharmony_ci		if (pmd_none(*pmd))
13248c2ecf20Sopenharmony_ci			if (alloc_pte_page(pmd))
13258c2ecf20Sopenharmony_ci				return -1;
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_ci		populate_pte(cpa, start, end, num_pages - cur_pages,
13288c2ecf20Sopenharmony_ci			     pmd, pgprot);
13298c2ecf20Sopenharmony_ci	}
13308c2ecf20Sopenharmony_ci	return num_pages;
13318c2ecf20Sopenharmony_ci}
13328c2ecf20Sopenharmony_ci
13338c2ecf20Sopenharmony_cistatic int populate_pud(struct cpa_data *cpa, unsigned long start, p4d_t *p4d,
13348c2ecf20Sopenharmony_ci			pgprot_t pgprot)
13358c2ecf20Sopenharmony_ci{
13368c2ecf20Sopenharmony_ci	pud_t *pud;
13378c2ecf20Sopenharmony_ci	unsigned long end;
13388c2ecf20Sopenharmony_ci	long cur_pages = 0;
13398c2ecf20Sopenharmony_ci	pgprot_t pud_pgprot;
13408c2ecf20Sopenharmony_ci
13418c2ecf20Sopenharmony_ci	end = start + (cpa->numpages << PAGE_SHIFT);
13428c2ecf20Sopenharmony_ci
13438c2ecf20Sopenharmony_ci	/*
13448c2ecf20Sopenharmony_ci	 * Not on a Gb page boundary? => map everything up to it with
13458c2ecf20Sopenharmony_ci	 * smaller pages.
13468c2ecf20Sopenharmony_ci	 */
13478c2ecf20Sopenharmony_ci	if (start & (PUD_SIZE - 1)) {
13488c2ecf20Sopenharmony_ci		unsigned long pre_end;
13498c2ecf20Sopenharmony_ci		unsigned long next_page = (start + PUD_SIZE) & PUD_MASK;
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_ci		pre_end   = min_t(unsigned long, end, next_page);
13528c2ecf20Sopenharmony_ci		cur_pages = (pre_end - start) >> PAGE_SHIFT;
13538c2ecf20Sopenharmony_ci		cur_pages = min_t(int, (int)cpa->numpages, cur_pages);
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci		pud = pud_offset(p4d, start);
13568c2ecf20Sopenharmony_ci
13578c2ecf20Sopenharmony_ci		/*
13588c2ecf20Sopenharmony_ci		 * Need a PMD page?
13598c2ecf20Sopenharmony_ci		 */
13608c2ecf20Sopenharmony_ci		if (pud_none(*pud))
13618c2ecf20Sopenharmony_ci			if (alloc_pmd_page(pud))
13628c2ecf20Sopenharmony_ci				return -1;
13638c2ecf20Sopenharmony_ci
13648c2ecf20Sopenharmony_ci		cur_pages = populate_pmd(cpa, start, pre_end, cur_pages,
13658c2ecf20Sopenharmony_ci					 pud, pgprot);
13668c2ecf20Sopenharmony_ci		if (cur_pages < 0)
13678c2ecf20Sopenharmony_ci			return cur_pages;
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_ci		start = pre_end;
13708c2ecf20Sopenharmony_ci	}
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_ci	/* We mapped them all? */
13738c2ecf20Sopenharmony_ci	if (cpa->numpages == cur_pages)
13748c2ecf20Sopenharmony_ci		return cur_pages;
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_ci	pud = pud_offset(p4d, start);
13778c2ecf20Sopenharmony_ci	pud_pgprot = pgprot_4k_2_large(pgprot);
13788c2ecf20Sopenharmony_ci
13798c2ecf20Sopenharmony_ci	/*
13808c2ecf20Sopenharmony_ci	 * Map everything starting from the Gb boundary, possibly with 1G pages
13818c2ecf20Sopenharmony_ci	 */
13828c2ecf20Sopenharmony_ci	while (boot_cpu_has(X86_FEATURE_GBPAGES) && end - start >= PUD_SIZE) {
13838c2ecf20Sopenharmony_ci		set_pud(pud, pud_mkhuge(pfn_pud(cpa->pfn,
13848c2ecf20Sopenharmony_ci				   canon_pgprot(pud_pgprot))));
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_ci		start	  += PUD_SIZE;
13878c2ecf20Sopenharmony_ci		cpa->pfn  += PUD_SIZE >> PAGE_SHIFT;
13888c2ecf20Sopenharmony_ci		cur_pages += PUD_SIZE >> PAGE_SHIFT;
13898c2ecf20Sopenharmony_ci		pud++;
13908c2ecf20Sopenharmony_ci	}
13918c2ecf20Sopenharmony_ci
13928c2ecf20Sopenharmony_ci	/* Map trailing leftover */
13938c2ecf20Sopenharmony_ci	if (start < end) {
13948c2ecf20Sopenharmony_ci		long tmp;
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci		pud = pud_offset(p4d, start);
13978c2ecf20Sopenharmony_ci		if (pud_none(*pud))
13988c2ecf20Sopenharmony_ci			if (alloc_pmd_page(pud))
13998c2ecf20Sopenharmony_ci				return -1;
14008c2ecf20Sopenharmony_ci
14018c2ecf20Sopenharmony_ci		tmp = populate_pmd(cpa, start, end, cpa->numpages - cur_pages,
14028c2ecf20Sopenharmony_ci				   pud, pgprot);
14038c2ecf20Sopenharmony_ci		if (tmp < 0)
14048c2ecf20Sopenharmony_ci			return cur_pages;
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci		cur_pages += tmp;
14078c2ecf20Sopenharmony_ci	}
14088c2ecf20Sopenharmony_ci	return cur_pages;
14098c2ecf20Sopenharmony_ci}
14108c2ecf20Sopenharmony_ci
14118c2ecf20Sopenharmony_ci/*
14128c2ecf20Sopenharmony_ci * Restrictions for kernel page table do not necessarily apply when mapping in
14138c2ecf20Sopenharmony_ci * an alternate PGD.
14148c2ecf20Sopenharmony_ci */
14158c2ecf20Sopenharmony_cistatic int populate_pgd(struct cpa_data *cpa, unsigned long addr)
14168c2ecf20Sopenharmony_ci{
14178c2ecf20Sopenharmony_ci	pgprot_t pgprot = __pgprot(_KERNPG_TABLE);
14188c2ecf20Sopenharmony_ci	pud_t *pud = NULL;	/* shut up gcc */
14198c2ecf20Sopenharmony_ci	p4d_t *p4d;
14208c2ecf20Sopenharmony_ci	pgd_t *pgd_entry;
14218c2ecf20Sopenharmony_ci	long ret;
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci	pgd_entry = cpa->pgd + pgd_index(addr);
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_ci	if (pgd_none(*pgd_entry)) {
14268c2ecf20Sopenharmony_ci		p4d = (p4d_t *)get_zeroed_page(GFP_KERNEL);
14278c2ecf20Sopenharmony_ci		if (!p4d)
14288c2ecf20Sopenharmony_ci			return -1;
14298c2ecf20Sopenharmony_ci
14308c2ecf20Sopenharmony_ci		set_pgd(pgd_entry, __pgd(__pa(p4d) | _KERNPG_TABLE));
14318c2ecf20Sopenharmony_ci	}
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_ci	/*
14348c2ecf20Sopenharmony_ci	 * Allocate a PUD page and hand it down for mapping.
14358c2ecf20Sopenharmony_ci	 */
14368c2ecf20Sopenharmony_ci	p4d = p4d_offset(pgd_entry, addr);
14378c2ecf20Sopenharmony_ci	if (p4d_none(*p4d)) {
14388c2ecf20Sopenharmony_ci		pud = (pud_t *)get_zeroed_page(GFP_KERNEL);
14398c2ecf20Sopenharmony_ci		if (!pud)
14408c2ecf20Sopenharmony_ci			return -1;
14418c2ecf20Sopenharmony_ci
14428c2ecf20Sopenharmony_ci		set_p4d(p4d, __p4d(__pa(pud) | _KERNPG_TABLE));
14438c2ecf20Sopenharmony_ci	}
14448c2ecf20Sopenharmony_ci
14458c2ecf20Sopenharmony_ci	pgprot_val(pgprot) &= ~pgprot_val(cpa->mask_clr);
14468c2ecf20Sopenharmony_ci	pgprot_val(pgprot) |=  pgprot_val(cpa->mask_set);
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci	ret = populate_pud(cpa, addr, p4d, pgprot);
14498c2ecf20Sopenharmony_ci	if (ret < 0) {
14508c2ecf20Sopenharmony_ci		/*
14518c2ecf20Sopenharmony_ci		 * Leave the PUD page in place in case some other CPU or thread
14528c2ecf20Sopenharmony_ci		 * already found it, but remove any useless entries we just
14538c2ecf20Sopenharmony_ci		 * added to it.
14548c2ecf20Sopenharmony_ci		 */
14558c2ecf20Sopenharmony_ci		unmap_pud_range(p4d, addr,
14568c2ecf20Sopenharmony_ci				addr + (cpa->numpages << PAGE_SHIFT));
14578c2ecf20Sopenharmony_ci		return ret;
14588c2ecf20Sopenharmony_ci	}
14598c2ecf20Sopenharmony_ci
14608c2ecf20Sopenharmony_ci	cpa->numpages = ret;
14618c2ecf20Sopenharmony_ci	return 0;
14628c2ecf20Sopenharmony_ci}
14638c2ecf20Sopenharmony_ci
14648c2ecf20Sopenharmony_cistatic int __cpa_process_fault(struct cpa_data *cpa, unsigned long vaddr,
14658c2ecf20Sopenharmony_ci			       int primary)
14668c2ecf20Sopenharmony_ci{
14678c2ecf20Sopenharmony_ci	if (cpa->pgd) {
14688c2ecf20Sopenharmony_ci		/*
14698c2ecf20Sopenharmony_ci		 * Right now, we only execute this code path when mapping
14708c2ecf20Sopenharmony_ci		 * the EFI virtual memory map regions, no other users
14718c2ecf20Sopenharmony_ci		 * provide a ->pgd value. This may change in the future.
14728c2ecf20Sopenharmony_ci		 */
14738c2ecf20Sopenharmony_ci		return populate_pgd(cpa, vaddr);
14748c2ecf20Sopenharmony_ci	}
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_ci	/*
14778c2ecf20Sopenharmony_ci	 * Ignore all non primary paths.
14788c2ecf20Sopenharmony_ci	 */
14798c2ecf20Sopenharmony_ci	if (!primary) {
14808c2ecf20Sopenharmony_ci		cpa->numpages = 1;
14818c2ecf20Sopenharmony_ci		return 0;
14828c2ecf20Sopenharmony_ci	}
14838c2ecf20Sopenharmony_ci
14848c2ecf20Sopenharmony_ci	/*
14858c2ecf20Sopenharmony_ci	 * Ignore the NULL PTE for kernel identity mapping, as it is expected
14868c2ecf20Sopenharmony_ci	 * to have holes.
14878c2ecf20Sopenharmony_ci	 * Also set numpages to '1' indicating that we processed cpa req for
14888c2ecf20Sopenharmony_ci	 * one virtual address page and its pfn. TBD: numpages can be set based
14898c2ecf20Sopenharmony_ci	 * on the initial value and the level returned by lookup_address().
14908c2ecf20Sopenharmony_ci	 */
14918c2ecf20Sopenharmony_ci	if (within(vaddr, PAGE_OFFSET,
14928c2ecf20Sopenharmony_ci		   PAGE_OFFSET + (max_pfn_mapped << PAGE_SHIFT))) {
14938c2ecf20Sopenharmony_ci		cpa->numpages = 1;
14948c2ecf20Sopenharmony_ci		cpa->pfn = __pa(vaddr) >> PAGE_SHIFT;
14958c2ecf20Sopenharmony_ci		return 0;
14968c2ecf20Sopenharmony_ci
14978c2ecf20Sopenharmony_ci	} else if (__cpa_pfn_in_highmap(cpa->pfn)) {
14988c2ecf20Sopenharmony_ci		/* Faults in the highmap are OK, so do not warn: */
14998c2ecf20Sopenharmony_ci		return -EFAULT;
15008c2ecf20Sopenharmony_ci	} else {
15018c2ecf20Sopenharmony_ci		WARN(1, KERN_WARNING "CPA: called for zero pte. "
15028c2ecf20Sopenharmony_ci			"vaddr = %lx cpa->vaddr = %lx\n", vaddr,
15038c2ecf20Sopenharmony_ci			*cpa->vaddr);
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_ci		return -EFAULT;
15068c2ecf20Sopenharmony_ci	}
15078c2ecf20Sopenharmony_ci}
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_cistatic int __change_page_attr(struct cpa_data *cpa, int primary)
15108c2ecf20Sopenharmony_ci{
15118c2ecf20Sopenharmony_ci	unsigned long address;
15128c2ecf20Sopenharmony_ci	int do_split, err;
15138c2ecf20Sopenharmony_ci	unsigned int level;
15148c2ecf20Sopenharmony_ci	pte_t *kpte, old_pte;
15158c2ecf20Sopenharmony_ci
15168c2ecf20Sopenharmony_ci	address = __cpa_addr(cpa, cpa->curpage);
15178c2ecf20Sopenharmony_cirepeat:
15188c2ecf20Sopenharmony_ci	kpte = _lookup_address_cpa(cpa, address, &level);
15198c2ecf20Sopenharmony_ci	if (!kpte)
15208c2ecf20Sopenharmony_ci		return __cpa_process_fault(cpa, address, primary);
15218c2ecf20Sopenharmony_ci
15228c2ecf20Sopenharmony_ci	old_pte = *kpte;
15238c2ecf20Sopenharmony_ci	if (pte_none(old_pte))
15248c2ecf20Sopenharmony_ci		return __cpa_process_fault(cpa, address, primary);
15258c2ecf20Sopenharmony_ci
15268c2ecf20Sopenharmony_ci	if (level == PG_LEVEL_4K) {
15278c2ecf20Sopenharmony_ci		pte_t new_pte;
15288c2ecf20Sopenharmony_ci		pgprot_t new_prot = pte_pgprot(old_pte);
15298c2ecf20Sopenharmony_ci		unsigned long pfn = pte_pfn(old_pte);
15308c2ecf20Sopenharmony_ci
15318c2ecf20Sopenharmony_ci		pgprot_val(new_prot) &= ~pgprot_val(cpa->mask_clr);
15328c2ecf20Sopenharmony_ci		pgprot_val(new_prot) |= pgprot_val(cpa->mask_set);
15338c2ecf20Sopenharmony_ci
15348c2ecf20Sopenharmony_ci		cpa_inc_4k_install();
15358c2ecf20Sopenharmony_ci		/* Hand in lpsize = 0 to enforce the protection mechanism */
15368c2ecf20Sopenharmony_ci		new_prot = static_protections(new_prot, address, pfn, 1, 0,
15378c2ecf20Sopenharmony_ci					      CPA_PROTECT);
15388c2ecf20Sopenharmony_ci
15398c2ecf20Sopenharmony_ci		new_prot = pgprot_clear_protnone_bits(new_prot);
15408c2ecf20Sopenharmony_ci
15418c2ecf20Sopenharmony_ci		/*
15428c2ecf20Sopenharmony_ci		 * We need to keep the pfn from the existing PTE,
15438c2ecf20Sopenharmony_ci		 * after all we're only going to change it's attributes
15448c2ecf20Sopenharmony_ci		 * not the memory it points to
15458c2ecf20Sopenharmony_ci		 */
15468c2ecf20Sopenharmony_ci		new_pte = pfn_pte(pfn, new_prot);
15478c2ecf20Sopenharmony_ci		cpa->pfn = pfn;
15488c2ecf20Sopenharmony_ci		/*
15498c2ecf20Sopenharmony_ci		 * Do we really change anything ?
15508c2ecf20Sopenharmony_ci		 */
15518c2ecf20Sopenharmony_ci		if (pte_val(old_pte) != pte_val(new_pte)) {
15528c2ecf20Sopenharmony_ci			set_pte_atomic(kpte, new_pte);
15538c2ecf20Sopenharmony_ci			cpa->flags |= CPA_FLUSHTLB;
15548c2ecf20Sopenharmony_ci		}
15558c2ecf20Sopenharmony_ci		cpa->numpages = 1;
15568c2ecf20Sopenharmony_ci		return 0;
15578c2ecf20Sopenharmony_ci	}
15588c2ecf20Sopenharmony_ci
15598c2ecf20Sopenharmony_ci	/*
15608c2ecf20Sopenharmony_ci	 * Check, whether we can keep the large page intact
15618c2ecf20Sopenharmony_ci	 * and just change the pte:
15628c2ecf20Sopenharmony_ci	 */
15638c2ecf20Sopenharmony_ci	do_split = should_split_large_page(kpte, address, cpa);
15648c2ecf20Sopenharmony_ci	/*
15658c2ecf20Sopenharmony_ci	 * When the range fits into the existing large page,
15668c2ecf20Sopenharmony_ci	 * return. cp->numpages and cpa->tlbflush have been updated in
15678c2ecf20Sopenharmony_ci	 * try_large_page:
15688c2ecf20Sopenharmony_ci	 */
15698c2ecf20Sopenharmony_ci	if (do_split <= 0)
15708c2ecf20Sopenharmony_ci		return do_split;
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_ci	/*
15738c2ecf20Sopenharmony_ci	 * We have to split the large page:
15748c2ecf20Sopenharmony_ci	 */
15758c2ecf20Sopenharmony_ci	err = split_large_page(cpa, kpte, address);
15768c2ecf20Sopenharmony_ci	if (!err)
15778c2ecf20Sopenharmony_ci		goto repeat;
15788c2ecf20Sopenharmony_ci
15798c2ecf20Sopenharmony_ci	return err;
15808c2ecf20Sopenharmony_ci}
15818c2ecf20Sopenharmony_ci
15828c2ecf20Sopenharmony_cistatic int __change_page_attr_set_clr(struct cpa_data *cpa, int checkalias);
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_cistatic int cpa_process_alias(struct cpa_data *cpa)
15858c2ecf20Sopenharmony_ci{
15868c2ecf20Sopenharmony_ci	struct cpa_data alias_cpa;
15878c2ecf20Sopenharmony_ci	unsigned long laddr = (unsigned long)__va(cpa->pfn << PAGE_SHIFT);
15888c2ecf20Sopenharmony_ci	unsigned long vaddr;
15898c2ecf20Sopenharmony_ci	int ret;
15908c2ecf20Sopenharmony_ci
15918c2ecf20Sopenharmony_ci	if (!pfn_range_is_mapped(cpa->pfn, cpa->pfn + 1))
15928c2ecf20Sopenharmony_ci		return 0;
15938c2ecf20Sopenharmony_ci
15948c2ecf20Sopenharmony_ci	/*
15958c2ecf20Sopenharmony_ci	 * No need to redo, when the primary call touched the direct
15968c2ecf20Sopenharmony_ci	 * mapping already:
15978c2ecf20Sopenharmony_ci	 */
15988c2ecf20Sopenharmony_ci	vaddr = __cpa_addr(cpa, cpa->curpage);
15998c2ecf20Sopenharmony_ci	if (!(within(vaddr, PAGE_OFFSET,
16008c2ecf20Sopenharmony_ci		    PAGE_OFFSET + (max_pfn_mapped << PAGE_SHIFT)))) {
16018c2ecf20Sopenharmony_ci
16028c2ecf20Sopenharmony_ci		alias_cpa = *cpa;
16038c2ecf20Sopenharmony_ci		alias_cpa.vaddr = &laddr;
16048c2ecf20Sopenharmony_ci		alias_cpa.flags &= ~(CPA_PAGES_ARRAY | CPA_ARRAY);
16058c2ecf20Sopenharmony_ci		alias_cpa.curpage = 0;
16068c2ecf20Sopenharmony_ci
16078c2ecf20Sopenharmony_ci		cpa->force_flush_all = 1;
16088c2ecf20Sopenharmony_ci
16098c2ecf20Sopenharmony_ci		ret = __change_page_attr_set_clr(&alias_cpa, 0);
16108c2ecf20Sopenharmony_ci		if (ret)
16118c2ecf20Sopenharmony_ci			return ret;
16128c2ecf20Sopenharmony_ci	}
16138c2ecf20Sopenharmony_ci
16148c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_64
16158c2ecf20Sopenharmony_ci	/*
16168c2ecf20Sopenharmony_ci	 * If the primary call didn't touch the high mapping already
16178c2ecf20Sopenharmony_ci	 * and the physical address is inside the kernel map, we need
16188c2ecf20Sopenharmony_ci	 * to touch the high mapped kernel as well:
16198c2ecf20Sopenharmony_ci	 */
16208c2ecf20Sopenharmony_ci	if (!within(vaddr, (unsigned long)_text, _brk_end) &&
16218c2ecf20Sopenharmony_ci	    __cpa_pfn_in_highmap(cpa->pfn)) {
16228c2ecf20Sopenharmony_ci		unsigned long temp_cpa_vaddr = (cpa->pfn << PAGE_SHIFT) +
16238c2ecf20Sopenharmony_ci					       __START_KERNEL_map - phys_base;
16248c2ecf20Sopenharmony_ci		alias_cpa = *cpa;
16258c2ecf20Sopenharmony_ci		alias_cpa.vaddr = &temp_cpa_vaddr;
16268c2ecf20Sopenharmony_ci		alias_cpa.flags &= ~(CPA_PAGES_ARRAY | CPA_ARRAY);
16278c2ecf20Sopenharmony_ci		alias_cpa.curpage = 0;
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_ci		cpa->force_flush_all = 1;
16308c2ecf20Sopenharmony_ci		/*
16318c2ecf20Sopenharmony_ci		 * The high mapping range is imprecise, so ignore the
16328c2ecf20Sopenharmony_ci		 * return value.
16338c2ecf20Sopenharmony_ci		 */
16348c2ecf20Sopenharmony_ci		__change_page_attr_set_clr(&alias_cpa, 0);
16358c2ecf20Sopenharmony_ci	}
16368c2ecf20Sopenharmony_ci#endif
16378c2ecf20Sopenharmony_ci
16388c2ecf20Sopenharmony_ci	return 0;
16398c2ecf20Sopenharmony_ci}
16408c2ecf20Sopenharmony_ci
16418c2ecf20Sopenharmony_cistatic int __change_page_attr_set_clr(struct cpa_data *cpa, int checkalias)
16428c2ecf20Sopenharmony_ci{
16438c2ecf20Sopenharmony_ci	unsigned long numpages = cpa->numpages;
16448c2ecf20Sopenharmony_ci	unsigned long rempages = numpages;
16458c2ecf20Sopenharmony_ci	int ret = 0;
16468c2ecf20Sopenharmony_ci
16478c2ecf20Sopenharmony_ci	while (rempages) {
16488c2ecf20Sopenharmony_ci		/*
16498c2ecf20Sopenharmony_ci		 * Store the remaining nr of pages for the large page
16508c2ecf20Sopenharmony_ci		 * preservation check.
16518c2ecf20Sopenharmony_ci		 */
16528c2ecf20Sopenharmony_ci		cpa->numpages = rempages;
16538c2ecf20Sopenharmony_ci		/* for array changes, we can't use large page */
16548c2ecf20Sopenharmony_ci		if (cpa->flags & (CPA_ARRAY | CPA_PAGES_ARRAY))
16558c2ecf20Sopenharmony_ci			cpa->numpages = 1;
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_ci		if (!debug_pagealloc_enabled())
16588c2ecf20Sopenharmony_ci			spin_lock(&cpa_lock);
16598c2ecf20Sopenharmony_ci		ret = __change_page_attr(cpa, checkalias);
16608c2ecf20Sopenharmony_ci		if (!debug_pagealloc_enabled())
16618c2ecf20Sopenharmony_ci			spin_unlock(&cpa_lock);
16628c2ecf20Sopenharmony_ci		if (ret)
16638c2ecf20Sopenharmony_ci			goto out;
16648c2ecf20Sopenharmony_ci
16658c2ecf20Sopenharmony_ci		if (checkalias) {
16668c2ecf20Sopenharmony_ci			ret = cpa_process_alias(cpa);
16678c2ecf20Sopenharmony_ci			if (ret)
16688c2ecf20Sopenharmony_ci				goto out;
16698c2ecf20Sopenharmony_ci		}
16708c2ecf20Sopenharmony_ci
16718c2ecf20Sopenharmony_ci		/*
16728c2ecf20Sopenharmony_ci		 * Adjust the number of pages with the result of the
16738c2ecf20Sopenharmony_ci		 * CPA operation. Either a large page has been
16748c2ecf20Sopenharmony_ci		 * preserved or a single page update happened.
16758c2ecf20Sopenharmony_ci		 */
16768c2ecf20Sopenharmony_ci		BUG_ON(cpa->numpages > rempages || !cpa->numpages);
16778c2ecf20Sopenharmony_ci		rempages -= cpa->numpages;
16788c2ecf20Sopenharmony_ci		cpa->curpage += cpa->numpages;
16798c2ecf20Sopenharmony_ci	}
16808c2ecf20Sopenharmony_ci
16818c2ecf20Sopenharmony_ciout:
16828c2ecf20Sopenharmony_ci	/* Restore the original numpages */
16838c2ecf20Sopenharmony_ci	cpa->numpages = numpages;
16848c2ecf20Sopenharmony_ci	return ret;
16858c2ecf20Sopenharmony_ci}
16868c2ecf20Sopenharmony_ci
16878c2ecf20Sopenharmony_cistatic int change_page_attr_set_clr(unsigned long *addr, int numpages,
16888c2ecf20Sopenharmony_ci				    pgprot_t mask_set, pgprot_t mask_clr,
16898c2ecf20Sopenharmony_ci				    int force_split, int in_flag,
16908c2ecf20Sopenharmony_ci				    struct page **pages)
16918c2ecf20Sopenharmony_ci{
16928c2ecf20Sopenharmony_ci	struct cpa_data cpa;
16938c2ecf20Sopenharmony_ci	int ret, cache, checkalias;
16948c2ecf20Sopenharmony_ci
16958c2ecf20Sopenharmony_ci	memset(&cpa, 0, sizeof(cpa));
16968c2ecf20Sopenharmony_ci
16978c2ecf20Sopenharmony_ci	/*
16988c2ecf20Sopenharmony_ci	 * Check, if we are requested to set a not supported
16998c2ecf20Sopenharmony_ci	 * feature.  Clearing non-supported features is OK.
17008c2ecf20Sopenharmony_ci	 */
17018c2ecf20Sopenharmony_ci	mask_set = canon_pgprot(mask_set);
17028c2ecf20Sopenharmony_ci
17038c2ecf20Sopenharmony_ci	if (!pgprot_val(mask_set) && !pgprot_val(mask_clr) && !force_split)
17048c2ecf20Sopenharmony_ci		return 0;
17058c2ecf20Sopenharmony_ci
17068c2ecf20Sopenharmony_ci	/* Ensure we are PAGE_SIZE aligned */
17078c2ecf20Sopenharmony_ci	if (in_flag & CPA_ARRAY) {
17088c2ecf20Sopenharmony_ci		int i;
17098c2ecf20Sopenharmony_ci		for (i = 0; i < numpages; i++) {
17108c2ecf20Sopenharmony_ci			if (addr[i] & ~PAGE_MASK) {
17118c2ecf20Sopenharmony_ci				addr[i] &= PAGE_MASK;
17128c2ecf20Sopenharmony_ci				WARN_ON_ONCE(1);
17138c2ecf20Sopenharmony_ci			}
17148c2ecf20Sopenharmony_ci		}
17158c2ecf20Sopenharmony_ci	} else if (!(in_flag & CPA_PAGES_ARRAY)) {
17168c2ecf20Sopenharmony_ci		/*
17178c2ecf20Sopenharmony_ci		 * in_flag of CPA_PAGES_ARRAY implies it is aligned.
17188c2ecf20Sopenharmony_ci		 * No need to check in that case
17198c2ecf20Sopenharmony_ci		 */
17208c2ecf20Sopenharmony_ci		if (*addr & ~PAGE_MASK) {
17218c2ecf20Sopenharmony_ci			*addr &= PAGE_MASK;
17228c2ecf20Sopenharmony_ci			/*
17238c2ecf20Sopenharmony_ci			 * People should not be passing in unaligned addresses:
17248c2ecf20Sopenharmony_ci			 */
17258c2ecf20Sopenharmony_ci			WARN_ON_ONCE(1);
17268c2ecf20Sopenharmony_ci		}
17278c2ecf20Sopenharmony_ci	}
17288c2ecf20Sopenharmony_ci
17298c2ecf20Sopenharmony_ci	/* Must avoid aliasing mappings in the highmem code */
17308c2ecf20Sopenharmony_ci	kmap_flush_unused();
17318c2ecf20Sopenharmony_ci
17328c2ecf20Sopenharmony_ci	vm_unmap_aliases();
17338c2ecf20Sopenharmony_ci
17348c2ecf20Sopenharmony_ci	cpa.vaddr = addr;
17358c2ecf20Sopenharmony_ci	cpa.pages = pages;
17368c2ecf20Sopenharmony_ci	cpa.numpages = numpages;
17378c2ecf20Sopenharmony_ci	cpa.mask_set = mask_set;
17388c2ecf20Sopenharmony_ci	cpa.mask_clr = mask_clr;
17398c2ecf20Sopenharmony_ci	cpa.flags = 0;
17408c2ecf20Sopenharmony_ci	cpa.curpage = 0;
17418c2ecf20Sopenharmony_ci	cpa.force_split = force_split;
17428c2ecf20Sopenharmony_ci
17438c2ecf20Sopenharmony_ci	if (in_flag & (CPA_ARRAY | CPA_PAGES_ARRAY))
17448c2ecf20Sopenharmony_ci		cpa.flags |= in_flag;
17458c2ecf20Sopenharmony_ci
17468c2ecf20Sopenharmony_ci	/* No alias checking for _NX bit modifications */
17478c2ecf20Sopenharmony_ci	checkalias = (pgprot_val(mask_set) | pgprot_val(mask_clr)) != _PAGE_NX;
17488c2ecf20Sopenharmony_ci	/* Has caller explicitly disabled alias checking? */
17498c2ecf20Sopenharmony_ci	if (in_flag & CPA_NO_CHECK_ALIAS)
17508c2ecf20Sopenharmony_ci		checkalias = 0;
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_ci	ret = __change_page_attr_set_clr(&cpa, checkalias);
17538c2ecf20Sopenharmony_ci
17548c2ecf20Sopenharmony_ci	/*
17558c2ecf20Sopenharmony_ci	 * Check whether we really changed something:
17568c2ecf20Sopenharmony_ci	 */
17578c2ecf20Sopenharmony_ci	if (!(cpa.flags & CPA_FLUSHTLB))
17588c2ecf20Sopenharmony_ci		goto out;
17598c2ecf20Sopenharmony_ci
17608c2ecf20Sopenharmony_ci	/*
17618c2ecf20Sopenharmony_ci	 * No need to flush, when we did not set any of the caching
17628c2ecf20Sopenharmony_ci	 * attributes:
17638c2ecf20Sopenharmony_ci	 */
17648c2ecf20Sopenharmony_ci	cache = !!pgprot2cachemode(mask_set);
17658c2ecf20Sopenharmony_ci
17668c2ecf20Sopenharmony_ci	/*
17678c2ecf20Sopenharmony_ci	 * On error; flush everything to be sure.
17688c2ecf20Sopenharmony_ci	 */
17698c2ecf20Sopenharmony_ci	if (ret) {
17708c2ecf20Sopenharmony_ci		cpa_flush_all(cache);
17718c2ecf20Sopenharmony_ci		goto out;
17728c2ecf20Sopenharmony_ci	}
17738c2ecf20Sopenharmony_ci
17748c2ecf20Sopenharmony_ci	cpa_flush(&cpa, cache);
17758c2ecf20Sopenharmony_ciout:
17768c2ecf20Sopenharmony_ci	return ret;
17778c2ecf20Sopenharmony_ci}
17788c2ecf20Sopenharmony_ci
17798c2ecf20Sopenharmony_cistatic inline int change_page_attr_set(unsigned long *addr, int numpages,
17808c2ecf20Sopenharmony_ci				       pgprot_t mask, int array)
17818c2ecf20Sopenharmony_ci{
17828c2ecf20Sopenharmony_ci	return change_page_attr_set_clr(addr, numpages, mask, __pgprot(0), 0,
17838c2ecf20Sopenharmony_ci		(array ? CPA_ARRAY : 0), NULL);
17848c2ecf20Sopenharmony_ci}
17858c2ecf20Sopenharmony_ci
17868c2ecf20Sopenharmony_cistatic inline int change_page_attr_clear(unsigned long *addr, int numpages,
17878c2ecf20Sopenharmony_ci					 pgprot_t mask, int array)
17888c2ecf20Sopenharmony_ci{
17898c2ecf20Sopenharmony_ci	return change_page_attr_set_clr(addr, numpages, __pgprot(0), mask, 0,
17908c2ecf20Sopenharmony_ci		(array ? CPA_ARRAY : 0), NULL);
17918c2ecf20Sopenharmony_ci}
17928c2ecf20Sopenharmony_ci
17938c2ecf20Sopenharmony_cistatic inline int cpa_set_pages_array(struct page **pages, int numpages,
17948c2ecf20Sopenharmony_ci				       pgprot_t mask)
17958c2ecf20Sopenharmony_ci{
17968c2ecf20Sopenharmony_ci	return change_page_attr_set_clr(NULL, numpages, mask, __pgprot(0), 0,
17978c2ecf20Sopenharmony_ci		CPA_PAGES_ARRAY, pages);
17988c2ecf20Sopenharmony_ci}
17998c2ecf20Sopenharmony_ci
18008c2ecf20Sopenharmony_cistatic inline int cpa_clear_pages_array(struct page **pages, int numpages,
18018c2ecf20Sopenharmony_ci					 pgprot_t mask)
18028c2ecf20Sopenharmony_ci{
18038c2ecf20Sopenharmony_ci	return change_page_attr_set_clr(NULL, numpages, __pgprot(0), mask, 0,
18048c2ecf20Sopenharmony_ci		CPA_PAGES_ARRAY, pages);
18058c2ecf20Sopenharmony_ci}
18068c2ecf20Sopenharmony_ci
18078c2ecf20Sopenharmony_ci/*
18088c2ecf20Sopenharmony_ci * _set_memory_prot is an internal helper for callers that have been passed
18098c2ecf20Sopenharmony_ci * a pgprot_t value from upper layers and a reservation has already been taken.
18108c2ecf20Sopenharmony_ci * If you want to set the pgprot to a specific page protocol, use the
18118c2ecf20Sopenharmony_ci * set_memory_xx() functions.
18128c2ecf20Sopenharmony_ci */
18138c2ecf20Sopenharmony_ciint __set_memory_prot(unsigned long addr, int numpages, pgprot_t prot)
18148c2ecf20Sopenharmony_ci{
18158c2ecf20Sopenharmony_ci	return change_page_attr_set_clr(&addr, numpages, prot,
18168c2ecf20Sopenharmony_ci					__pgprot(~pgprot_val(prot)), 0, 0,
18178c2ecf20Sopenharmony_ci					NULL);
18188c2ecf20Sopenharmony_ci}
18198c2ecf20Sopenharmony_ci
18208c2ecf20Sopenharmony_ciint _set_memory_uc(unsigned long addr, int numpages)
18218c2ecf20Sopenharmony_ci{
18228c2ecf20Sopenharmony_ci	/*
18238c2ecf20Sopenharmony_ci	 * for now UC MINUS. see comments in ioremap()
18248c2ecf20Sopenharmony_ci	 * If you really need strong UC use ioremap_uc(), but note
18258c2ecf20Sopenharmony_ci	 * that you cannot override IO areas with set_memory_*() as
18268c2ecf20Sopenharmony_ci	 * these helpers cannot work with IO memory.
18278c2ecf20Sopenharmony_ci	 */
18288c2ecf20Sopenharmony_ci	return change_page_attr_set(&addr, numpages,
18298c2ecf20Sopenharmony_ci				    cachemode2pgprot(_PAGE_CACHE_MODE_UC_MINUS),
18308c2ecf20Sopenharmony_ci				    0);
18318c2ecf20Sopenharmony_ci}
18328c2ecf20Sopenharmony_ci
18338c2ecf20Sopenharmony_ciint set_memory_uc(unsigned long addr, int numpages)
18348c2ecf20Sopenharmony_ci{
18358c2ecf20Sopenharmony_ci	int ret;
18368c2ecf20Sopenharmony_ci
18378c2ecf20Sopenharmony_ci	/*
18388c2ecf20Sopenharmony_ci	 * for now UC MINUS. see comments in ioremap()
18398c2ecf20Sopenharmony_ci	 */
18408c2ecf20Sopenharmony_ci	ret = memtype_reserve(__pa(addr), __pa(addr) + numpages * PAGE_SIZE,
18418c2ecf20Sopenharmony_ci			      _PAGE_CACHE_MODE_UC_MINUS, NULL);
18428c2ecf20Sopenharmony_ci	if (ret)
18438c2ecf20Sopenharmony_ci		goto out_err;
18448c2ecf20Sopenharmony_ci
18458c2ecf20Sopenharmony_ci	ret = _set_memory_uc(addr, numpages);
18468c2ecf20Sopenharmony_ci	if (ret)
18478c2ecf20Sopenharmony_ci		goto out_free;
18488c2ecf20Sopenharmony_ci
18498c2ecf20Sopenharmony_ci	return 0;
18508c2ecf20Sopenharmony_ci
18518c2ecf20Sopenharmony_ciout_free:
18528c2ecf20Sopenharmony_ci	memtype_free(__pa(addr), __pa(addr) + numpages * PAGE_SIZE);
18538c2ecf20Sopenharmony_ciout_err:
18548c2ecf20Sopenharmony_ci	return ret;
18558c2ecf20Sopenharmony_ci}
18568c2ecf20Sopenharmony_ciEXPORT_SYMBOL(set_memory_uc);
18578c2ecf20Sopenharmony_ci
18588c2ecf20Sopenharmony_ciint _set_memory_wc(unsigned long addr, int numpages)
18598c2ecf20Sopenharmony_ci{
18608c2ecf20Sopenharmony_ci	int ret;
18618c2ecf20Sopenharmony_ci
18628c2ecf20Sopenharmony_ci	ret = change_page_attr_set(&addr, numpages,
18638c2ecf20Sopenharmony_ci				   cachemode2pgprot(_PAGE_CACHE_MODE_UC_MINUS),
18648c2ecf20Sopenharmony_ci				   0);
18658c2ecf20Sopenharmony_ci	if (!ret) {
18668c2ecf20Sopenharmony_ci		ret = change_page_attr_set_clr(&addr, numpages,
18678c2ecf20Sopenharmony_ci					       cachemode2pgprot(_PAGE_CACHE_MODE_WC),
18688c2ecf20Sopenharmony_ci					       __pgprot(_PAGE_CACHE_MASK),
18698c2ecf20Sopenharmony_ci					       0, 0, NULL);
18708c2ecf20Sopenharmony_ci	}
18718c2ecf20Sopenharmony_ci	return ret;
18728c2ecf20Sopenharmony_ci}
18738c2ecf20Sopenharmony_ci
18748c2ecf20Sopenharmony_ciint set_memory_wc(unsigned long addr, int numpages)
18758c2ecf20Sopenharmony_ci{
18768c2ecf20Sopenharmony_ci	int ret;
18778c2ecf20Sopenharmony_ci
18788c2ecf20Sopenharmony_ci	ret = memtype_reserve(__pa(addr), __pa(addr) + numpages * PAGE_SIZE,
18798c2ecf20Sopenharmony_ci		_PAGE_CACHE_MODE_WC, NULL);
18808c2ecf20Sopenharmony_ci	if (ret)
18818c2ecf20Sopenharmony_ci		return ret;
18828c2ecf20Sopenharmony_ci
18838c2ecf20Sopenharmony_ci	ret = _set_memory_wc(addr, numpages);
18848c2ecf20Sopenharmony_ci	if (ret)
18858c2ecf20Sopenharmony_ci		memtype_free(__pa(addr), __pa(addr) + numpages * PAGE_SIZE);
18868c2ecf20Sopenharmony_ci
18878c2ecf20Sopenharmony_ci	return ret;
18888c2ecf20Sopenharmony_ci}
18898c2ecf20Sopenharmony_ciEXPORT_SYMBOL(set_memory_wc);
18908c2ecf20Sopenharmony_ci
18918c2ecf20Sopenharmony_ciint _set_memory_wt(unsigned long addr, int numpages)
18928c2ecf20Sopenharmony_ci{
18938c2ecf20Sopenharmony_ci	return change_page_attr_set(&addr, numpages,
18948c2ecf20Sopenharmony_ci				    cachemode2pgprot(_PAGE_CACHE_MODE_WT), 0);
18958c2ecf20Sopenharmony_ci}
18968c2ecf20Sopenharmony_ci
18978c2ecf20Sopenharmony_ciint _set_memory_wb(unsigned long addr, int numpages)
18988c2ecf20Sopenharmony_ci{
18998c2ecf20Sopenharmony_ci	/* WB cache mode is hard wired to all cache attribute bits being 0 */
19008c2ecf20Sopenharmony_ci	return change_page_attr_clear(&addr, numpages,
19018c2ecf20Sopenharmony_ci				      __pgprot(_PAGE_CACHE_MASK), 0);
19028c2ecf20Sopenharmony_ci}
19038c2ecf20Sopenharmony_ci
19048c2ecf20Sopenharmony_ciint set_memory_wb(unsigned long addr, int numpages)
19058c2ecf20Sopenharmony_ci{
19068c2ecf20Sopenharmony_ci	int ret;
19078c2ecf20Sopenharmony_ci
19088c2ecf20Sopenharmony_ci	ret = _set_memory_wb(addr, numpages);
19098c2ecf20Sopenharmony_ci	if (ret)
19108c2ecf20Sopenharmony_ci		return ret;
19118c2ecf20Sopenharmony_ci
19128c2ecf20Sopenharmony_ci	memtype_free(__pa(addr), __pa(addr) + numpages * PAGE_SIZE);
19138c2ecf20Sopenharmony_ci	return 0;
19148c2ecf20Sopenharmony_ci}
19158c2ecf20Sopenharmony_ciEXPORT_SYMBOL(set_memory_wb);
19168c2ecf20Sopenharmony_ci
19178c2ecf20Sopenharmony_ciint set_memory_x(unsigned long addr, int numpages)
19188c2ecf20Sopenharmony_ci{
19198c2ecf20Sopenharmony_ci	if (!(__supported_pte_mask & _PAGE_NX))
19208c2ecf20Sopenharmony_ci		return 0;
19218c2ecf20Sopenharmony_ci
19228c2ecf20Sopenharmony_ci	return change_page_attr_clear(&addr, numpages, __pgprot(_PAGE_NX), 0);
19238c2ecf20Sopenharmony_ci}
19248c2ecf20Sopenharmony_ci
19258c2ecf20Sopenharmony_ciint set_memory_nx(unsigned long addr, int numpages)
19268c2ecf20Sopenharmony_ci{
19278c2ecf20Sopenharmony_ci	if (!(__supported_pte_mask & _PAGE_NX))
19288c2ecf20Sopenharmony_ci		return 0;
19298c2ecf20Sopenharmony_ci
19308c2ecf20Sopenharmony_ci	return change_page_attr_set(&addr, numpages, __pgprot(_PAGE_NX), 0);
19318c2ecf20Sopenharmony_ci}
19328c2ecf20Sopenharmony_ci
19338c2ecf20Sopenharmony_ciint set_memory_ro(unsigned long addr, int numpages)
19348c2ecf20Sopenharmony_ci{
19358c2ecf20Sopenharmony_ci	return change_page_attr_clear(&addr, numpages, __pgprot(_PAGE_RW), 0);
19368c2ecf20Sopenharmony_ci}
19378c2ecf20Sopenharmony_ci
19388c2ecf20Sopenharmony_ciint set_memory_rw(unsigned long addr, int numpages)
19398c2ecf20Sopenharmony_ci{
19408c2ecf20Sopenharmony_ci	return change_page_attr_set(&addr, numpages, __pgprot(_PAGE_RW), 0);
19418c2ecf20Sopenharmony_ci}
19428c2ecf20Sopenharmony_ci
19438c2ecf20Sopenharmony_ciint set_memory_np(unsigned long addr, int numpages)
19448c2ecf20Sopenharmony_ci{
19458c2ecf20Sopenharmony_ci	return change_page_attr_clear(&addr, numpages, __pgprot(_PAGE_PRESENT), 0);
19468c2ecf20Sopenharmony_ci}
19478c2ecf20Sopenharmony_ci
19488c2ecf20Sopenharmony_ciint set_memory_np_noalias(unsigned long addr, int numpages)
19498c2ecf20Sopenharmony_ci{
19508c2ecf20Sopenharmony_ci	int cpa_flags = CPA_NO_CHECK_ALIAS;
19518c2ecf20Sopenharmony_ci
19528c2ecf20Sopenharmony_ci	return change_page_attr_set_clr(&addr, numpages, __pgprot(0),
19538c2ecf20Sopenharmony_ci					__pgprot(_PAGE_PRESENT), 0,
19548c2ecf20Sopenharmony_ci					cpa_flags, NULL);
19558c2ecf20Sopenharmony_ci}
19568c2ecf20Sopenharmony_ci
19578c2ecf20Sopenharmony_ciint set_memory_4k(unsigned long addr, int numpages)
19588c2ecf20Sopenharmony_ci{
19598c2ecf20Sopenharmony_ci	return change_page_attr_set_clr(&addr, numpages, __pgprot(0),
19608c2ecf20Sopenharmony_ci					__pgprot(0), 1, 0, NULL);
19618c2ecf20Sopenharmony_ci}
19628c2ecf20Sopenharmony_ci
19638c2ecf20Sopenharmony_ciint set_memory_nonglobal(unsigned long addr, int numpages)
19648c2ecf20Sopenharmony_ci{
19658c2ecf20Sopenharmony_ci	return change_page_attr_clear(&addr, numpages,
19668c2ecf20Sopenharmony_ci				      __pgprot(_PAGE_GLOBAL), 0);
19678c2ecf20Sopenharmony_ci}
19688c2ecf20Sopenharmony_ci
19698c2ecf20Sopenharmony_ciint set_memory_global(unsigned long addr, int numpages)
19708c2ecf20Sopenharmony_ci{
19718c2ecf20Sopenharmony_ci	return change_page_attr_set(&addr, numpages,
19728c2ecf20Sopenharmony_ci				    __pgprot(_PAGE_GLOBAL), 0);
19738c2ecf20Sopenharmony_ci}
19748c2ecf20Sopenharmony_ci
19758c2ecf20Sopenharmony_cistatic int __set_memory_enc_dec(unsigned long addr, int numpages, bool enc)
19768c2ecf20Sopenharmony_ci{
19778c2ecf20Sopenharmony_ci	struct cpa_data cpa;
19788c2ecf20Sopenharmony_ci	int ret;
19798c2ecf20Sopenharmony_ci
19808c2ecf20Sopenharmony_ci	/* Nothing to do if memory encryption is not active */
19818c2ecf20Sopenharmony_ci	if (!mem_encrypt_active())
19828c2ecf20Sopenharmony_ci		return 0;
19838c2ecf20Sopenharmony_ci
19848c2ecf20Sopenharmony_ci	/* Should not be working on unaligned addresses */
19858c2ecf20Sopenharmony_ci	if (WARN_ONCE(addr & ~PAGE_MASK, "misaligned address: %#lx\n", addr))
19868c2ecf20Sopenharmony_ci		addr &= PAGE_MASK;
19878c2ecf20Sopenharmony_ci
19888c2ecf20Sopenharmony_ci	memset(&cpa, 0, sizeof(cpa));
19898c2ecf20Sopenharmony_ci	cpa.vaddr = &addr;
19908c2ecf20Sopenharmony_ci	cpa.numpages = numpages;
19918c2ecf20Sopenharmony_ci	cpa.mask_set = enc ? __pgprot(_PAGE_ENC) : __pgprot(0);
19928c2ecf20Sopenharmony_ci	cpa.mask_clr = enc ? __pgprot(0) : __pgprot(_PAGE_ENC);
19938c2ecf20Sopenharmony_ci	cpa.pgd = init_mm.pgd;
19948c2ecf20Sopenharmony_ci
19958c2ecf20Sopenharmony_ci	/* Must avoid aliasing mappings in the highmem code */
19968c2ecf20Sopenharmony_ci	kmap_flush_unused();
19978c2ecf20Sopenharmony_ci	vm_unmap_aliases();
19988c2ecf20Sopenharmony_ci
19998c2ecf20Sopenharmony_ci	/*
20008c2ecf20Sopenharmony_ci	 * Before changing the encryption attribute, we need to flush caches.
20018c2ecf20Sopenharmony_ci	 */
20028c2ecf20Sopenharmony_ci	cpa_flush(&cpa, !this_cpu_has(X86_FEATURE_SME_COHERENT));
20038c2ecf20Sopenharmony_ci
20048c2ecf20Sopenharmony_ci	ret = __change_page_attr_set_clr(&cpa, 1);
20058c2ecf20Sopenharmony_ci
20068c2ecf20Sopenharmony_ci	/*
20078c2ecf20Sopenharmony_ci	 * After changing the encryption attribute, we need to flush TLBs again
20088c2ecf20Sopenharmony_ci	 * in case any speculative TLB caching occurred (but no need to flush
20098c2ecf20Sopenharmony_ci	 * caches again).  We could just use cpa_flush_all(), but in case TLB
20108c2ecf20Sopenharmony_ci	 * flushing gets optimized in the cpa_flush() path use the same logic
20118c2ecf20Sopenharmony_ci	 * as above.
20128c2ecf20Sopenharmony_ci	 */
20138c2ecf20Sopenharmony_ci	cpa_flush(&cpa, 0);
20148c2ecf20Sopenharmony_ci
20158c2ecf20Sopenharmony_ci	return ret;
20168c2ecf20Sopenharmony_ci}
20178c2ecf20Sopenharmony_ci
20188c2ecf20Sopenharmony_ciint set_memory_encrypted(unsigned long addr, int numpages)
20198c2ecf20Sopenharmony_ci{
20208c2ecf20Sopenharmony_ci	return __set_memory_enc_dec(addr, numpages, true);
20218c2ecf20Sopenharmony_ci}
20228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(set_memory_encrypted);
20238c2ecf20Sopenharmony_ci
20248c2ecf20Sopenharmony_ciint set_memory_decrypted(unsigned long addr, int numpages)
20258c2ecf20Sopenharmony_ci{
20268c2ecf20Sopenharmony_ci	return __set_memory_enc_dec(addr, numpages, false);
20278c2ecf20Sopenharmony_ci}
20288c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(set_memory_decrypted);
20298c2ecf20Sopenharmony_ci
20308c2ecf20Sopenharmony_ciint set_pages_uc(struct page *page, int numpages)
20318c2ecf20Sopenharmony_ci{
20328c2ecf20Sopenharmony_ci	unsigned long addr = (unsigned long)page_address(page);
20338c2ecf20Sopenharmony_ci
20348c2ecf20Sopenharmony_ci	return set_memory_uc(addr, numpages);
20358c2ecf20Sopenharmony_ci}
20368c2ecf20Sopenharmony_ciEXPORT_SYMBOL(set_pages_uc);
20378c2ecf20Sopenharmony_ci
20388c2ecf20Sopenharmony_cistatic int _set_pages_array(struct page **pages, int numpages,
20398c2ecf20Sopenharmony_ci		enum page_cache_mode new_type)
20408c2ecf20Sopenharmony_ci{
20418c2ecf20Sopenharmony_ci	unsigned long start;
20428c2ecf20Sopenharmony_ci	unsigned long end;
20438c2ecf20Sopenharmony_ci	enum page_cache_mode set_type;
20448c2ecf20Sopenharmony_ci	int i;
20458c2ecf20Sopenharmony_ci	int free_idx;
20468c2ecf20Sopenharmony_ci	int ret;
20478c2ecf20Sopenharmony_ci
20488c2ecf20Sopenharmony_ci	for (i = 0; i < numpages; i++) {
20498c2ecf20Sopenharmony_ci		if (PageHighMem(pages[i]))
20508c2ecf20Sopenharmony_ci			continue;
20518c2ecf20Sopenharmony_ci		start = page_to_pfn(pages[i]) << PAGE_SHIFT;
20528c2ecf20Sopenharmony_ci		end = start + PAGE_SIZE;
20538c2ecf20Sopenharmony_ci		if (memtype_reserve(start, end, new_type, NULL))
20548c2ecf20Sopenharmony_ci			goto err_out;
20558c2ecf20Sopenharmony_ci	}
20568c2ecf20Sopenharmony_ci
20578c2ecf20Sopenharmony_ci	/* If WC, set to UC- first and then WC */
20588c2ecf20Sopenharmony_ci	set_type = (new_type == _PAGE_CACHE_MODE_WC) ?
20598c2ecf20Sopenharmony_ci				_PAGE_CACHE_MODE_UC_MINUS : new_type;
20608c2ecf20Sopenharmony_ci
20618c2ecf20Sopenharmony_ci	ret = cpa_set_pages_array(pages, numpages,
20628c2ecf20Sopenharmony_ci				  cachemode2pgprot(set_type));
20638c2ecf20Sopenharmony_ci	if (!ret && new_type == _PAGE_CACHE_MODE_WC)
20648c2ecf20Sopenharmony_ci		ret = change_page_attr_set_clr(NULL, numpages,
20658c2ecf20Sopenharmony_ci					       cachemode2pgprot(
20668c2ecf20Sopenharmony_ci						_PAGE_CACHE_MODE_WC),
20678c2ecf20Sopenharmony_ci					       __pgprot(_PAGE_CACHE_MASK),
20688c2ecf20Sopenharmony_ci					       0, CPA_PAGES_ARRAY, pages);
20698c2ecf20Sopenharmony_ci	if (ret)
20708c2ecf20Sopenharmony_ci		goto err_out;
20718c2ecf20Sopenharmony_ci	return 0; /* Success */
20728c2ecf20Sopenharmony_cierr_out:
20738c2ecf20Sopenharmony_ci	free_idx = i;
20748c2ecf20Sopenharmony_ci	for (i = 0; i < free_idx; i++) {
20758c2ecf20Sopenharmony_ci		if (PageHighMem(pages[i]))
20768c2ecf20Sopenharmony_ci			continue;
20778c2ecf20Sopenharmony_ci		start = page_to_pfn(pages[i]) << PAGE_SHIFT;
20788c2ecf20Sopenharmony_ci		end = start + PAGE_SIZE;
20798c2ecf20Sopenharmony_ci		memtype_free(start, end);
20808c2ecf20Sopenharmony_ci	}
20818c2ecf20Sopenharmony_ci	return -EINVAL;
20828c2ecf20Sopenharmony_ci}
20838c2ecf20Sopenharmony_ci
20848c2ecf20Sopenharmony_ciint set_pages_array_uc(struct page **pages, int numpages)
20858c2ecf20Sopenharmony_ci{
20868c2ecf20Sopenharmony_ci	return _set_pages_array(pages, numpages, _PAGE_CACHE_MODE_UC_MINUS);
20878c2ecf20Sopenharmony_ci}
20888c2ecf20Sopenharmony_ciEXPORT_SYMBOL(set_pages_array_uc);
20898c2ecf20Sopenharmony_ci
20908c2ecf20Sopenharmony_ciint set_pages_array_wc(struct page **pages, int numpages)
20918c2ecf20Sopenharmony_ci{
20928c2ecf20Sopenharmony_ci	return _set_pages_array(pages, numpages, _PAGE_CACHE_MODE_WC);
20938c2ecf20Sopenharmony_ci}
20948c2ecf20Sopenharmony_ciEXPORT_SYMBOL(set_pages_array_wc);
20958c2ecf20Sopenharmony_ci
20968c2ecf20Sopenharmony_ciint set_pages_array_wt(struct page **pages, int numpages)
20978c2ecf20Sopenharmony_ci{
20988c2ecf20Sopenharmony_ci	return _set_pages_array(pages, numpages, _PAGE_CACHE_MODE_WT);
20998c2ecf20Sopenharmony_ci}
21008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(set_pages_array_wt);
21018c2ecf20Sopenharmony_ci
21028c2ecf20Sopenharmony_ciint set_pages_wb(struct page *page, int numpages)
21038c2ecf20Sopenharmony_ci{
21048c2ecf20Sopenharmony_ci	unsigned long addr = (unsigned long)page_address(page);
21058c2ecf20Sopenharmony_ci
21068c2ecf20Sopenharmony_ci	return set_memory_wb(addr, numpages);
21078c2ecf20Sopenharmony_ci}
21088c2ecf20Sopenharmony_ciEXPORT_SYMBOL(set_pages_wb);
21098c2ecf20Sopenharmony_ci
21108c2ecf20Sopenharmony_ciint set_pages_array_wb(struct page **pages, int numpages)
21118c2ecf20Sopenharmony_ci{
21128c2ecf20Sopenharmony_ci	int retval;
21138c2ecf20Sopenharmony_ci	unsigned long start;
21148c2ecf20Sopenharmony_ci	unsigned long end;
21158c2ecf20Sopenharmony_ci	int i;
21168c2ecf20Sopenharmony_ci
21178c2ecf20Sopenharmony_ci	/* WB cache mode is hard wired to all cache attribute bits being 0 */
21188c2ecf20Sopenharmony_ci	retval = cpa_clear_pages_array(pages, numpages,
21198c2ecf20Sopenharmony_ci			__pgprot(_PAGE_CACHE_MASK));
21208c2ecf20Sopenharmony_ci	if (retval)
21218c2ecf20Sopenharmony_ci		return retval;
21228c2ecf20Sopenharmony_ci
21238c2ecf20Sopenharmony_ci	for (i = 0; i < numpages; i++) {
21248c2ecf20Sopenharmony_ci		if (PageHighMem(pages[i]))
21258c2ecf20Sopenharmony_ci			continue;
21268c2ecf20Sopenharmony_ci		start = page_to_pfn(pages[i]) << PAGE_SHIFT;
21278c2ecf20Sopenharmony_ci		end = start + PAGE_SIZE;
21288c2ecf20Sopenharmony_ci		memtype_free(start, end);
21298c2ecf20Sopenharmony_ci	}
21308c2ecf20Sopenharmony_ci
21318c2ecf20Sopenharmony_ci	return 0;
21328c2ecf20Sopenharmony_ci}
21338c2ecf20Sopenharmony_ciEXPORT_SYMBOL(set_pages_array_wb);
21348c2ecf20Sopenharmony_ci
21358c2ecf20Sopenharmony_ciint set_pages_ro(struct page *page, int numpages)
21368c2ecf20Sopenharmony_ci{
21378c2ecf20Sopenharmony_ci	unsigned long addr = (unsigned long)page_address(page);
21388c2ecf20Sopenharmony_ci
21398c2ecf20Sopenharmony_ci	return set_memory_ro(addr, numpages);
21408c2ecf20Sopenharmony_ci}
21418c2ecf20Sopenharmony_ci
21428c2ecf20Sopenharmony_ciint set_pages_rw(struct page *page, int numpages)
21438c2ecf20Sopenharmony_ci{
21448c2ecf20Sopenharmony_ci	unsigned long addr = (unsigned long)page_address(page);
21458c2ecf20Sopenharmony_ci
21468c2ecf20Sopenharmony_ci	return set_memory_rw(addr, numpages);
21478c2ecf20Sopenharmony_ci}
21488c2ecf20Sopenharmony_ci
21498c2ecf20Sopenharmony_cistatic int __set_pages_p(struct page *page, int numpages)
21508c2ecf20Sopenharmony_ci{
21518c2ecf20Sopenharmony_ci	unsigned long tempaddr = (unsigned long) page_address(page);
21528c2ecf20Sopenharmony_ci	struct cpa_data cpa = { .vaddr = &tempaddr,
21538c2ecf20Sopenharmony_ci				.pgd = NULL,
21548c2ecf20Sopenharmony_ci				.numpages = numpages,
21558c2ecf20Sopenharmony_ci				.mask_set = __pgprot(_PAGE_PRESENT | _PAGE_RW),
21568c2ecf20Sopenharmony_ci				.mask_clr = __pgprot(0),
21578c2ecf20Sopenharmony_ci				.flags = 0};
21588c2ecf20Sopenharmony_ci
21598c2ecf20Sopenharmony_ci	/*
21608c2ecf20Sopenharmony_ci	 * No alias checking needed for setting present flag. otherwise,
21618c2ecf20Sopenharmony_ci	 * we may need to break large pages for 64-bit kernel text
21628c2ecf20Sopenharmony_ci	 * mappings (this adds to complexity if we want to do this from
21638c2ecf20Sopenharmony_ci	 * atomic context especially). Let's keep it simple!
21648c2ecf20Sopenharmony_ci	 */
21658c2ecf20Sopenharmony_ci	return __change_page_attr_set_clr(&cpa, 0);
21668c2ecf20Sopenharmony_ci}
21678c2ecf20Sopenharmony_ci
21688c2ecf20Sopenharmony_cistatic int __set_pages_np(struct page *page, int numpages)
21698c2ecf20Sopenharmony_ci{
21708c2ecf20Sopenharmony_ci	unsigned long tempaddr = (unsigned long) page_address(page);
21718c2ecf20Sopenharmony_ci	struct cpa_data cpa = { .vaddr = &tempaddr,
21728c2ecf20Sopenharmony_ci				.pgd = NULL,
21738c2ecf20Sopenharmony_ci				.numpages = numpages,
21748c2ecf20Sopenharmony_ci				.mask_set = __pgprot(0),
21758c2ecf20Sopenharmony_ci				.mask_clr = __pgprot(_PAGE_PRESENT | _PAGE_RW),
21768c2ecf20Sopenharmony_ci				.flags = 0};
21778c2ecf20Sopenharmony_ci
21788c2ecf20Sopenharmony_ci	/*
21798c2ecf20Sopenharmony_ci	 * No alias checking needed for setting not present flag. otherwise,
21808c2ecf20Sopenharmony_ci	 * we may need to break large pages for 64-bit kernel text
21818c2ecf20Sopenharmony_ci	 * mappings (this adds to complexity if we want to do this from
21828c2ecf20Sopenharmony_ci	 * atomic context especially). Let's keep it simple!
21838c2ecf20Sopenharmony_ci	 */
21848c2ecf20Sopenharmony_ci	return __change_page_attr_set_clr(&cpa, 0);
21858c2ecf20Sopenharmony_ci}
21868c2ecf20Sopenharmony_ci
21878c2ecf20Sopenharmony_ciint set_direct_map_invalid_noflush(struct page *page)
21888c2ecf20Sopenharmony_ci{
21898c2ecf20Sopenharmony_ci	return __set_pages_np(page, 1);
21908c2ecf20Sopenharmony_ci}
21918c2ecf20Sopenharmony_ci
21928c2ecf20Sopenharmony_ciint set_direct_map_default_noflush(struct page *page)
21938c2ecf20Sopenharmony_ci{
21948c2ecf20Sopenharmony_ci	return __set_pages_p(page, 1);
21958c2ecf20Sopenharmony_ci}
21968c2ecf20Sopenharmony_ci
21978c2ecf20Sopenharmony_civoid __kernel_map_pages(struct page *page, int numpages, int enable)
21988c2ecf20Sopenharmony_ci{
21998c2ecf20Sopenharmony_ci	if (PageHighMem(page))
22008c2ecf20Sopenharmony_ci		return;
22018c2ecf20Sopenharmony_ci	if (!enable) {
22028c2ecf20Sopenharmony_ci		debug_check_no_locks_freed(page_address(page),
22038c2ecf20Sopenharmony_ci					   numpages * PAGE_SIZE);
22048c2ecf20Sopenharmony_ci	}
22058c2ecf20Sopenharmony_ci
22068c2ecf20Sopenharmony_ci	/*
22078c2ecf20Sopenharmony_ci	 * The return value is ignored as the calls cannot fail.
22088c2ecf20Sopenharmony_ci	 * Large pages for identity mappings are not used at boot time
22098c2ecf20Sopenharmony_ci	 * and hence no memory allocations during large page split.
22108c2ecf20Sopenharmony_ci	 */
22118c2ecf20Sopenharmony_ci	if (enable)
22128c2ecf20Sopenharmony_ci		__set_pages_p(page, numpages);
22138c2ecf20Sopenharmony_ci	else
22148c2ecf20Sopenharmony_ci		__set_pages_np(page, numpages);
22158c2ecf20Sopenharmony_ci
22168c2ecf20Sopenharmony_ci	/*
22178c2ecf20Sopenharmony_ci	 * We should perform an IPI and flush all tlbs,
22188c2ecf20Sopenharmony_ci	 * but that can deadlock->flush only current cpu.
22198c2ecf20Sopenharmony_ci	 * Preemption needs to be disabled around __flush_tlb_all() due to
22208c2ecf20Sopenharmony_ci	 * CR3 reload in __native_flush_tlb().
22218c2ecf20Sopenharmony_ci	 */
22228c2ecf20Sopenharmony_ci	preempt_disable();
22238c2ecf20Sopenharmony_ci	__flush_tlb_all();
22248c2ecf20Sopenharmony_ci	preempt_enable();
22258c2ecf20Sopenharmony_ci
22268c2ecf20Sopenharmony_ci	arch_flush_lazy_mmu_mode();
22278c2ecf20Sopenharmony_ci}
22288c2ecf20Sopenharmony_ci
22298c2ecf20Sopenharmony_ci#ifdef CONFIG_HIBERNATION
22308c2ecf20Sopenharmony_cibool kernel_page_present(struct page *page)
22318c2ecf20Sopenharmony_ci{
22328c2ecf20Sopenharmony_ci	unsigned int level;
22338c2ecf20Sopenharmony_ci	pte_t *pte;
22348c2ecf20Sopenharmony_ci
22358c2ecf20Sopenharmony_ci	if (PageHighMem(page))
22368c2ecf20Sopenharmony_ci		return false;
22378c2ecf20Sopenharmony_ci
22388c2ecf20Sopenharmony_ci	pte = lookup_address((unsigned long)page_address(page), &level);
22398c2ecf20Sopenharmony_ci	return (pte_val(*pte) & _PAGE_PRESENT);
22408c2ecf20Sopenharmony_ci}
22418c2ecf20Sopenharmony_ci#endif /* CONFIG_HIBERNATION */
22428c2ecf20Sopenharmony_ci
22438c2ecf20Sopenharmony_ciint __init kernel_map_pages_in_pgd(pgd_t *pgd, u64 pfn, unsigned long address,
22448c2ecf20Sopenharmony_ci				   unsigned numpages, unsigned long page_flags)
22458c2ecf20Sopenharmony_ci{
22468c2ecf20Sopenharmony_ci	int retval = -EINVAL;
22478c2ecf20Sopenharmony_ci
22488c2ecf20Sopenharmony_ci	struct cpa_data cpa = {
22498c2ecf20Sopenharmony_ci		.vaddr = &address,
22508c2ecf20Sopenharmony_ci		.pfn = pfn,
22518c2ecf20Sopenharmony_ci		.pgd = pgd,
22528c2ecf20Sopenharmony_ci		.numpages = numpages,
22538c2ecf20Sopenharmony_ci		.mask_set = __pgprot(0),
22548c2ecf20Sopenharmony_ci		.mask_clr = __pgprot(~page_flags & (_PAGE_NX|_PAGE_RW)),
22558c2ecf20Sopenharmony_ci		.flags = 0,
22568c2ecf20Sopenharmony_ci	};
22578c2ecf20Sopenharmony_ci
22588c2ecf20Sopenharmony_ci	WARN_ONCE(num_online_cpus() > 1, "Don't call after initializing SMP");
22598c2ecf20Sopenharmony_ci
22608c2ecf20Sopenharmony_ci	if (!(__supported_pte_mask & _PAGE_NX))
22618c2ecf20Sopenharmony_ci		goto out;
22628c2ecf20Sopenharmony_ci
22638c2ecf20Sopenharmony_ci	if (!(page_flags & _PAGE_ENC))
22648c2ecf20Sopenharmony_ci		cpa.mask_clr = pgprot_encrypted(cpa.mask_clr);
22658c2ecf20Sopenharmony_ci
22668c2ecf20Sopenharmony_ci	cpa.mask_set = __pgprot(_PAGE_PRESENT | page_flags);
22678c2ecf20Sopenharmony_ci
22688c2ecf20Sopenharmony_ci	retval = __change_page_attr_set_clr(&cpa, 0);
22698c2ecf20Sopenharmony_ci	__flush_tlb_all();
22708c2ecf20Sopenharmony_ci
22718c2ecf20Sopenharmony_ciout:
22728c2ecf20Sopenharmony_ci	return retval;
22738c2ecf20Sopenharmony_ci}
22748c2ecf20Sopenharmony_ci
22758c2ecf20Sopenharmony_ci/*
22768c2ecf20Sopenharmony_ci * __flush_tlb_all() flushes mappings only on current CPU and hence this
22778c2ecf20Sopenharmony_ci * function shouldn't be used in an SMP environment. Presently, it's used only
22788c2ecf20Sopenharmony_ci * during boot (way before smp_init()) by EFI subsystem and hence is ok.
22798c2ecf20Sopenharmony_ci */
22808c2ecf20Sopenharmony_ciint __init kernel_unmap_pages_in_pgd(pgd_t *pgd, unsigned long address,
22818c2ecf20Sopenharmony_ci				     unsigned long numpages)
22828c2ecf20Sopenharmony_ci{
22838c2ecf20Sopenharmony_ci	int retval;
22848c2ecf20Sopenharmony_ci
22858c2ecf20Sopenharmony_ci	/*
22868c2ecf20Sopenharmony_ci	 * The typical sequence for unmapping is to find a pte through
22878c2ecf20Sopenharmony_ci	 * lookup_address_in_pgd() (ideally, it should never return NULL because
22888c2ecf20Sopenharmony_ci	 * the address is already mapped) and change it's protections. As pfn is
22898c2ecf20Sopenharmony_ci	 * the *target* of a mapping, it's not useful while unmapping.
22908c2ecf20Sopenharmony_ci	 */
22918c2ecf20Sopenharmony_ci	struct cpa_data cpa = {
22928c2ecf20Sopenharmony_ci		.vaddr		= &address,
22938c2ecf20Sopenharmony_ci		.pfn		= 0,
22948c2ecf20Sopenharmony_ci		.pgd		= pgd,
22958c2ecf20Sopenharmony_ci		.numpages	= numpages,
22968c2ecf20Sopenharmony_ci		.mask_set	= __pgprot(0),
22978c2ecf20Sopenharmony_ci		.mask_clr	= __pgprot(_PAGE_PRESENT | _PAGE_RW),
22988c2ecf20Sopenharmony_ci		.flags		= 0,
22998c2ecf20Sopenharmony_ci	};
23008c2ecf20Sopenharmony_ci
23018c2ecf20Sopenharmony_ci	WARN_ONCE(num_online_cpus() > 1, "Don't call after initializing SMP");
23028c2ecf20Sopenharmony_ci
23038c2ecf20Sopenharmony_ci	retval = __change_page_attr_set_clr(&cpa, 0);
23048c2ecf20Sopenharmony_ci	__flush_tlb_all();
23058c2ecf20Sopenharmony_ci
23068c2ecf20Sopenharmony_ci	return retval;
23078c2ecf20Sopenharmony_ci}
23088c2ecf20Sopenharmony_ci
23098c2ecf20Sopenharmony_ci/*
23108c2ecf20Sopenharmony_ci * The testcases use internal knowledge of the implementation that shouldn't
23118c2ecf20Sopenharmony_ci * be exposed to the rest of the kernel. Include these directly here.
23128c2ecf20Sopenharmony_ci */
23138c2ecf20Sopenharmony_ci#ifdef CONFIG_CPA_DEBUG
23148c2ecf20Sopenharmony_ci#include "cpa-test.c"
23158c2ecf20Sopenharmony_ci#endif
2316