| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 1 | /* | 
| Uwe Zeisberger | f30c226 | 2006-10-03 23:01:26 +0200 | [diff] [blame] | 2 |  * arch/xtensa/kernel/module.c | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 3 |  * | 
 | 4 |  * Module support. | 
 | 5 |  * | 
 | 6 |  * This file is subject to the terms and conditions of the GNU General Public | 
 | 7 |  * License.  See the file "COPYING" in the main directory of this archive | 
 | 8 |  * for more details. | 
 | 9 |  * | 
| Chris Zankel | ff6fd46 | 2007-08-14 13:02:06 -0700 | [diff] [blame] | 10 |  * Copyright (C) 2001 - 2006 Tensilica Inc. | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 11 |  * | 
 | 12 |  * Chris Zankel <chris@zankel.net> | 
 | 13 |  * | 
 | 14 |  */ | 
 | 15 |  | 
 | 16 | #include <linux/module.h> | 
 | 17 | #include <linux/moduleloader.h> | 
 | 18 | #include <linux/elf.h> | 
 | 19 | #include <linux/vmalloc.h> | 
 | 20 | #include <linux/fs.h> | 
 | 21 | #include <linux/string.h> | 
 | 22 | #include <linux/kernel.h> | 
 | 23 | #include <linux/cache.h> | 
 | 24 |  | 
