1e01402b1SRalf Baechle /* 25792bf64SSteven J. Hill * This file is subject to the terms and conditions of the GNU General Public 35792bf64SSteven J. Hill * License. See the file "COPYING" in the main directory of this archive 4e01402b1SRalf Baechle * for more details. 5e01402b1SRalf Baechle * 65792bf64SSteven J. Hill * Copyright (C) 2004, 2005 MIPS Technologies, Inc. All rights reserved. 75792bf64SSteven J. Hill * Copyright (C) 2013 Imagination Technologies Ltd. 8e01402b1SRalf Baechle * 95792bf64SSteven J. Hill * VPE spport module for loading a MIPS SP program into VPE1. The SP 105792bf64SSteven J. Hill * environment is rather simple since there are no TLBs. It needs 115792bf64SSteven J. Hill * to be relocatable (or partiall linked). Initialize your stack in 125792bf64SSteven J. Hill * the startup-code. The loader looks for the symbol __start and sets 135792bf64SSteven J. Hill * up the execution to resume from there. To load and run, simply do 145792bf64SSteven J. Hill * a cat SP 'binary' to the /dev/vpe1 device. 15e01402b1SRalf Baechle */ 16e01402b1SRalf Baechle #include <linux/kernel.h> 1727a3bbafSRalf Baechle #include <linux/device.h> 18e01402b1SRalf Baechle #include <linux/fs.h> 19e01402b1SRalf Baechle #include <linux/init.h> 20e01402b1SRalf Baechle #include <linux/slab.h> 21e01402b1SRalf Baechle #include <linux/list.h> 22e01402b1SRalf Baechle #include <linux/vmalloc.h> 23e01402b1SRalf Baechle #include <linux/elf.h> 24e01402b1SRalf Baechle #include <linux/seq_file.h> 25e01402b1SRalf Baechle #include <linux/syscalls.h> 26e01402b1SRalf Baechle #include <linux/moduleloader.h> 27e01402b1SRalf Baechle #include <linux/interrupt.h> 28e01402b1SRalf Baechle #include <linux/poll.h> 2957c8a661SMike Rapoport #include <linux/memblock.h> 30e01402b1SRalf Baechle #include <asm/mipsregs.h> 31340ee4b9SRalf Baechle #include <asm/mipsmtregs.h> 32e01402b1SRalf Baechle #include <asm/cacheflush.h> 3360063497SArun Sharma #include <linux/atomic.h> 3427a3bbafSRalf Baechle #include <asm/mips_mt.h> 35e01402b1SRalf Baechle #include <asm/processor.h> 362600990eSRalf Baechle #include <asm/vpe.h> 37e01402b1SRalf Baechle 38e01402b1SRalf Baechle #ifndef ARCH_SHF_SMALL 39e01402b1SRalf Baechle #define ARCH_SHF_SMALL 0 40e01402b1SRalf Baechle #endif 41e01402b1SRalf Baechle 42e01402b1SRalf Baechle /* If this is set, the section belongs in the init part of the module */ 43e01402b1SRalf Baechle #define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1)) 44e01402b1SRalf Baechle 451a2a6d7eSDeng-Cheng Zhu struct vpe_control vpecontrol = { 4652bd080dSThomas Gleixner .vpe_list_lock = __SPIN_LOCK_UNLOCKED(vpe_list_lock), 479cfdf6f1SRalf Baechle .vpe_list = LIST_HEAD_INIT(vpecontrol.vpe_list), 4852bd080dSThomas Gleixner .tc_list_lock = __SPIN_LOCK_UNLOCKED(tc_list_lock), 499cfdf6f1SRalf Baechle .tc_list = LIST_HEAD_INIT(vpecontrol.tc_list) 509cfdf6f1SRalf Baechle }; 51e01402b1SRalf Baechle 52e01402b1SRalf Baechle /* get the vpe associated with this minor */ 531a2a6d7eSDeng-Cheng Zhu struct vpe *get_vpe(int minor) 54e01402b1SRalf Baechle { 551bbfc20dSRalf Baechle struct vpe *res, *v; 56e01402b1SRalf Baechle 572600990eSRalf Baechle if (!cpu_has_mipsmt) 582600990eSRalf Baechle return NULL; 592600990eSRalf Baechle 601bbfc20dSRalf Baechle res = NULL; 611bbfc20dSRalf Baechle spin_lock(&vpecontrol.vpe_list_lock); 62e01402b1SRalf Baechle list_for_each_entry(v, &vpecontrol.vpe_list, list) { 631a2a6d7eSDeng-Cheng Zhu if (v->minor == VPE_MODULE_MINOR) { 641bbfc20dSRalf Baechle res = v; 651bbfc20dSRalf Baechle break; 66e01402b1SRalf Baechle } 671bbfc20dSRalf Baechle } 681bbfc20dSRalf Baechle spin_unlock(&vpecontrol.vpe_list_lock); 69e01402b1SRalf Baechle 701bbfc20dSRalf Baechle return res; 71e01402b1SRalf Baechle } 72e01402b1SRalf Baechle 73e01402b1SRalf Baechle /* get the vpe associated with this minor */ 741a2a6d7eSDeng-Cheng Zhu struct tc *get_tc(int index) 75e01402b1SRalf Baechle { 761bbfc20dSRalf Baechle struct tc *res, *t; 77e01402b1SRalf Baechle 781bbfc20dSRalf Baechle res = NULL; 791bbfc20dSRalf Baechle spin_lock(&vpecontrol.tc_list_lock); 80e01402b1SRalf Baechle list_for_each_entry(t, &vpecontrol.tc_list, list) { 811bbfc20dSRalf Baechle if (t->index == index) { 821bbfc20dSRalf Baechle res = t; 831bbfc20dSRalf Baechle break; 84e01402b1SRalf Baechle } 851bbfc20dSRalf Baechle } 861bbfc20dSRalf Baechle spin_unlock(&vpecontrol.tc_list_lock); 87e01402b1SRalf Baechle 889fbcbd7eSHillf Danton return res; 89e01402b1SRalf Baechle } 90e01402b1SRalf Baechle 91e01402b1SRalf Baechle /* allocate a vpe and associate it with this minor (or index) */ 921a2a6d7eSDeng-Cheng Zhu struct vpe *alloc_vpe(int minor) 93e01402b1SRalf Baechle { 94e01402b1SRalf Baechle struct vpe *v; 95e01402b1SRalf Baechle 965792bf64SSteven J. Hill v = kzalloc(sizeof(struct vpe), GFP_KERNEL); 975792bf64SSteven J. Hill if (v == NULL) 985792bf64SSteven J. Hill goto out; 99e01402b1SRalf Baechle 100e01402b1SRalf Baechle INIT_LIST_HEAD(&v->tc); 1011bbfc20dSRalf Baechle spin_lock(&vpecontrol.vpe_list_lock); 102e01402b1SRalf Baechle list_add_tail(&v->list, &vpecontrol.vpe_list); 1031bbfc20dSRalf Baechle spin_unlock(&vpecontrol.vpe_list_lock); 104e01402b1SRalf Baechle 1052600990eSRalf Baechle INIT_LIST_HEAD(&v->notify); 1061a2a6d7eSDeng-Cheng Zhu v->minor = VPE_MODULE_MINOR; 1071bbfc20dSRalf Baechle 1085792bf64SSteven J. Hill out: 109e01402b1SRalf Baechle return v; 110e01402b1SRalf Baechle } 111e01402b1SRalf Baechle 112e01402b1SRalf Baechle /* allocate a tc. At startup only tc0 is running, all other can be halted. */ 1131a2a6d7eSDeng-Cheng Zhu struct tc *alloc_tc(int index) 114e01402b1SRalf Baechle { 11507cc0c9eSRalf Baechle struct tc *tc; 116e01402b1SRalf Baechle 1175792bf64SSteven J. Hill tc = kzalloc(sizeof(struct tc), GFP_KERNEL); 1185792bf64SSteven J. Hill if (tc == NULL) 11907cc0c9eSRalf Baechle goto out; 120e01402b1SRalf Baechle 12107cc0c9eSRalf Baechle INIT_LIST_HEAD(&tc->tc); 12207cc0c9eSRalf Baechle tc->index = index; 1231bbfc20dSRalf Baechle 1241bbfc20dSRalf Baechle spin_lock(&vpecontrol.tc_list_lock); 12507cc0c9eSRalf Baechle list_add_tail(&tc->list, &vpecontrol.tc_list); 1261bbfc20dSRalf Baechle spin_unlock(&vpecontrol.tc_list_lock); 127e01402b1SRalf Baechle 12807cc0c9eSRalf Baechle out: 12907cc0c9eSRalf Baechle return tc; 130e01402b1SRalf Baechle } 131e01402b1SRalf Baechle 132e01402b1SRalf Baechle /* clean up and free everything */ 1331a2a6d7eSDeng-Cheng Zhu void release_vpe(struct vpe *v) 134e01402b1SRalf Baechle { 135e01402b1SRalf Baechle list_del(&v->list); 136e01402b1SRalf Baechle if (v->load_addr) 137bef8e2dfSChristophe JAILLET release_progmem(v->load_addr); 138e01402b1SRalf Baechle kfree(v); 139e01402b1SRalf Baechle } 140e01402b1SRalf Baechle 141e01402b1SRalf Baechle /* Find some VPE program space */ 1421a2a6d7eSDeng-Cheng Zhu void *alloc_progmem(unsigned long len) 143e01402b1SRalf Baechle { 1445408c490SRalf Baechle void *addr; 1455408c490SRalf Baechle 146e01402b1SRalf Baechle #ifdef CONFIG_MIPS_VPE_LOADER_TOM 1475408c490SRalf Baechle /* 1485408c490SRalf Baechle * This means you must tell Linux to use less memory than you 1495408c490SRalf Baechle * physically have, for example by passing a mem= boot argument. 1505408c490SRalf Baechle */ 1519f2546adSRalf Baechle addr = pfn_to_kaddr(max_low_pfn); 1525408c490SRalf Baechle memset(addr, 0, len); 153e01402b1SRalf Baechle #else 1545408c490SRalf Baechle /* simple grab some mem for now */ 1555408c490SRalf Baechle addr = kzalloc(len, GFP_KERNEL); 156e01402b1SRalf Baechle #endif 1575408c490SRalf Baechle 1585408c490SRalf Baechle return addr; 159e01402b1SRalf Baechle } 160e01402b1SRalf Baechle 1611a2a6d7eSDeng-Cheng Zhu void release_progmem(void *ptr) 162e01402b1SRalf Baechle { 163e01402b1SRalf Baechle #ifndef CONFIG_MIPS_VPE_LOADER_TOM 164e01402b1SRalf Baechle kfree(ptr); 165e01402b1SRalf Baechle #endif 166e01402b1SRalf Baechle } 167e01402b1SRalf Baechle 168e01402b1SRalf Baechle /* Update size with this section: return offset. */ 169e01402b1SRalf Baechle static long get_offset(unsigned long *size, Elf_Shdr *sechdr) 170e01402b1SRalf Baechle { 171e01402b1SRalf Baechle long ret; 172e01402b1SRalf Baechle 173e01402b1SRalf Baechle ret = ALIGN(*size, sechdr->sh_addralign ? : 1); 174e01402b1SRalf Baechle *size = ret + sechdr->sh_size; 175e01402b1SRalf Baechle return ret; 176e01402b1SRalf Baechle } 177e01402b1SRalf Baechle 178e01402b1SRalf Baechle /* Lay out the SHF_ALLOC sections in a way not dissimilar to how ld 179e01402b1SRalf Baechle might -- code, read-only data, read-write data, small data. Tally 180e01402b1SRalf Baechle sizes, and place the offsets into sh_entsize fields: high bit means it 181e01402b1SRalf Baechle belongs in init. */ 182e01402b1SRalf Baechle static void layout_sections(struct module *mod, const Elf_Ehdr *hdr, 183e01402b1SRalf Baechle Elf_Shdr *sechdrs, const char *secstrings) 184e01402b1SRalf Baechle { 185e01402b1SRalf Baechle static unsigned long const masks[][2] = { 186e01402b1SRalf Baechle /* NOTE: all executable code must be the first section 187e01402b1SRalf Baechle * in this array; otherwise modify the text_size 188e01402b1SRalf Baechle * finder in the two loops below */ 189e01402b1SRalf Baechle {SHF_EXECINSTR | SHF_ALLOC, ARCH_SHF_SMALL}, 190e01402b1SRalf Baechle {SHF_ALLOC, SHF_WRITE | ARCH_SHF_SMALL}, 191e01402b1SRalf Baechle {SHF_WRITE | SHF_ALLOC, ARCH_SHF_SMALL}, 192e01402b1SRalf Baechle {ARCH_SHF_SMALL | SHF_ALLOC, 0} 193e01402b1SRalf Baechle }; 194e01402b1SRalf Baechle unsigned int m, i; 195e01402b1SRalf Baechle 196e01402b1SRalf Baechle for (i = 0; i < hdr->e_shnum; i++) 197e01402b1SRalf Baechle sechdrs[i].sh_entsize = ~0UL; 198e01402b1SRalf Baechle 199e01402b1SRalf Baechle for (m = 0; m < ARRAY_SIZE(masks); ++m) { 200e01402b1SRalf Baechle for (i = 0; i < hdr->e_shnum; ++i) { 201e01402b1SRalf Baechle Elf_Shdr *s = &sechdrs[i]; 202e01402b1SRalf Baechle 203e01402b1SRalf Baechle if ((s->sh_flags & masks[m][0]) != masks[m][0] 204e01402b1SRalf Baechle || (s->sh_flags & masks[m][1]) 205e01402b1SRalf Baechle || s->sh_entsize != ~0UL) 206e01402b1SRalf Baechle continue; 207e2a9cf96SRaghu Gandham s->sh_entsize = 2087523e4dcSRusty Russell get_offset((unsigned long *)&mod->core_layout.size, s); 209e01402b1SRalf Baechle } 210e01402b1SRalf Baechle 211e01402b1SRalf Baechle if (m == 0) 2127523e4dcSRusty Russell mod->core_layout.text_size = mod->core_layout.size; 213e01402b1SRalf Baechle 214e01402b1SRalf Baechle } 215e01402b1SRalf Baechle } 216e01402b1SRalf Baechle 217e01402b1SRalf Baechle /* from module-elf32.c, but subverted a little */ 218e01402b1SRalf Baechle 219e01402b1SRalf Baechle struct mips_hi16 { 220e01402b1SRalf Baechle struct mips_hi16 *next; 221e01402b1SRalf Baechle Elf32_Addr *addr; 222e01402b1SRalf Baechle Elf32_Addr value; 223e01402b1SRalf Baechle }; 224e01402b1SRalf Baechle 225e01402b1SRalf Baechle static struct mips_hi16 *mips_hi16_list; 226e01402b1SRalf Baechle static unsigned int gp_offs, gp_addr; 227e01402b1SRalf Baechle 228e01402b1SRalf Baechle static int apply_r_mips_none(struct module *me, uint32_t *location, 229e01402b1SRalf Baechle Elf32_Addr v) 230e01402b1SRalf Baechle { 231e01402b1SRalf Baechle return 0; 232e01402b1SRalf Baechle } 233e01402b1SRalf Baechle 234e01402b1SRalf Baechle static int apply_r_mips_gprel16(struct module *me, uint32_t *location, 235e01402b1SRalf Baechle Elf32_Addr v) 236e01402b1SRalf Baechle { 237e01402b1SRalf Baechle int rel; 238e01402b1SRalf Baechle 239e01402b1SRalf Baechle if (!(*location & 0xffff)) { 240e01402b1SRalf Baechle rel = (int)v - gp_addr; 2415792bf64SSteven J. Hill } else { 242e01402b1SRalf Baechle /* .sbss + gp(relative) + offset */ 243e01402b1SRalf Baechle /* kludge! */ 244e01402b1SRalf Baechle rel = (int)(short)((int)v + gp_offs + 245e01402b1SRalf Baechle (int)(short)(*location & 0xffff) - gp_addr); 246e01402b1SRalf Baechle } 247e01402b1SRalf Baechle 248e01402b1SRalf Baechle if ((rel > 32768) || (rel < -32768)) { 2495792bf64SSteven J. Hill pr_debug("VPE loader: apply_r_mips_gprel16: relative address 0x%x out of range of gp register\n", 2502600990eSRalf Baechle rel); 251e01402b1SRalf Baechle return -ENOEXEC; 252e01402b1SRalf Baechle } 253e01402b1SRalf Baechle 254e01402b1SRalf Baechle *location = (*location & 0xffff0000) | (rel & 0xffff); 255e01402b1SRalf Baechle 256e01402b1SRalf Baechle return 0; 257e01402b1SRalf Baechle } 258e01402b1SRalf Baechle 259e01402b1SRalf Baechle static int apply_r_mips_pc16(struct module *me, uint32_t *location, 260e01402b1SRalf Baechle Elf32_Addr v) 261e01402b1SRalf Baechle { 262e01402b1SRalf Baechle int rel; 263e01402b1SRalf Baechle rel = (((unsigned int)v - (unsigned int)location)); 2645792bf64SSteven J. Hill rel >>= 2; /* because the offset is in _instructions_ not bytes. */ 2655792bf64SSteven J. Hill rel -= 1; /* and one instruction less due to the branch delay slot. */ 266e01402b1SRalf Baechle 267e01402b1SRalf Baechle if ((rel > 32768) || (rel < -32768)) { 2685792bf64SSteven J. Hill pr_debug("VPE loader: apply_r_mips_pc16: relative address out of range 0x%x\n", 2695792bf64SSteven J. Hill rel); 270e01402b1SRalf Baechle return -ENOEXEC; 271e01402b1SRalf Baechle } 272e01402b1SRalf Baechle 273e01402b1SRalf Baechle *location = (*location & 0xffff0000) | (rel & 0xffff); 274e01402b1SRalf Baechle 275e01402b1SRalf Baechle return 0; 276e01402b1SRalf Baechle } 277e01402b1SRalf Baechle 278e01402b1SRalf Baechle static int apply_r_mips_32(struct module *me, uint32_t *location, 279e01402b1SRalf Baechle Elf32_Addr v) 280e01402b1SRalf Baechle { 281e01402b1SRalf Baechle *location += v; 282e01402b1SRalf Baechle 283e01402b1SRalf Baechle return 0; 284e01402b1SRalf Baechle } 285e01402b1SRalf Baechle 286e01402b1SRalf Baechle static int apply_r_mips_26(struct module *me, uint32_t *location, 287e01402b1SRalf Baechle Elf32_Addr v) 288e01402b1SRalf Baechle { 289e01402b1SRalf Baechle if (v % 4) { 2905792bf64SSteven J. Hill pr_debug("VPE loader: apply_r_mips_26: unaligned relocation\n"); 291e01402b1SRalf Baechle return -ENOEXEC; 292e01402b1SRalf Baechle } 293e01402b1SRalf Baechle 294307bd284SRalf Baechle /* 295307bd284SRalf Baechle * Not desperately convinced this is a good check of an overflow condition 296307bd284SRalf Baechle * anyway. But it gets in the way of handling undefined weak symbols which 297307bd284SRalf Baechle * we want to set to zero. 298307bd284SRalf Baechle * if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) { 299307bd284SRalf Baechle * printk(KERN_ERR 300307bd284SRalf Baechle * "module %s: relocation overflow\n", 301307bd284SRalf Baechle * me->name); 302307bd284SRalf Baechle * return -ENOEXEC; 303307bd284SRalf Baechle * } 304e01402b1SRalf Baechle */ 305e01402b1SRalf Baechle 306e01402b1SRalf Baechle *location = (*location & ~0x03ffffff) | 307e01402b1SRalf Baechle ((*location + (v >> 2)) & 0x03ffffff); 308e01402b1SRalf Baechle return 0; 309e01402b1SRalf Baechle } 310e01402b1SRalf Baechle 311e01402b1SRalf Baechle static int apply_r_mips_hi16(struct module *me, uint32_t *location, 312e01402b1SRalf Baechle Elf32_Addr v) 313e01402b1SRalf Baechle { 314e01402b1SRalf Baechle struct mips_hi16 *n; 315e01402b1SRalf Baechle 316e01402b1SRalf Baechle /* 317e01402b1SRalf Baechle * We cannot relocate this one now because we don't know the value of 318e01402b1SRalf Baechle * the carry we need to add. Save the information, and let LO16 do the 319e01402b1SRalf Baechle * actual relocation. 320e01402b1SRalf Baechle */ 3215792bf64SSteven J. Hill n = kmalloc(sizeof(*n), GFP_KERNEL); 322e01402b1SRalf Baechle if (!n) 323e01402b1SRalf Baechle return -ENOMEM; 324e01402b1SRalf Baechle 325e01402b1SRalf Baechle n->addr = location; 326e01402b1SRalf Baechle n->value = v; 327e01402b1SRalf Baechle n->next = mips_hi16_list; 328e01402b1SRalf Baechle mips_hi16_list = n; 329e01402b1SRalf Baechle 330e01402b1SRalf Baechle return 0; 331e01402b1SRalf Baechle } 332e01402b1SRalf Baechle 333e01402b1SRalf Baechle static int apply_r_mips_lo16(struct module *me, uint32_t *location, 334e01402b1SRalf Baechle Elf32_Addr v) 335e01402b1SRalf Baechle { 336e01402b1SRalf Baechle unsigned long insnlo = *location; 337e01402b1SRalf Baechle Elf32_Addr val, vallo; 338477c4b07SRalf Baechle struct mips_hi16 *l, *next; 339e01402b1SRalf Baechle 340e01402b1SRalf Baechle /* Sign extend the addend we extract from the lo insn. */ 341e01402b1SRalf Baechle vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000; 342e01402b1SRalf Baechle 343e01402b1SRalf Baechle if (mips_hi16_list != NULL) { 344e01402b1SRalf Baechle 345e01402b1SRalf Baechle l = mips_hi16_list; 346e01402b1SRalf Baechle while (l != NULL) { 347e01402b1SRalf Baechle unsigned long insn; 348e01402b1SRalf Baechle 349e01402b1SRalf Baechle /* 350e01402b1SRalf Baechle * The value for the HI16 had best be the same. 351e01402b1SRalf Baechle */ 352e01402b1SRalf Baechle if (v != l->value) { 3535792bf64SSteven J. Hill pr_debug("VPE loader: apply_r_mips_lo16/hi16: inconsistent value information\n"); 354477c4b07SRalf Baechle goto out_free; 355e01402b1SRalf Baechle } 356e01402b1SRalf Baechle 357e01402b1SRalf Baechle /* 358e01402b1SRalf Baechle * Do the HI16 relocation. Note that we actually don't 359e01402b1SRalf Baechle * need to know anything about the LO16 itself, except 360e01402b1SRalf Baechle * where to find the low 16 bits of the addend needed 361e01402b1SRalf Baechle * by the LO16. 362e01402b1SRalf Baechle */ 363e01402b1SRalf Baechle insn = *l->addr; 364e01402b1SRalf Baechle val = ((insn & 0xffff) << 16) + vallo; 365e01402b1SRalf Baechle val += v; 366e01402b1SRalf Baechle 367e01402b1SRalf Baechle /* 368e01402b1SRalf Baechle * Account for the sign extension that will happen in 369e01402b1SRalf Baechle * the low bits. 370e01402b1SRalf Baechle */ 371e01402b1SRalf Baechle val = ((val >> 16) + ((val & 0x8000) != 0)) & 0xffff; 372e01402b1SRalf Baechle 373e01402b1SRalf Baechle insn = (insn & ~0xffff) | val; 374e01402b1SRalf Baechle *l->addr = insn; 375e01402b1SRalf Baechle 376e01402b1SRalf Baechle next = l->next; 377e01402b1SRalf Baechle kfree(l); 378e01402b1SRalf Baechle l = next; 379e01402b1SRalf Baechle } 380e01402b1SRalf Baechle 381e01402b1SRalf Baechle mips_hi16_list = NULL; 382e01402b1SRalf Baechle } 383e01402b1SRalf Baechle 384e01402b1SRalf Baechle /* 385e01402b1SRalf Baechle * Ok, we're done with the HI16 relocs. Now deal with the LO16. 386e01402b1SRalf Baechle */ 387e01402b1SRalf Baechle val = v + vallo; 388e01402b1SRalf Baechle insnlo = (insnlo & ~0xffff) | (val & 0xffff); 389e01402b1SRalf Baechle *location = insnlo; 390e01402b1SRalf Baechle 391e01402b1SRalf Baechle return 0; 392477c4b07SRalf Baechle 393477c4b07SRalf Baechle out_free: 394477c4b07SRalf Baechle while (l != NULL) { 395477c4b07SRalf Baechle next = l->next; 396477c4b07SRalf Baechle kfree(l); 397477c4b07SRalf Baechle l = next; 398477c4b07SRalf Baechle } 399477c4b07SRalf Baechle mips_hi16_list = NULL; 400477c4b07SRalf Baechle 401477c4b07SRalf Baechle return -ENOEXEC; 402e01402b1SRalf Baechle } 403e01402b1SRalf Baechle 404e01402b1SRalf Baechle static int (*reloc_handlers[]) (struct module *me, uint32_t *location, 405e01402b1SRalf Baechle Elf32_Addr v) = { 406e01402b1SRalf Baechle [R_MIPS_NONE] = apply_r_mips_none, 407e01402b1SRalf Baechle [R_MIPS_32] = apply_r_mips_32, 408e01402b1SRalf Baechle [R_MIPS_26] = apply_r_mips_26, 409e01402b1SRalf Baechle [R_MIPS_HI16] = apply_r_mips_hi16, 410e01402b1SRalf Baechle [R_MIPS_LO16] = apply_r_mips_lo16, 411e01402b1SRalf Baechle [R_MIPS_GPREL16] = apply_r_mips_gprel16, 412e01402b1SRalf Baechle [R_MIPS_PC16] = apply_r_mips_pc16 413e01402b1SRalf Baechle }; 414e01402b1SRalf Baechle 4152600990eSRalf Baechle static char *rstrs[] = { 4162600990eSRalf Baechle [R_MIPS_NONE] = "MIPS_NONE", 4172600990eSRalf Baechle [R_MIPS_32] = "MIPS_32", 4182600990eSRalf Baechle [R_MIPS_26] = "MIPS_26", 4192600990eSRalf Baechle [R_MIPS_HI16] = "MIPS_HI16", 4202600990eSRalf Baechle [R_MIPS_LO16] = "MIPS_LO16", 4212600990eSRalf Baechle [R_MIPS_GPREL16] = "MIPS_GPREL16", 4222600990eSRalf Baechle [R_MIPS_PC16] = "MIPS_PC16" 4232600990eSRalf Baechle }; 424e01402b1SRalf Baechle 425f18b51ccSRalf Baechle static int apply_relocations(Elf32_Shdr *sechdrs, 426e01402b1SRalf Baechle const char *strtab, 427e01402b1SRalf Baechle unsigned int symindex, 428e01402b1SRalf Baechle unsigned int relsec, 429e01402b1SRalf Baechle struct module *me) 430e01402b1SRalf Baechle { 431e01402b1SRalf Baechle Elf32_Rel *rel = (void *) sechdrs[relsec].sh_addr; 432e01402b1SRalf Baechle Elf32_Sym *sym; 433e01402b1SRalf Baechle uint32_t *location; 434e01402b1SRalf Baechle unsigned int i; 435e01402b1SRalf Baechle Elf32_Addr v; 436e01402b1SRalf Baechle int res; 437e01402b1SRalf Baechle 438e01402b1SRalf Baechle for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { 439e01402b1SRalf Baechle Elf32_Word r_info = rel[i].r_info; 440e01402b1SRalf Baechle 441e01402b1SRalf Baechle /* This is where to make the change */ 442e01402b1SRalf Baechle location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr 443e01402b1SRalf Baechle + rel[i].r_offset; 444e01402b1SRalf Baechle /* This is the symbol it is referring to */ 445e01402b1SRalf Baechle sym = (Elf32_Sym *)sechdrs[symindex].sh_addr 446e01402b1SRalf Baechle + ELF32_R_SYM(r_info); 447e01402b1SRalf Baechle 448e01402b1SRalf Baechle if (!sym->st_value) { 4495792bf64SSteven J. Hill pr_debug("%s: undefined weak symbol %s\n", 450e01402b1SRalf Baechle me->name, strtab + sym->st_name); 451e01402b1SRalf Baechle /* just print the warning, dont barf */ 452e01402b1SRalf Baechle } 453e01402b1SRalf Baechle 454e01402b1SRalf Baechle v = sym->st_value; 455e01402b1SRalf Baechle 456e01402b1SRalf Baechle res = reloc_handlers[ELF32_R_TYPE(r_info)](me, location, v); 457e01402b1SRalf Baechle if (res) { 4582600990eSRalf Baechle char *r = rstrs[ELF32_R_TYPE(r_info)]; 4595792bf64SSteven J. Hill pr_warn("VPE loader: .text+0x%x relocation type %s for symbol \"%s\" failed\n", 4602600990eSRalf Baechle rel[i].r_offset, r ? r : "UNKNOWN", 4612600990eSRalf Baechle strtab + sym->st_name); 462e01402b1SRalf Baechle return res; 463e01402b1SRalf Baechle } 4642600990eSRalf Baechle } 465e01402b1SRalf Baechle 466e01402b1SRalf Baechle return 0; 467e01402b1SRalf Baechle } 468e01402b1SRalf Baechle 469f18b51ccSRalf Baechle static inline void save_gp_address(unsigned int secbase, unsigned int rel) 470e01402b1SRalf Baechle { 471e01402b1SRalf Baechle gp_addr = secbase + rel; 472e01402b1SRalf Baechle gp_offs = gp_addr - (secbase & 0xffff0000); 473e01402b1SRalf Baechle } 474e01402b1SRalf Baechle /* end module-elf32.c */ 475e01402b1SRalf Baechle 476e01402b1SRalf Baechle /* Change all symbols so that sh_value encodes the pointer directly. */ 4772600990eSRalf Baechle static void simplify_symbols(Elf_Shdr *sechdrs, 478e01402b1SRalf Baechle unsigned int symindex, 479e01402b1SRalf Baechle const char *strtab, 480e01402b1SRalf Baechle const char *secstrings, 481e01402b1SRalf Baechle unsigned int nsecs, struct module *mod) 482e01402b1SRalf Baechle { 483e01402b1SRalf Baechle Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr; 484e01402b1SRalf Baechle unsigned long secbase, bssbase = 0; 485e01402b1SRalf Baechle unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym); 4862600990eSRalf Baechle int size; 487e01402b1SRalf Baechle 488e01402b1SRalf Baechle /* find the .bss section for COMMON symbols */ 489e01402b1SRalf Baechle for (i = 0; i < nsecs; i++) { 4902600990eSRalf Baechle if (strncmp(secstrings + sechdrs[i].sh_name, ".bss", 4) == 0) { 491e01402b1SRalf Baechle bssbase = sechdrs[i].sh_addr; 4922600990eSRalf Baechle break; 4932600990eSRalf Baechle } 494e01402b1SRalf Baechle } 495e01402b1SRalf Baechle 496e01402b1SRalf Baechle for (i = 1; i < n; i++) { 497e01402b1SRalf Baechle switch (sym[i].st_shndx) { 498e01402b1SRalf Baechle case SHN_COMMON: 4992600990eSRalf Baechle /* Allocate space for the symbol in the .bss section. 5002600990eSRalf Baechle st_value is currently size. 501e01402b1SRalf Baechle We want it to have the address of the symbol. */ 502e01402b1SRalf Baechle 503e01402b1SRalf Baechle size = sym[i].st_value; 504e01402b1SRalf Baechle sym[i].st_value = bssbase; 505e01402b1SRalf Baechle 506e01402b1SRalf Baechle bssbase += size; 507e01402b1SRalf Baechle break; 508e01402b1SRalf Baechle 509e01402b1SRalf Baechle case SHN_ABS: 510e01402b1SRalf Baechle /* Don't need to do anything */ 511e01402b1SRalf Baechle break; 512e01402b1SRalf Baechle 513e01402b1SRalf Baechle case SHN_UNDEF: 514e01402b1SRalf Baechle /* ret = -ENOENT; */ 515e01402b1SRalf Baechle break; 516e01402b1SRalf Baechle 517e01402b1SRalf Baechle case SHN_MIPS_SCOMMON: 5185792bf64SSteven J. Hill pr_debug("simplify_symbols: ignoring SHN_MIPS_SCOMMON symbol <%s> st_shndx %d\n", 5195792bf64SSteven J. Hill strtab + sym[i].st_name, sym[i].st_shndx); 5205792bf64SSteven J. Hill /* .sbss section */ 521e01402b1SRalf Baechle break; 522e01402b1SRalf Baechle 523e01402b1SRalf Baechle default: 524e01402b1SRalf Baechle secbase = sechdrs[sym[i].st_shndx].sh_addr; 525e01402b1SRalf Baechle 5265792bf64SSteven J. Hill if (strncmp(strtab + sym[i].st_name, "_gp", 3) == 0) 527e01402b1SRalf Baechle save_gp_address(secbase, sym[i].st_value); 528e01402b1SRalf Baechle 529e01402b1SRalf Baechle sym[i].st_value += secbase; 530e01402b1SRalf Baechle break; 531e01402b1SRalf Baechle } 532e01402b1SRalf Baechle } 533e01402b1SRalf Baechle } 534e01402b1SRalf Baechle 535e01402b1SRalf Baechle #ifdef DEBUG_ELFLOADER 536e01402b1SRalf Baechle static void dump_elfsymbols(Elf_Shdr *sechdrs, unsigned int symindex, 537e01402b1SRalf Baechle const char *strtab, struct module *mod) 538e01402b1SRalf Baechle { 539e01402b1SRalf Baechle Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr; 540e01402b1SRalf Baechle unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym); 541e01402b1SRalf Baechle 5425792bf64SSteven J. Hill pr_debug("dump_elfsymbols: n %d\n", n); 543e01402b1SRalf Baechle for (i = 1; i < n; i++) { 5445792bf64SSteven J. Hill pr_debug(" i %d name <%s> 0x%x\n", i, strtab + sym[i].st_name, 5455792bf64SSteven J. Hill sym[i].st_value); 546e01402b1SRalf Baechle } 547e01402b1SRalf Baechle } 548e01402b1SRalf Baechle #endif 549e01402b1SRalf Baechle 5502600990eSRalf Baechle static int find_vpe_symbols(struct vpe *v, Elf_Shdr *sechdrs, 551e01402b1SRalf Baechle unsigned int symindex, const char *strtab, 552e01402b1SRalf Baechle struct module *mod) 553e01402b1SRalf Baechle { 554e01402b1SRalf Baechle Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr; 555e01402b1SRalf Baechle unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym); 556e01402b1SRalf Baechle 557e01402b1SRalf Baechle for (i = 1; i < n; i++) { 5585792bf64SSteven J. Hill if (strcmp(strtab + sym[i].st_name, "__start") == 0) 559e01402b1SRalf Baechle v->__start = sym[i].st_value; 560e01402b1SRalf Baechle 5615792bf64SSteven J. Hill if (strcmp(strtab + sym[i].st_name, "vpe_shared") == 0) 562e01402b1SRalf Baechle v->shared_ptr = (void *)sym[i].st_value; 563e01402b1SRalf Baechle } 564e01402b1SRalf Baechle 5652600990eSRalf Baechle if ((v->__start == 0) || (v->shared_ptr == NULL)) 5662600990eSRalf Baechle return -1; 5672600990eSRalf Baechle 568e01402b1SRalf Baechle return 0; 569e01402b1SRalf Baechle } 570e01402b1SRalf Baechle 571307bd284SRalf Baechle /* 5722600990eSRalf Baechle * Allocates a VPE with some program code space(the load address), copies the 5732600990eSRalf Baechle * contents of the program (p)buffer performing relocatations/etc, free's it 5742600990eSRalf Baechle * when finished. 575e01402b1SRalf Baechle */ 576be6e1437SRalf Baechle static int vpe_elfload(struct vpe *v) 577e01402b1SRalf Baechle { 578e01402b1SRalf Baechle Elf_Ehdr *hdr; 579e01402b1SRalf Baechle Elf_Shdr *sechdrs; 580e01402b1SRalf Baechle long err = 0; 581e01402b1SRalf Baechle char *secstrings, *strtab = NULL; 5822600990eSRalf Baechle unsigned int len, i, symindex = 0, strindex = 0, relocate = 0; 5835792bf64SSteven J. Hill struct module mod; /* so we can re-use the relocations code */ 584e01402b1SRalf Baechle 585e01402b1SRalf Baechle memset(&mod, 0, sizeof(struct module)); 5862600990eSRalf Baechle strcpy(mod.name, "VPE loader"); 587e01402b1SRalf Baechle 588e01402b1SRalf Baechle hdr = (Elf_Ehdr *) v->pbuffer; 589e01402b1SRalf Baechle len = v->plen; 590e01402b1SRalf Baechle 591e01402b1SRalf Baechle /* Sanity checks against insmoding binaries or wrong arch, 592e01402b1SRalf Baechle weird elf version */ 593d303f4a1SCyrill Gorcunov if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0 5942600990eSRalf Baechle || (hdr->e_type != ET_REL && hdr->e_type != ET_EXEC) 5952600990eSRalf Baechle || !elf_check_arch(hdr) 596e01402b1SRalf Baechle || hdr->e_shentsize != sizeof(*sechdrs)) { 5975792bf64SSteven J. Hill pr_warn("VPE loader: program wrong arch or weird elf version\n"); 598e01402b1SRalf Baechle 599e01402b1SRalf Baechle return -ENOEXEC; 600e01402b1SRalf Baechle } 601e01402b1SRalf Baechle 6022600990eSRalf Baechle if (hdr->e_type == ET_REL) 6032600990eSRalf Baechle relocate = 1; 6042600990eSRalf Baechle 605e01402b1SRalf Baechle if (len < hdr->e_shoff + hdr->e_shnum * sizeof(Elf_Shdr)) { 6065792bf64SSteven J. Hill pr_err("VPE loader: program length %u truncated\n", len); 6072600990eSRalf Baechle 608e01402b1SRalf Baechle return -ENOEXEC; 609e01402b1SRalf Baechle } 610e01402b1SRalf Baechle 611e01402b1SRalf Baechle /* Convenience variables */ 612e01402b1SRalf Baechle sechdrs = (void *)hdr + hdr->e_shoff; 613e01402b1SRalf Baechle secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; 614e01402b1SRalf Baechle sechdrs[0].sh_addr = 0; 615e01402b1SRalf Baechle 616e01402b1SRalf Baechle /* And these should exist, but gcc whinges if we don't init them */ 617e01402b1SRalf Baechle symindex = strindex = 0; 618e01402b1SRalf Baechle 6192600990eSRalf Baechle if (relocate) { 620e01402b1SRalf Baechle for (i = 1; i < hdr->e_shnum; i++) { 6215792bf64SSteven J. Hill if ((sechdrs[i].sh_type != SHT_NOBITS) && 6225792bf64SSteven J. Hill (len < sechdrs[i].sh_offset + sechdrs[i].sh_size)) { 6235792bf64SSteven J. Hill pr_err("VPE program length %u truncated\n", 624e01402b1SRalf Baechle len); 625e01402b1SRalf Baechle return -ENOEXEC; 626e01402b1SRalf Baechle } 627e01402b1SRalf Baechle 628e01402b1SRalf Baechle /* Mark all sections sh_addr with their address in the 629e01402b1SRalf Baechle temporary image. */ 6305792bf64SSteven J. Hill sechdrs[i].sh_addr = (size_t) hdr + 6315792bf64SSteven J. Hill sechdrs[i].sh_offset; 632e01402b1SRalf Baechle 633e01402b1SRalf Baechle /* Internal symbols and strings. */ 634e01402b1SRalf Baechle if (sechdrs[i].sh_type == SHT_SYMTAB) { 635e01402b1SRalf Baechle symindex = i; 636e01402b1SRalf Baechle strindex = sechdrs[i].sh_link; 6375792bf64SSteven J. Hill strtab = (char *)hdr + 6385792bf64SSteven J. Hill sechdrs[strindex].sh_offset; 639e01402b1SRalf Baechle } 640e01402b1SRalf Baechle } 641e01402b1SRalf Baechle layout_sections(&mod, hdr, sechdrs, secstrings); 6422600990eSRalf Baechle } 643e01402b1SRalf Baechle 6447523e4dcSRusty Russell v->load_addr = alloc_progmem(mod.core_layout.size); 6455408c490SRalf Baechle if (!v->load_addr) 6465408c490SRalf Baechle return -ENOMEM; 647e01402b1SRalf Baechle 6485408c490SRalf Baechle pr_info("VPE loader: loading to %p\n", v->load_addr); 649e01402b1SRalf Baechle 6502600990eSRalf Baechle if (relocate) { 651e01402b1SRalf Baechle for (i = 0; i < hdr->e_shnum; i++) { 652e01402b1SRalf Baechle void *dest; 653e01402b1SRalf Baechle 654e01402b1SRalf Baechle if (!(sechdrs[i].sh_flags & SHF_ALLOC)) 655e01402b1SRalf Baechle continue; 656e01402b1SRalf Baechle 657e01402b1SRalf Baechle dest = v->load_addr + sechdrs[i].sh_entsize; 658e01402b1SRalf Baechle 659e01402b1SRalf Baechle if (sechdrs[i].sh_type != SHT_NOBITS) 660e01402b1SRalf Baechle memcpy(dest, (void *)sechdrs[i].sh_addr, 661e01402b1SRalf Baechle sechdrs[i].sh_size); 662e01402b1SRalf Baechle /* Update sh_addr to point to copy in image. */ 663e01402b1SRalf Baechle sechdrs[i].sh_addr = (unsigned long)dest; 6642600990eSRalf Baechle 6655792bf64SSteven J. Hill pr_debug(" section sh_name %s sh_addr 0x%x\n", 6665792bf64SSteven J. Hill secstrings + sechdrs[i].sh_name, 6675792bf64SSteven J. Hill sechdrs[i].sh_addr); 668e01402b1SRalf Baechle } 669e01402b1SRalf Baechle 670e01402b1SRalf Baechle /* Fix up syms, so that st_value is a pointer to location. */ 671e01402b1SRalf Baechle simplify_symbols(sechdrs, symindex, strtab, secstrings, 672e01402b1SRalf Baechle hdr->e_shnum, &mod); 673e01402b1SRalf Baechle 674e01402b1SRalf Baechle /* Now do relocations. */ 675e01402b1SRalf Baechle for (i = 1; i < hdr->e_shnum; i++) { 676e01402b1SRalf Baechle const char *strtab = (char *)sechdrs[strindex].sh_addr; 677e01402b1SRalf Baechle unsigned int info = sechdrs[i].sh_info; 678e01402b1SRalf Baechle 679e01402b1SRalf Baechle /* Not a valid relocation section? */ 680e01402b1SRalf Baechle if (info >= hdr->e_shnum) 681e01402b1SRalf Baechle continue; 682e01402b1SRalf Baechle 683e01402b1SRalf Baechle /* Don't bother with non-allocated sections */ 684e01402b1SRalf Baechle if (!(sechdrs[info].sh_flags & SHF_ALLOC)) 685e01402b1SRalf Baechle continue; 686e01402b1SRalf Baechle 687e01402b1SRalf Baechle if (sechdrs[i].sh_type == SHT_REL) 6885792bf64SSteven J. Hill err = apply_relocations(sechdrs, strtab, 6895792bf64SSteven J. Hill symindex, i, &mod); 690e01402b1SRalf Baechle else if (sechdrs[i].sh_type == SHT_RELA) 6915792bf64SSteven J. Hill err = apply_relocate_add(sechdrs, strtab, 6925792bf64SSteven J. Hill symindex, i, &mod); 6932600990eSRalf Baechle if (err < 0) 6942600990eSRalf Baechle return err; 6952600990eSRalf Baechle 6962600990eSRalf Baechle } 6972600990eSRalf Baechle } else { 6985792bf64SSteven J. Hill struct elf_phdr *phdr = (struct elf_phdr *) 6995792bf64SSteven J. Hill ((char *)hdr + hdr->e_phoff); 7002600990eSRalf Baechle 701bdf5d42cSRalf Baechle for (i = 0; i < hdr->e_phnum; i++) { 702b618336aSKevin D. Kissell if (phdr->p_type == PT_LOAD) { 703b618336aSKevin D. Kissell memcpy((void *)phdr->p_paddr, 704b618336aSKevin D. Kissell (char *)hdr + phdr->p_offset, 705b618336aSKevin D. Kissell phdr->p_filesz); 706b618336aSKevin D. Kissell memset((void *)phdr->p_paddr + phdr->p_filesz, 707b618336aSKevin D. Kissell 0, phdr->p_memsz - phdr->p_filesz); 708b618336aSKevin D. Kissell } 709bdf5d42cSRalf Baechle phdr++; 710bdf5d42cSRalf Baechle } 711bdf5d42cSRalf Baechle 712bdf5d42cSRalf Baechle for (i = 0; i < hdr->e_shnum; i++) { 7132600990eSRalf Baechle /* Internal symbols and strings. */ 7142600990eSRalf Baechle if (sechdrs[i].sh_type == SHT_SYMTAB) { 7152600990eSRalf Baechle symindex = i; 7162600990eSRalf Baechle strindex = sechdrs[i].sh_link; 7175792bf64SSteven J. Hill strtab = (char *)hdr + 7185792bf64SSteven J. Hill sechdrs[strindex].sh_offset; 7192600990eSRalf Baechle 7205792bf64SSteven J. Hill /* 7215792bf64SSteven J. Hill * mark symtab's address for when we try 7225792bf64SSteven J. Hill * to find the magic symbols 7235792bf64SSteven J. Hill */ 7245792bf64SSteven J. Hill sechdrs[i].sh_addr = (size_t) hdr + 7255792bf64SSteven J. Hill sechdrs[i].sh_offset; 7262600990eSRalf Baechle } 727e01402b1SRalf Baechle } 728e01402b1SRalf Baechle } 729e01402b1SRalf Baechle 730e01402b1SRalf Baechle /* make sure it's physically written out */ 731e01402b1SRalf Baechle flush_icache_range((unsigned long)v->load_addr, 732e01402b1SRalf Baechle (unsigned long)v->load_addr + v->len); 733e01402b1SRalf Baechle 734e01402b1SRalf Baechle if ((find_vpe_symbols(v, sechdrs, symindex, strtab, &mod)) < 0) { 7352600990eSRalf Baechle if (v->__start == 0) { 7365792bf64SSteven J. Hill pr_warn("VPE loader: program does not contain a __start symbol\n"); 7372600990eSRalf Baechle return -ENOEXEC; 7382600990eSRalf Baechle } 739e01402b1SRalf Baechle 7402600990eSRalf Baechle if (v->shared_ptr == NULL) 7415792bf64SSteven J. Hill pr_warn("VPE loader: program does not contain vpe_shared symbol.\n" 7422600990eSRalf Baechle " Unable to use AMVP (AP/SP) facilities.\n"); 743e01402b1SRalf Baechle } 744e01402b1SRalf Baechle 7455792bf64SSteven J. Hill pr_info(" elf loaded\n"); 7462600990eSRalf Baechle return 0; 747e01402b1SRalf Baechle } 748e01402b1SRalf Baechle 7492600990eSRalf Baechle static int getcwd(char *buff, int size) 7502600990eSRalf Baechle { 7512600990eSRalf Baechle mm_segment_t old_fs; 7522600990eSRalf Baechle int ret; 7532600990eSRalf Baechle 7542600990eSRalf Baechle old_fs = get_fs(); 7552600990eSRalf Baechle set_fs(KERNEL_DS); 7562600990eSRalf Baechle 7572600990eSRalf Baechle ret = sys_getcwd(buff, size); 7582600990eSRalf Baechle 7592600990eSRalf Baechle set_fs(old_fs); 7602600990eSRalf Baechle 7612600990eSRalf Baechle return ret; 7622600990eSRalf Baechle } 7632600990eSRalf Baechle 7642600990eSRalf Baechle /* checks VPE is unused and gets ready to load program */ 7652600990eSRalf Baechle static int vpe_open(struct inode *inode, struct file *filp) 7662600990eSRalf Baechle { 767c4c4018bSRalf Baechle enum vpe_state state; 7685792bf64SSteven J. Hill struct vpe_notifications *notifier; 76907cc0c9eSRalf Baechle struct vpe *v; 7701bbfc20dSRalf Baechle int ret; 7712600990eSRalf Baechle 7721a2a6d7eSDeng-Cheng Zhu if (VPE_MODULE_MINOR != iminor(inode)) { 77307cc0c9eSRalf Baechle /* assume only 1 device at the moment. */ 7745792bf64SSteven J. Hill pr_warn("VPE loader: only vpe1 is supported\n"); 7751bbfc20dSRalf Baechle 7761bbfc20dSRalf Baechle return -ENODEV; 7772600990eSRalf Baechle } 7782600990eSRalf Baechle 7795792bf64SSteven J. Hill v = get_vpe(aprp_cpu_index()); 7805792bf64SSteven J. Hill if (v == NULL) { 7815792bf64SSteven J. Hill pr_warn("VPE loader: unable to get vpe\n"); 7821bbfc20dSRalf Baechle 7831bbfc20dSRalf Baechle return -ENODEV; 7842600990eSRalf Baechle } 7852600990eSRalf Baechle 786c4c4018bSRalf Baechle state = xchg(&v->state, VPE_STATE_INUSE); 787c4c4018bSRalf Baechle if (state != VPE_STATE_UNUSED) { 7885792bf64SSteven J. Hill pr_debug("VPE loader: tc in use dumping regs\n"); 7892600990eSRalf Baechle 7905792bf64SSteven J. Hill list_for_each_entry(notifier, &v->notify, list) 7915792bf64SSteven J. Hill notifier->stop(aprp_cpu_index()); 7922600990eSRalf Baechle 7932600990eSRalf Baechle release_progmem(v->load_addr); 7941a2a6d7eSDeng-Cheng Zhu cleanup_tc(get_tc(aprp_cpu_index())); 795e01402b1SRalf Baechle } 796e01402b1SRalf Baechle 797e01402b1SRalf Baechle /* this of-course trashes what was there before... */ 798e01402b1SRalf Baechle v->pbuffer = vmalloc(P_SIZE); 799863abad4SJesper Juhl if (!v->pbuffer) { 8005792bf64SSteven J. Hill pr_warn("VPE loader: unable to allocate memory\n"); 801863abad4SJesper Juhl return -ENOMEM; 802863abad4SJesper Juhl } 803e01402b1SRalf Baechle v->plen = P_SIZE; 804e01402b1SRalf Baechle v->load_addr = NULL; 805e01402b1SRalf Baechle v->len = 0; 806e01402b1SRalf Baechle 8072600990eSRalf Baechle v->cwd[0] = 0; 8082600990eSRalf Baechle ret = getcwd(v->cwd, VPE_PATH_MAX); 8092600990eSRalf Baechle if (ret < 0) 8105792bf64SSteven J. Hill pr_warn("VPE loader: open, getcwd returned %d\n", ret); 8112600990eSRalf Baechle 8122600990eSRalf Baechle v->shared_ptr = NULL; 8132600990eSRalf Baechle v->__start = 0; 81407cc0c9eSRalf Baechle 815e01402b1SRalf Baechle return 0; 816e01402b1SRalf Baechle } 817e01402b1SRalf Baechle 818e01402b1SRalf Baechle static int vpe_release(struct inode *inode, struct file *filp) 819e01402b1SRalf Baechle { 820c60f9944SBjorn Helgaas #if defined(CONFIG_MIPS_VPE_LOADER_MT) || defined(CONFIG_MIPS_VPE_LOADER_CMP) 821307bd284SRalf Baechle struct vpe *v; 822e01402b1SRalf Baechle Elf_Ehdr *hdr; 82307cc0c9eSRalf Baechle int ret = 0; 824e01402b1SRalf Baechle 8251a2a6d7eSDeng-Cheng Zhu v = get_vpe(aprp_cpu_index()); 82607cc0c9eSRalf Baechle if (v == NULL) 827e01402b1SRalf Baechle return -ENODEV; 828e01402b1SRalf Baechle 829e01402b1SRalf Baechle hdr = (Elf_Ehdr *) v->pbuffer; 830d303f4a1SCyrill Gorcunov if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) == 0) { 8311c205b9cSBjorn Helgaas if (vpe_elfload(v) >= 0) { 832e01402b1SRalf Baechle vpe_run(v); 83307cc0c9eSRalf Baechle } else { 8345792bf64SSteven J. Hill pr_warn("VPE loader: ELF load failed.\n"); 835e01402b1SRalf Baechle ret = -ENOEXEC; 836e01402b1SRalf Baechle } 837e01402b1SRalf Baechle } else { 8385792bf64SSteven J. Hill pr_warn("VPE loader: only elf files are supported\n"); 839e01402b1SRalf Baechle ret = -ENOEXEC; 840e01402b1SRalf Baechle } 841e01402b1SRalf Baechle 8422600990eSRalf Baechle /* It's good to be able to run the SP and if it chokes have a look at 8432600990eSRalf Baechle the /dev/rt?. But if we reset the pointer to the shared struct we 8448ebcfc8bSNick Andrew lose what has happened. So perhaps if garbage is sent to the vpe 8452600990eSRalf Baechle device, use it as a trigger for the reset. Hopefully a nice 8462600990eSRalf Baechle executable will be along shortly. */ 8472600990eSRalf Baechle if (ret < 0) 8482600990eSRalf Baechle v->shared_ptr = NULL; 8492600990eSRalf Baechle 850e01402b1SRalf Baechle vfree(v->pbuffer); 851e01402b1SRalf Baechle v->plen = 0; 852863abad4SJesper Juhl 853e01402b1SRalf Baechle return ret; 854c60f9944SBjorn Helgaas #else 855c60f9944SBjorn Helgaas pr_warn("VPE loader: ELF load failed.\n"); 856c60f9944SBjorn Helgaas return -ENOEXEC; 857c60f9944SBjorn Helgaas #endif 858e01402b1SRalf Baechle } 859e01402b1SRalf Baechle 860e01402b1SRalf Baechle static ssize_t vpe_write(struct file *file, const char __user *buffer, 861e01402b1SRalf Baechle size_t count, loff_t *ppos) 862e01402b1SRalf Baechle { 863e01402b1SRalf Baechle size_t ret = count; 864307bd284SRalf Baechle struct vpe *v; 865e01402b1SRalf Baechle 8661a2a6d7eSDeng-Cheng Zhu if (iminor(file_inode(file)) != VPE_MODULE_MINOR) 86707cc0c9eSRalf Baechle return -ENODEV; 86807cc0c9eSRalf Baechle 8691a2a6d7eSDeng-Cheng Zhu v = get_vpe(aprp_cpu_index()); 8705792bf64SSteven J. Hill 87107cc0c9eSRalf Baechle if (v == NULL) 872e01402b1SRalf Baechle return -ENODEV; 873e01402b1SRalf Baechle 874e01402b1SRalf Baechle if ((count + v->len) > v->plen) { 875aae22f16SColin Ian King pr_warn("VPE loader: elf size too big. Perhaps strip unneeded symbols\n"); 876e01402b1SRalf Baechle return -ENOMEM; 877e01402b1SRalf Baechle } 878e01402b1SRalf Baechle 879e01402b1SRalf Baechle count -= copy_from_user(v->pbuffer + v->len, buffer, count); 8802600990eSRalf Baechle if (!count) 881e01402b1SRalf Baechle return -EFAULT; 882e01402b1SRalf Baechle 883e01402b1SRalf Baechle v->len += count; 884e01402b1SRalf Baechle return ret; 885e01402b1SRalf Baechle } 886e01402b1SRalf Baechle 8871a2a6d7eSDeng-Cheng Zhu const struct file_operations vpe_fops = { 888e01402b1SRalf Baechle .owner = THIS_MODULE, 889e01402b1SRalf Baechle .open = vpe_open, 890e01402b1SRalf Baechle .release = vpe_release, 8916038f373SArnd Bergmann .write = vpe_write, 8926038f373SArnd Bergmann .llseek = noop_llseek, 893e01402b1SRalf Baechle }; 894e01402b1SRalf Baechle 895e01402b1SRalf Baechle void *vpe_get_shared(int index) 896e01402b1SRalf Baechle { 8975792bf64SSteven J. Hill struct vpe *v = get_vpe(index); 898e01402b1SRalf Baechle 8995792bf64SSteven J. Hill if (v == NULL) 900e01402b1SRalf Baechle return NULL; 901e01402b1SRalf Baechle 902e01402b1SRalf Baechle return v->shared_ptr; 903e01402b1SRalf Baechle } 904e01402b1SRalf Baechle EXPORT_SYMBOL(vpe_get_shared); 905e01402b1SRalf Baechle 9062600990eSRalf Baechle int vpe_notify(int index, struct vpe_notifications *notify) 9072600990eSRalf Baechle { 9085792bf64SSteven J. Hill struct vpe *v = get_vpe(index); 9092600990eSRalf Baechle 9105792bf64SSteven J. Hill if (v == NULL) 9112600990eSRalf Baechle return -1; 9122600990eSRalf Baechle 9132600990eSRalf Baechle list_add(¬ify->list, &v->notify); 9142600990eSRalf Baechle return 0; 9152600990eSRalf Baechle } 9162600990eSRalf Baechle EXPORT_SYMBOL(vpe_notify); 9172600990eSRalf Baechle 9182600990eSRalf Baechle char *vpe_getcwd(int index) 9192600990eSRalf Baechle { 9205792bf64SSteven J. Hill struct vpe *v = get_vpe(index); 9212600990eSRalf Baechle 9225792bf64SSteven J. Hill if (v == NULL) 9232600990eSRalf Baechle return NULL; 9242600990eSRalf Baechle 9252600990eSRalf Baechle return v->cwd; 9262600990eSRalf Baechle } 9272600990eSRalf Baechle EXPORT_SYMBOL(vpe_getcwd); 9282600990eSRalf Baechle 929e01402b1SRalf Baechle module_init(vpe_module_init); 930e01402b1SRalf Baechle module_exit(vpe_module_exit); 931e01402b1SRalf Baechle MODULE_DESCRIPTION("MIPS VPE Loader"); 9322600990eSRalf Baechle MODULE_AUTHOR("Elizabeth Oldham, MIPS Technologies, Inc."); 933e01402b1SRalf Baechle MODULE_LICENSE("GPL"); 934