18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * SRAM protect-exec region helper functions 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2017 Texas Instruments Incorporated - https://www.ti.com/ 58c2ecf20Sopenharmony_ci * Dave Gerlach 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 88c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License version 2 as 98c2ecf20Sopenharmony_ci * published by the Free Software Foundation. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * This program is distributed "as is" WITHOUT ANY WARRANTY of any 128c2ecf20Sopenharmony_ci * kind, whether express or implied; without even the implied warranty 138c2ecf20Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 148c2ecf20Sopenharmony_ci * GNU General Public License for more details. 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/device.h> 188c2ecf20Sopenharmony_ci#include <linux/genalloc.h> 198c2ecf20Sopenharmony_ci#include <linux/mm.h> 208c2ecf20Sopenharmony_ci#include <linux/sram.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <asm/fncpy.h> 238c2ecf20Sopenharmony_ci#include <asm/set_memory.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "sram.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(exec_pool_list_mutex); 288c2ecf20Sopenharmony_cistatic LIST_HEAD(exec_pool_list); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ciint sram_check_protect_exec(struct sram_dev *sram, struct sram_reserve *block, 318c2ecf20Sopenharmony_ci struct sram_partition *part) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci unsigned long base = (unsigned long)part->base; 348c2ecf20Sopenharmony_ci unsigned long end = base + block->size; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci if (!PAGE_ALIGNED(base) || !PAGE_ALIGNED(end)) { 378c2ecf20Sopenharmony_ci dev_err(sram->dev, 388c2ecf20Sopenharmony_ci "SRAM pool marked with 'protect-exec' is not page aligned and will not be created.\n"); 398c2ecf20Sopenharmony_ci return -ENOMEM; 408c2ecf20Sopenharmony_ci } 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci return 0; 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ciint sram_add_protect_exec(struct sram_partition *part) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci mutex_lock(&exec_pool_list_mutex); 488c2ecf20Sopenharmony_ci list_add_tail(&part->list, &exec_pool_list); 498c2ecf20Sopenharmony_ci mutex_unlock(&exec_pool_list_mutex); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci return 0; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/** 558c2ecf20Sopenharmony_ci * sram_exec_copy - copy data to a protected executable region of sram 568c2ecf20Sopenharmony_ci * 578c2ecf20Sopenharmony_ci * @pool: struct gen_pool retrieved that is part of this sram 588c2ecf20Sopenharmony_ci * @dst: Destination address for the copy, that must be inside pool 598c2ecf20Sopenharmony_ci * @src: Source address for the data to copy 608c2ecf20Sopenharmony_ci * @size: Size of copy to perform, which starting from dst, must reside in pool 618c2ecf20Sopenharmony_ci * 628c2ecf20Sopenharmony_ci * Return: Address for copied data that can safely be called through function 638c2ecf20Sopenharmony_ci * pointer, or NULL if problem. 648c2ecf20Sopenharmony_ci * 658c2ecf20Sopenharmony_ci * This helper function allows sram driver to act as central control location 668c2ecf20Sopenharmony_ci * of 'protect-exec' pools which are normal sram pools but are always set 678c2ecf20Sopenharmony_ci * read-only and executable except when copying data to them, at which point 688c2ecf20Sopenharmony_ci * they are set to read-write non-executable, to make sure no memory is 698c2ecf20Sopenharmony_ci * writeable and executable at the same time. This region must be page-aligned 708c2ecf20Sopenharmony_ci * and is checked during probe, otherwise page attribute manipulation would 718c2ecf20Sopenharmony_ci * not be possible. Care must be taken to only call the returned address as 728c2ecf20Sopenharmony_ci * dst address is not guaranteed to be safely callable. 738c2ecf20Sopenharmony_ci * 748c2ecf20Sopenharmony_ci * NOTE: This function uses the fncpy macro to move code to the executable 758c2ecf20Sopenharmony_ci * region. Some architectures have strict requirements for relocating 768c2ecf20Sopenharmony_ci * executable code, so fncpy is a macro that must be defined by any arch 778c2ecf20Sopenharmony_ci * making use of this functionality that guarantees a safe copy of exec 788c2ecf20Sopenharmony_ci * data and returns a safe address that can be called as a C function 798c2ecf20Sopenharmony_ci * pointer. 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_civoid *sram_exec_copy(struct gen_pool *pool, void *dst, void *src, 828c2ecf20Sopenharmony_ci size_t size) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci struct sram_partition *part = NULL, *p; 858c2ecf20Sopenharmony_ci unsigned long base; 868c2ecf20Sopenharmony_ci int pages; 878c2ecf20Sopenharmony_ci void *dst_cpy; 888c2ecf20Sopenharmony_ci int ret; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci mutex_lock(&exec_pool_list_mutex); 918c2ecf20Sopenharmony_ci list_for_each_entry(p, &exec_pool_list, list) { 928c2ecf20Sopenharmony_ci if (p->pool == pool) 938c2ecf20Sopenharmony_ci part = p; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci mutex_unlock(&exec_pool_list_mutex); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci if (!part) 988c2ecf20Sopenharmony_ci return NULL; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (!gen_pool_has_addr(pool, (unsigned long)dst, size)) 1018c2ecf20Sopenharmony_ci return NULL; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci base = (unsigned long)part->base; 1048c2ecf20Sopenharmony_ci pages = PAGE_ALIGN(size) / PAGE_SIZE; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci mutex_lock(&part->lock); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci ret = set_memory_nx((unsigned long)base, pages); 1098c2ecf20Sopenharmony_ci if (ret) 1108c2ecf20Sopenharmony_ci goto error_out; 1118c2ecf20Sopenharmony_ci ret = set_memory_rw((unsigned long)base, pages); 1128c2ecf20Sopenharmony_ci if (ret) 1138c2ecf20Sopenharmony_ci goto error_out; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci dst_cpy = fncpy(dst, src, size); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci ret = set_memory_ro((unsigned long)base, pages); 1188c2ecf20Sopenharmony_ci if (ret) 1198c2ecf20Sopenharmony_ci goto error_out; 1208c2ecf20Sopenharmony_ci ret = set_memory_x((unsigned long)base, pages); 1218c2ecf20Sopenharmony_ci if (ret) 1228c2ecf20Sopenharmony_ci goto error_out; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci mutex_unlock(&part->lock); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci return dst_cpy; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cierror_out: 1298c2ecf20Sopenharmony_ci mutex_unlock(&part->lock); 1308c2ecf20Sopenharmony_ci return NULL; 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sram_exec_copy); 133