| Chris Zankel | ff6fd46 | 2007-08-14 13:02:06 -0700 | [diff] [blame] | 25 | #undef DEBUG_RELOCATE | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 26 |  | 
 | 27 | void *module_alloc(unsigned long size) | 
 | 28 | { | 
| Chris Zankel | ff6fd46 | 2007-08-14 13:02:06 -0700 | [diff] [blame] | 29 | 	if (size == 0) | 
 | 30 | 		return NULL; | 
| Chris Zankel | 3b4a49e | 2008-01-07 16:42:21 -0800 | [diff] [blame] | 31 | 	return vmalloc_exec(size); | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 32 | } | 
 | 33 |  | 
 | 34 | void module_free(struct module *mod, void *module_region) | 
 | 35 | { | 
| Chris Zankel | ff6fd46 | 2007-08-14 13:02:06 -0700 | [diff] [blame] | 36 | 	vfree(module_region); | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 37 | } | 
 | 38 |  | 
 | 39 | int module_frob_arch_sections(Elf32_Ehdr *hdr, | 
 | 40 |     			      Elf32_Shdr *sechdrs, | 
 | 41 | 			      char *secstrings, | 
| Chris Zankel | ff6fd46 | 2007-08-14 13:02:06 -0700 | [diff] [blame] | 42 | 			      struct module *mod) | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 43 | { | 
| Chris Zankel | ff6fd46 | 2007-08-14 13:02:06 -0700 | [diff] [blame] | 44 | 	return 0; | 
 | 45 | } | 
 | 46 |  | 
 | 47 | static int | 
 | 48 | decode_calln_opcode (unsigned char *location) | 
 | 49 | { | 
 | 50 | #ifdef __XTENSA_EB__ | 
 | 51 | 	return (location[0] & 0xf0) == 0x50; | 
 | 52 | #endif | 
 | 53 | #ifdef __XTENSA_EL__ | 
 | 54 | 	return (location[0] & 0xf) == 0x5; | 
 | 55 | #endif | 
 | 56 | } | 
 | 57 |  | 
 | 58 | static int | 
 | 59 | decode_l32r_opcode (unsigned char *location) | 
 | 60 | { | 
 | 61 | #ifdef __XTENSA_EB__ | 
 | 62 | 	return (location[0] & 0xf0) == 0x10; | 
 | 63 | #endif | 
 | 64 | #ifdef __XTENSA_EL__ | 
 | 65 | 	return (location[0] & 0xf) == 0x1; | 
 | 66 | #endif | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 67 | } | 
 | 68 |  | 
 | 69 | int apply_relocate(Elf32_Shdr *sechdrs, | 
 | 70 |     		   const char *strtab, | 
 | 71 | 		   unsigned int symindex, | 
 | 72 | 		   unsigned int relsec, | 
| Chris Zankel | ff6fd46 | 2007-08-14 13:02:06 -0700 | [diff] [blame] | 73 | 		   struct module *mod) | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 74 | { | 
| Chris Zankel | ff6fd46 | 2007-08-14 13:02:06 -0700 | [diff] [blame] | 75 |         printk(KERN_ERR "module %s: REL RELOCATION unsupported\n", | 
 | 76 |                mod->name); | 
 | 77 |         return -ENOEXEC; | 
 | 78 |  | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 79 | } | 
 | 80 |  | 
 | 81 | int apply_relocate_add(Elf32_Shdr *sechdrs, | 
 | 82 | 		       const char *strtab, | 
 | 83 | 		       unsigned int symindex, | 
 | 84 | 		       unsigned int relsec, | 
| Chris Zankel | ff6fd46 | 2007-08-14 13:02:06 -0700 | [diff] [blame] | 85 | 		       struct module *mod) | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 86 | { | 
| Chris Zankel | ff6fd46 | 2007-08-14 13:02:06 -0700 | [diff] [blame] | 87 | 	unsigned int i; | 
 | 88 |         Elf32_Rela *rela = (void *)sechdrs[relsec].sh_addr; | 
 | 89 | 	Elf32_Sym *sym; | 
 | 90 | 	unsigned char *location; | 
 | 91 | 	uint32_t value; | 
 | 92 |  | 
 | 93 | #ifdef DEBUG_RELOCATE | 
 | 94 | 	printk("Applying relocate section %u to %u\n", relsec, | 
 | 95 | 	       sechdrs[relsec].sh_info); | 
 | 96 | #endif | 
 | 97 | 	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) { | 
 | 98 | 		location = (char *)sechdrs[sechdrs[relsec].sh_info].sh_addr | 
 | 99 | 			+ rela[i].r_offset; | 
 | 100 | 		sym = (Elf32_Sym *)sechdrs[symindex].sh_addr | 
 | 101 | 			+ ELF32_R_SYM(rela[i].r_info); | 
 | 102 | 		value = sym->st_value + rela[i].r_addend; | 
 | 103 |  | 
 | 104 | 		switch (ELF32_R_TYPE(rela[i].r_info)) { | 
 | 105 | 		case R_XTENSA_NONE: | 
 | 106 | 		case R_XTENSA_DIFF8: | 
 | 107 | 		case R_XTENSA_DIFF16: | 
 | 108 | 		case R_XTENSA_DIFF32: | 
 | 109 | 		case R_XTENSA_ASM_EXPAND: | 
 | 110 | 			break; | 
 | 111 |  | 
 | 112 | 		case R_XTENSA_32: | 
 | 113 | 		case R_XTENSA_PLT: | 
 | 114 | 			*(uint32_t *)location += value; | 
 | 115 | 			break; | 
 | 116 |  | 
 | 117 | 		case R_XTENSA_SLOT0_OP: | 
 | 118 | 			if (decode_calln_opcode(location)) { | 
 | 119 | 				value -= ((unsigned long)location & -4) + 4; | 
 | 120 | 				if ((value & 3) != 0 || | 
 | 121 | 				    ((value + (1 << 19)) >> 20) != 0) { | 
 | 122 | 					printk("%s: relocation out of range, " | 
 | 123 | 					       "section %d reloc %d " | 
 | 124 | 					       "sym '%s'\n", | 
 | 125 | 					       mod->name, relsec, i, | 
 | 126 | 					       strtab + sym->st_name); | 
 | 127 | 					return -ENOEXEC; | 
 | 128 | 				} | 
 | 129 | 				value = (signed int)value >> 2; | 
 | 130 | #ifdef __XTENSA_EB__ | 
 | 131 | 				location[0] = ((location[0] & ~0x3) | | 
 | 132 | 					    ((value >> 16) & 0x3)); | 
 | 133 | 				location[1] = (value >> 8) & 0xff; | 
 | 134 | 				location[2] = value & 0xff; | 
 | 135 | #endif | 
 | 136 | #ifdef __XTENSA_EL__ | 
 | 137 | 				location[0] = ((location[0] & ~0xc0) | | 
 | 138 | 					    ((value << 6) & 0xc0)); | 
 | 139 | 				location[1] = (value >> 2) & 0xff; | 
 | 140 | 				location[2] = (value >> 10) & 0xff; | 
 | 141 | #endif | 
 | 142 | 			} else if (decode_l32r_opcode(location)) { | 
 | 143 | 				value -= (((unsigned long)location + 3) & -4); | 
 | 144 | 				if ((value & 3) != 0 || | 
 | 145 | 				    (signed int)value >> 18 != -1) { | 
 | 146 | 					printk("%s: relocation out of range, " | 
 | 147 | 					       "section %d reloc %d " | 
 | 148 | 					       "sym '%s'\n", | 
 | 149 | 					       mod->name, relsec, i, | 
 | 150 | 					       strtab + sym->st_name); | 
 | 151 | 					return -ENOEXEC; | 
 | 152 | 				} | 
 | 153 | 				value = (signed int)value >> 2; | 
 | 154 |  | 
 | 155 | #ifdef __XTENSA_EB__ | 
 | 156 | 				location[1] = (value >> 8) & 0xff; | 
 | 157 | 				location[2] = value & 0xff; | 
 | 158 | #endif | 
 | 159 | #ifdef __XTENSA_EL__ | 
 | 160 | 				location[1] = value & 0xff; | 
 | 161 | 				location[2] = (value >> 8) & 0xff; | 
 | 162 | #endif | 
 | 163 | 			} | 
 | 164 | 			/* FIXME: Ignore any other opcodes.  The Xtensa | 
 | 165 | 			   assembler currently assumes that the linker will | 
 | 166 | 			   always do relaxation and so all PC-relative | 
 | 167 | 			   operands need relocations.  (The assembler also | 
 | 168 | 			   writes out the tentative PC-relative values, | 
 | 169 | 			   assuming no link-time relaxation, so it is usually | 
 | 170 | 			   safe to ignore the relocations.)  If the | 
 | 171 | 			   assembler's "--no-link-relax" flag can be made to | 
 | 172 | 			   work, and if all kernel modules can be assembled | 
 | 173 | 			   with that flag, then unexpected relocations could | 
 | 174 | 			   be detected here.  */ | 
 | 175 | 			break; | 
 | 176 |  | 
 | 177 | 		case R_XTENSA_SLOT1_OP: | 
 | 178 | 		case R_XTENSA_SLOT2_OP: | 
 | 179 | 		case R_XTENSA_SLOT3_OP: | 
 | 180 | 		case R_XTENSA_SLOT4_OP: | 
 | 181 | 		case R_XTENSA_SLOT5_OP: | 
 | 182 | 		case R_XTENSA_SLOT6_OP: | 
 | 183 | 		case R_XTENSA_SLOT7_OP: | 
 | 184 | 		case R_XTENSA_SLOT8_OP: | 
 | 185 | 		case R_XTENSA_SLOT9_OP: | 
 | 186 | 		case R_XTENSA_SLOT10_OP: | 
 | 187 | 		case R_XTENSA_SLOT11_OP: | 
 | 188 | 		case R_XTENSA_SLOT12_OP: | 
 | 189 | 		case R_XTENSA_SLOT13_OP: | 
 | 190 | 		case R_XTENSA_SLOT14_OP: | 
 | 191 | 			printk("%s: unexpected FLIX relocation: %u\n", | 
 | 192 | 			       mod->name, | 
 | 193 | 			       ELF32_R_TYPE(rela[i].r_info)); | 
 | 194 | 			return -ENOEXEC; | 
 | 195 |  | 
 | 196 | 		case R_XTENSA_SLOT0_ALT: | 
 | 197 | 		case R_XTENSA_SLOT1_ALT: | 
 | 198 | 		case R_XTENSA_SLOT2_ALT: | 
 | 199 | 		case R_XTENSA_SLOT3_ALT: | 
 | 200 | 		case R_XTENSA_SLOT4_ALT: | 
 | 201 | 		case R_XTENSA_SLOT5_ALT: | 
 | 202 | 		case R_XTENSA_SLOT6_ALT: | 
 | 203 | 		case R_XTENSA_SLOT7_ALT: | 
 | 204 | 		case R_XTENSA_SLOT8_ALT: | 
 | 205 | 		case R_XTENSA_SLOT9_ALT: | 
 | 206 | 		case R_XTENSA_SLOT10_ALT: | 
 | 207 | 		case R_XTENSA_SLOT11_ALT: | 
 | 208 | 		case R_XTENSA_SLOT12_ALT: | 
 | 209 | 		case R_XTENSA_SLOT13_ALT: | 
 | 210 | 		case R_XTENSA_SLOT14_ALT: | 
 | 211 | 			printk("%s: unexpected ALT relocation: %u\n", | 
 | 212 | 			       mod->name, | 
 | 213 | 			       ELF32_R_TYPE(rela[i].r_info)); | 
 | 214 | 			return -ENOEXEC; | 
 | 215 |  | 
 | 216 | 		default: | 
 | 217 | 			printk("%s: unexpected relocation: %u\n", | 
 | 218 | 			       mod->name, | 
 | 219 | 			       ELF32_R_TYPE(rela[i].r_info)); | 
 | 220 | 			return -ENOEXEC; | 
 | 221 | 		} | 
 | 222 | 	} | 
 | 223 | 	return 0; | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 224 | } | 
 | 225 |  | 
 | 226 | int module_finalize(const Elf_Ehdr *hdr, | 
 | 227 |     		    const Elf_Shdr *sechdrs, | 
| Chris Zankel | ff6fd46 | 2007-08-14 13:02:06 -0700 | [diff] [blame] | 228 | 		    struct module *mod) | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 229 | { | 
| Chris Zankel | ff6fd46 | 2007-08-14 13:02:06 -0700 | [diff] [blame] | 230 | 	return 0; | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 231 | } | 
 | 232 |  | 
 | 233 | void module_arch_cleanup(struct module *mod) | 
 | 234 | { | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 235 | } |