Home | History | Annotate | Line # | Download | only in mips
      1 /*	$NetBSD: kobj_machdep.c,v 1.3 2026/07/30 22:39:02 andvar 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: kobj_machdep.c,v 1.3 2026/07/30 22:39:02 andvar Exp $");
     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 
     62 int
     63 kobj_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 
    236 static void
    237 kobj_idcache_wbinv_all(void)
    238 {
    239 
    240 	mips_icache_sync_all();
    241 	mips_dcache_wbinv_all();	/* XXX needed? */
    242 }
    243 
    244 int
    245 kobj_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