linux/arch/s390/mm/pageattr.c
<<
>>
Prefs
   1/*
   2 * Copyright IBM Corp. 2011
   3 * Author(s): Jan Glauber <jang@linux.vnet.ibm.com>
   4 */
   5#include <linux/hugetlb.h>
   6#include <linux/module.h>
   7#include <linux/mm.h>
   8#include <asm/cacheflush.h>
   9#include <asm/pgtable.h>
  10#include <asm/page.h>
  11
  12static inline unsigned long sske_frame(unsigned long addr, unsigned char skey)
  13{
  14        asm volatile(".insn rrf,0xb22b0000,%[skey],%[addr],9,0"
  15                     : [addr] "+a" (addr) : [skey] "d" (skey));
  16        return addr;
  17}
  18
  19void storage_key_init_range(unsigned long start, unsigned long end)
  20{
  21        unsigned long boundary, size;
  22
  23        while (start < end) {
  24                if (MACHINE_HAS_EDAT1) {
  25                        /* set storage keys for a 1MB frame */
  26                        size = 1UL << 20;
  27                        boundary = (start + size) & ~(size - 1);
  28                        if (boundary <= end) {
  29                                do {
  30                                        start = sske_frame(start, PAGE_DEFAULT_KEY);
  31                                } while (start < boundary);
  32                                continue;
  33                        }
  34                }
  35                page_set_storage_key(start, PAGE_DEFAULT_KEY, 0);
  36                start += PAGE_SIZE;
  37        }
  38}
  39
  40static pte_t *walk_page_table(unsigned long addr)
  41{
  42        pgd_t *pgdp;
  43        pud_t *pudp;
  44        pmd_t *pmdp;
  45        pte_t *ptep;
  46
  47        pgdp = pgd_offset_k(addr);
  48        if (pgd_none(*pgdp))
  49                return NULL;
  50        pudp = pud_offset(pgdp, addr);
  51        if (pud_none(*pudp) || pud_large(*pudp))
  52                return NULL;
  53        pmdp = pmd_offset(pudp, addr);
  54        if (pmd_none(*pmdp) || pmd_large(*pmdp))
  55                return NULL;
  56        ptep = pte_offset_kernel(pmdp, addr);
  57        if (pte_none(*ptep))
  58                return NULL;
  59        return ptep;
  60}
  61
  62static void change_page_attr(unsigned long addr, int numpages,
  63                             pte_t (*set) (pte_t))
  64{
  65        pte_t *ptep, pte;
  66        int i;
  67
  68        for (i = 0; i < numpages; i++) {
  69                ptep = walk_page_table(addr);
  70                if (WARN_ON_ONCE(!ptep))
  71                        break;
  72                pte = *ptep;
  73                pte = set(pte);
  74                __ptep_ipte(addr, ptep);
  75                *ptep = pte;
  76                addr += PAGE_SIZE;
  77        }
  78}
  79
  80int set_memory_ro(unsigned long addr, int numpages)
  81{
  82        change_page_attr(addr, numpages, pte_wrprotect);
  83        return 0;
  84}
  85
  86int set_memory_rw(unsigned long addr, int numpages)
  87{
  88        change_page_attr(addr, numpages, pte_mkwrite);
  89        return 0;
  90}
  91
  92/* not possible */
  93int set_memory_nx(unsigned long addr, int numpages)
  94{
  95        return 0;
  96}
  97
  98int set_memory_x(unsigned long addr, int numpages)
  99{
 100        return 0;
 101}
 102
 103#ifdef CONFIG_DEBUG_PAGEALLOC
 104void kernel_map_pages(struct page *page, int numpages, int enable)
 105{
 106        unsigned long address;
 107        pgd_t *pgd;
 108        pud_t *pud;
 109        pmd_t *pmd;
 110        pte_t *pte;
 111        int i;
 112
 113        for (i = 0; i < numpages; i++) {
 114                address = page_to_phys(page + i);
 115                pgd = pgd_offset_k(address);
 116                pud = pud_offset(pgd, address);
 117                pmd = pmd_offset(pud, address);
 118                pte = pte_offset_kernel(pmd, address);
 119                if (!enable) {
 120                        __ptep_ipte(address, pte);
 121                        pte_val(*pte) = _PAGE_TYPE_EMPTY;
 122                        continue;
 123                }
 124                pte_val(*pte) = __pa(address);
 125        }
 126}
 127
 128#ifdef CONFIG_HIBERNATION
 129bool kernel_page_present(struct page *page)
 130{
 131        unsigned long addr;
 132        int cc;
 133
 134        addr = page_to_phys(page);
 135        asm volatile(
 136                "       lra     %1,0(%1)\n"
 137                "       ipm     %0\n"
 138                "       srl     %0,28"
 139                : "=d" (cc), "+a" (addr) : : "cc");
 140        return cc == 0;
 141}
 142#endif /* CONFIG_HIBERNATION */
 143
 144#endif /* CONFIG_DEBUG_PAGEALLOC */
 145