18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Cell Broadband Engine OProfile Support 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (C) Copyright IBM Corporation 2006 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Maynard Johnson <maynardj@us.ibm.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci/* The code in this source file is responsible for generating 118c2ecf20Sopenharmony_ci * vma-to-fileOffset maps for both overlay and non-overlay SPU 128c2ecf20Sopenharmony_ci * applications. 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/mm.h> 168c2ecf20Sopenharmony_ci#include <linux/string.h> 178c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 188c2ecf20Sopenharmony_ci#include <linux/elf.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include "pr_util.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_civoid vma_map_free(struct vma_to_fileoffset_map *map) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci while (map) { 268c2ecf20Sopenharmony_ci struct vma_to_fileoffset_map *next = map->next; 278c2ecf20Sopenharmony_ci kfree(map); 288c2ecf20Sopenharmony_ci map = next; 298c2ecf20Sopenharmony_ci } 308c2ecf20Sopenharmony_ci} 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ciunsigned int 338c2ecf20Sopenharmony_civma_map_lookup(struct vma_to_fileoffset_map *map, unsigned int vma, 348c2ecf20Sopenharmony_ci const struct spu *aSpu, int *grd_val) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci /* 378c2ecf20Sopenharmony_ci * Default the offset to the physical address + a flag value. 388c2ecf20Sopenharmony_ci * Addresses of dynamically generated code can't be found in the vma 398c2ecf20Sopenharmony_ci * map. For those addresses the flagged value will be sent on to 408c2ecf20Sopenharmony_ci * the user space tools so they can be reported rather than just 418c2ecf20Sopenharmony_ci * thrown away. 428c2ecf20Sopenharmony_ci */ 438c2ecf20Sopenharmony_ci u32 offset = 0x10000000 + vma; 448c2ecf20Sopenharmony_ci u32 ovly_grd; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci for (; map; map = map->next) { 478c2ecf20Sopenharmony_ci if (vma < map->vma || vma >= map->vma + map->size) 488c2ecf20Sopenharmony_ci continue; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci if (map->guard_ptr) { 518c2ecf20Sopenharmony_ci ovly_grd = *(u32 *)(aSpu->local_store + map->guard_ptr); 528c2ecf20Sopenharmony_ci if (ovly_grd != map->guard_val) 538c2ecf20Sopenharmony_ci continue; 548c2ecf20Sopenharmony_ci *grd_val = ovly_grd; 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci offset = vma - map->vma + map->offset; 578c2ecf20Sopenharmony_ci break; 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci return offset; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic struct vma_to_fileoffset_map * 648c2ecf20Sopenharmony_civma_map_add(struct vma_to_fileoffset_map *map, unsigned int vma, 658c2ecf20Sopenharmony_ci unsigned int size, unsigned int offset, unsigned int guard_ptr, 668c2ecf20Sopenharmony_ci unsigned int guard_val) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct vma_to_fileoffset_map *new = kzalloc(sizeof(*new), GFP_KERNEL); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci if (!new) { 718c2ecf20Sopenharmony_ci printk(KERN_ERR "SPU_PROF: %s, line %d: malloc failed\n", 728c2ecf20Sopenharmony_ci __func__, __LINE__); 738c2ecf20Sopenharmony_ci vma_map_free(map); 748c2ecf20Sopenharmony_ci return NULL; 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci new->next = map; 788c2ecf20Sopenharmony_ci new->vma = vma; 798c2ecf20Sopenharmony_ci new->size = size; 808c2ecf20Sopenharmony_ci new->offset = offset; 818c2ecf20Sopenharmony_ci new->guard_ptr = guard_ptr; 828c2ecf20Sopenharmony_ci new->guard_val = guard_val; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci return new; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/* Parse SPE ELF header and generate a list of vma_maps. 898c2ecf20Sopenharmony_ci * A pointer to the first vma_map in the generated list 908c2ecf20Sopenharmony_ci * of vma_maps is returned. */ 918c2ecf20Sopenharmony_cistruct vma_to_fileoffset_map *create_vma_map(const struct spu *aSpu, 928c2ecf20Sopenharmony_ci unsigned long __spu_elf_start) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci static const unsigned char expected[EI_PAD] = { 958c2ecf20Sopenharmony_ci [EI_MAG0] = ELFMAG0, 968c2ecf20Sopenharmony_ci [EI_MAG1] = ELFMAG1, 978c2ecf20Sopenharmony_ci [EI_MAG2] = ELFMAG2, 988c2ecf20Sopenharmony_ci [EI_MAG3] = ELFMAG3, 998c2ecf20Sopenharmony_ci [EI_CLASS] = ELFCLASS32, 1008c2ecf20Sopenharmony_ci [EI_DATA] = ELFDATA2MSB, 1018c2ecf20Sopenharmony_ci [EI_VERSION] = EV_CURRENT, 1028c2ecf20Sopenharmony_ci [EI_OSABI] = ELFOSABI_NONE 1038c2ecf20Sopenharmony_ci }; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci int grd_val; 1068c2ecf20Sopenharmony_ci struct vma_to_fileoffset_map *map = NULL; 1078c2ecf20Sopenharmony_ci void __user *spu_elf_start = (void __user *)__spu_elf_start; 1088c2ecf20Sopenharmony_ci struct spu_overlay_info ovly; 1098c2ecf20Sopenharmony_ci unsigned int overlay_tbl_offset = -1; 1108c2ecf20Sopenharmony_ci Elf32_Phdr __user *phdr_start; 1118c2ecf20Sopenharmony_ci Elf32_Shdr __user *shdr_start; 1128c2ecf20Sopenharmony_ci Elf32_Ehdr ehdr; 1138c2ecf20Sopenharmony_ci Elf32_Phdr phdr; 1148c2ecf20Sopenharmony_ci Elf32_Shdr shdr, shdr_str; 1158c2ecf20Sopenharmony_ci Elf32_Sym sym; 1168c2ecf20Sopenharmony_ci int i, j; 1178c2ecf20Sopenharmony_ci char name[32]; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci unsigned int ovly_table_sym = 0; 1208c2ecf20Sopenharmony_ci unsigned int ovly_buf_table_sym = 0; 1218c2ecf20Sopenharmony_ci unsigned int ovly_table_end_sym = 0; 1228c2ecf20Sopenharmony_ci unsigned int ovly_buf_table_end_sym = 0; 1238c2ecf20Sopenharmony_ci struct spu_overlay_info __user *ovly_table; 1248c2ecf20Sopenharmony_ci unsigned int n_ovlys; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* Get and validate ELF header. */ 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci if (copy_from_user(&ehdr, spu_elf_start, sizeof (ehdr))) 1298c2ecf20Sopenharmony_ci goto fail; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (memcmp(ehdr.e_ident, expected, EI_PAD) != 0) { 1328c2ecf20Sopenharmony_ci printk(KERN_ERR "SPU_PROF: " 1338c2ecf20Sopenharmony_ci "%s, line %d: Unexpected e_ident parsing SPU ELF\n", 1348c2ecf20Sopenharmony_ci __func__, __LINE__); 1358c2ecf20Sopenharmony_ci goto fail; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci if (ehdr.e_machine != EM_SPU) { 1388c2ecf20Sopenharmony_ci printk(KERN_ERR "SPU_PROF: " 1398c2ecf20Sopenharmony_ci "%s, line %d: Unexpected e_machine parsing SPU ELF\n", 1408c2ecf20Sopenharmony_ci __func__, __LINE__); 1418c2ecf20Sopenharmony_ci goto fail; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci if (ehdr.e_type != ET_EXEC) { 1448c2ecf20Sopenharmony_ci printk(KERN_ERR "SPU_PROF: " 1458c2ecf20Sopenharmony_ci "%s, line %d: Unexpected e_type parsing SPU ELF\n", 1468c2ecf20Sopenharmony_ci __func__, __LINE__); 1478c2ecf20Sopenharmony_ci goto fail; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci phdr_start = spu_elf_start + ehdr.e_phoff; 1508c2ecf20Sopenharmony_ci shdr_start = spu_elf_start + ehdr.e_shoff; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* Traverse program headers. */ 1538c2ecf20Sopenharmony_ci for (i = 0; i < ehdr.e_phnum; i++) { 1548c2ecf20Sopenharmony_ci if (copy_from_user(&phdr, phdr_start + i, sizeof(phdr))) 1558c2ecf20Sopenharmony_ci goto fail; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (phdr.p_type != PT_LOAD) 1588c2ecf20Sopenharmony_ci continue; 1598c2ecf20Sopenharmony_ci if (phdr.p_flags & (1 << 27)) 1608c2ecf20Sopenharmony_ci continue; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci map = vma_map_add(map, phdr.p_vaddr, phdr.p_memsz, 1638c2ecf20Sopenharmony_ci phdr.p_offset, 0, 0); 1648c2ecf20Sopenharmony_ci if (!map) 1658c2ecf20Sopenharmony_ci goto fail; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci pr_debug("SPU_PROF: Created non-overlay maps\n"); 1698c2ecf20Sopenharmony_ci /* Traverse section table and search for overlay-related symbols. */ 1708c2ecf20Sopenharmony_ci for (i = 0; i < ehdr.e_shnum; i++) { 1718c2ecf20Sopenharmony_ci if (copy_from_user(&shdr, shdr_start + i, sizeof(shdr))) 1728c2ecf20Sopenharmony_ci goto fail; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (shdr.sh_type != SHT_SYMTAB) 1758c2ecf20Sopenharmony_ci continue; 1768c2ecf20Sopenharmony_ci if (shdr.sh_entsize != sizeof (sym)) 1778c2ecf20Sopenharmony_ci continue; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (copy_from_user(&shdr_str, 1808c2ecf20Sopenharmony_ci shdr_start + shdr.sh_link, 1818c2ecf20Sopenharmony_ci sizeof(shdr))) 1828c2ecf20Sopenharmony_ci goto fail; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci if (shdr_str.sh_type != SHT_STRTAB) 1858c2ecf20Sopenharmony_ci goto fail; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci for (j = 0; j < shdr.sh_size / sizeof (sym); j++) { 1888c2ecf20Sopenharmony_ci if (copy_from_user(&sym, spu_elf_start + 1898c2ecf20Sopenharmony_ci shdr.sh_offset + 1908c2ecf20Sopenharmony_ci j * sizeof (sym), 1918c2ecf20Sopenharmony_ci sizeof (sym))) 1928c2ecf20Sopenharmony_ci goto fail; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (copy_from_user(name, 1958c2ecf20Sopenharmony_ci spu_elf_start + shdr_str.sh_offset + 1968c2ecf20Sopenharmony_ci sym.st_name, 1978c2ecf20Sopenharmony_ci 20)) 1988c2ecf20Sopenharmony_ci goto fail; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (memcmp(name, "_ovly_table", 12) == 0) 2018c2ecf20Sopenharmony_ci ovly_table_sym = sym.st_value; 2028c2ecf20Sopenharmony_ci if (memcmp(name, "_ovly_buf_table", 16) == 0) 2038c2ecf20Sopenharmony_ci ovly_buf_table_sym = sym.st_value; 2048c2ecf20Sopenharmony_ci if (memcmp(name, "_ovly_table_end", 16) == 0) 2058c2ecf20Sopenharmony_ci ovly_table_end_sym = sym.st_value; 2068c2ecf20Sopenharmony_ci if (memcmp(name, "_ovly_buf_table_end", 20) == 0) 2078c2ecf20Sopenharmony_ci ovly_buf_table_end_sym = sym.st_value; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci /* If we don't have overlays, we're done. */ 2128c2ecf20Sopenharmony_ci if (ovly_table_sym == 0 || ovly_buf_table_sym == 0 2138c2ecf20Sopenharmony_ci || ovly_table_end_sym == 0 || ovly_buf_table_end_sym == 0) { 2148c2ecf20Sopenharmony_ci pr_debug("SPU_PROF: No overlay table found\n"); 2158c2ecf20Sopenharmony_ci goto out; 2168c2ecf20Sopenharmony_ci } else { 2178c2ecf20Sopenharmony_ci pr_debug("SPU_PROF: Overlay table found\n"); 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci /* The _ovly_table symbol represents a table with one entry 2218c2ecf20Sopenharmony_ci * per overlay section. The _ovly_buf_table symbol represents 2228c2ecf20Sopenharmony_ci * a table with one entry per overlay region. 2238c2ecf20Sopenharmony_ci * The struct spu_overlay_info gives the structure of the _ovly_table 2248c2ecf20Sopenharmony_ci * entries. The structure of _ovly_table_buf is simply one 2258c2ecf20Sopenharmony_ci * u32 word per entry. 2268c2ecf20Sopenharmony_ci */ 2278c2ecf20Sopenharmony_ci overlay_tbl_offset = vma_map_lookup(map, ovly_table_sym, 2288c2ecf20Sopenharmony_ci aSpu, &grd_val); 2298c2ecf20Sopenharmony_ci if (overlay_tbl_offset > 0x10000000) { 2308c2ecf20Sopenharmony_ci printk(KERN_ERR "SPU_PROF: " 2318c2ecf20Sopenharmony_ci "%s, line %d: Error finding SPU overlay table\n", 2328c2ecf20Sopenharmony_ci __func__, __LINE__); 2338c2ecf20Sopenharmony_ci goto fail; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci ovly_table = spu_elf_start + overlay_tbl_offset; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci n_ovlys = (ovly_table_end_sym - 2388c2ecf20Sopenharmony_ci ovly_table_sym) / sizeof (ovly); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci /* Traverse overlay table. */ 2418c2ecf20Sopenharmony_ci for (i = 0; i < n_ovlys; i++) { 2428c2ecf20Sopenharmony_ci if (copy_from_user(&ovly, ovly_table + i, sizeof (ovly))) 2438c2ecf20Sopenharmony_ci goto fail; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci /* The ovly.vma/size/offset arguments are analogous to the same 2468c2ecf20Sopenharmony_ci * arguments used above for non-overlay maps. The final two 2478c2ecf20Sopenharmony_ci * args are referred to as the guard pointer and the guard 2488c2ecf20Sopenharmony_ci * value. 2498c2ecf20Sopenharmony_ci * The guard pointer is an entry in the _ovly_buf_table, 2508c2ecf20Sopenharmony_ci * computed using ovly.buf as the index into the table. Since 2518c2ecf20Sopenharmony_ci * ovly.buf values begin at '1' to reference the first (or 0th) 2528c2ecf20Sopenharmony_ci * entry in the _ovly_buf_table, the computation subtracts 1 2538c2ecf20Sopenharmony_ci * from ovly.buf. 2548c2ecf20Sopenharmony_ci * The guard value is stored in the _ovly_buf_table entry and 2558c2ecf20Sopenharmony_ci * is an index (starting at 1) back to the _ovly_table entry 2568c2ecf20Sopenharmony_ci * that is pointing at this _ovly_buf_table entry. So, for 2578c2ecf20Sopenharmony_ci * example, for an overlay scenario with one overlay segment 2588c2ecf20Sopenharmony_ci * and two overlay sections: 2598c2ecf20Sopenharmony_ci * - Section 1 points to the first entry of the 2608c2ecf20Sopenharmony_ci * _ovly_buf_table, which contains a guard value 2618c2ecf20Sopenharmony_ci * of '1', referencing the first (index=0) entry of 2628c2ecf20Sopenharmony_ci * _ovly_table. 2638c2ecf20Sopenharmony_ci * - Section 2 points to the second entry of the 2648c2ecf20Sopenharmony_ci * _ovly_buf_table, which contains a guard value 2658c2ecf20Sopenharmony_ci * of '2', referencing the second (index=1) entry of 2668c2ecf20Sopenharmony_ci * _ovly_table. 2678c2ecf20Sopenharmony_ci */ 2688c2ecf20Sopenharmony_ci map = vma_map_add(map, ovly.vma, ovly.size, ovly.offset, 2698c2ecf20Sopenharmony_ci ovly_buf_table_sym + (ovly.buf-1) * 4, i+1); 2708c2ecf20Sopenharmony_ci if (!map) 2718c2ecf20Sopenharmony_ci goto fail; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci goto out; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci fail: 2768c2ecf20Sopenharmony_ci map = NULL; 2778c2ecf20Sopenharmony_ci out: 2788c2ecf20Sopenharmony_ci return map; 2798c2ecf20Sopenharmony_ci} 280