xref: /linux/drivers/misc/sram-exec.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
15a729246SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2728bbe75SDave Gerlach /*
3728bbe75SDave Gerlach  * SRAM protect-exec region helper functions
4728bbe75SDave Gerlach  *
54e74eeb2SAlexander A. Klimov  * Copyright (C) 2017 Texas Instruments Incorporated - https://www.ti.com/
6728bbe75SDave Gerlach  *	Dave Gerlach
7728bbe75SDave Gerlach  */
8728bbe75SDave Gerlach 
9728bbe75SDave Gerlach #include <linux/device.h>
10728bbe75SDave Gerlach #include <linux/genalloc.h>
11056d16b2SLaura Abbott #include <linux/mm.h>
12728bbe75SDave Gerlach #include <linux/sram.h>
13*d48567c9SPeter Zijlstra #include <linux/set_memory.h>
14728bbe75SDave Gerlach 
1534cfb106SDave Gerlach #include <asm/fncpy.h>
16728bbe75SDave Gerlach 
17728bbe75SDave Gerlach #include "sram.h"
18728bbe75SDave Gerlach 
19728bbe75SDave Gerlach static DEFINE_MUTEX(exec_pool_list_mutex);
20728bbe75SDave Gerlach static LIST_HEAD(exec_pool_list);
21728bbe75SDave Gerlach 
sram_check_protect_exec(struct sram_dev * sram,struct sram_reserve * block,struct sram_partition * part)22728bbe75SDave Gerlach int sram_check_protect_exec(struct sram_dev *sram, struct sram_reserve *block,
23728bbe75SDave Gerlach 			    struct sram_partition *part)
24728bbe75SDave Gerlach {
25728bbe75SDave Gerlach 	unsigned long base = (unsigned long)part->base;
26728bbe75SDave Gerlach 	unsigned long end = base + block->size;
27728bbe75SDave Gerlach 
28728bbe75SDave Gerlach 	if (!PAGE_ALIGNED(base) || !PAGE_ALIGNED(end)) {
29728bbe75SDave Gerlach 		dev_err(sram->dev,
30728bbe75SDave Gerlach 			"SRAM pool marked with 'protect-exec' is not page aligned and will not be created.\n");
31728bbe75SDave Gerlach 		return -ENOMEM;
32728bbe75SDave Gerlach 	}
33728bbe75SDave Gerlach 
34728bbe75SDave Gerlach 	return 0;
35728bbe75SDave Gerlach }
36728bbe75SDave Gerlach 
sram_add_protect_exec(struct sram_partition * part)37728bbe75SDave Gerlach int sram_add_protect_exec(struct sram_partition *part)
38728bbe75SDave Gerlach {
39728bbe75SDave Gerlach 	mutex_lock(&exec_pool_list_mutex);
40728bbe75SDave Gerlach 	list_add_tail(&part->list, &exec_pool_list);
41728bbe75SDave Gerlach 	mutex_unlock(&exec_pool_list_mutex);
42728bbe75SDave Gerlach 
43728bbe75SDave Gerlach 	return 0;
44728bbe75SDave Gerlach }
45728bbe75SDave Gerlach 
46728bbe75SDave Gerlach /**
47728bbe75SDave Gerlach  * sram_exec_copy - copy data to a protected executable region of sram
48728bbe75SDave Gerlach  *
49728bbe75SDave Gerlach  * @pool: struct gen_pool retrieved that is part of this sram
50728bbe75SDave Gerlach  * @dst: Destination address for the copy, that must be inside pool
51728bbe75SDave Gerlach  * @src: Source address for the data to copy
52728bbe75SDave Gerlach  * @size: Size of copy to perform, which starting from dst, must reside in pool
53728bbe75SDave Gerlach  *
5434cfb106SDave Gerlach  * Return: Address for copied data that can safely be called through function
5534cfb106SDave Gerlach  *	   pointer, or NULL if problem.
5634cfb106SDave Gerlach  *
57728bbe75SDave Gerlach  * This helper function allows sram driver to act as central control location
58728bbe75SDave Gerlach  * of 'protect-exec' pools which are normal sram pools but are always set
59728bbe75SDave Gerlach  * read-only and executable except when copying data to them, at which point
60728bbe75SDave Gerlach  * they are set to read-write non-executable, to make sure no memory is
61728bbe75SDave Gerlach  * writeable and executable at the same time. This region must be page-aligned
62728bbe75SDave Gerlach  * and is checked during probe, otherwise page attribute manipulation would
6334cfb106SDave Gerlach  * not be possible. Care must be taken to only call the returned address as
6434cfb106SDave Gerlach  * dst address is not guaranteed to be safely callable.
6534cfb106SDave Gerlach  *
6634cfb106SDave Gerlach  * NOTE: This function uses the fncpy macro to move code to the executable
6734cfb106SDave Gerlach  * region. Some architectures have strict requirements for relocating
6834cfb106SDave Gerlach  * executable code, so fncpy is a macro that must be defined by any arch
6934cfb106SDave Gerlach  * making use of this functionality that guarantees a safe copy of exec
7034cfb106SDave Gerlach  * data and returns a safe address that can be called as a C function
7134cfb106SDave Gerlach  * pointer.
72728bbe75SDave Gerlach  */
sram_exec_copy(struct gen_pool * pool,void * dst,void * src,size_t size)7334cfb106SDave Gerlach void *sram_exec_copy(struct gen_pool *pool, void *dst, void *src,
74728bbe75SDave Gerlach 		     size_t size)
75728bbe75SDave Gerlach {
76728bbe75SDave Gerlach 	struct sram_partition *part = NULL, *p;
77728bbe75SDave Gerlach 	unsigned long base;
78728bbe75SDave Gerlach 	int pages;
7934cfb106SDave Gerlach 	void *dst_cpy;
80c576eddfSTianlin Li 	int ret;
81728bbe75SDave Gerlach 
82728bbe75SDave Gerlach 	mutex_lock(&exec_pool_list_mutex);
83728bbe75SDave Gerlach 	list_for_each_entry(p, &exec_pool_list, list) {
84728bbe75SDave Gerlach 		if (p->pool == pool)
85728bbe75SDave Gerlach 			part = p;
86728bbe75SDave Gerlach 	}
87728bbe75SDave Gerlach 	mutex_unlock(&exec_pool_list_mutex);
88728bbe75SDave Gerlach 
89728bbe75SDave Gerlach 	if (!part)
9034cfb106SDave Gerlach 		return NULL;
91728bbe75SDave Gerlach 
92964975acSHuang Shijie 	if (!gen_pool_has_addr(pool, (unsigned long)dst, size))
9334cfb106SDave Gerlach 		return NULL;
94728bbe75SDave Gerlach 
95728bbe75SDave Gerlach 	base = (unsigned long)part->base;
96728bbe75SDave Gerlach 	pages = PAGE_ALIGN(size) / PAGE_SIZE;
97728bbe75SDave Gerlach 
98728bbe75SDave Gerlach 	mutex_lock(&part->lock);
99728bbe75SDave Gerlach 
100c576eddfSTianlin Li 	ret = set_memory_nx((unsigned long)base, pages);
101c576eddfSTianlin Li 	if (ret)
102c576eddfSTianlin Li 		goto error_out;
103c576eddfSTianlin Li 	ret = set_memory_rw((unsigned long)base, pages);
104c576eddfSTianlin Li 	if (ret)
105c576eddfSTianlin Li 		goto error_out;
106728bbe75SDave Gerlach 
10734cfb106SDave Gerlach 	dst_cpy = fncpy(dst, src, size);
108728bbe75SDave Gerlach 
109*d48567c9SPeter Zijlstra 	ret = set_memory_rox((unsigned long)base, pages);
110c576eddfSTianlin Li 	if (ret)
111c576eddfSTianlin Li 		goto error_out;
112728bbe75SDave Gerlach 
113728bbe75SDave Gerlach 	mutex_unlock(&part->lock);
114728bbe75SDave Gerlach 
11534cfb106SDave Gerlach 	return dst_cpy;
116c576eddfSTianlin Li 
117c576eddfSTianlin Li error_out:
118c576eddfSTianlin Li 	mutex_unlock(&part->lock);
119c576eddfSTianlin Li 	return NULL;
120728bbe75SDave Gerlach }
121728bbe75SDave Gerlach EXPORT_SYMBOL_GPL(sram_exec_copy);
122