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