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