164236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot/* 264236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot * Port on Texas Instruments TMS320C6x architecture 364236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot * 464236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot * Copyright (C) 2005, 2009, 2010, 2011 Texas Instruments Incorporated 564236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot * Author: Thomas Charleux (thomas.charleux@jaluna.com) 664236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot * 764236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot * This program is free software; you can redistribute it and/or modify 864236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot * it under the terms of the GNU General Public License version 2 as 964236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot * published by the Free Software Foundation. 1064236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot * 1164236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot */ 1264236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot#include <linux/moduleloader.h> 1364236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot#include <linux/elf.h> 1464236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot#include <linux/vmalloc.h> 1564236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot#include <linux/kernel.h> 1664236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot 1764236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiotstatic inline int fixup_pcr(u32 *ip, Elf32_Addr dest, u32 maskbits, int shift) 1864236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot{ 1964236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot u32 opcode; 2064236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot long ep = (long)ip & ~31; 2164236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot long delta = ((long)dest - ep) >> 2; 2264236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot long mask = (1 << maskbits) - 1; 2364236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot 2464236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot if ((delta >> (maskbits - 1)) == 0 || 2564236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot (delta >> (maskbits - 1)) == -1) { 2664236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot opcode = *ip; 2764236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot opcode &= ~(mask << shift); 2864236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot opcode |= ((delta & mask) << shift); 2964236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot *ip = opcode; 3064236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot 3164236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot pr_debug("REL PCR_S%d[%p] dest[%p] opcode[%08x]\n", 3264236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot maskbits, ip, (void *)dest, opcode); 3364236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot 3464236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot return 0; 3564236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot } 3664236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot pr_err("PCR_S%d reloc %p -> %p out of range!\n", 3764236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot maskbits, ip, (void *)dest); 3864236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot 3964236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot return -1; 4064236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot} 4164236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot 4264236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot/* 4364236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot * apply a RELA relocation 4464236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot */ 4564236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiotint apply_relocate_add(Elf32_Shdr *sechdrs, 4664236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot const char *strtab, 4764236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot unsigned int symindex, 4864236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot unsigned int relsec, 4964236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot struct module *me) 5064236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot{ 5164236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot Elf32_Rela *rel = (void *) sechdrs[relsec].sh_addr; 5264236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot Elf_Sym *sym; 5364236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot u32 *location, opcode; 5464236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot unsigned int i; 5564236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot Elf32_Addr v; 5664236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot Elf_Addr offset = 0; 5764236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot 5864236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot pr_debug("Applying relocate section %u to %u with offset 0x%x\n", 5964236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot relsec, sechdrs[relsec].sh_info, offset); 6064236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot 6164236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { 6264236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot /* This is where to make the change */ 6364236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr 6464236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot + rel[i].r_offset - offset; 6564236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot 6664236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot /* This is the symbol it is referring to. Note that all 6764236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot undefined symbols have been resolved. */ 6864236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot sym = (Elf_Sym *)sechdrs[symindex].sh_addr 6964236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot + ELF32_R_SYM(rel[i].r_info); 7064236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot 7164236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot /* this is the adjustment to be made */ 7264236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot v = sym->st_value + rel[i].r_addend; 7364236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot 7464236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot switch (ELF32_R_TYPE(rel[i].r_info)) { 7564236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot case R_C6000_ABS32: 7664236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot pr_debug("RELA ABS32: [%p] = 0x%x\n", location, v); 7764236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot *location = v; 7864236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot break; 7964236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot case R_C6000_ABS16: 8064236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot pr_debug("RELA ABS16: [%p] = 0x%x\n", location, v); 8164236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot *(u16 *)location = v; 8264236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot break; 8364236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot case R_C6000_ABS8: 8464236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot pr_debug("RELA ABS8: [%p] = 0x%x\n", location, v); 8564236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot *(u8 *)location = v; 8664236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot break; 8764236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot case R_C6000_ABS_L16: 8864236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot opcode = *location; 8964236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot opcode &= ~0x7fff80; 9064236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot opcode |= ((v & 0xffff) << 7); 9164236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot pr_debug("RELA ABS_L16[%p] v[0x%x] opcode[0x%x]\n", 9264236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot location, v, opcode); 9364236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot *location = opcode; 9464236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot break; 9564236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot case R_C6000_ABS_H16: 9664236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot opcode = *location; 9764236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot opcode &= ~0x7fff80; 9864236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot opcode |= ((v >> 9) & 0x7fff80); 9964236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot pr_debug("RELA ABS_H16[%p] v[0x%x] opcode[0x%x]\n", 10064236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot location, v, opcode); 10164236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot *location = opcode; 10264236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot break; 10364236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot case R_C6000_PCR_S21: 10464236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot if (fixup_pcr(location, v, 21, 7)) 10564236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot return -ENOEXEC; 10664236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot break; 10764236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot case R_C6000_PCR_S12: 10864236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot if (fixup_pcr(location, v, 12, 16)) 10964236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot return -ENOEXEC; 11064236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot break; 11164236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot case R_C6000_PCR_S10: 11264236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot if (fixup_pcr(location, v, 10, 13)) 11364236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot return -ENOEXEC; 11464236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot break; 11564236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot default: 11664236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot pr_err("module %s: Unknown RELA relocation: %u\n", 11764236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot me->name, ELF32_R_TYPE(rel[i].r_info)); 11864236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot return -ENOEXEC; 11964236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot } 12064236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot } 12164236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot 12264236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot return 0; 12364236ac1444eecca4b7b51270879d58bd291c8c2Aurelien Jacquiot} 124