xref: /netbsd-src/sys/arch/powerpc/powerpc/fixup.c (revision 3ab3953d7e8c0cd99e17f8aedbd9132142e0ce7a)
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