| /* | 
 |  * arch/v850/kernel/module.c -- Architecture-specific module functions | 
 |  * | 
 |  *  Copyright (C) 2002,03  NEC Electronics Corporation | 
 |  *  Copyright (C) 2002,03  Miles Bader <miles@gnu.org> | 
 |  *  Copyright (C) 2001,03  Rusty Russell | 
 |  * | 
 |  * This file is subject to the terms and conditions of the GNU General | 
 |  * Public License.  See the file COPYING in the main directory of this | 
 |  * archive for more details. | 
 |  * | 
 |  * Written by Miles Bader <miles@gnu.org> | 
 |  * | 
 |  * Derived in part from arch/ppc/kernel/module.c | 
 |  */ | 
 |  | 
 | #include <linux/kernel.h> | 
 | #include <linux/vmalloc.h> | 
 | #include <linux/moduleloader.h> | 
 | #include <linux/elf.h> | 
 |  | 
 | #if 0 | 
 | #define DEBUGP printk | 
 | #else | 
 | #define DEBUGP(fmt , ...) | 
 | #endif | 
 |  | 
 | void *module_alloc (unsigned long size) | 
 | { | 
 | 	return size == 0 ? 0 : vmalloc (size); | 
 | } | 
 |  | 
 | void module_free (struct module *mod, void *module_region) | 
 | { | 
 | 	vfree (module_region); | 
 | 	/* FIXME: If module_region == mod->init_region, trim exception | 
 |            table entries. */ | 
 | } | 
 |  | 
 | int module_finalize (const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, | 
 | 		     struct module *mod) | 
 | { | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* Count how many different relocations (different symbol, different | 
 |    addend) */ | 
 | static unsigned int count_relocs(const Elf32_Rela *rela, unsigned int num) | 
 | { | 
 | 	unsigned int i, j, ret = 0; | 
 |  | 
 | 	/* Sure, this is order(n^2), but it's usually short, and not | 
 |            time critical */ | 
 | 	for (i = 0; i < num; i++) { | 
 | 		for (j = 0; j < i; j++) { | 
 | 			/* If this addend appeared before, it's | 
 |                            already been counted */ | 
 | 			if (ELF32_R_SYM(rela[i].r_info) | 
 | 			    == ELF32_R_SYM(rela[j].r_info) | 
 | 			    && rela[i].r_addend == rela[j].r_addend) | 
 | 				break; | 
 | 		} | 
 | 		if (j == i) ret++; | 
 | 	} | 
 | 	return ret; | 
 | } | 
 |  | 
 | /* Get the potential trampolines size required of the init and | 
 |    non-init sections */ | 
 | static unsigned long get_plt_size(const Elf32_Ehdr *hdr, | 
 | 				  const Elf32_Shdr *sechdrs, | 
 | 				  const char *secstrings, | 
 | 				  int is_init) | 
 | { | 
 | 	unsigned long ret = 0; | 
 | 	unsigned i; | 
 |  | 
 | 	/* Everything marked ALLOC (this includes the exported | 
 |            symbols) */ | 
 | 	for (i = 1; i < hdr->e_shnum; i++) { | 
 | 		/* If it's called *.init*, and we're not init, we're | 
 |                    not interested */ | 
 | 		if ((strstr(secstrings + sechdrs[i].sh_name, ".init") != 0) | 
 | 		    != is_init) | 
 | 			continue; | 
 |  | 
 | 		if (sechdrs[i].sh_type == SHT_RELA) { | 
 | 			DEBUGP("Found relocations in section %u\n", i); | 
 | 			DEBUGP("Ptr: %p.  Number: %u\n", | 
 | 			       (void *)hdr + sechdrs[i].sh_offset, | 
 | 			       sechdrs[i].sh_size / sizeof(Elf32_Rela)); | 
 | 			ret += count_relocs((void *)hdr | 
 | 					     + sechdrs[i].sh_offset, | 
 | 					     sechdrs[i].sh_size | 
 | 					     / sizeof(Elf32_Rela)) | 
 | 				* sizeof(struct v850_plt_entry); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | int module_frob_arch_sections(Elf32_Ehdr *hdr, | 
 | 			      Elf32_Shdr *sechdrs, | 
 | 			      char *secstrings, | 
 | 			      struct module *me) | 
 | { | 
 | 	unsigned int i; | 
 |  | 
 | 	/* Find .plt and .pltinit sections */ | 
 | 	for (i = 0; i < hdr->e_shnum; i++) { | 
 | 		if (strcmp(secstrings + sechdrs[i].sh_name, ".init.plt") == 0) | 
 | 			me->arch.init_plt_section = i; | 
 | 		else if (strcmp(secstrings + sechdrs[i].sh_name, ".plt") == 0) | 
 | 			me->arch.core_plt_section = i; | 
 | 	} | 
 | 	if (!me->arch.core_plt_section || !me->arch.init_plt_section) { | 
 | 		printk("Module doesn't contain .plt or .plt.init sections.\n"); | 
 | 		return -ENOEXEC; | 
 | 	} | 
 |  | 
 | 	/* Override their sizes */ | 
 | 	sechdrs[me->arch.core_plt_section].sh_size | 
 | 		= get_plt_size(hdr, sechdrs, secstrings, 0); | 
 | 	sechdrs[me->arch.init_plt_section].sh_size | 
 | 		= get_plt_size(hdr, sechdrs, secstrings, 1); | 
 | 	return 0; | 
 | } | 
 |  | 
 | int apply_relocate (Elf32_Shdr *sechdrs, const char *strtab, | 
 | 		    unsigned int symindex, unsigned int relsec, | 
 | 		    struct module *mod) | 
 | { | 
 | 	printk ("Barf\n"); | 
 | 	return -ENOEXEC; | 
 | } | 
 |  | 
 | /* Set up a trampoline in the PLT to bounce us to the distant function */ | 
 | static uint32_t do_plt_call (void *location, Elf32_Addr val, | 
 | 			     Elf32_Shdr *sechdrs, struct module *mod) | 
 | { | 
 | 	struct v850_plt_entry *entry; | 
 | 	/* Instructions used to do the indirect jump.  */ | 
 | 	uint32_t tramp[2]; | 
 |  | 
 | 	/* We have to trash a register, so we assume that any control | 
 | 	   transfer more than 21-bits away must be a function call | 
 | 	   (so we can use a call-clobbered register).  */ | 
 | 	tramp[0] = 0x0621 + ((val & 0xffff) << 16);   /* mov sym, r1 ... */ | 
 | 	tramp[1] = ((val >> 16) & 0xffff) + 0x610000; /* ...; jmp r1 */ | 
 |  | 
 | 	/* Init, or core PLT? */ | 
 | 	if (location >= mod->module_core | 
 | 	    && location < mod->module_core + mod->core_size) | 
 | 		entry = (void *)sechdrs[mod->arch.core_plt_section].sh_addr; | 
 | 	else | 
 | 		entry = (void *)sechdrs[mod->arch.init_plt_section].sh_addr; | 
 |  | 
 | 	/* Find this entry, or if that fails, the next avail. entry */ | 
 | 	while (entry->tramp[0]) | 
 | 		if (entry->tramp[0] == tramp[0] && entry->tramp[1] == tramp[1]) | 
 | 			return (uint32_t)entry; | 
 | 		else | 
 | 			entry++; | 
 |  | 
 | 	entry->tramp[0] = tramp[0]; | 
 | 	entry->tramp[1] = tramp[1]; | 
 |  | 
 | 	return (uint32_t)entry; | 
 | } | 
 |  | 
 | int apply_relocate_add (Elf32_Shdr *sechdrs, const char *strtab, | 
 | 			unsigned int symindex, unsigned int relsec, | 
 | 			struct module *mod) | 
 | { | 
 | 	unsigned int i; | 
 | 	Elf32_Rela *rela = (void *)sechdrs[relsec].sh_addr; | 
 |  | 
 | 	DEBUGP ("Applying relocate section %u to %u\n", relsec, | 
 | 		sechdrs[relsec].sh_info); | 
 |  | 
 | 	for (i = 0; i < sechdrs[relsec].sh_size / sizeof (*rela); i++) { | 
 | 		/* This is where to make the change */ | 
 | 		uint32_t *loc | 
 | 			= ((void *)sechdrs[sechdrs[relsec].sh_info].sh_addr | 
 | 			   + rela[i].r_offset); | 
 | 		/* This is the symbol it is referring to.  Note that all | 
 | 		   undefined symbols have been resolved.  */ | 
 | 		Elf32_Sym *sym | 
 | 			= ((Elf32_Sym *)sechdrs[symindex].sh_addr | 
 | 			   + ELF32_R_SYM (rela[i].r_info)); | 
 | 		uint32_t val = sym->st_value + rela[i].r_addend; | 
 |  | 
 | 		switch (ELF32_R_TYPE (rela[i].r_info)) { | 
 | 		case R_V850_32: | 
 | 			/* We write two shorts instead of a long because even | 
 | 			   32-bit insns only need half-word alignment, but | 
 | 			   32-bit data writes need to be long-word aligned.  */ | 
 | 			val += ((uint16_t *)loc)[0]; | 
 | 			val += ((uint16_t *)loc)[1] << 16; | 
 | 			((uint16_t *)loc)[0] = val & 0xffff; | 
 | 			((uint16_t *)loc)[1] = (val >> 16) & 0xffff; | 
 | 			break; | 
 |  | 
 | 		case R_V850_22_PCREL: | 
 | 			/* Maybe jump indirectly via a PLT table entry.  */ | 
 | 			if ((int32_t)(val - (uint32_t)loc) > 0x1fffff | 
 | 			    || (int32_t)(val - (uint32_t)loc) < -0x200000) | 
 | 				val = do_plt_call (loc, val, sechdrs, mod); | 
 |  | 
 | 			val -= (uint32_t)loc; | 
 |  | 
 | 			/* We write two shorts instead of a long because | 
 | 			   even 32-bit insns only need half-word alignment, | 
 | 			   but 32-bit data writes need to be long-word | 
 | 			   aligned.  */ | 
 | 			((uint16_t *)loc)[0] = | 
 | 				(*(uint16_t *)loc & 0xffc0) /* opcode + reg */ | 
 | 				| ((val >> 16) & 0xffc03f); /* offs high */ | 
 | 			((uint16_t *)loc)[1] = | 
 | 				(val & 0xffff);		    /* offs low */ | 
 | 			break; | 
 |  | 
 | 		default: | 
 | 			printk (KERN_ERR "module %s: Unknown reloc: %u\n", | 
 | 				mod->name, ELF32_R_TYPE (rela[i].r_info)); | 
 | 			return -ENOEXEC; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | void | 
 | module_arch_cleanup(struct module *mod) | 
 | { | 
 | } |