#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: kobj_machdep.c,v 1.16 2023/04/28 07:33:56 skrll Exp $");
#define ELFSIZE ARCH_ELFSIZE
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kobj.h>
#include <sys/exec.h>
#include <sys/exec_elf.h>
#include <uvm/uvm_extern.h>
#include <machine/cpufunc.h>
#include <machine/pmap.h>
static inline unsigned int
RND(unsigned int x)
{
return ((x + 0x1000) & 0xffffe000);
}
static inline unsigned int
R(unsigned int x)
{
return (x & 0x000007ff);
}
static inline unsigned int
L(unsigned int x)
{
return (x & 0xfffff800);
}
static inline unsigned int
LR(unsigned int x, unsigned int constant)
{
return L(x + RND(constant));
}
static inline unsigned int
RR(unsigned int x, unsigned int constant)
{
return R(x + RND(constant)) + (constant - RND(constant));
}
#define RELOC_ALIGNED_P(x) \
(((uintptr_t)(x) & (sizeof(void *) - 1)) == 0)
static inline Elf_Addr
load_ptr(void *where)
{
if (__predict_true(RELOC_ALIGNED_P(where)))
return *(Elf_Addr *)where;
else {
Elf_Addr res;
(void)memcpy(&res, where, sizeof(res));
return res;
}
}
static inline void
store_ptr(void *where, Elf_Addr val)
{
if (__predict_true(RELOC_ALIGNED_P(where)))
*(Elf_Addr *)where = val;
else
(void)memcpy(where, &val, sizeof(val));
}
int
kobj_reloc(kobj_t ko, uintptr_t relocbase, const void *data,
bool isrela, bool local)
{
Elf_Addr *where;
Elf_Addr addr, value;
Elf_Word rtype, symidx;
const Elf_Rela *rela;
extern int __data_start;
unsigned int GP = (int) &__data_start;
if (!isrela) {
printf("kobj_reloc: only support RELA relocations\n");
return -1;
}
rela = (const Elf_Rela *)data;
where = (Elf_Addr *) (relocbase + rela->r_offset);
value = rela->r_addend;
rtype = ELF_R_TYPE(rela->r_info);
symidx = ELF_R_SYM(rela->r_info);
const Elf_Sym *sym = kobj_symbol(ko, symidx);
if (!local && ELF_ST_BIND(sym->st_info) == STB_LOCAL) {
return 0;
}
switch (rtype) {
case R_TYPE(NONE):
break;
case R_TYPE(DIR32):
kobj_sym_lookup(ko, symidx, &addr);
value += addr;
break;
case R_TYPE(DIR21L):
kobj_sym_lookup(ko, symidx, &addr);
value = LR(addr, value);
break;
case R_TYPE(DIR17R):
case R_TYPE(DIR14R):
kobj_sym_lookup(ko, symidx, &addr);
value = RR(addr, value);
break;
case R_TYPE(PCREL32):
case R_TYPE(PCREL17F):
kobj_sym_lookup(ko, symidx, &addr);
value += addr - (Elf_Word)where - 8;
break;
case R_TYPE(DPREL21L):
kobj_sym_lookup(ko, symidx, &addr);
value = LR(addr - GP, value);
break;
case R_TYPE(DPREL14R):
kobj_sym_lookup(ko, symidx, &addr);
value = RR(addr - GP, value);
break;
case R_TYPE(PLABEL32):
kobj_sym_lookup(ko, symidx, &addr);
value = addr;
break;
case R_TYPE(SEGREL32):
kobj_sym_lookup(ko, symidx, &addr);
value += addr;
break;
default:
printf("%s: unexpected relocation type %d\n", __func__, rtype);
return -1;
}
switch (rtype) {
case R_TYPE(DIR32):
case R_TYPE(PCREL32):
case R_TYPE(PLABEL32):
case R_TYPE(SEGREL32):
store_ptr(where, value);
break;
case R_TYPE(DIR14R):
case R_TYPE(DPREL14R):
*where |=
(((value >> 0) & 0x1fff) << 1) |
(((value >> 13) & 0x1) << 0);
break;
case R_TYPE(DIR17R):
case R_TYPE(PCREL17F):
value >>= 2;
*where |=
(((value & 0x10000) >> 16) << 0) |
(((value & 0x0f800) >> 11) << 16) |
(((value & 0x00400) >> 10) << 2) |
(((value & 0x003ff) << 1) << 2);
break;
case R_TYPE(DIR21L):
case R_TYPE(DPREL21L):
*where |=
(((value >> 31) & 0x001) << 0) |
(((value >> 20) & 0x7ff) << 1) |
(((value >> 18) & 0x003) << 14) |
(((value >> 13) & 0x01f) << 16) |
(((value >> 11) & 0x003) << 12);
break;
}
return 0;
}
int
kobj_machdep(kobj_t ko, void *base, size_t size, bool load)
{
if (load) {
fdcache(HPPA_SID_KERNEL, (vaddr_t)base, size);
ficache(HPPA_SID_KERNEL, (vaddr_t)base, size);
}
return 0;
}