pageattr.c 2.9 KB
Newer Older
1 2 3 4
/*
 * Copyright IBM Corp. 2011
 * Author(s): Jan Glauber <jang@linux.vnet.ibm.com>
 */
5
#include <linux/hugetlb.h>
6 7
#include <linux/module.h>
#include <linux/mm.h>
8
#include <asm/cacheflush.h>
9
#include <asm/pgtable.h>
10 11
#include <asm/page.h>

12 13 14 15 16 17 18
static inline unsigned long sske_frame(unsigned long addr, unsigned char skey)
{
	asm volatile(".insn rrf,0xb22b0000,%[skey],%[addr],9,0"
		     : [addr] "+a" (addr) : [skey] "d" (skey));
	return addr;
}

19 20
void storage_key_init_range(unsigned long start, unsigned long end)
{
21
	unsigned long boundary, size;
22 23 24 25 26 27 28 29

	while (start < end) {
		if (MACHINE_HAS_EDAT1) {
			/* set storage keys for a 1MB frame */
			size = 1UL << 20;
			boundary = (start + size) & ~(size - 1);
			if (boundary <= end) {
				do {
30
					start = sske_frame(start, PAGE_DEFAULT_KEY);
31 32 33 34 35 36 37 38
				} while (start < boundary);
				continue;
			}
		}
		page_set_storage_key(start, PAGE_DEFAULT_KEY, 0);
		start += PAGE_SIZE;
	}
}
39

40 41 42 43 44 45 46 47 48 49 50
static pte_t *walk_page_table(unsigned long addr)
{
	pgd_t *pgdp;
	pud_t *pudp;
	pmd_t *pmdp;
	pte_t *ptep;

	pgdp = pgd_offset_k(addr);
	if (pgd_none(*pgdp))
		return NULL;
	pudp = pud_offset(pgdp, addr);
51
	if (pud_none(*pudp) || pud_large(*pudp))
52 53 54 55 56 57 58 59 60 61
		return NULL;
	pmdp = pmd_offset(pudp, addr);
	if (pmd_none(*pmdp) || pmd_large(*pmdp))
		return NULL;
	ptep = pte_offset_kernel(pmdp, addr);
	if (pte_none(*ptep))
		return NULL;
	return ptep;
}

62 63 64 65 66 67 68
static void change_page_attr(unsigned long addr, int numpages,
			     pte_t (*set) (pte_t))
{
	pte_t *ptep, pte;
	int i;

	for (i = 0; i < numpages; i++) {
69 70 71
		ptep = walk_page_table(addr);
		if (WARN_ON_ONCE(!ptep))
			break;
72 73
		pte = *ptep;
		pte = set(pte);
74
		__ptep_ipte(addr, ptep);
75
		*ptep = pte;
76
		addr += PAGE_SIZE;
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
	}
}

int set_memory_ro(unsigned long addr, int numpages)
{
	change_page_attr(addr, numpages, pte_wrprotect);
	return 0;
}

int set_memory_rw(unsigned long addr, int numpages)
{
	change_page_attr(addr, numpages, pte_mkwrite);
	return 0;
}

/* not possible */
int set_memory_nx(unsigned long addr, int numpages)
{
	return 0;
}
97 98 99 100 101

int set_memory_x(unsigned long addr, int numpages)
{
	return 0;
}
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120

#ifdef CONFIG_DEBUG_PAGEALLOC
void kernel_map_pages(struct page *page, int numpages, int enable)
{
	unsigned long address;
	pgd_t *pgd;
	pud_t *pud;
	pmd_t *pmd;
	pte_t *pte;
	int i;

	for (i = 0; i < numpages; i++) {
		address = page_to_phys(page + i);
		pgd = pgd_offset_k(address);
		pud = pud_offset(pgd, address);
		pmd = pmd_offset(pud, address);
		pte = pte_offset_kernel(pmd, address);
		if (!enable) {
			__ptep_ipte(address, pte);
121
			pte_val(*pte) = _PAGE_INVALID;
122 123
			continue;
		}
124
		pte_val(*pte) = __pa(address);
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
	}
}

#ifdef CONFIG_HIBERNATION
bool kernel_page_present(struct page *page)
{
	unsigned long addr;
	int cc;

	addr = page_to_phys(page);
	asm volatile(
		"	lra	%1,0(%1)\n"
		"	ipm	%0\n"
		"	srl	%0,28"
		: "=d" (cc), "+a" (addr) : : "cc");
	return cc == 0;
}
#endif /* CONFIG_HIBERNATION */

#endif /* CONFIG_DEBUG_PAGEALLOC */