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