1*3ab3953dSmacallan /* $NetBSD: fixup.c,v 1.13 2022/01/01 01:15:11 macallan Exp $ */
2b8ea2c8cSmatt /*-
3b8ea2c8cSmatt * Copyright (c) 2010, 2011 The NetBSD Foundation, Inc.
4b8ea2c8cSmatt * All rights reserved.
5b8ea2c8cSmatt *
6b8ea2c8cSmatt * This code is derived from software contributed to The NetBSD Foundation
7b8ea2c8cSmatt * by Raytheon BBN Technologies Corp and Defense Advanced Research Projects
8b8ea2c8cSmatt * Agency and which was developed by Matt Thomas of 3am Software Foundry.
9b8ea2c8cSmatt *
10b8ea2c8cSmatt * This material is based upon work supported by the Defense Advanced Research
11b8ea2c8cSmatt * Projects Agency and Space and Naval Warfare Systems Center, Pacific, under
12b8ea2c8cSmatt * Contract No. N66001-09-C-2073.
13b8ea2c8cSmatt * Approved for Public Release, Distribution Unlimited
14b8ea2c8cSmatt *
15b8ea2c8cSmatt * Redistribution and use in source and binary forms, with or without
16b8ea2c8cSmatt * modification, are permitted provided that the following conditions
17b8ea2c8cSmatt * are met:
18b8ea2c8cSmatt * 1. Redistributions of source code must retain the above copyright
19b8ea2c8cSmatt * notice, this list of conditions and the following disclaimer.
20b8ea2c8cSmatt * 2. Redistributions in binary form must reproduce the above copyright
21b8ea2c8cSmatt * notice, this list of conditions and the following disclaimer in the
22b8ea2c8cSmatt * documentation and/or other materials provided with the distribution.
23b8ea2c8cSmatt *
24b8ea2c8cSmatt * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
25b8ea2c8cSmatt * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26b8ea2c8cSmatt * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27b8ea2c8cSmatt * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
28b8ea2c8cSmatt * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29b8ea2c8cSmatt * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30b8ea2c8cSmatt * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31b8ea2c8cSmatt * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32b8ea2c8cSmatt * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33b8ea2c8cSmatt * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34b8ea2c8cSmatt * POSSIBILITY OF SUCH DAMAGE.
35b8ea2c8cSmatt */
36b8ea2c8cSmatt
37b8ea2c8cSmatt #include <sys/cdefs.h>
38*3ab3953dSmacallan __KERNEL_RCSID(0, "$NetBSD: fixup.c,v 1.13 2022/01/01 01:15:11 macallan Exp $");
3936c20c46Srin
4036c20c46Srin #ifdef _KERNEL_OPT
4136c20c46Srin #include "opt_ppcarch.h"
4236c20c46Srin #endif
43b8ea2c8cSmatt
44b8ea2c8cSmatt #include <sys/param.h>
45b8ea2c8cSmatt #include <sys/types.h>
46b8ea2c8cSmatt
47b8ea2c8cSmatt #include <powerpc/instr.h>
48b8ea2c8cSmatt #include <powerpc/spr.h>
49b01d00d9Smacallan #include <powerpc/include/cpu.h>
50b01d00d9Smacallan #include <powerpc/include/oea/spr.h>
51b8ea2c8cSmatt
52b8ea2c8cSmatt static inline void
fixup_jump(uint32_t * insnp,const struct powerpc_jump_fixup_info * jfi)53b8ea2c8cSmatt fixup_jump(uint32_t *insnp, const struct powerpc_jump_fixup_info *jfi)
54b8ea2c8cSmatt {
55b8ea2c8cSmatt union instr instr = { .i_int = *insnp };
56b8ea2c8cSmatt
57b8ea2c8cSmatt KASSERT(instr.i_any.i_opcd == OPC_B);
58b8ea2c8cSmatt
59b8ea2c8cSmatt instr.i_i.i_li = jfi->jfi_real - fixup_addr2offset(insnp);
60b8ea2c8cSmatt
61b8ea2c8cSmatt *insnp = instr.i_int;
62b8ea2c8cSmatt
63b8ea2c8cSmatt __asm(
64b8ea2c8cSmatt "dcbst 0,%0" "\n\t"
65b8ea2c8cSmatt "sync" "\n\t"
66b8ea2c8cSmatt "icbi 0,%0" "\n\t"
67b8ea2c8cSmatt "sync" "\n\t"
68b8ea2c8cSmatt "isync"
69b8ea2c8cSmatt :: "b"(insnp));
70b8ea2c8cSmatt }
71b8ea2c8cSmatt
72b8ea2c8cSmatt void
powerpc_fixup_stubs(uint32_t * start,uint32_t * end,uint32_t * stub_start,uint32_t * stub_end)73e13c032fSmatt powerpc_fixup_stubs(uint32_t *start, uint32_t *end,
74e13c032fSmatt uint32_t *stub_start, uint32_t *stub_end)
75b8ea2c8cSmatt {
76b8ea2c8cSmatt extern uint32_t __stub_start[], __stub_end[];
77b8ea2c8cSmatt #ifdef DEBUG
78b8ea2c8cSmatt size_t fixups_done = 0;
79b01d00d9Smacallan uint64_t cycles = 0;
80b01d00d9Smacallan #ifdef PPC_OEA601
81b01d00d9Smacallan if ((mfpvr() >> 16) == MPC601)
82b01d00d9Smacallan cycles = rtc_nanosecs() >> 7;
83b01d00d9Smacallan else
84b01d00d9Smacallan #endif
85b01d00d9Smacallan cycles = mftb();
86b8ea2c8cSmatt #endif
87b8ea2c8cSmatt
88e13c032fSmatt if (stub_start == NULL) {
89e13c032fSmatt stub_start = __stub_start;
90e13c032fSmatt stub_end = __stub_end;
91e13c032fSmatt }
92e13c032fSmatt
93b8ea2c8cSmatt if (end > __stub_start)
94b8ea2c8cSmatt end = __stub_start;
95b8ea2c8cSmatt
96b8ea2c8cSmatt for (uint32_t *insnp = start; insnp < end; insnp++) {
97b8ea2c8cSmatt struct powerpc_jump_fixup_info fixup;
98b8ea2c8cSmatt union instr instr = { .i_int = *insnp };
99b8ea2c8cSmatt uint32_t *stub = insnp + instr.i_i.i_li;
100b8ea2c8cSmatt u_int opcode = instr.i_any.i_opcd;
101b8ea2c8cSmatt
102b8ea2c8cSmatt /*
103b8ea2c8cSmatt * First we check to see if this is a jump and whether it is
104b8ea2c8cSmatt * within the range we are interested in.
105b8ea2c8cSmatt */
106e13c032fSmatt if (opcode != OPC_B || stub < stub_start || stub_end <= stub)
107b8ea2c8cSmatt continue;
108b8ea2c8cSmatt
109b8ea2c8cSmatt fixup.jfi_stub = fixup_addr2offset(stub);
110b8ea2c8cSmatt fixup.jfi_real = 0;
111b8ea2c8cSmatt
112b8ea2c8cSmatt /*
113b8ea2c8cSmatt * We know it's a jump, now we need to figure out where it goes.
114b8ea2c8cSmatt */
115b8ea2c8cSmatt register_t fixreg[32];
116b8ea2c8cSmatt register_t ctr = 0;
117b8ea2c8cSmatt uint32_t valid_mask = (1 << 1);
1185c696828Snisimura #ifdef DIAGNOSTIC
119b8ea2c8cSmatt int r_lr = -1;
1205c696828Snisimura #endif
121e13c032fSmatt for (; stub < stub_end && fixup.jfi_real == 0; stub++) {
122b8ea2c8cSmatt const union instr i = { .i_int = *stub };
123b8ea2c8cSmatt
124b8ea2c8cSmatt switch (i.i_any.i_opcd) {
125b8ea2c8cSmatt case OPC_integer_31: {
126b8ea2c8cSmatt const u_int rs = i.i_x.i_rs;
127b8ea2c8cSmatt const u_int ra = i.i_x.i_ra;
128b8ea2c8cSmatt const u_int rb = i.i_x.i_rb;
129b8ea2c8cSmatt switch (i.i_x.i_xo) {
130b8ea2c8cSmatt case OPC31_MFSPR: {
131e13c032fSmatt #ifdef DIAGNOSTIC
132b8ea2c8cSmatt const u_int spr = (rb << 5) | ra;
133b8ea2c8cSmatt KASSERT(spr == SPR_LR);
1345c696828Snisimura r_lr = rs;
135e13c032fSmatt #endif
136b8ea2c8cSmatt valid_mask |= (1 << rs);
137b8ea2c8cSmatt break;
138b8ea2c8cSmatt }
139b8ea2c8cSmatt case OPC31_MTSPR: {
140e13c032fSmatt #ifdef DIAGNOSTIC
141b8ea2c8cSmatt const u_int spr = (rb << 5) | ra;
142b8ea2c8cSmatt KASSERT(valid_mask & (1 << rs));
143b8ea2c8cSmatt KASSERT(spr == SPR_CTR);
144e13c032fSmatt #endif
145b8ea2c8cSmatt ctr = fixreg[rs];
146b8ea2c8cSmatt break;
147b8ea2c8cSmatt }
1481aabba0fSmatt case OPC31_OR: {
1491aabba0fSmatt #ifdef DIAGNOSTIC
1501aabba0fSmatt KASSERT(valid_mask & (1 << rs));
1511aabba0fSmatt KASSERT(valid_mask & (1 << rb));
1521aabba0fSmatt #endif
1531aabba0fSmatt fixreg[ra] = fixreg[rs] | fixreg[rb];
1541aabba0fSmatt valid_mask |= 1 << ra;
1551aabba0fSmatt break;
1561aabba0fSmatt }
157b8ea2c8cSmatt default:
158b8ea2c8cSmatt panic("%s: %p: unexpected insn 0x%08x",
159b8ea2c8cSmatt __func__, stub, i.i_int);
160b8ea2c8cSmatt }
161b8ea2c8cSmatt break;
162b8ea2c8cSmatt }
163b8ea2c8cSmatt case OPC_ADDI:
164b8ea2c8cSmatt case OPC_ADDIS: {
165b8ea2c8cSmatt const u_int rs = i.i_d.i_rs;
166b8ea2c8cSmatt const u_int ra = i.i_d.i_ra;
1677b9e6b6dSmatt register_t d = i.i_d.i_d << ((i.i_d.i_opcd & 1) * 16);
168b8ea2c8cSmatt if (ra) {
169b8ea2c8cSmatt KASSERT(valid_mask & (1 << ra));
170b8ea2c8cSmatt d += fixreg[ra];
171b8ea2c8cSmatt }
172b8ea2c8cSmatt fixreg[rs] = d;
173b8ea2c8cSmatt valid_mask |= (1 << rs);
174b8ea2c8cSmatt break;
175b8ea2c8cSmatt }
176b8ea2c8cSmatt case OPC_LWZ: {
177b8ea2c8cSmatt const u_int rs = i.i_d.i_rs;
178b8ea2c8cSmatt const u_int ra = i.i_d.i_ra;
1797b9e6b6dSmatt register_t addr = i.i_d.i_d;
180b8ea2c8cSmatt if (ra) {
181b8ea2c8cSmatt KASSERT(valid_mask & (1 << ra));
182b8ea2c8cSmatt addr += fixreg[ra];
183b8ea2c8cSmatt }
184b8ea2c8cSmatt fixreg[rs] = *(uint32_t *)addr;
185b8ea2c8cSmatt valid_mask |= (1 << rs);
186b8ea2c8cSmatt break;
187b8ea2c8cSmatt }
188b8ea2c8cSmatt case OPC_STW: {
189*3ab3953dSmacallan #ifdef DIAGNOSTIC
1901aabba0fSmatt KASSERT((i.i_d.i_rs == r_lr || i.i_d.i_rs == 31) && i.i_d.i_ra == 1);
191*3ab3953dSmacallan #endif
192b8ea2c8cSmatt break;
193b8ea2c8cSmatt }
194b8ea2c8cSmatt case OPC_STWU: {
195b8ea2c8cSmatt KASSERT(i.i_d.i_rs == 1 && i.i_d.i_ra == 1);
196b8ea2c8cSmatt KASSERT(i.i_d.i_d < 0);
197b8ea2c8cSmatt break;
198b8ea2c8cSmatt }
199b8ea2c8cSmatt case OPC_branch_19: {
200*3ab3953dSmacallan #ifdef DIAGNOSTIC
201b8ea2c8cSmatt KASSERT(r_lr == -1 || i.i_int == 0x4e800421);
202b8ea2c8cSmatt KASSERT(r_lr != -1 || i.i_int == 0x4e800420);
203*3ab3953dSmacallan #endif
204d75955b9Smatt if (ctr == 0) {
205d75955b9Smatt panic("%s: jump at %p to %p would "
206d75955b9Smatt "branch to 0", __func__, insnp,
207d75955b9Smatt insnp + instr.i_i.i_li);
208d75955b9Smatt }
209b8ea2c8cSmatt fixup.jfi_real = fixup_addr2offset(ctr);
210b8ea2c8cSmatt break;
211b8ea2c8cSmatt }
2121aabba0fSmatt case OPC_RLWINM: {
2131aabba0fSmatt // LLVM emits these for bool operands.
2141aabba0fSmatt // Ignore them.
2151aabba0fSmatt break;
2161aabba0fSmatt }
217b8ea2c8cSmatt default: {
218b8ea2c8cSmatt panic("%s: %p: unexpected insn 0x%08x",
219b8ea2c8cSmatt __func__, stub, i.i_int);
220b8ea2c8cSmatt }
221b8ea2c8cSmatt }
222b8ea2c8cSmatt }
223b8ea2c8cSmatt KASSERT(fixup.jfi_real != 0);
224b8ea2c8cSmatt /*
225b8ea2c8cSmatt * Now we know the real destination to branch to. Replace the
226b8ea2c8cSmatt * old displacement with the new displacement.
227b8ea2c8cSmatt */
228b8ea2c8cSmatt #if 0
229b8ea2c8cSmatt printf("%s: %p: change from %#x to %#x\n",
230b8ea2c8cSmatt __func__, insnp, fixup.jfi_stub << 2, fixup.jfi_real << 2);
231b8ea2c8cSmatt #endif
232b8ea2c8cSmatt fixup_jump(insnp, &fixup);
233b8ea2c8cSmatt #ifdef DEBUG
234b8ea2c8cSmatt fixups_done++;
235b8ea2c8cSmatt #endif
236b8ea2c8cSmatt }
237b8ea2c8cSmatt
238b8ea2c8cSmatt #ifdef DEBUG
239b01d00d9Smacallan
240b01d00d9Smacallan #ifdef PPC_OEA601
241b01d00d9Smacallan if ((mfpvr() >> 16) == MPC601)
242b01d00d9Smacallan cycles = (rtc_nanosecs() >> 7) - cycles;
243b01d00d9Smacallan else
244b01d00d9Smacallan #endif
245b8ea2c8cSmatt cycles = mftb() - cycles;
246b01d00d9Smacallan
247b8ea2c8cSmatt printf("%s: %zu fixup%s done in %"PRIu64" cycles\n", __func__,
248b8ea2c8cSmatt fixups_done, fixups_done == 1 ? "" : "s",
249b8ea2c8cSmatt cycles);
250b8ea2c8cSmatt #endif
251b8ea2c8cSmatt }
252d5e2b0cfSmatt
253d5e2b0cfSmatt void
cpu_fixup_stubs(void)254d5e2b0cfSmatt cpu_fixup_stubs(void)
255d5e2b0cfSmatt {
256d5e2b0cfSmatt extern uint32_t _ftext[];
257d5e2b0cfSmatt extern uint32_t _etext[];
258d5e2b0cfSmatt
259d5e2b0cfSmatt powerpc_fixup_stubs(_ftext, _etext, NULL, NULL);
260d5e2b0cfSmatt }
261