xref: /openbmc/linux/arch/csky/kernel/module.c (revision 57427df3)
19d056df0SGuo Ren // SPDX-License-Identifier: GPL-2.0
29d056df0SGuo Ren // Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
39d056df0SGuo Ren 
49d056df0SGuo Ren #include <linux/moduleloader.h>
59d056df0SGuo Ren #include <linux/elf.h>
69d056df0SGuo Ren #include <linux/mm.h>
79d056df0SGuo Ren #include <linux/vmalloc.h>
89d056df0SGuo Ren #include <linux/slab.h>
99d056df0SGuo Ren #include <linux/fs.h>
109d056df0SGuo Ren #include <linux/string.h>
119d056df0SGuo Ren #include <linux/kernel.h>
129d056df0SGuo Ren #include <linux/spinlock.h>
139d056df0SGuo Ren 
14f553aa1cSGuo Ren #ifdef CONFIG_CPU_CK810
159d056df0SGuo Ren #define IS_BSR32(hi16, lo16)		(((hi16) & 0xFC00) == 0xE000)
169d056df0SGuo Ren #define IS_JSRI32(hi16, lo16)		((hi16) == 0xEAE0)
179d056df0SGuo Ren 
189d056df0SGuo Ren #define CHANGE_JSRI_TO_LRW(addr) do {					\
199d056df0SGuo Ren 	*(uint16_t *)(addr) = (*(uint16_t *)(addr) & 0xFF9F) | 0x001a;	\
209d056df0SGuo Ren 	*((uint16_t *)(addr) + 1) = *((uint16_t *)(addr) + 1) & 0xFFFF;	\
219d056df0SGuo Ren } while (0)
229d056df0SGuo Ren 
239d056df0SGuo Ren #define SET_JSR32_R26(addr) do {		\
249d056df0SGuo Ren 	*(uint16_t *)(addr) = 0xE8Fa;		\
259d056df0SGuo Ren 	*((uint16_t *)(addr) + 1) = 0x0000;	\
269d056df0SGuo Ren } while (0)
27f553aa1cSGuo Ren 
jsri_2_lrw_jsr(uint32_t * location)28f553aa1cSGuo Ren static void jsri_2_lrw_jsr(uint32_t *location)
29f553aa1cSGuo Ren {
3070c25259SGuo Ren 	uint16_t *location_tmp = (uint16_t *)location;
31f553aa1cSGuo Ren 
32f553aa1cSGuo Ren 	if (IS_BSR32(*location_tmp, *(location_tmp + 1)))
33f553aa1cSGuo Ren 		return;
34f553aa1cSGuo Ren 
35f553aa1cSGuo Ren 	if (IS_JSRI32(*location_tmp, *(location_tmp + 1))) {
36f553aa1cSGuo Ren 		/* jsri 0x...  --> lrw r26, 0x... */
37f553aa1cSGuo Ren 		CHANGE_JSRI_TO_LRW(location);
38f553aa1cSGuo Ren 		/* lsli r0, r0 --> jsr r26 */
39f553aa1cSGuo Ren 		SET_JSR32_R26(location + 1);
40f553aa1cSGuo Ren 	}
41f553aa1cSGuo Ren }
42f553aa1cSGuo Ren #else
jsri_2_lrw_jsr(uint32_t * location)43*57427df3SArnd Bergmann static inline void jsri_2_lrw_jsr(uint32_t *location)
44f553aa1cSGuo Ren {
45f553aa1cSGuo Ren 	return;
46f553aa1cSGuo Ren }
479d056df0SGuo Ren #endif
489d056df0SGuo Ren 
apply_relocate_add(Elf32_Shdr * sechdrs,const char * strtab,unsigned int symindex,unsigned int relsec,struct module * me)499d056df0SGuo Ren int apply_relocate_add(Elf32_Shdr *sechdrs, const char *strtab,
509d056df0SGuo Ren 		unsigned int symindex, unsigned int relsec, struct module *me)
519d056df0SGuo Ren {
529d056df0SGuo Ren 	unsigned int i;
539d056df0SGuo Ren 	Elf32_Rela	*rel = (void *) sechdrs[relsec].sh_addr;
549d056df0SGuo Ren 	Elf32_Sym	*sym;
559d056df0SGuo Ren 	uint32_t	*location;
569d056df0SGuo Ren 	short		*temp;
579d056df0SGuo Ren 
589d056df0SGuo Ren 	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
599d056df0SGuo Ren 		/* This is where to make the change */
609d056df0SGuo Ren 		location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
619d056df0SGuo Ren 							+ rel[i].r_offset;
629d056df0SGuo Ren 		sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
639d056df0SGuo Ren 						+ ELF32_R_SYM(rel[i].r_info);
649d056df0SGuo Ren 
659d056df0SGuo Ren 		switch (ELF32_R_TYPE(rel[i].r_info)) {
669d056df0SGuo Ren 		case R_CSKY_32:
679d056df0SGuo Ren 			/* We add the value into the location given */
689d056df0SGuo Ren 			*location = rel[i].r_addend + sym->st_value;
699d056df0SGuo Ren 			break;
709d056df0SGuo Ren 		case R_CSKY_PC32:
71d17ecf44SJulia Lawall 			/* Add the value, subtract its position */
729d056df0SGuo Ren 			*location = rel[i].r_addend + sym->st_value
739d056df0SGuo Ren 							- (uint32_t)location;
749d056df0SGuo Ren 			break;
759d056df0SGuo Ren 		case R_CSKY_PCRELJSR_IMM11BY2:
769d056df0SGuo Ren 			break;
779d056df0SGuo Ren 		case R_CSKY_PCRELJSR_IMM26BY2:
78f553aa1cSGuo Ren 			jsri_2_lrw_jsr(location);
799d056df0SGuo Ren 			break;
809d056df0SGuo Ren 		case R_CSKY_ADDR_HI16:
819d056df0SGuo Ren 			temp = ((short  *)location) + 1;
829d056df0SGuo Ren 			*temp = (short)
839d056df0SGuo Ren 				((rel[i].r_addend + sym->st_value) >> 16);
849d056df0SGuo Ren 			break;
859d056df0SGuo Ren 		case R_CSKY_ADDR_LO16:
869d056df0SGuo Ren 			temp = ((short  *)location) + 1;
879d056df0SGuo Ren 			*temp = (short)
889d056df0SGuo Ren 				((rel[i].r_addend + sym->st_value) & 0xffff);
899d056df0SGuo Ren 			break;
909d056df0SGuo Ren 		default:
919d056df0SGuo Ren 			pr_err("module %s: Unknown relocation: %u\n",
929d056df0SGuo Ren 				me->name, ELF32_R_TYPE(rel[i].r_info));
939d056df0SGuo Ren 			return -ENOEXEC;
949d056df0SGuo Ren 		}
959d056df0SGuo Ren 	}
969d056df0SGuo Ren 	return 0;
979d056df0SGuo Ren }
98