1/* 2 * Port on Texas Instruments TMS320C6x architecture 3 * 4 * Copyright (C) 2005, 2009, 2010, 2011 Texas Instruments Incorporated 5 * Author: Thomas Charleux (thomas.charleux@jaluna.com) 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 */ 12#include <linux/moduleloader.h> 13#include <linux/elf.h> 14#include <linux/vmalloc.h> 15#include <linux/kernel.h> 16 17static inline int fixup_pcr(u32 *ip, Elf32_Addr dest, u32 maskbits, int shift) 18{ 19 u32 opcode; 20 long ep = (long)ip & ~31; 21 long delta = ((long)dest - ep) >> 2; 22 long mask = (1 << maskbits) - 1; 23 24 if ((delta >> (maskbits - 1)) == 0 || 25 (delta >> (maskbits - 1)) == -1) { 26 opcode = *ip; 27 opcode &= ~(mask << shift); 28 opcode |= ((delta & mask) << shift); 29 *ip = opcode; 30 31 pr_debug("REL PCR_S%d[%p] dest[%p] opcode[%08x]\n", 32 maskbits, ip, (void *)dest, opcode); 33 34 return 0; 35 } 36 pr_err("PCR_S%d reloc %p -> %p out of range!\n", 37 maskbits, ip, (void *)dest); 38 39 return -1; 40} 41 42/* 43 * apply a RELA relocation 44 */ 45int apply_relocate_add(Elf32_Shdr *sechdrs, 46 const char *strtab, 47 unsigned int symindex, 48 unsigned int relsec, 49 struct module *me) 50{ 51 Elf32_Rela *rel = (void *) sechdrs[relsec].sh_addr; 52 Elf_Sym *sym; 53 u32 *location, opcode; 54 unsigned int i; 55 Elf32_Addr v; 56 Elf_Addr offset = 0; 57 58 pr_debug("Applying relocate section %u to %u with offset 0x%x\n", 59 relsec, sechdrs[relsec].sh_info, offset); 60 61 for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { 62 /* This is where to make the change */ 63 location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr 64 + rel[i].r_offset - offset; 65 66 /* This is the symbol it is referring to. Note that all 67 undefined symbols have been resolved. */ 68 sym = (Elf_Sym *)sechdrs[symindex].sh_addr 69 + ELF32_R_SYM(rel[i].r_info); 70 71 /* this is the adjustment to be made */ 72 v = sym->st_value + rel[i].r_addend; 73 74 switch (ELF32_R_TYPE(rel[i].r_info)) { 75 case R_C6000_ABS32: 76 pr_debug("RELA ABS32: [%p] = 0x%x\n", location, v); 77 *location = v; 78 break; 79 case R_C6000_ABS16: 80 pr_debug("RELA ABS16: [%p] = 0x%x\n", location, v); 81 *(u16 *)location = v; 82 break; 83 case R_C6000_ABS8: 84 pr_debug("RELA ABS8: [%p] = 0x%x\n", location, v); 85 *(u8 *)location = v; 86 break; 87 case R_C6000_ABS_L16: 88 opcode = *location; 89 opcode &= ~0x7fff80; 90 opcode |= ((v & 0xffff) << 7); 91 pr_debug("RELA ABS_L16[%p] v[0x%x] opcode[0x%x]\n", 92 location, v, opcode); 93 *location = opcode; 94 break; 95 case R_C6000_ABS_H16: 96 opcode = *location; 97 opcode &= ~0x7fff80; 98 opcode |= ((v >> 9) & 0x7fff80); 99 pr_debug("RELA ABS_H16[%p] v[0x%x] opcode[0x%x]\n", 100 location, v, opcode); 101 *location = opcode; 102 break; 103 case R_C6000_PCR_S21: 104 if (fixup_pcr(location, v, 21, 7)) 105 return -ENOEXEC; 106 break; 107 case R_C6000_PCR_S12: 108 if (fixup_pcr(location, v, 12, 16)) 109 return -ENOEXEC; 110 break; 111 case R_C6000_PCR_S10: 112 if (fixup_pcr(location, v, 10, 13)) 113 return -ENOEXEC; 114 break; 115 default: 116 pr_err("module %s: Unknown RELA relocation: %u\n", 117 me->name, ELF32_R_TYPE(rel[i].r_info)); 118 return -ENOEXEC; 119 } 120 } 121 122 return 0; 123} 124