cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

page-states.c (4960B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright IBM Corp. 2008
      4 *
      5 * Guest page hinting for unused pages.
      6 *
      7 * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
      8 */
      9
     10#include <linux/kernel.h>
     11#include <linux/errno.h>
     12#include <linux/types.h>
     13#include <linux/mm.h>
     14#include <linux/memblock.h>
     15#include <linux/gfp.h>
     16#include <linux/init.h>
     17#include <asm/asm-extable.h>
     18#include <asm/facility.h>
     19#include <asm/page-states.h>
     20
     21static int cmma_flag = 1;
     22
     23static int __init cmma(char *str)
     24{
     25	bool enabled;
     26
     27	if (!kstrtobool(str, &enabled))
     28		cmma_flag = enabled;
     29	return 1;
     30}
     31__setup("cmma=", cmma);
     32
     33static inline int cmma_test_essa(void)
     34{
     35	unsigned long tmp = 0;
     36	int rc = -EOPNOTSUPP;
     37
     38	/* test ESSA_GET_STATE */
     39	asm volatile(
     40		"	.insn	rrf,0xb9ab0000,%[tmp],%[tmp],%[cmd],0\n"
     41		"0:     la      %[rc],0\n"
     42		"1:\n"
     43		EX_TABLE(0b,1b)
     44		: [rc] "+&d" (rc), [tmp] "+&d" (tmp)
     45		: [cmd] "i" (ESSA_GET_STATE));
     46	return rc;
     47}
     48
     49void __init cmma_init(void)
     50{
     51	if (!cmma_flag)
     52		return;
     53	if (cmma_test_essa()) {
     54		cmma_flag = 0;
     55		return;
     56	}
     57	if (test_facility(147))
     58		cmma_flag = 2;
     59}
     60
     61static inline unsigned char get_page_state(struct page *page)
     62{
     63	unsigned char state;
     64
     65	asm volatile("	.insn	rrf,0xb9ab0000,%0,%1,%2,0"
     66		     : "=&d" (state)
     67		     : "a" (page_to_phys(page)),
     68		       "i" (ESSA_GET_STATE));
     69	return state & 0x3f;
     70}
     71
     72static inline void set_page_unused(struct page *page, int order)
     73{
     74	int i, rc;
     75
     76	for (i = 0; i < (1 << order); i++)
     77		asm volatile(".insn rrf,0xb9ab0000,%0,%1,%2,0"
     78			     : "=&d" (rc)
     79			     : "a" (page_to_phys(page + i)),
     80			       "i" (ESSA_SET_UNUSED));
     81}
     82
     83static inline void set_page_stable_dat(struct page *page, int order)
     84{
     85	int i, rc;
     86
     87	for (i = 0; i < (1 << order); i++)
     88		asm volatile(".insn rrf,0xb9ab0000,%0,%1,%2,0"
     89			     : "=&d" (rc)
     90			     : "a" (page_to_phys(page + i)),
     91			       "i" (ESSA_SET_STABLE));
     92}
     93
     94static inline void set_page_stable_nodat(struct page *page, int order)
     95{
     96	int i, rc;
     97
     98	for (i = 0; i < (1 << order); i++)
     99		asm volatile(".insn rrf,0xb9ab0000,%0,%1,%2,0"
    100			     : "=&d" (rc)
    101			     : "a" (page_to_phys(page + i)),
    102			       "i" (ESSA_SET_STABLE_NODAT));
    103}
    104
    105static void mark_kernel_pmd(pud_t *pud, unsigned long addr, unsigned long end)
    106{
    107	unsigned long next;
    108	struct page *page;
    109	pmd_t *pmd;
    110
    111	pmd = pmd_offset(pud, addr);
    112	do {
    113		next = pmd_addr_end(addr, end);
    114		if (pmd_none(*pmd) || pmd_large(*pmd))
    115			continue;
    116		page = phys_to_page(pmd_val(*pmd));
    117		set_bit(PG_arch_1, &page->flags);
    118	} while (pmd++, addr = next, addr != end);
    119}
    120
    121static void mark_kernel_pud(p4d_t *p4d, unsigned long addr, unsigned long end)
    122{
    123	unsigned long next;
    124	struct page *page;
    125	pud_t *pud;
    126	int i;
    127
    128	pud = pud_offset(p4d, addr);
    129	do {
    130		next = pud_addr_end(addr, end);
    131		if (pud_none(*pud) || pud_large(*pud))
    132			continue;
    133		if (!pud_folded(*pud)) {
    134			page = phys_to_page(pud_val(*pud));
    135			for (i = 0; i < 3; i++)
    136				set_bit(PG_arch_1, &page[i].flags);
    137		}
    138		mark_kernel_pmd(pud, addr, next);
    139	} while (pud++, addr = next, addr != end);
    140}
    141
    142static void mark_kernel_p4d(pgd_t *pgd, unsigned long addr, unsigned long end)
    143{
    144	unsigned long next;
    145	struct page *page;
    146	p4d_t *p4d;
    147	int i;
    148
    149	p4d = p4d_offset(pgd, addr);
    150	do {
    151		next = p4d_addr_end(addr, end);
    152		if (p4d_none(*p4d))
    153			continue;
    154		if (!p4d_folded(*p4d)) {
    155			page = phys_to_page(p4d_val(*p4d));
    156			for (i = 0; i < 3; i++)
    157				set_bit(PG_arch_1, &page[i].flags);
    158		}
    159		mark_kernel_pud(p4d, addr, next);
    160	} while (p4d++, addr = next, addr != end);
    161}
    162
    163static void mark_kernel_pgd(void)
    164{
    165	unsigned long addr, next;
    166	struct page *page;
    167	pgd_t *pgd;
    168	int i;
    169
    170	addr = 0;
    171	pgd = pgd_offset_k(addr);
    172	do {
    173		next = pgd_addr_end(addr, MODULES_END);
    174		if (pgd_none(*pgd))
    175			continue;
    176		if (!pgd_folded(*pgd)) {
    177			page = phys_to_page(pgd_val(*pgd));
    178			for (i = 0; i < 3; i++)
    179				set_bit(PG_arch_1, &page[i].flags);
    180		}
    181		mark_kernel_p4d(pgd, addr, next);
    182	} while (pgd++, addr = next, addr != MODULES_END);
    183}
    184
    185void __init cmma_init_nodat(void)
    186{
    187	struct page *page;
    188	unsigned long start, end, ix;
    189	int i;
    190
    191	if (cmma_flag < 2)
    192		return;
    193	/* Mark pages used in kernel page tables */
    194	mark_kernel_pgd();
    195
    196	/* Set all kernel pages not used for page tables to stable/no-dat */
    197	for_each_mem_pfn_range(i, MAX_NUMNODES, &start, &end, NULL) {
    198		page = pfn_to_page(start);
    199		for (ix = start; ix < end; ix++, page++) {
    200			if (__test_and_clear_bit(PG_arch_1, &page->flags))
    201				continue;	/* skip page table pages */
    202			if (!list_empty(&page->lru))
    203				continue;	/* skip free pages */
    204			set_page_stable_nodat(page, 0);
    205		}
    206	}
    207}
    208
    209void arch_free_page(struct page *page, int order)
    210{
    211	if (!cmma_flag)
    212		return;
    213	set_page_unused(page, order);
    214}
    215
    216void arch_alloc_page(struct page *page, int order)
    217{
    218	if (!cmma_flag)
    219		return;
    220	if (cmma_flag < 2)
    221		set_page_stable_dat(page, order);
    222	else
    223		set_page_stable_nodat(page, order);
    224}
    225
    226void arch_set_page_dat(struct page *page, int order)
    227{
    228	if (!cmma_flag)
    229		return;
    230	set_page_stable_dat(page, order);
    231}