162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright IBM Corp. 2008
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Guest page hinting for unused pages.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/errno.h>
1262306a36Sopenharmony_ci#include <linux/types.h>
1362306a36Sopenharmony_ci#include <linux/mm.h>
1462306a36Sopenharmony_ci#include <linux/memblock.h>
1562306a36Sopenharmony_ci#include <linux/gfp.h>
1662306a36Sopenharmony_ci#include <linux/init.h>
1762306a36Sopenharmony_ci#include <asm/asm-extable.h>
1862306a36Sopenharmony_ci#include <asm/facility.h>
1962306a36Sopenharmony_ci#include <asm/page-states.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic int cmma_flag = 1;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic int __init cmma(char *str)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	bool enabled;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	if (!kstrtobool(str, &enabled))
2862306a36Sopenharmony_ci		cmma_flag = enabled;
2962306a36Sopenharmony_ci	return 1;
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci__setup("cmma=", cmma);
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic inline int cmma_test_essa(void)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	unsigned long tmp = 0;
3662306a36Sopenharmony_ci	int rc = -EOPNOTSUPP;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	/* test ESSA_GET_STATE */
3962306a36Sopenharmony_ci	asm volatile(
4062306a36Sopenharmony_ci		"	.insn	rrf,0xb9ab0000,%[tmp],%[tmp],%[cmd],0\n"
4162306a36Sopenharmony_ci		"0:     la      %[rc],0\n"
4262306a36Sopenharmony_ci		"1:\n"
4362306a36Sopenharmony_ci		EX_TABLE(0b,1b)
4462306a36Sopenharmony_ci		: [rc] "+&d" (rc), [tmp] "+&d" (tmp)
4562306a36Sopenharmony_ci		: [cmd] "i" (ESSA_GET_STATE));
4662306a36Sopenharmony_ci	return rc;
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_civoid __init cmma_init(void)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	if (!cmma_flag)
5262306a36Sopenharmony_ci		return;
5362306a36Sopenharmony_ci	if (cmma_test_essa()) {
5462306a36Sopenharmony_ci		cmma_flag = 0;
5562306a36Sopenharmony_ci		return;
5662306a36Sopenharmony_ci	}
5762306a36Sopenharmony_ci	if (test_facility(147))
5862306a36Sopenharmony_ci		cmma_flag = 2;
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic inline void set_page_unused(struct page *page, int order)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	int i, rc;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	for (i = 0; i < (1 << order); i++)
6662306a36Sopenharmony_ci		asm volatile(".insn rrf,0xb9ab0000,%0,%1,%2,0"
6762306a36Sopenharmony_ci			     : "=&d" (rc)
6862306a36Sopenharmony_ci			     : "a" (page_to_phys(page + i)),
6962306a36Sopenharmony_ci			       "i" (ESSA_SET_UNUSED));
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic inline void set_page_stable_dat(struct page *page, int order)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	int i, rc;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	for (i = 0; i < (1 << order); i++)
7762306a36Sopenharmony_ci		asm volatile(".insn rrf,0xb9ab0000,%0,%1,%2,0"
7862306a36Sopenharmony_ci			     : "=&d" (rc)
7962306a36Sopenharmony_ci			     : "a" (page_to_phys(page + i)),
8062306a36Sopenharmony_ci			       "i" (ESSA_SET_STABLE));
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic inline void set_page_stable_nodat(struct page *page, int order)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	int i, rc;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	for (i = 0; i < (1 << order); i++)
8862306a36Sopenharmony_ci		asm volatile(".insn rrf,0xb9ab0000,%0,%1,%2,0"
8962306a36Sopenharmony_ci			     : "=&d" (rc)
9062306a36Sopenharmony_ci			     : "a" (page_to_phys(page + i)),
9162306a36Sopenharmony_ci			       "i" (ESSA_SET_STABLE_NODAT));
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic void mark_kernel_pmd(pud_t *pud, unsigned long addr, unsigned long end)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	unsigned long next;
9762306a36Sopenharmony_ci	struct page *page;
9862306a36Sopenharmony_ci	pmd_t *pmd;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	pmd = pmd_offset(pud, addr);
10162306a36Sopenharmony_ci	do {
10262306a36Sopenharmony_ci		next = pmd_addr_end(addr, end);
10362306a36Sopenharmony_ci		if (pmd_none(*pmd) || pmd_large(*pmd))
10462306a36Sopenharmony_ci			continue;
10562306a36Sopenharmony_ci		page = phys_to_page(pmd_val(*pmd));
10662306a36Sopenharmony_ci		set_bit(PG_arch_1, &page->flags);
10762306a36Sopenharmony_ci	} while (pmd++, addr = next, addr != end);
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic void mark_kernel_pud(p4d_t *p4d, unsigned long addr, unsigned long end)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	unsigned long next;
11362306a36Sopenharmony_ci	struct page *page;
11462306a36Sopenharmony_ci	pud_t *pud;
11562306a36Sopenharmony_ci	int i;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	pud = pud_offset(p4d, addr);
11862306a36Sopenharmony_ci	do {
11962306a36Sopenharmony_ci		next = pud_addr_end(addr, end);
12062306a36Sopenharmony_ci		if (pud_none(*pud) || pud_large(*pud))
12162306a36Sopenharmony_ci			continue;
12262306a36Sopenharmony_ci		if (!pud_folded(*pud)) {
12362306a36Sopenharmony_ci			page = phys_to_page(pud_val(*pud));
12462306a36Sopenharmony_ci			for (i = 0; i < 4; i++)
12562306a36Sopenharmony_ci				set_bit(PG_arch_1, &page[i].flags);
12662306a36Sopenharmony_ci		}
12762306a36Sopenharmony_ci		mark_kernel_pmd(pud, addr, next);
12862306a36Sopenharmony_ci	} while (pud++, addr = next, addr != end);
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic void mark_kernel_p4d(pgd_t *pgd, unsigned long addr, unsigned long end)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	unsigned long next;
13462306a36Sopenharmony_ci	struct page *page;
13562306a36Sopenharmony_ci	p4d_t *p4d;
13662306a36Sopenharmony_ci	int i;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	p4d = p4d_offset(pgd, addr);
13962306a36Sopenharmony_ci	do {
14062306a36Sopenharmony_ci		next = p4d_addr_end(addr, end);
14162306a36Sopenharmony_ci		if (p4d_none(*p4d))
14262306a36Sopenharmony_ci			continue;
14362306a36Sopenharmony_ci		if (!p4d_folded(*p4d)) {
14462306a36Sopenharmony_ci			page = phys_to_page(p4d_val(*p4d));
14562306a36Sopenharmony_ci			for (i = 0; i < 4; i++)
14662306a36Sopenharmony_ci				set_bit(PG_arch_1, &page[i].flags);
14762306a36Sopenharmony_ci		}
14862306a36Sopenharmony_ci		mark_kernel_pud(p4d, addr, next);
14962306a36Sopenharmony_ci	} while (p4d++, addr = next, addr != end);
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic void mark_kernel_pgd(void)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	unsigned long addr, next;
15562306a36Sopenharmony_ci	struct page *page;
15662306a36Sopenharmony_ci	pgd_t *pgd;
15762306a36Sopenharmony_ci	int i;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	addr = 0;
16062306a36Sopenharmony_ci	pgd = pgd_offset_k(addr);
16162306a36Sopenharmony_ci	do {
16262306a36Sopenharmony_ci		next = pgd_addr_end(addr, MODULES_END);
16362306a36Sopenharmony_ci		if (pgd_none(*pgd))
16462306a36Sopenharmony_ci			continue;
16562306a36Sopenharmony_ci		if (!pgd_folded(*pgd)) {
16662306a36Sopenharmony_ci			page = phys_to_page(pgd_val(*pgd));
16762306a36Sopenharmony_ci			for (i = 0; i < 4; i++)
16862306a36Sopenharmony_ci				set_bit(PG_arch_1, &page[i].flags);
16962306a36Sopenharmony_ci		}
17062306a36Sopenharmony_ci		mark_kernel_p4d(pgd, addr, next);
17162306a36Sopenharmony_ci	} while (pgd++, addr = next, addr != MODULES_END);
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_civoid __init cmma_init_nodat(void)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	struct page *page;
17762306a36Sopenharmony_ci	unsigned long start, end, ix;
17862306a36Sopenharmony_ci	int i;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	if (cmma_flag < 2)
18162306a36Sopenharmony_ci		return;
18262306a36Sopenharmony_ci	/* Mark pages used in kernel page tables */
18362306a36Sopenharmony_ci	mark_kernel_pgd();
18462306a36Sopenharmony_ci	page = virt_to_page(&swapper_pg_dir);
18562306a36Sopenharmony_ci	for (i = 0; i < 4; i++)
18662306a36Sopenharmony_ci		set_bit(PG_arch_1, &page[i].flags);
18762306a36Sopenharmony_ci	page = virt_to_page(&invalid_pg_dir);
18862306a36Sopenharmony_ci	for (i = 0; i < 4; i++)
18962306a36Sopenharmony_ci		set_bit(PG_arch_1, &page[i].flags);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	/* Set all kernel pages not used for page tables to stable/no-dat */
19262306a36Sopenharmony_ci	for_each_mem_pfn_range(i, MAX_NUMNODES, &start, &end, NULL) {
19362306a36Sopenharmony_ci		page = pfn_to_page(start);
19462306a36Sopenharmony_ci		for (ix = start; ix < end; ix++, page++) {
19562306a36Sopenharmony_ci			if (__test_and_clear_bit(PG_arch_1, &page->flags))
19662306a36Sopenharmony_ci				continue;	/* skip page table pages */
19762306a36Sopenharmony_ci			if (!list_empty(&page->lru))
19862306a36Sopenharmony_ci				continue;	/* skip free pages */
19962306a36Sopenharmony_ci			set_page_stable_nodat(page, 0);
20062306a36Sopenharmony_ci		}
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_civoid arch_free_page(struct page *page, int order)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	if (!cmma_flag)
20762306a36Sopenharmony_ci		return;
20862306a36Sopenharmony_ci	set_page_unused(page, order);
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_civoid arch_alloc_page(struct page *page, int order)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	if (!cmma_flag)
21462306a36Sopenharmony_ci		return;
21562306a36Sopenharmony_ci	if (cmma_flag < 2)
21662306a36Sopenharmony_ci		set_page_stable_dat(page, order);
21762306a36Sopenharmony_ci	else
21862306a36Sopenharmony_ci		set_page_stable_nodat(page, order);
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_civoid arch_set_page_dat(struct page *page, int order)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	if (!cmma_flag)
22462306a36Sopenharmony_ci		return;
22562306a36Sopenharmony_ci	set_page_stable_dat(page, order);
22662306a36Sopenharmony_ci}
227