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