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 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 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 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