1/* $NetBSD: kobj_machdep.c,v 1.2 2023/04/28 07:33:56 skrll Exp $ */ 2 3/*- 4 * Copyright (c) 2021 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Simon Burge. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32#include <sys/cdefs.h> 33 34__RCSID("$NetBSD"); 35 36#include <sys/param.h> 37#include <sys/systm.h> 38#include <sys/exec_elf.h> 39#include <sys/kernel.h> 40#include <sys/kobj.h> 41#include <sys/xcall.h> 42 43#include <mips/cache.h> 44 45#ifndef __mips_n64 46#error update for non-N64 abis /* XXX */ 47#endif 48 49#define RELOC_LO16(x) ((x) & 0xffff) 50#define RELOC_HI16(x) ((((int64_t)(x) + 0x8000LL) >> 16) & 0xffff) 51#define RELOC_HIGHER(x) ((((int64_t)(x) + 0x80008000LL) >> 32) & 0xffff) 52#define RELOC_HIGHEST(x) ((((int64_t)(x) + 0x800080008000LL) >> 48) & 0xffff) 53 54#undef MIPS_MODULE_DEBUG 55#ifdef MIPS_MODULE_DEBUG 56#define DPRINTF(fmt, args...) \ 57 do { printf(fmt, ## args); } while (0) 58#else 59#define DPRINTF(fmt, args...) __nothing 60#endif 61 62int 63kobj_reloc(kobj_t ko, uintptr_t relocbase, const void *data, 64 bool isrela, bool local) 65{ 66 Elf_Addr addr, addend, *where; 67 Elf32_Addr *where32; 68 Elf_Word rtype, symidx; 69 const Elf_Rela *rela; 70 uint32_t *insn; 71 int error; 72 73 DPRINTF("%s(kobj %p, reloc %#lx,\n data %p, rela %d, local %d)\n", 74 __func__, ko, relocbase, data, isrela, local); 75 76 if (!isrela) { 77 printf("%s: REL relocations not supported", __func__); 78 return -1; 79 } 80 81 rela = (const Elf_Rela *)data; 82 where = (Elf_Addr *)(relocbase + rela->r_offset); 83 addend = rela->r_addend; 84 rtype = ELF_R_TYPE(rela->r_info); 85 symidx = ELF_R_SYM(rela->r_info); 86 87 const Elf_Sym *sym = kobj_symbol(ko, symidx); 88 89 if (!local && ELF_ST_BIND(sym->st_info) == STB_LOCAL) { 90 return 0; 91 } 92 93 /* pointer to 32bit value and instruction */ 94 insn = (void *)where; 95 96 /* check alignment */ 97 KASSERT(((intptr_t)where & (sizeof(int32_t) - 1)) == 0); 98 99 switch (rtype) { 100 case R_TYPE(NONE): /* none */ 101 DPRINTF(" reloc R_MIPS_NONE\n"); 102 break; 103 /* R_TYPE(16) */ 104 case R_TYPE(32): /* S + A */ 105 DPRINTF(" reloc R_MIPS_32\n"); 106 error = kobj_sym_lookup(ko, symidx, &addr); 107 if (error) 108 return -1; 109 addr += addend; 110 where32 = (void *)where; 111 DPRINTF(" orig = 0x%08x\n", *where32); 112 *where32 = addr; 113 DPRINTF(" new = 0x%08x\n", *where32); 114 break; 115 /* R_TYPE(REL32) */ 116 case R_TYPE(26): /* (((A << 2) | (P & 0xf0000000)) + S) >> 2 */ 117 /* XXXXXX untested */ 118 DPRINTF(" reloc R_MIPS_26 (untested)\n"); 119 error = kobj_sym_lookup(ko, symidx, &addr); 120 if (error) 121 return -1; 122 123 addend &= __BITS(25, 0); /* mask off lower 26 bits */ 124 addend <<= 2; 125 126 addr += ((intptr_t)where & 0xf0000000) | addend; 127 addr >>= 2; 128 129 KASSERT((*insn & 0x3ffffff) == 0); 130 DPRINTF(" orig insn = 0x%08x\n", *insn); 131 *insn |= addr; 132 DPRINTF(" new insn = 0x%08x\n", *insn); 133 134 break; 135 case R_TYPE(HI16): /* %high(AHL + S) = (x - (short)x) >> 16 */ 136 DPRINTF(" reloc R_MIPS_HI16\n"); 137 error = kobj_sym_lookup(ko, symidx, &addr); 138 if (error) 139 return -1; 140 141 addr += addend; 142 KASSERT((*insn & 0xffff) == 0); 143 DPRINTF(" orig insn = 0x%08x\n", *insn); 144 DPRINTF(" HI16(%#lx) = 0x%04llx\n", addr, RELOC_HI16(addr)); 145 *insn |= RELOC_HI16(addr); 146 DPRINTF(" new insn = 0x%08x\n", *insn); 147 break; 148 case R_TYPE(LO16): /* AHL + S */ 149 DPRINTF(" reloc R_MIPS_LO16\n"); 150 error = kobj_sym_lookup(ko, symidx, &addr); 151 if (error) 152 return -1; 153 154 addr += addend; 155 KASSERT((*insn & 0xffff) == 0); 156 DPRINTF(" orig insn = 0x%08x\n", *insn); 157 DPRINTF(" LO16(%#lx) = 0x%04lx\n", addr, RELOC_LO16(addr)); 158 *insn |= RELOC_LO16(addr); 159 DPRINTF(" new insn = 0x%08x\n", *insn); 160 break; 161 /* R_TYPE(GPREL16) */ 162 /* R_TYPE(LITERAL) */ 163 /* R_TYPE(GOT16) */ 164 /* R_TYPE(PC16) */ 165 /* R_TYPE(CALL16) */ 166#ifdef _LP64 167 /* R_TYPE(GPREL32) */ 168 /* R_TYPE(SHIFT5) */ 169 /* R_TYPE(SHIFT6) */ 170 case R_TYPE(64): /* S + A */ 171 DPRINTF(" reloc R_MIPS_64\n"); 172 error = kobj_sym_lookup(ko, symidx, &addr); 173 if (error) 174 return -1; 175 176 addr += addend; 177 DPRINTF(" orig = 0x%016lx\n", *where); 178 *where = addr; 179 DPRINTF(" new = 0x%016lx\n", *where); 180 break; 181 /* R_TYPE(GOT_DISP) */ 182 /* R_TYPE(GOT_PAGE) */ 183 /* R_TYPE(GOT_OFST) */ 184 /* R_TYPE(GOT_HI16) */ 185 /* R_TYPE(GOT_LO16) */ 186 /* R_TYPE(SUB) */ 187 /* R_TYPE(INSERT_A) */ 188 /* R_TYPE(INSERT_B) */ 189 /* R_TYPE(DELETE) */ 190 case R_TYPE(HIGHER): /* %higher(A + S) = 191 (((long long)x + 0x80008000LL) >> 32) & 0xffff */ 192 DPRINTF(" reloc R_MIPS_HIGHER\n"); 193 error = kobj_sym_lookup(ko, symidx, &addr); 194 if (error) 195 return -1; 196 197 addr += addend; 198 KASSERT((*insn & 0xffff) == 0); 199 DPRINTF(" orig insn = 0x%08x\n", *insn); 200 DPRINTF(" HIGHER(%#lx) = 0x%04llx\n", addr, RELOC_HIGHER(addr)); 201 *insn |= RELOC_HIGHER(addr); 202 DPRINTF(" new insn = 0x%08x\n", *insn); 203 break; 204 case R_TYPE(HIGHEST): /* %highest(A + S) = 205 (((long long)x + 0x800080008000LL) >> 48) & 0xffff */ 206 DPRINTF(" reloc R_MIPS_HIGHEST\n"); 207 error = kobj_sym_lookup(ko, symidx, &addr); 208 if (error) 209 return -1; 210 211 addr += addend; 212 KASSERT((*insn & 0xffff) == 0); 213 DPRINTF(" orig insn = 0x%08x\n", *insn); 214 DPRINTF(" HIGHEST(%#lx) = 0x%04llx\n", addr, RELOC_HIGHEST(addr)); 215 *insn |= RELOC_HIGHEST(addr); 216 DPRINTF(" new insn = 0x%08x\n", *insn); 217 break; 218 /* R_TYPE(CALL_HI16) */ 219 /* R_TYPE(CALL_LO16) */ 220 /* R_TYPE(SCN_DISP) */ 221 /* R_TYPE(REL16) */ 222 /* R_TYPE(ADD_IMMEDIATE) */ 223 /* R_TYPE(PJUMP) */ 224 /* R_TYPE(RELGOT) */ 225 /* R_TYPE(JALR) */ 226#endif /* _LP64 */ 227 default: 228 printf("%s: unknown reloc type %d @ %p\n", 229 __func__, rtype, where); 230 return -1; 231 } 232 233 return 0; 234} 235 236static void 237kobj_idcache_wbinv_all(void) 238{ 239 240 mips_icache_sync_all(); 241 mips_dcache_wbinv_all(); /* XXX needed? */ 242} 243 244int 245kobj_machdep(kobj_t ko, void *base, size_t size, bool load) 246{ 247 248 uint64_t where; 249 250 DPRINTF("%s(kobj %p, base %p,\n size %zu, load %d)\n", 251 __func__, ko, base, size, load); 252 if (load) { 253 if (cold) { 254 kobj_idcache_wbinv_all(); 255 } else { 256 where = xc_broadcast(0, 257 (xcfunc_t)kobj_idcache_wbinv_all, NULL, NULL); 258 xc_wait(where); 259 } 260 } 261 262 return 0; 263} 264