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
62 int
kobj_reloc(kobj_t ko,uintptr_t relocbase,const void * data,bool isrela,bool local)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
kobj_idcache_wbinv_all(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
kobj_machdep(kobj_t ko,void * base,size_t size,bool load)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