linux-old/arch/sparc/kernel/unaligned.c
<<
>>
Prefs
   1/* $Id: unaligned.c,v 1.22.2.1 2001/12/21 00:52:47 davem Exp $
   2 * unaligned.c: Unaligned load/store trap handling with special
   3 *              cases for the kernel to do them more quickly.
   4 *
   5 * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
   6 * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
   7 */
   8
   9
  10#include <linux/kernel.h>
  11#include <linux/sched.h>
  12#include <linux/mm.h>
  13#include <asm/ptrace.h>
  14#include <asm/processor.h>
  15#include <asm/system.h>
  16#include <asm/uaccess.h>
  17#include <linux/smp.h>
  18#include <linux/smp_lock.h>
  19
  20/* #define DEBUG_MNA */
  21
  22enum direction {
  23        load,    /* ld, ldd, ldh, ldsh */
  24        store,   /* st, std, sth, stsh */
  25        both,    /* Swap, ldstub, etc. */
  26        fpload,
  27        fpstore,
  28        invalid,
  29};
  30
  31#ifdef DEBUG_MNA
  32static char *dirstrings[] = {
  33  "load", "store", "both", "fpload", "fpstore", "invalid"
  34};
  35#endif
  36
  37static inline enum direction decode_direction(unsigned int insn)
  38{
  39        unsigned long tmp = (insn >> 21) & 1;
  40
  41        if(!tmp)
  42                return load;
  43        else {
  44                if(((insn>>19)&0x3f) == 15)
  45                        return both;
  46                else
  47                        return store;
  48        }
  49}
  50
  51/* 8 = double-word, 4 = word, 2 = half-word */
  52static inline int decode_access_size(unsigned int insn)
  53{
  54        insn = (insn >> 19) & 3;
  55
  56        if(!insn)
  57                return 4;
  58        else if(insn == 3)
  59                return 8;
  60        else if(insn == 2)
  61                return 2;
  62        else {
  63                printk("Impossible unaligned trap. insn=%08x\n", insn);
  64                die_if_kernel("Byte sized unaligned access?!?!", current->thread.kregs);
  65                return 4; /* just to keep gcc happy. */
  66        }
  67}
  68
  69/* 0x400000 = signed, 0 = unsigned */
  70static inline int decode_signedness(unsigned int insn)
  71{
  72        return (insn & 0x400000);
  73}
  74
  75static inline void maybe_flush_windows(unsigned int rs1, unsigned int rs2,
  76                                       unsigned int rd)
  77{
  78        if(rs2 >= 16 || rs1 >= 16 || rd >= 16) {
  79                /* Wheee... */
  80                __asm__ __volatile__("save %sp, -0x40, %sp\n\t"
  81                                     "save %sp, -0x40, %sp\n\t"
  82                                     "save %sp, -0x40, %sp\n\t"
  83                                     "save %sp, -0x40, %sp\n\t"
  84                                     "save %sp, -0x40, %sp\n\t"
  85                                     "save %sp, -0x40, %sp\n\t"
  86                                     "save %sp, -0x40, %sp\n\t"
  87                                     "restore; restore; restore; restore;\n\t"
  88                                     "restore; restore; restore;\n\t");
  89        }
  90}
  91
  92static inline int sign_extend_imm13(int imm)
  93{
  94        return imm << 19 >> 19;
  95}
  96
  97static inline unsigned long fetch_reg(unsigned int reg, struct pt_regs *regs)
  98{
  99        struct reg_window *win;
 100
 101        if(reg < 16)
 102                return (!reg ? 0 : regs->u_regs[reg]);
 103
 104        /* Ho hum, the slightly complicated case. */
 105        win = (struct reg_window *) regs->u_regs[UREG_FP];
 106        return win->locals[reg - 16]; /* yes, I know what this does... */
 107}
 108
 109static inline unsigned long safe_fetch_reg(unsigned int reg, struct pt_regs *regs)
 110{
 111        struct reg_window *win;
 112        unsigned long ret;
 113
 114        if(reg < 16)
 115                return (!reg ? 0 : regs->u_regs[reg]);
 116
 117        /* Ho hum, the slightly complicated case. */
 118        win = (struct reg_window *) regs->u_regs[UREG_FP];
 119
 120        if ((unsigned long)win & 3)
 121                return -1;
 122
 123        if (get_user(ret, &win->locals[reg - 16]))
 124                return -1;
 125
 126        return ret;
 127}
 128
 129static inline unsigned long *fetch_reg_addr(unsigned int reg, struct pt_regs *regs)
 130{
 131        struct reg_window *win;
 132
 133        if(reg < 16)
 134                return &regs->u_regs[reg];
 135        win = (struct reg_window *) regs->u_regs[UREG_FP];
 136        return &win->locals[reg - 16];
 137}
 138
 139static unsigned long compute_effective_address(struct pt_regs *regs,
 140                                               unsigned int insn)
 141{
 142        unsigned int rs1 = (insn >> 14) & 0x1f;
 143        unsigned int rs2 = insn & 0x1f;
 144        unsigned int rd = (insn >> 25) & 0x1f;
 145
 146        if(insn & 0x2000) {
 147                maybe_flush_windows(rs1, 0, rd);
 148                return (fetch_reg(rs1, regs) + sign_extend_imm13(insn));
 149        } else {
 150                maybe_flush_windows(rs1, rs2, rd);
 151                return (fetch_reg(rs1, regs) + fetch_reg(rs2, regs));
 152        }
 153}
 154
 155unsigned long safe_compute_effective_address(struct pt_regs *regs,
 156                                             unsigned int insn)
 157{
 158        unsigned int rs1 = (insn >> 14) & 0x1f;
 159        unsigned int rs2 = insn & 0x1f;
 160        unsigned int rd = (insn >> 25) & 0x1f;
 161
 162        if(insn & 0x2000) {
 163                maybe_flush_windows(rs1, 0, rd);
 164                return (safe_fetch_reg(rs1, regs) + sign_extend_imm13(insn));
 165        } else {
 166                maybe_flush_windows(rs1, rs2, rd);
 167                return (safe_fetch_reg(rs1, regs) + safe_fetch_reg(rs2, regs));
 168        }
 169}
 170
 171/* This is just to make gcc think panic does return... */
 172static void unaligned_panic(char *str)
 173{
 174        panic(str);
 175}
 176
 177#define do_integer_load(dest_reg, size, saddr, is_signed, errh) ({              \
 178__asm__ __volatile__ (                                                          \
 179        "cmp    %1, 8\n\t"                                                      \
 180        "be     9f\n\t"                                                         \
 181        " cmp   %1, 4\n\t"                                                      \
 182        "be     6f\n"                                                           \
 183"4:\t"  " ldub  [%2], %%l1\n"                                                   \
 184"5:\t"  "ldub   [%2 + 1], %%l2\n\t"                                             \
 185        "sll    %%l1, 8, %%l1\n\t"                                              \
 186        "tst    %3\n\t"                                                         \
 187        "be     3f\n\t"                                                         \
 188        " add   %%l1, %%l2, %%l1\n\t"                                           \
 189        "sll    %%l1, 16, %%l1\n\t"                                             \
 190        "sra    %%l1, 16, %%l1\n"                                               \
 191"3:\t"  "b      0f\n\t"                                                         \
 192        " st    %%l1, [%0]\n"                                                   \
 193"6:\t"  "ldub   [%2 + 1], %%l2\n\t"                                             \
 194        "sll    %%l1, 24, %%l1\n"                                               \
 195"7:\t"  "ldub   [%2 + 2], %%g7\n\t"                                             \
 196        "sll    %%l2, 16, %%l2\n"                                               \
 197"8:\t"  "ldub   [%2 + 3], %%g1\n\t"                                             \
 198        "sll    %%g7, 8, %%g7\n\t"                                              \
 199        "or     %%l1, %%l2, %%l1\n\t"                                           \
 200        "or     %%g7, %%g1, %%g7\n\t"                                           \
 201        "or     %%l1, %%g7, %%l1\n\t"                                           \
 202        "b      0f\n\t"                                                         \
 203        " st    %%l1, [%0]\n"                                                   \
 204"9:\t"  "ldub   [%2], %%l1\n"                                                   \
 205"10:\t" "ldub   [%2 + 1], %%l2\n\t"                                             \
 206        "sll    %%l1, 24, %%l1\n"                                               \
 207"11:\t" "ldub   [%2 + 2], %%g7\n\t"                                             \
 208        "sll    %%l2, 16, %%l2\n"                                               \
 209"12:\t" "ldub   [%2 + 3], %%g1\n\t"                                             \
 210        "sll    %%g7, 8, %%g7\n\t"                                              \
 211        "or     %%l1, %%l2, %%l1\n\t"                                           \
 212        "or     %%g7, %%g1, %%g7\n\t"                                           \
 213        "or     %%l1, %%g7, %%g7\n"                                             \
 214"13:\t" "ldub   [%2 + 4], %%l1\n\t"                                             \
 215        "st     %%g7, [%0]\n"                                                   \
 216"14:\t" "ldub   [%2 + 5], %%l2\n\t"                                             \
 217        "sll    %%l1, 24, %%l1\n"                                               \
 218"15:\t" "ldub   [%2 + 6], %%g7\n\t"                                             \
 219        "sll    %%l2, 16, %%l2\n"                                               \
 220"16:\t" "ldub   [%2 + 7], %%g1\n\t"                                             \
 221        "sll    %%g7, 8, %%g7\n\t"                                              \
 222        "or     %%l1, %%l2, %%l1\n\t"                                           \
 223        "or     %%g7, %%g1, %%g7\n\t"                                           \
 224        "or     %%l1, %%g7, %%g7\n\t"                                           \
 225        "st     %%g7, [%0 + 4]\n"                                               \
 226"0:\n\n\t"                                                                      \
 227        ".section __ex_table,#alloc\n\t"                                        \
 228        ".word  4b, " #errh "\n\t"                                              \
 229        ".word  5b, " #errh "\n\t"                                              \
 230        ".word  6b, " #errh "\n\t"                                              \
 231        ".word  7b, " #errh "\n\t"                                              \
 232        ".word  8b, " #errh "\n\t"                                              \
 233        ".word  9b, " #errh "\n\t"                                              \
 234        ".word  10b, " #errh "\n\t"                                             \
 235        ".word  11b, " #errh "\n\t"                                             \
 236        ".word  12b, " #errh "\n\t"                                             \
 237        ".word  13b, " #errh "\n\t"                                             \
 238        ".word  14b, " #errh "\n\t"                                             \
 239        ".word  15b, " #errh "\n\t"                                             \
 240        ".word  16b, " #errh "\n\n\t"                                           \
 241        ".previous\n\t"                                                         \
 242        : : "r" (dest_reg), "r" (size), "r" (saddr), "r" (is_signed)            \
 243        : "l1", "l2", "g7", "g1", "cc");                                        \
 244})
 245        
 246#define store_common(dst_addr, size, src_val, errh) ({                          \
 247__asm__ __volatile__ (                                                          \
 248        "ld     [%2], %%l1\n"                                                   \
 249        "cmp    %1, 2\n\t"                                                      \
 250        "be     2f\n\t"                                                         \
 251        " cmp   %1, 4\n\t"                                                      \
 252        "be     1f\n\t"                                                         \
 253        " srl   %%l1, 24, %%l2\n\t"                                             \
 254        "srl    %%l1, 16, %%g7\n"                                               \
 255"4:\t"  "stb    %%l2, [%0]\n\t"                                                 \
 256        "srl    %%l1, 8, %%l2\n"                                                \
 257"5:\t"  "stb    %%g7, [%0 + 1]\n\t"                                             \
 258        "ld     [%2 + 4], %%g7\n"                                               \
 259"6:\t"  "stb    %%l2, [%0 + 2]\n\t"                                             \
 260        "srl    %%g7, 24, %%l2\n"                                               \
 261"7:\t"  "stb    %%l1, [%0 + 3]\n\t"                                             \
 262        "srl    %%g7, 16, %%l1\n"                                               \
 263"8:\t"  "stb    %%l2, [%0 + 4]\n\t"                                             \
 264        "srl    %%g7, 8, %%l2\n"                                                \
 265"9:\t"  "stb    %%l1, [%0 + 5]\n"                                               \
 266"10:\t" "stb    %%l2, [%0 + 6]\n\t"                                             \
 267        "b      0f\n"                                                           \
 268"11:\t" " stb   %%g7, [%0 + 7]\n"                                               \
 269"1:\t"  "srl    %%l1, 16, %%g7\n"                                               \
 270"12:\t" "stb    %%l2, [%0]\n\t"                                                 \
 271        "srl    %%l1, 8, %%l2\n"                                                \
 272"13:\t" "stb    %%g7, [%0 + 1]\n"                                               \
 273"14:\t" "stb    %%l2, [%0 + 2]\n\t"                                             \
 274        "b      0f\n"                                                           \
 275"15:\t" " stb   %%l1, [%0 + 3]\n"                                               \
 276"2:\t"  "srl    %%l1, 8, %%l2\n"                                                \
 277"16:\t" "stb    %%l2, [%0]\n"                                                   \
 278"17:\t" "stb    %%l1, [%0 + 1]\n"                                               \
 279"0:\n\n\t"                                                                      \
 280        ".section __ex_table,#alloc\n\t"                                        \
 281        ".word  4b, " #errh "\n\t"                                              \
 282        ".word  5b, " #errh "\n\t"                                              \
 283        ".word  6b, " #errh "\n\t"                                              \
 284        ".word  7b, " #errh "\n\t"                                              \
 285        ".word  8b, " #errh "\n\t"                                              \
 286        ".word  9b, " #errh "\n\t"                                              \
 287        ".word  10b, " #errh "\n\t"                                             \
 288        ".word  11b, " #errh "\n\t"                                             \
 289        ".word  12b, " #errh "\n\t"                                             \
 290        ".word  13b, " #errh "\n\t"                                             \
 291        ".word  14b, " #errh "\n\t"                                             \
 292        ".word  15b, " #errh "\n\t"                                             \
 293        ".word  16b, " #errh "\n\t"                                             \
 294        ".word  17b, " #errh "\n\n\t"                                           \
 295        ".previous\n\t"                                                         \
 296        : : "r" (dst_addr), "r" (size), "r" (src_val)                           \
 297        : "l1", "l2", "g7", "g1", "cc");                                        \
 298})
 299
 300#define do_integer_store(reg_num, size, dst_addr, regs, errh) ({                \
 301        unsigned long *src_val;                                                 \
 302        static unsigned long zero[2] = { 0, };                                  \
 303                                                                                \
 304        if (reg_num) src_val = fetch_reg_addr(reg_num, regs);                   \
 305        else {                                                                  \
 306                src_val = &zero[0];                                             \
 307                if (size == 8)                                                  \
 308                        zero[1] = fetch_reg(1, regs);                           \
 309        }                                                                       \
 310        store_common(dst_addr, size, src_val, errh);                            \
 311})
 312
 313/* XXX Need to capture/release other cpu's for SMP around this. */
 314#define do_atomic(srcdest_reg, mem, errh) ({                                    \
 315        unsigned long flags, tmp;                                               \
 316                                                                                \
 317        save_and_cli(flags);                                                    \
 318        tmp = *srcdest_reg;                                                     \
 319        do_integer_load(srcdest_reg, 4, mem, 0, errh);                          \
 320        store_common(mem, 4, &tmp, errh);                                       \
 321        restore_flags(flags);                                                   \
 322})
 323
 324static inline void advance(struct pt_regs *regs)
 325{
 326        regs->pc   = regs->npc;
 327        regs->npc += 4;
 328}
 329
 330static inline int floating_point_load_or_store_p(unsigned int insn)
 331{
 332        return (insn >> 24) & 1;
 333}
 334
 335static inline int ok_for_kernel(unsigned int insn)
 336{
 337        return !floating_point_load_or_store_p(insn);
 338}
 339
 340void kernel_mna_trap_fault(struct pt_regs *regs, unsigned int insn) __asm__ ("kernel_mna_trap_fault");
 341
 342void kernel_mna_trap_fault(struct pt_regs *regs, unsigned int insn)
 343{
 344        unsigned long g2 = regs->u_regs [UREG_G2];
 345        unsigned long fixup = search_exception_table (regs->pc, &g2);
 346
 347        if (!fixup) {
 348                unsigned long address = compute_effective_address(regs, insn);
 349                if(address < PAGE_SIZE) {
 350                        printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference in mna handler");
 351                } else
 352                        printk(KERN_ALERT "Unable to handle kernel paging request in mna handler");
 353                printk(KERN_ALERT " at virtual address %08lx\n",address);
 354                printk(KERN_ALERT "current->{mm,active_mm}->context = %08lx\n",
 355                        (current->mm ? current->mm->context :
 356                        current->active_mm->context));
 357                printk(KERN_ALERT "current->{mm,active_mm}->pgd = %08lx\n",
 358                        (current->mm ? (unsigned long) current->mm->pgd :
 359                        (unsigned long) current->active_mm->pgd));
 360                die_if_kernel("Oops", regs);
 361                /* Not reached */
 362        }
 363        regs->pc = fixup;
 364        regs->npc = regs->pc + 4;
 365        regs->u_regs [UREG_G2] = g2;
 366}
 367
 368asmlinkage void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn)
 369{
 370        enum direction dir = decode_direction(insn);
 371        int size = decode_access_size(insn);
 372
 373        if(!ok_for_kernel(insn) || dir == both) {
 374                printk("Unsupported unaligned load/store trap for kernel at <%08lx>.\n",
 375                       regs->pc);
 376                unaligned_panic("Wheee. Kernel does fpu/atomic unaligned load/store.");
 377
 378                __asm__ __volatile__ ("\n"
 379"kernel_unaligned_trap_fault:\n\t"
 380                "mov    %0, %%o0\n\t"
 381                "call   kernel_mna_trap_fault\n\t"
 382                " mov   %1, %%o1\n\t"
 383                :
 384                : "r" (regs), "r" (insn)
 385                : "o0", "o1", "o2", "o3", "o4", "o5", "o7",
 386                  "g1", "g2", "g3", "g4", "g5", "g7", "cc");
 387        } else {
 388                unsigned long addr = compute_effective_address(regs, insn);
 389
 390#ifdef DEBUG_MNA
 391                printk("KMNA: pc=%08lx [dir=%s addr=%08lx size=%d] retpc[%08lx]\n",
 392                       regs->pc, dirstrings[dir], addr, size, regs->u_regs[UREG_RETPC]);
 393#endif
 394                switch(dir) {
 395                case load:
 396                        do_integer_load(fetch_reg_addr(((insn>>25)&0x1f), regs),
 397                                        size, (unsigned long *) addr,
 398                                        decode_signedness(insn),
 399                                        kernel_unaligned_trap_fault);
 400                        break;
 401
 402                case store:
 403                        do_integer_store(((insn>>25)&0x1f), size,
 404                                         (unsigned long *) addr, regs,
 405                                         kernel_unaligned_trap_fault);
 406                        break;
 407#if 0 /* unsupported */
 408                case both:
 409                        do_atomic(fetch_reg_addr(((insn>>25)&0x1f), regs),
 410                                  (unsigned long *) addr,
 411                                  kernel_unaligned_trap_fault);
 412                        break;
 413#endif
 414                default:
 415                        panic("Impossible kernel unaligned trap.");
 416                        /* Not reached... */
 417                }
 418                advance(regs);
 419        }
 420}
 421
 422static inline int ok_for_user(struct pt_regs *regs, unsigned int insn,
 423                              enum direction dir)
 424{
 425        unsigned int reg;
 426        int retval, check = (dir == load) ? VERIFY_READ : VERIFY_WRITE;
 427        int size = ((insn >> 19) & 3) == 3 ? 8 : 4;
 428
 429        if((regs->pc | regs->npc) & 3)
 430                return 0;
 431
 432        /* Must verify_area() in all the necessary places. */
 433#define WINREG_ADDR(regnum) ((void *)(((unsigned long *)regs->u_regs[UREG_FP])+(regnum)))
 434        retval = 0;
 435        reg = (insn >> 25) & 0x1f;
 436        if(reg >= 16) {
 437                retval = verify_area(check, WINREG_ADDR(reg - 16), size);
 438                if(retval)
 439                        return retval;
 440        }
 441        reg = (insn >> 14) & 0x1f;
 442        if(reg >= 16) {
 443                retval = verify_area(check, WINREG_ADDR(reg - 16), size);
 444                if(retval)
 445                        return retval;
 446        }
 447        if(!(insn & 0x2000)) {
 448                reg = (insn & 0x1f);
 449                if(reg >= 16) {
 450                        retval = verify_area(check, WINREG_ADDR(reg - 16), size);
 451                        if(retval)
 452                                return retval;
 453                }
 454        }
 455        return retval;
 456#undef WINREG_ADDR
 457}
 458
 459void user_mna_trap_fault(struct pt_regs *regs, unsigned int insn) __asm__ ("user_mna_trap_fault");
 460
 461void user_mna_trap_fault(struct pt_regs *regs, unsigned int insn)
 462{
 463        siginfo_t info;
 464
 465        info.si_signo = SIGBUS;
 466        info.si_errno = 0;
 467        info.si_code = BUS_ADRALN;
 468        info.si_addr = (void *)safe_compute_effective_address(regs, insn);
 469        info.si_trapno = 0;
 470        send_sig_info(SIGBUS, &info, current);
 471}
 472
 473asmlinkage void user_unaligned_trap(struct pt_regs *regs, unsigned int insn)
 474{
 475        enum direction dir;
 476
 477        lock_kernel();
 478        if(!(current->thread.flags & SPARC_FLAG_UNALIGNED) ||
 479           (((insn >> 30) & 3) != 3))
 480                goto kill_user;
 481        dir = decode_direction(insn);
 482        if(!ok_for_user(regs, insn, dir)) {
 483                goto kill_user;
 484        } else {
 485                int size = decode_access_size(insn);
 486                unsigned long addr;
 487
 488                if(floating_point_load_or_store_p(insn)) {
 489                        printk("User FPU load/store unaligned unsupported.\n");
 490                        goto kill_user;
 491                }
 492
 493                addr = compute_effective_address(regs, insn);
 494                switch(dir) {
 495                case load:
 496                        do_integer_load(fetch_reg_addr(((insn>>25)&0x1f), regs),
 497                                        size, (unsigned long *) addr,
 498                                        decode_signedness(insn),
 499                                        user_unaligned_trap_fault);
 500                        break;
 501
 502                case store:
 503                        do_integer_store(((insn>>25)&0x1f), size,
 504                                         (unsigned long *) addr, regs,
 505                                         user_unaligned_trap_fault);
 506                        break;
 507
 508                case both:
 509                        do_atomic(fetch_reg_addr(((insn>>25)&0x1f), regs),
 510                                  (unsigned long *) addr,
 511                                  user_unaligned_trap_fault);
 512                        break;
 513
 514                default:
 515                        unaligned_panic("Impossible user unaligned trap.");
 516
 517                        __asm__ __volatile__ ("\n"
 518"user_unaligned_trap_fault:\n\t"
 519                        "mov    %0, %%o0\n\t"
 520                        "call   user_mna_trap_fault\n\t"
 521                        " mov   %1, %%o1\n\t"
 522                        :
 523                        : "r" (regs), "r" (insn)
 524                        : "o0", "o1", "o2", "o3", "o4", "o5", "o7",
 525                          "g1", "g2", "g3", "g4", "g5", "g7", "cc");
 526                        goto out;
 527                }
 528                advance(regs);
 529                goto out;
 530        }
 531
 532kill_user:
 533        user_mna_trap_fault(regs, insn);
 534out:
 535        unlock_kernel();
 536}
 537
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.