162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * OpenRISC module.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Linux architectural port borrowing liberally from similar works of
662306a36Sopenharmony_ci * others.  All original copyrights apply as per the original source
762306a36Sopenharmony_ci * declaration.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Modifications for the OpenRISC architecture:
1062306a36Sopenharmony_ci * Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se>
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/moduleloader.h>
1462306a36Sopenharmony_ci#include <linux/elf.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ciint apply_relocate_add(Elf32_Shdr *sechdrs,
1762306a36Sopenharmony_ci		       const char *strtab,
1862306a36Sopenharmony_ci		       unsigned int symindex,
1962306a36Sopenharmony_ci		       unsigned int relsec,
2062306a36Sopenharmony_ci		       struct module *me)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	unsigned int i;
2362306a36Sopenharmony_ci	Elf32_Rela *rel = (void *)sechdrs[relsec].sh_addr;
2462306a36Sopenharmony_ci	Elf32_Sym *sym;
2562306a36Sopenharmony_ci	uint32_t *location;
2662306a36Sopenharmony_ci	uint32_t value;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	pr_debug("Applying relocate section %u to %u\n", relsec,
2962306a36Sopenharmony_ci		 sechdrs[relsec].sh_info);
3062306a36Sopenharmony_ci	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
3162306a36Sopenharmony_ci		/* This is where to make the change */
3262306a36Sopenharmony_ci		location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
3362306a36Sopenharmony_ci			+ rel[i].r_offset;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci		/* This is the symbol it is referring to.  Note that all
3662306a36Sopenharmony_ci		   undefined symbols have been resolved.  */
3762306a36Sopenharmony_ci		sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
3862306a36Sopenharmony_ci			+ ELF32_R_SYM(rel[i].r_info);
3962306a36Sopenharmony_ci		value = sym->st_value + rel[i].r_addend;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci		switch (ELF32_R_TYPE(rel[i].r_info)) {
4262306a36Sopenharmony_ci		case R_OR32_32:
4362306a36Sopenharmony_ci			*location = value;
4462306a36Sopenharmony_ci			break;
4562306a36Sopenharmony_ci		case R_OR32_CONST:
4662306a36Sopenharmony_ci			*((uint16_t *)location + 1) = value;
4762306a36Sopenharmony_ci			break;
4862306a36Sopenharmony_ci		case R_OR32_CONSTH:
4962306a36Sopenharmony_ci			*((uint16_t *)location + 1) = value >> 16;
5062306a36Sopenharmony_ci			break;
5162306a36Sopenharmony_ci		case R_OR32_JUMPTARG:
5262306a36Sopenharmony_ci			value -= (uint32_t)location;
5362306a36Sopenharmony_ci			value >>= 2;
5462306a36Sopenharmony_ci			value &= 0x03ffffff;
5562306a36Sopenharmony_ci			value |= *location & 0xfc000000;
5662306a36Sopenharmony_ci			*location = value;
5762306a36Sopenharmony_ci			break;
5862306a36Sopenharmony_ci		default:
5962306a36Sopenharmony_ci			pr_err("module %s: Unknown relocation: %u\n",
6062306a36Sopenharmony_ci			       me->name, ELF32_R_TYPE(rel[i].r_info));
6162306a36Sopenharmony_ci			break;
6262306a36Sopenharmony_ci		}
6362306a36Sopenharmony_ci	}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	return 0;
6662306a36Sopenharmony_ci}
67