162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Kernel module loader for Hexagon
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <asm/module.h>
962306a36Sopenharmony_ci#include <linux/elf.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/moduleloader.h>
1262306a36Sopenharmony_ci#include <linux/vmalloc.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#if 0
1562306a36Sopenharmony_ci#define DEBUGP printk
1662306a36Sopenharmony_ci#else
1762306a36Sopenharmony_ci#define DEBUGP(fmt , ...)
1862306a36Sopenharmony_ci#endif
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/*
2162306a36Sopenharmony_ci * module_frob_arch_sections - tweak got/plt sections.
2262306a36Sopenharmony_ci * @hdr - pointer to elf header
2362306a36Sopenharmony_ci * @sechdrs - pointer to elf load section headers
2462306a36Sopenharmony_ci * @secstrings - symbol names
2562306a36Sopenharmony_ci * @mod - pointer to module
2662306a36Sopenharmony_ci */
2762306a36Sopenharmony_ciint module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
2862306a36Sopenharmony_ci				char *secstrings,
2962306a36Sopenharmony_ci				struct module *mod)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	unsigned int i;
3262306a36Sopenharmony_ci	int found = 0;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	/* Look for .plt and/or .got.plt and/or .init.plt sections */
3562306a36Sopenharmony_ci	for (i = 0; i < hdr->e_shnum; i++) {
3662306a36Sopenharmony_ci		DEBUGP("Section %d is %s\n", i,
3762306a36Sopenharmony_ci		       secstrings + sechdrs[i].sh_name);
3862306a36Sopenharmony_ci		if (strcmp(secstrings + sechdrs[i].sh_name, ".plt") == 0)
3962306a36Sopenharmony_ci			found = i+1;
4062306a36Sopenharmony_ci		if (strcmp(secstrings + sechdrs[i].sh_name, ".got.plt") == 0)
4162306a36Sopenharmony_ci			found = i+1;
4262306a36Sopenharmony_ci		if (strcmp(secstrings + sechdrs[i].sh_name, ".rela.plt") == 0)
4362306a36Sopenharmony_ci			found = i+1;
4462306a36Sopenharmony_ci	}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	/* At this time, we don't support modules comiled with -shared */
4762306a36Sopenharmony_ci	if (found) {
4862306a36Sopenharmony_ci		printk(KERN_WARNING
4962306a36Sopenharmony_ci			"Module '%s' contains unexpected .plt/.got sections.\n",
5062306a36Sopenharmony_ci			mod->name);
5162306a36Sopenharmony_ci		/*  return -ENOEXEC;  */
5262306a36Sopenharmony_ci	}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	return 0;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci/*
5862306a36Sopenharmony_ci * apply_relocate_add - perform rela relocations.
5962306a36Sopenharmony_ci * @sechdrs - pointer to section headers
6062306a36Sopenharmony_ci * @strtab - some sort of start address?
6162306a36Sopenharmony_ci * @symindex - symbol index offset or something?
6262306a36Sopenharmony_ci * @relsec - address to relocate to?
6362306a36Sopenharmony_ci * @module - pointer to module
6462306a36Sopenharmony_ci *
6562306a36Sopenharmony_ci * Perform rela relocations.
6662306a36Sopenharmony_ci */
6762306a36Sopenharmony_ciint apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab,
6862306a36Sopenharmony_ci			unsigned int symindex, unsigned int relsec,
6962306a36Sopenharmony_ci			struct module *module)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	unsigned int i;
7262306a36Sopenharmony_ci	Elf32_Sym *sym;
7362306a36Sopenharmony_ci	uint32_t *location;
7462306a36Sopenharmony_ci	uint32_t value;
7562306a36Sopenharmony_ci	unsigned int nrelocs = sechdrs[relsec].sh_size / sizeof(Elf32_Rela);
7662306a36Sopenharmony_ci	Elf32_Rela *rela = (void *)sechdrs[relsec].sh_addr;
7762306a36Sopenharmony_ci	Elf32_Word sym_info = sechdrs[relsec].sh_info;
7862306a36Sopenharmony_ci	Elf32_Sym *sym_base = (Elf32_Sym *) sechdrs[symindex].sh_addr;
7962306a36Sopenharmony_ci	void *loc_base = (void *) sechdrs[sym_info].sh_addr;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	DEBUGP("Applying relocations in section %u to section %u base=%p\n",
8262306a36Sopenharmony_ci	       relsec, sym_info, loc_base);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	for (i = 0; i < nrelocs; i++) {
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci		/* Symbol to relocate */
8762306a36Sopenharmony_ci		sym = sym_base + ELF32_R_SYM(rela[i].r_info);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci		/* Where to make the change */
9062306a36Sopenharmony_ci		location = loc_base + rela[i].r_offset;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci		/* `Everything is relative'. */
9362306a36Sopenharmony_ci		value = sym->st_value + rela[i].r_addend;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci		DEBUGP("%d: value=%08x loc=%p reloc=%d symbol=%s\n",
9662306a36Sopenharmony_ci		       i, value, location, ELF32_R_TYPE(rela[i].r_info),
9762306a36Sopenharmony_ci		       sym->st_name ?
9862306a36Sopenharmony_ci		       &strtab[sym->st_name] : "(anonymous)");
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci		switch (ELF32_R_TYPE(rela[i].r_info)) {
10162306a36Sopenharmony_ci		case R_HEXAGON_B22_PCREL: {
10262306a36Sopenharmony_ci			int dist = (int)(value - (uint32_t)location);
10362306a36Sopenharmony_ci			if ((dist < -0x00800000) ||
10462306a36Sopenharmony_ci			    (dist >= 0x00800000)) {
10562306a36Sopenharmony_ci				printk(KERN_ERR
10662306a36Sopenharmony_ci				       "%s: %s: %08x=%08x-%08x %s\n",
10762306a36Sopenharmony_ci				       module->name,
10862306a36Sopenharmony_ci				       "R_HEXAGON_B22_PCREL reloc out of range",
10962306a36Sopenharmony_ci				       dist, value, (uint32_t)location,
11062306a36Sopenharmony_ci				       sym->st_name ?
11162306a36Sopenharmony_ci				       &strtab[sym->st_name] : "(anonymous)");
11262306a36Sopenharmony_ci				return -ENOEXEC;
11362306a36Sopenharmony_ci			}
11462306a36Sopenharmony_ci			DEBUGP("B22_PCREL contents: %08X.\n", *location);
11562306a36Sopenharmony_ci			*location &= ~0x01ff3fff;
11662306a36Sopenharmony_ci			*location |= 0x00003fff & dist;
11762306a36Sopenharmony_ci			*location |= 0x01ff0000 & (dist<<2);
11862306a36Sopenharmony_ci			DEBUGP("Contents after reloc: %08x\n", *location);
11962306a36Sopenharmony_ci			break;
12062306a36Sopenharmony_ci		}
12162306a36Sopenharmony_ci		case R_HEXAGON_HI16:
12262306a36Sopenharmony_ci			value = (value>>16) & 0xffff;
12362306a36Sopenharmony_ci			fallthrough;
12462306a36Sopenharmony_ci		case R_HEXAGON_LO16:
12562306a36Sopenharmony_ci			*location &= ~0x00c03fff;
12662306a36Sopenharmony_ci			*location |= value & 0x3fff;
12762306a36Sopenharmony_ci			*location |= (value & 0xc000) << 8;
12862306a36Sopenharmony_ci			break;
12962306a36Sopenharmony_ci		case R_HEXAGON_32:
13062306a36Sopenharmony_ci			*location = value;
13162306a36Sopenharmony_ci			break;
13262306a36Sopenharmony_ci		case R_HEXAGON_32_PCREL:
13362306a36Sopenharmony_ci			*location = value - (uint32_t)location;
13462306a36Sopenharmony_ci			break;
13562306a36Sopenharmony_ci		case R_HEXAGON_PLT_B22_PCREL:
13662306a36Sopenharmony_ci		case R_HEXAGON_GOTOFF_LO16:
13762306a36Sopenharmony_ci		case R_HEXAGON_GOTOFF_HI16:
13862306a36Sopenharmony_ci			printk(KERN_ERR "%s: GOT/PLT relocations unsupported\n",
13962306a36Sopenharmony_ci			       module->name);
14062306a36Sopenharmony_ci			return -ENOEXEC;
14162306a36Sopenharmony_ci		default:
14262306a36Sopenharmony_ci			printk(KERN_ERR "%s: unknown relocation: %u\n",
14362306a36Sopenharmony_ci			       module->name,
14462306a36Sopenharmony_ci			       ELF32_R_TYPE(rela[i].r_info));
14562306a36Sopenharmony_ci			return -ENOEXEC;
14662306a36Sopenharmony_ci		}
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci	return 0;
14962306a36Sopenharmony_ci}
150