1/* $Id: btfixup.c,v 1.1.1.1 2007/08/03 18:52:18 Exp $ 2 * btfixup.c: Boot time code fixup and relocator, so that 3 * we can get rid of most indirect calls to achieve single 4 * image sun4c and srmmu kernel. 5 * 6 * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) 7 */ 8 9#include <linux/kernel.h> 10#include <linux/init.h> 11#include <asm/btfixup.h> 12#include <asm/page.h> 13#include <asm/pgalloc.h> 14#include <asm/pgtable.h> 15#include <asm/oplib.h> 16#include <asm/system.h> 17#include <asm/cacheflush.h> 18 19#define BTFIXUP_OPTIMIZE_NOP 20#define BTFIXUP_OPTIMIZE_OTHER 21 22extern char *srmmu_name; 23static char version[] __initdata = "Boot time fixup v1.6. 4/Mar/98 Jakub Jelinek (jj@ultra.linux.cz). Patching kernel for "; 24#ifdef CONFIG_SUN4 25static char str_sun4c[] __initdata = "sun4\n"; 26#else 27static char str_sun4c[] __initdata = "sun4c\n"; 28#endif 29static char str_srmmu[] __initdata = "srmmu[%s]/"; 30static char str_iommu[] __initdata = "iommu\n"; 31static char str_iounit[] __initdata = "io-unit\n"; 32 33static int visited __initdata = 0; 34extern unsigned int ___btfixup_start[], ___btfixup_end[], __init_begin[], __init_end[], __init_text_end[]; 35extern unsigned int _stext[], _end[], __start___ksymtab[], __stop___ksymtab[]; 36static char wrong_f[] __initdata = "Trying to set f fixup %p to invalid function %08x\n"; 37static char wrong_b[] __initdata = "Trying to set b fixup %p to invalid function %08x\n"; 38static char wrong_s[] __initdata = "Trying to set s fixup %p to invalid value %08x\n"; 39static char wrong_h[] __initdata = "Trying to set h fixup %p to invalid value %08x\n"; 40static char wrong_a[] __initdata = "Trying to set a fixup %p to invalid value %08x\n"; 41static char wrong[] __initdata = "Wrong address for %c fixup %p\n"; 42static char insn_f[] __initdata = "Fixup f %p refers to weird instructions at %p[%08x,%08x]\n"; 43static char insn_b[] __initdata = "Fixup b %p doesn't refer to a SETHI at %p[%08x]\n"; 44static char insn_s[] __initdata = "Fixup s %p doesn't refer to an OR at %p[%08x]\n"; 45static char insn_h[] __initdata = "Fixup h %p doesn't refer to a SETHI at %p[%08x]\n"; 46static char insn_a[] __initdata = "Fixup a %p doesn't refer to a SETHI nor OR at %p[%08x]\n"; 47static char insn_i[] __initdata = "Fixup i %p doesn't refer to a valid instruction at %p[%08x]\n"; 48static char fca_und[] __initdata = "flush_cache_all undefined in btfixup()\n"; 49static char wrong_setaddr[] __initdata = "Garbled CALL/INT patch at %p[%08x,%08x,%08x]=%08x\n"; 50 51#ifdef BTFIXUP_OPTIMIZE_OTHER 52static void __init set_addr(unsigned int *addr, unsigned int q1, int fmangled, unsigned int value) 53{ 54 if (!fmangled) 55 *addr = value; 56 else { 57 unsigned int *q = (unsigned int *)q1; 58 if (*addr == 0x01000000) { 59 /* Noped */ 60 *q = value; 61 } else if (addr[-1] == *q) { 62 /* Moved */ 63 addr[-1] = value; 64 *q = value; 65 } else { 66 prom_printf(wrong_setaddr, addr-1, addr[-1], *addr, *q, value); 67 prom_halt(); 68 } 69 } 70} 71#else 72static __inline__ void set_addr(unsigned int *addr, unsigned int q1, int fmangled, unsigned int value) 73{ 74 *addr = value; 75} 76#endif 77 78void __init btfixup(void) 79{ 80 unsigned int *p, *q; 81 int type, count; 82 unsigned insn; 83 unsigned *addr; 84 int fmangled = 0; 85 void (*flush_cacheall)(void); 86 87 if (!visited) { 88 visited++; 89 printk(version); 90 if (ARCH_SUN4C_SUN4) 91 printk(str_sun4c); 92 else { 93 printk(str_srmmu, srmmu_name); 94 if (sparc_cpu_model == sun4d) 95 printk(str_iounit); 96 else 97 printk(str_iommu); 98 } 99 } 100 for (p = ___btfixup_start; p < ___btfixup_end; ) { 101 count = p[2]; 102 q = p + 3; 103 switch (type = *(unsigned char *)p) { 104 case 'f': 105 count = p[3]; 106 q = p + 4; 107 if (((p[0] & 1) || p[1]) 108 && ((p[1] & 3) || (unsigned *)(p[1]) < _stext || (unsigned *)(p[1]) >= _end)) { 109 prom_printf(wrong_f, p, p[1]); 110 prom_halt(); 111 } 112 break; 113 case 'b': 114 if (p[1] < (unsigned long)__init_begin || p[1] >= (unsigned long)__init_text_end || (p[1] & 3)) { 115 prom_printf(wrong_b, p, p[1]); 116 prom_halt(); 117 } 118 break; 119 case 's': 120 if (p[1] + 0x1000 >= 0x2000) { 121 prom_printf(wrong_s, p, p[1]); 122 prom_halt(); 123 } 124 break; 125 case 'h': 126 if (p[1] & 0x3ff) { 127 prom_printf(wrong_h, p, p[1]); 128 prom_halt(); 129 } 130 break; 131 case 'a': 132 if (p[1] + 0x1000 >= 0x2000 && (p[1] & 0x3ff)) { 133 prom_printf(wrong_a, p, p[1]); 134 prom_halt(); 135 } 136 break; 137 } 138 if (p[0] & 1) { 139 p[0] &= ~1; 140 while (count) { 141 fmangled = 0; 142 addr = (unsigned *)*q; 143 if (addr < _stext || addr >= _end) { 144 prom_printf(wrong, type, p); 145 prom_halt(); 146 } 147 insn = *addr; 148#ifdef BTFIXUP_OPTIMIZE_OTHER 149 if (type != 'f' && q[1]) { 150 insn = *(unsigned int *)q[1]; 151 if (!insn || insn == 1) 152 insn = *addr; 153 else 154 fmangled = 1; 155 } 156#endif 157 switch (type) { 158 case 'f': /* CALL */ 159 if (addr >= __start___ksymtab && addr < __stop___ksymtab) { 160 *addr = p[1]; 161 break; 162 } else if (!q[1]) { 163 if ((insn & 0xc1c00000) == 0x01000000) { /* SETHI */ 164 *addr = (insn & 0xffc00000) | (p[1] >> 10); break; 165 } else if ((insn & 0xc1f82000) == 0x80102000) { /* OR X, %LO(i), Y */ 166 *addr = (insn & 0xffffe000) | (p[1] & 0x3ff); break; 167 } else if ((insn & 0xc0000000) != 0x40000000) { /* !CALL */ 168 bad_f: 169 prom_printf(insn_f, p, addr, insn, addr[1]); 170 prom_halt(); 171 } 172 } else if (q[1] != 1) 173 addr[1] = q[1]; 174 if (p[2] == BTFIXUPCALL_NORM) { 175 norm_f: 176 *addr = 0x40000000 | ((p[1] - (unsigned)addr) >> 2); 177 q[1] = 0; 178 break; 179 } 180#ifndef BTFIXUP_OPTIMIZE_NOP 181 goto norm_f; 182#else 183 if (!(addr[1] & 0x80000000)) { 184 if ((addr[1] & 0xc1c00000) != 0x01000000) /* !SETHI */ 185 goto bad_f; /* CALL, Bicc, FBfcc, CBccc are weird in delay slot, aren't they? */ 186 } else { 187 if ((addr[1] & 0x01800000) == 0x01800000) { 188 if ((addr[1] & 0x01f80000) == 0x01e80000) { 189 /* RESTORE */ 190 goto norm_f; /* It is dangerous to patch that */ 191 } 192 goto bad_f; 193 } 194 if ((addr[1] & 0xffffe003) == 0x9e03e000) { 195 /* ADD %O7, XX, %o7 */ 196 int displac = (addr[1] << 19); 197 198 displac = (displac >> 21) + 2; 199 *addr = (0x10800000) + (displac & 0x3fffff); 200 q[1] = addr[1]; 201 addr[1] = p[2]; 202 break; 203 } 204 if ((addr[1] & 0x201f) == 0x200f || (addr[1] & 0x7c000) == 0x3c000) 205 goto norm_f; /* Someone is playing bad tricks with us: rs1 or rs2 is o7 */ 206 if ((addr[1] & 0x3e000000) == 0x1e000000) 207 goto norm_f; /* rd is %o7. We'd better take care. */ 208 } 209 if (p[2] == BTFIXUPCALL_NOP) { 210 *addr = 0x01000000; 211 q[1] = 1; 212 break; 213 } 214#ifndef BTFIXUP_OPTIMIZE_OTHER 215 goto norm_f; 216#else 217 if (addr[1] == 0x01000000) { /* NOP in the delay slot */ 218 q[1] = addr[1]; 219 *addr = p[2]; 220 break; 221 } 222 if ((addr[1] & 0xc0000000) != 0xc0000000) { 223 /* Not a memory operation */ 224 if ((addr[1] & 0x30000000) == 0x10000000) { 225 /* Ok, non-memory op with rd %oX */ 226 if ((addr[1] & 0x3e000000) == 0x1c000000) 227 goto bad_f; /* Aiee. Someone is playing strange %sp tricks */ 228 if ((addr[1] & 0x3e000000) > 0x12000000 || 229 ((addr[1] & 0x3e000000) == 0x12000000 && 230 p[2] != BTFIXUPCALL_STO1O0 && p[2] != BTFIXUPCALL_SWAPO0O1) || 231 ((p[2] & 0xffffe000) == BTFIXUPCALL_RETINT(0))) { 232 /* Nobody uses the result. We can nop it out. */ 233 *addr = p[2]; 234 q[1] = addr[1]; 235 addr[1] = 0x01000000; 236 break; 237 } 238 if ((addr[1] & 0xf1ffffe0) == 0x90100000) { 239 /* MOV %reg, %Ox */ 240 if ((addr[1] & 0x3e000000) == 0x10000000 && 241 (p[2] & 0x7c000) == 0x20000) { 242 /* Ok, it is call xx; mov reg, %o0 and call optimizes 243 to doing something on %o0. Patch the patch. */ 244 *addr = (p[2] & ~0x7c000) | ((addr[1] & 0x1f) << 14); 245 q[1] = addr[1]; 246 addr[1] = 0x01000000; 247 break; 248 } 249 if ((addr[1] & 0x3e000000) == 0x12000000 && 250 p[2] == BTFIXUPCALL_STO1O0) { 251 *addr = (p[2] & ~0x3e000000) | ((addr[1] & 0x1f) << 25); 252 q[1] = addr[1]; 253 addr[1] = 0x01000000; 254 break; 255 } 256 } 257 } 258 } 259 *addr = addr[1]; 260 q[1] = addr[1]; 261 addr[1] = p[2]; 262 break; 263#endif /* BTFIXUP_OPTIMIZE_OTHER */ 264#endif /* BTFIXUP_OPTIMIZE_NOP */ 265 case 'b': /* BLACKBOX */ 266 /* Has to be sethi i, xx */ 267 if ((insn & 0xc1c00000) != 0x01000000) { 268 prom_printf(insn_b, p, addr, insn); 269 prom_halt(); 270 } else { 271 void (*do_fixup)(unsigned *); 272 273 do_fixup = (void (*)(unsigned *))p[1]; 274 do_fixup(addr); 275 } 276 break; 277 case 's': /* SIMM13 */ 278 /* Has to be or %g0, i, xx */ 279 if ((insn & 0xc1ffe000) != 0x80102000) { 280 prom_printf(insn_s, p, addr, insn); 281 prom_halt(); 282 } 283 set_addr(addr, q[1], fmangled, (insn & 0xffffe000) | (p[1] & 0x1fff)); 284 break; 285 case 'h': /* SETHI */ 286 /* Has to be sethi i, xx */ 287 if ((insn & 0xc1c00000) != 0x01000000) { 288 prom_printf(insn_h, p, addr, insn); 289 prom_halt(); 290 } 291 set_addr(addr, q[1], fmangled, (insn & 0xffc00000) | (p[1] >> 10)); 292 break; 293 case 'a': /* HALF */ 294 /* Has to be sethi i, xx or or %g0, i, xx */ 295 if ((insn & 0xc1c00000) != 0x01000000 && 296 (insn & 0xc1ffe000) != 0x80102000) { 297 prom_printf(insn_a, p, addr, insn); 298 prom_halt(); 299 } 300 if (p[1] & 0x3ff) 301 set_addr(addr, q[1], fmangled, 302 (insn & 0x3e000000) | 0x80102000 | (p[1] & 0x1fff)); 303 else 304 set_addr(addr, q[1], fmangled, 305 (insn & 0x3e000000) | 0x01000000 | (p[1] >> 10)); 306 break; 307 case 'i': /* INT */ 308 if ((insn & 0xc1c00000) == 0x01000000) /* %HI */ 309 set_addr(addr, q[1], fmangled, (insn & 0xffc00000) | (p[1] >> 10)); 310 else if ((insn & 0x80002000) == 0x80002000 && 311 (insn & 0x01800000) != 0x01800000) /* %LO */ 312 set_addr(addr, q[1], fmangled, (insn & 0xffffe000) | (p[1] & 0x3ff)); 313 else { 314 prom_printf(insn_i, p, addr, insn); 315 prom_halt(); 316 } 317 break; 318 } 319 count -= 2; 320 q += 2; 321 } 322 } else 323 p = q + count; 324 } 325#ifdef CONFIG_SMP 326 flush_cacheall = (void (*)(void))BTFIXUPVAL_CALL(local_flush_cache_all); 327#else 328 flush_cacheall = (void (*)(void))BTFIXUPVAL_CALL(flush_cache_all); 329#endif 330 if (!flush_cacheall) { 331 prom_printf(fca_und); 332 prom_halt(); 333 } 334 (*flush_cacheall)(); 335} 336