linux/drivers/misc/sram-exec.c
<<
>>
Prefs
   1/*
   2 * SRAM protect-exec region helper functions
   3 *
   4 * Copyright (C) 2017 Texas Instruments Incorporated - https://www.ti.com/
   5 *      Dave Gerlach
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License version 2 as
   9 * published by the Free Software Foundation.
  10 *
  11 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
  12 * kind, whether express or implied; without even the implied warranty
  13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 */
  16
  17#include <linux/device.h>
  18#include <linux/genalloc.h>
  19#include <linux/mm.h>
  20#include <linux/sram.h>
  21
  22#include <asm/fncpy.h>
  23#include <asm/set_memory.h>
  24
  25#include "sram.h"
  26
  27static DEFINE_MUTEX(exec_pool_list_mutex);
  28static LIST_HEAD(exec_pool_list);
  29
  30int sram_check_protect_exec(struct sram_dev *sram, struct sram_reserve *block,
  31                            struct sram_partition *part)
  32{
  33        unsigned long base = (unsigned long)part->base;
  34        unsigned long end = base + block->size;
  35
  36        if (!PAGE_ALIGNED(base) || !PAGE_ALIGNED(end)) {
  37                dev_err(sram->dev,
  38                        "SRAM pool marked with 'protect-exec' is not page aligned and will not be created.\n");
  39                return -ENOMEM;
  40        }
  41
  42        return 0;
  43}
  44
  45int sram_add_protect_exec(struct sram_partition *part)
  46{
  47        mutex_lock(&exec_pool_list_mutex);
  48        list_add_tail(&part->list, &exec_pool_list);
  49        mutex_unlock(&exec_pool_list_mutex);
  50
  51        return 0;
  52}
  53
  54/**
  55 * sram_exec_copy - copy data to a protected executable region of sram
  56 *
  57 * @pool: struct gen_pool retrieved that is part of this sram
  58 * @dst: Destination address for the copy, that must be inside pool
  59 * @src: Source address for the data to copy
  60 * @size: Size of copy to perform, which starting from dst, must reside in pool
  61 *
  62 * Return: Address for copied data that can safely be called through function
  63 *         pointer, or NULL if problem.
  64 *
  65 * This helper function allows sram driver to act as central control location
  66 * of 'protect-exec' pools which are normal sram pools but are always set
  67 * read-only and executable except when copying data to them, at which point
  68 * they are set to read-write non-executable, to make sure no memory is
  69 * writeable and executable at the same time. This region must be page-aligned
  70 * and is checked during probe, otherwise page attribute manipulation would
  71 * not be possible. Care must be taken to only call the returned address as
  72 * dst address is not guaranteed to be safely callable.
  73 *
  74 * NOTE: This function uses the fncpy macro to move code to the executable
  75 * region. Some architectures have strict requirements for relocating
  76 * executable code, so fncpy is a macro that must be defined by any arch
  77 * making use of this functionality that guarantees a safe copy of exec
  78 * data and returns a safe address that can be called as a C function
  79 * pointer.
  80 */
  81void *sram_exec_copy(struct gen_pool *pool, void *dst, void *src,
  82                     size_t size)
  83{
  84        struct sram_partition *part = NULL, *p;
  85        unsigned long base;
  86        int pages;
  87        void *dst_cpy;
  88        int ret;
  89
  90        mutex_lock(&exec_pool_list_mutex);
  91        list_for_each_entry(p, &exec_pool_list, list) {
  92                if (p->pool == pool)
  93                        part = p;
  94        }
  95        mutex_unlock(&exec_pool_list_mutex);
  96
  97        if (!part)
  98                return NULL;
  99
 100        if (!gen_pool_has_addr(pool, (unsigned long)dst, size))
 101                return NULL;
 102
 103        base = (unsigned long)part->base;
 104        pages = PAGE_ALIGN(size) / PAGE_SIZE;
 105
 106        mutex_lock(&part->lock);
 107
 108        ret = set_memory_nx((unsigned long)base, pages);
 109        if (ret)
 110                goto error_out;
 111        ret = set_memory_rw((unsigned long)base, pages);
 112        if (ret)
 113                goto error_out;
 114
 115        dst_cpy = fncpy(dst, src, size);
 116
 117        ret = set_memory_ro((unsigned long)base, pages);
 118        if (ret)
 119                goto error_out;
 120        ret = set_memory_x((unsigned long)base, pages);
 121        if (ret)
 122                goto error_out;
 123
 124        mutex_unlock(&part->lock);
 125
 126        return dst_cpy;
 127
 128error_out:
 129        mutex_unlock(&part->lock);
 130        return NULL;
 131}
 132EXPORT_SYMBOL_GPL(sram_exec_copy);
 133