linux/arch/arm/mm/pageattr.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2014, The Linux Foundation. All rights reserved.
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 and
   6 * only version 2 as published by the Free Software Foundation.
   7 *
   8 * This program is distributed in the hope that it will be useful,
   9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11 * GNU General Public License for more details.
  12 */
  13#include <linux/mm.h>
  14#include <linux/module.h>
  15
  16#include <asm/pgtable.h>
  17#include <asm/tlbflush.h>
  18#include <asm/set_memory.h>
  19
  20struct page_change_data {
  21        pgprot_t set_mask;
  22        pgprot_t clear_mask;
  23};
  24
  25static int change_page_range(pte_t *ptep, pgtable_t token, unsigned long addr,
  26                        void *data)
  27{
  28        struct page_change_data *cdata = data;
  29        pte_t pte = *ptep;
  30
  31        pte = clear_pte_bit(pte, cdata->clear_mask);
  32        pte = set_pte_bit(pte, cdata->set_mask);
  33
  34        set_pte_ext(ptep, pte, 0);
  35        return 0;
  36}
  37
  38static bool in_range(unsigned long start, unsigned long size,
  39        unsigned long range_start, unsigned long range_end)
  40{
  41        return start >= range_start && start < range_end &&
  42                size <= range_end - start;
  43}
  44
  45static int change_memory_common(unsigned long addr, int numpages,
  46                                pgprot_t set_mask, pgprot_t clear_mask)
  47{
  48        unsigned long start = addr & PAGE_MASK;
  49        unsigned long end = PAGE_ALIGN(addr) + numpages * PAGE_SIZE;
  50        unsigned long size = end - start;
  51        int ret;
  52        struct page_change_data data;
  53
  54        WARN_ON_ONCE(start != addr);
  55
  56        if (!size)
  57                return 0;
  58
  59        if (!in_range(start, size, MODULES_VADDR, MODULES_END) &&
  60            !in_range(start, size, VMALLOC_START, VMALLOC_END))
  61                return -EINVAL;
  62
  63        data.set_mask = set_mask;
  64        data.clear_mask = clear_mask;
  65
  66        ret = apply_to_page_range(&init_mm, start, size, change_page_range,
  67                                        &data);
  68
  69        flush_tlb_kernel_range(start, end);
  70        return ret;
  71}
  72
  73int set_memory_ro(unsigned long addr, int numpages)
  74{
  75        return change_memory_common(addr, numpages,
  76                                        __pgprot(L_PTE_RDONLY),
  77                                        __pgprot(0));
  78}
  79
  80int set_memory_rw(unsigned long addr, int numpages)
  81{
  82        return change_memory_common(addr, numpages,
  83                                        __pgprot(0),
  84                                        __pgprot(L_PTE_RDONLY));
  85}
  86
  87int set_memory_nx(unsigned long addr, int numpages)
  88{
  89        return change_memory_common(addr, numpages,
  90                                        __pgprot(L_PTE_XN),
  91                                        __pgprot(0));
  92}
  93
  94int set_memory_x(unsigned long addr, int numpages)
  95{
  96        return change_memory_common(addr, numpages,
  97                                        __pgprot(0),
  98                                        __pgprot(L_PTE_XN));
  99}
 100