1/* MN10300 Kernel module helper routines 2 * 3 * Copyright (C) 2007, 2008, 2009 Red Hat, Inc. All Rights Reserved. 4 * Written by Mark Salter (msalter@redhat.com) 5 * - Derived from arch/i386/kernel/module.c 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public Licence as published by 9 * the Free Software Foundation; either version 2 of the Licence, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public Licence for more details. 16 * 17 * You should have received a copy of the GNU General Public Licence 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21#include <linux/moduleloader.h> 22#include <linux/elf.h> 23#include <linux/vmalloc.h> 24#include <linux/fs.h> 25#include <linux/string.h> 26#include <linux/kernel.h> 27#include <linux/bug.h> 28 29#define DEBUGP(fmt, ...) 30 31/* 32 * allocate storage for a module 33 */ 34void *module_alloc(unsigned long size) 35{ 36 if (size == 0) 37 return NULL; 38 return vmalloc_exec(size); 39} 40 41/* 42 * free memory returned from module_alloc() 43 */ 44void module_free(struct module *mod, void *module_region) 45{ 46 vfree(module_region); 47} 48 49/* 50 * allow the arch to fix up the section table 51 * - we don't need anything special 52 */ 53int module_frob_arch_sections(Elf_Ehdr *hdr, 54 Elf_Shdr *sechdrs, 55 char *secstrings, 56 struct module *mod) 57{ 58 return 0; 59} 60 61static void reloc_put16(uint8_t *p, uint32_t val) 62{ 63 p[0] = val & 0xff; 64 p[1] = (val >> 8) & 0xff; 65} 66 67static void reloc_put24(uint8_t *p, uint32_t val) 68{ 69 reloc_put16(p, val); 70 p[2] = (val >> 16) & 0xff; 71} 72 73static void reloc_put32(uint8_t *p, uint32_t val) 74{ 75 reloc_put16(p, val); 76 reloc_put16(p+2, val >> 16); 77} 78 79/* 80 * apply a REL relocation 81 */ 82int apply_relocate(Elf32_Shdr *sechdrs, 83 const char *strtab, 84 unsigned int symindex, 85 unsigned int relsec, 86 struct module *me) 87{ 88 printk(KERN_ERR "module %s: RELOCATION unsupported\n", 89 me->name); 90 return -ENOEXEC; 91} 92 93/* 94 * apply a RELA relocation 95 */ 96int apply_relocate_add(Elf32_Shdr *sechdrs, 97 const char *strtab, 98 unsigned int symindex, 99 unsigned int relsec, 100 struct module *me) 101{ 102 unsigned int i, sym_diff_seen = 0; 103 Elf32_Rela *rel = (void *)sechdrs[relsec].sh_addr; 104 Elf32_Sym *sym; 105 Elf32_Addr relocation, sym_diff_val = 0; 106 uint8_t *location; 107 uint32_t value; 108 109 DEBUGP("Applying relocate section %u to %u\n", 110 relsec, sechdrs[relsec].sh_info); 111 112 for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { 113 /* this is where to make the change */ 114 location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr 115 + rel[i].r_offset; 116 117 /* this is the symbol the relocation is referring to (note that 118 * all undefined symbols have been resolved by the caller) */ 119 sym = (Elf32_Sym *)sechdrs[symindex].sh_addr 120 + ELF32_R_SYM(rel[i].r_info); 121 122 /* this is the adjustment to be made */ 123 relocation = sym->st_value + rel[i].r_addend; 124 125 if (sym_diff_seen) { 126 switch (ELF32_R_TYPE(rel[i].r_info)) { 127 case R_MN10300_32: 128 case R_MN10300_24: 129 case R_MN10300_16: 130 case R_MN10300_8: 131 relocation -= sym_diff_val; 132 sym_diff_seen = 0; 133 break; 134 default: 135 printk(KERN_ERR "module %s: Unexpected SYM_DIFF relocation: %u\n", 136 me->name, ELF32_R_TYPE(rel[i].r_info)); 137 return -ENOEXEC; 138 } 139 } 140 141 switch (ELF32_R_TYPE(rel[i].r_info)) { 142 /* for the first four relocation types, we simply 143 * store the adjustment at the location given */ 144 case R_MN10300_32: 145 reloc_put32(location, relocation); 146 break; 147 case R_MN10300_24: 148 reloc_put24(location, relocation); 149 break; 150 case R_MN10300_16: 151 reloc_put16(location, relocation); 152 break; 153 case R_MN10300_8: 154 *location = relocation; 155 break; 156 157 /* for the next three relocation types, we write the 158 * adjustment with the address subtracted over the 159 * value at the location given */ 160 case R_MN10300_PCREL32: 161 value = relocation - (uint32_t) location; 162 reloc_put32(location, value); 163 break; 164 case R_MN10300_PCREL16: 165 value = relocation - (uint32_t) location; 166 reloc_put16(location, value); 167 break; 168 case R_MN10300_PCREL8: 169 *location = relocation - (uint32_t) location; 170 break; 171 172 case R_MN10300_SYM_DIFF: 173 /* This is used to adjust the next reloc as required 174 * by relaxation. */ 175 sym_diff_seen = 1; 176 sym_diff_val = sym->st_value; 177 break; 178 179 case R_MN10300_ALIGN: 180 /* Just ignore the ALIGN relocs. 181 * Only interesting if kernel performed relaxation. */ 182 continue; 183 184 default: 185 printk(KERN_ERR "module %s: Unknown relocation: %u\n", 186 me->name, ELF32_R_TYPE(rel[i].r_info)); 187 return -ENOEXEC; 188 } 189 } 190 if (sym_diff_seen) { 191 printk(KERN_ERR "module %s: Nothing follows SYM_DIFF relocation: %u\n", 192 me->name, ELF32_R_TYPE(rel[i].r_info)); 193 return -ENOEXEC; 194 } 195 return 0; 196} 197 198/* 199 * finish loading the module 200 */ 201int module_finalize(const Elf_Ehdr *hdr, 202 const Elf_Shdr *sechdrs, 203 struct module *me) 204{ 205 return 0; 206} 207 208/* 209 * finish clearing the module 210 */ 211void module_arch_cleanup(struct module *mod) 212{ 213} 214