linux/arch/parisc/kernel/unaligned.c
<<
>>
Prefs
   1/*
   2 *    Unaligned memory access handler
   3 *
   4 *    Copyright (C) 2001 Randolph Chung <tausq@debian.org>
   5 *    Significantly tweaked by LaMont Jones <lamont@debian.org>
   6 *
   7 *    This program is free software; you can redistribute it and/or modify
   8 *    it under the terms of the GNU General Public License as published by
   9 *    the Free Software Foundation; either version 2, or (at your option)
  10 *    any later version.
  11 *
  12 *    This program is distributed in the hope that it will be useful,
  13 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 *    GNU General Public License for more details.
  16 *
  17 *    You should have received a copy of the GNU General Public License
  18 *    along with this program; if not, write to the Free Software
  19 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20 *
  21 */
  22
  23#include <linux/jiffies.h>
  24#include <linux/kernel.h>
  25#include <linux/module.h>
  26#include <linux/sched.h>
  27#include <linux/signal.h>
  28#include <asm/uaccess.h>
  29
  30/* #define DEBUG_UNALIGNED 1 */
  31
  32#ifdef DEBUG_UNALIGNED
  33#define DPRINTF(fmt, args...) do { printk(KERN_DEBUG "%s:%d:%s ", __FILE__, __LINE__, __func__ ); printk(KERN_DEBUG fmt, ##args ); } while (0)
  34#else
  35#define DPRINTF(fmt, args...)
  36#endif
  37
  38#ifdef CONFIG_64BIT
  39#define RFMT "%016lx"
  40#else
  41#define RFMT "%08lx"
  42#endif
  43
  44#define FIXUP_BRANCH(lbl) \
  45        "\tldil L%%" #lbl ", %%r1\n"                    \
  46        "\tldo R%%" #lbl "(%%r1), %%r1\n"               \
  47        "\tbv,n %%r0(%%r1)\n"
  48/* If you use FIXUP_BRANCH, then you must list this clobber */
  49#define FIXUP_BRANCH_CLOBBER "r1"
  50
  51/* 1111 1100 0000 0000 0001 0011 1100 0000 */
  52#define OPCODE1(a,b,c)  ((a)<<26|(b)<<12|(c)<<6) 
  53#define OPCODE2(a,b)    ((a)<<26|(b)<<1)
  54#define OPCODE3(a,b)    ((a)<<26|(b)<<2)
  55#define OPCODE4(a)      ((a)<<26)
  56#define OPCODE1_MASK    OPCODE1(0x3f,1,0xf)
  57#define OPCODE2_MASK    OPCODE2(0x3f,1)
  58#define OPCODE3_MASK    OPCODE3(0x3f,1)
  59#define OPCODE4_MASK    OPCODE4(0x3f)
  60
  61/* skip LDB - never unaligned (index) */
  62#define OPCODE_LDH_I    OPCODE1(0x03,0,0x1)
  63#define OPCODE_LDW_I    OPCODE1(0x03,0,0x2)
  64#define OPCODE_LDD_I    OPCODE1(0x03,0,0x3)
  65#define OPCODE_LDDA_I   OPCODE1(0x03,0,0x4)
  66#define OPCODE_LDCD_I   OPCODE1(0x03,0,0x5)
  67#define OPCODE_LDWA_I   OPCODE1(0x03,0,0x6)
  68#define OPCODE_LDCW_I   OPCODE1(0x03,0,0x7)
  69/* skip LDB - never unaligned (short) */
  70#define OPCODE_LDH_S    OPCODE1(0x03,1,0x1)
  71#define OPCODE_LDW_S    OPCODE1(0x03,1,0x2)
  72#define OPCODE_LDD_S    OPCODE1(0x03,1,0x3)
  73#define OPCODE_LDDA_S   OPCODE1(0x03,1,0x4)
  74#define OPCODE_LDCD_S   OPCODE1(0x03,1,0x5)
  75#define OPCODE_LDWA_S   OPCODE1(0x03,1,0x6)
  76#define OPCODE_LDCW_S   OPCODE1(0x03,1,0x7)
  77/* skip STB - never unaligned */
  78#define OPCODE_STH      OPCODE1(0x03,1,0x9)
  79#define OPCODE_STW      OPCODE1(0x03,1,0xa)
  80#define OPCODE_STD      OPCODE1(0x03,1,0xb)
  81/* skip STBY - never unaligned */
  82/* skip STDBY - never unaligned */
  83#define OPCODE_STWA     OPCODE1(0x03,1,0xe)
  84#define OPCODE_STDA     OPCODE1(0x03,1,0xf)
  85
  86#define OPCODE_FLDWX    OPCODE1(0x09,0,0x0)
  87#define OPCODE_FLDWXR   OPCODE1(0x09,0,0x1)
  88#define OPCODE_FSTWX    OPCODE1(0x09,0,0x8)
  89#define OPCODE_FSTWXR   OPCODE1(0x09,0,0x9)
  90#define OPCODE_FLDWS    OPCODE1(0x09,1,0x0)
  91#define OPCODE_FLDWSR   OPCODE1(0x09,1,0x1)
  92#define OPCODE_FSTWS    OPCODE1(0x09,1,0x8)
  93#define OPCODE_FSTWSR   OPCODE1(0x09,1,0x9)
  94#define OPCODE_FLDDX    OPCODE1(0x0b,0,0x0)
  95#define OPCODE_FSTDX    OPCODE1(0x0b,0,0x8)
  96#define OPCODE_FLDDS    OPCODE1(0x0b,1,0x0)
  97#define OPCODE_FSTDS    OPCODE1(0x0b,1,0x8)
  98
  99#define OPCODE_LDD_L    OPCODE2(0x14,0)
 100#define OPCODE_FLDD_L   OPCODE2(0x14,1)
 101#define OPCODE_STD_L    OPCODE2(0x1c,0)
 102#define OPCODE_FSTD_L   OPCODE2(0x1c,1)
 103
 104#define OPCODE_LDW_M    OPCODE3(0x17,1)
 105#define OPCODE_FLDW_L   OPCODE3(0x17,0)
 106#define OPCODE_FSTW_L   OPCODE3(0x1f,0)
 107#define OPCODE_STW_M    OPCODE3(0x1f,1)
 108
 109#define OPCODE_LDH_L    OPCODE4(0x11)
 110#define OPCODE_LDW_L    OPCODE4(0x12)
 111#define OPCODE_LDWM     OPCODE4(0x13)
 112#define OPCODE_STH_L    OPCODE4(0x19)
 113#define OPCODE_STW_L    OPCODE4(0x1A)
 114#define OPCODE_STWM     OPCODE4(0x1B)
 115
 116#define MAJOR_OP(i) (((i)>>26)&0x3f)
 117#define R1(i) (((i)>>21)&0x1f)
 118#define R2(i) (((i)>>16)&0x1f)
 119#define R3(i) ((i)&0x1f)
 120#define FR3(i) ((((i)<<1)&0x1f)|(((i)>>6)&1))
 121#define IM(i,n) (((i)>>1&((1<<(n-1))-1))|((i)&1?((0-1L)<<(n-1)):0))
 122#define IM5_2(i) IM((i)>>16,5)
 123#define IM5_3(i) IM((i),5)
 124#define IM14(i) IM((i),14)
 125
 126#define ERR_NOTHANDLED  -1
 127#define ERR_PAGEFAULT   -2
 128
 129int unaligned_enabled __read_mostly = 1;
 130
 131void die_if_kernel (char *str, struct pt_regs *regs, long err);
 132
 133static int emulate_ldh(struct pt_regs *regs, int toreg)
 134{
 135        unsigned long saddr = regs->ior;
 136        unsigned long val = 0;
 137        int ret;
 138
 139        DPRINTF("load " RFMT ":" RFMT " to r%d for 2 bytes\n", 
 140                regs->isr, regs->ior, toreg);
 141
 142        __asm__ __volatile__  (
 143"       mtsp    %4, %%sr1\n"
 144"1:     ldbs    0(%%sr1,%3), %%r20\n"
 145"2:     ldbs    1(%%sr1,%3), %0\n"
 146"       depw    %%r20, 23, 24, %0\n"
 147"       copy    %%r0, %1\n"
 148"3:     \n"
 149"       .section .fixup,\"ax\"\n"
 150"4:     ldi     -2, %1\n"
 151        FIXUP_BRANCH(3b)
 152"       .previous\n"
 153        ASM_EXCEPTIONTABLE_ENTRY(1b, 4b)
 154        ASM_EXCEPTIONTABLE_ENTRY(2b, 4b)
 155        : "=r" (val), "=r" (ret)
 156        : "0" (val), "r" (saddr), "r" (regs->isr)
 157        : "r20", FIXUP_BRANCH_CLOBBER );
 158
 159        DPRINTF("val = 0x" RFMT "\n", val);
 160
 161        if (toreg)
 162                regs->gr[toreg] = val;
 163
 164        return ret;
 165}
 166
 167static int emulate_ldw(struct pt_regs *regs, int toreg, int flop)
 168{
 169        unsigned long saddr = regs->ior;
 170        unsigned long val = 0;
 171        int ret;
 172
 173        DPRINTF("load " RFMT ":" RFMT " to r%d for 4 bytes\n", 
 174                regs->isr, regs->ior, toreg);
 175
 176        __asm__ __volatile__  (
 177"       zdep    %3,28,2,%%r19\n"                /* r19=(ofs&3)*8 */
 178"       mtsp    %4, %%sr1\n"
 179"       depw    %%r0,31,2,%3\n"
 180"1:     ldw     0(%%sr1,%3),%0\n"
 181"2:     ldw     4(%%sr1,%3),%%r20\n"
 182"       subi    32,%%r19,%%r19\n"
 183"       mtctl   %%r19,11\n"
 184"       vshd    %0,%%r20,%0\n"
 185"       copy    %%r0, %1\n"
 186"3:     \n"
 187"       .section .fixup,\"ax\"\n"
 188"4:     ldi     -2, %1\n"
 189        FIXUP_BRANCH(3b)
 190"       .previous\n"
 191        ASM_EXCEPTIONTABLE_ENTRY(1b, 4b)
 192        ASM_EXCEPTIONTABLE_ENTRY(2b, 4b)
 193        : "=r" (val), "=r" (ret)
 194        : "0" (val), "r" (saddr), "r" (regs->isr)
 195        : "r19", "r20", FIXUP_BRANCH_CLOBBER );
 196
 197        DPRINTF("val = 0x" RFMT "\n", val);
 198
 199        if (flop)
 200                ((__u32*)(regs->fr))[toreg] = val;
 201        else if (toreg)
 202                regs->gr[toreg] = val;
 203
 204        return ret;
 205}
 206static int emulate_ldd(struct pt_regs *regs, int toreg, int flop)
 207{
 208        unsigned long saddr = regs->ior;
 209        __u64 val = 0;
 210        int ret;
 211
 212        DPRINTF("load " RFMT ":" RFMT " to r%d for 8 bytes\n", 
 213                regs->isr, regs->ior, toreg);
 214#ifdef CONFIG_PA20
 215
 216#ifndef CONFIG_64BIT
 217        if (!flop)
 218                return -1;
 219#endif
 220        __asm__ __volatile__  (
 221"       depd,z  %3,60,3,%%r19\n"                /* r19=(ofs&7)*8 */
 222"       mtsp    %4, %%sr1\n"
 223"       depd    %%r0,63,3,%3\n"
 224"1:     ldd     0(%%sr1,%3),%0\n"
 225"2:     ldd     8(%%sr1,%3),%%r20\n"
 226"       subi    64,%%r19,%%r19\n"
 227"       mtsar   %%r19\n"
 228"       shrpd   %0,%%r20,%%sar,%0\n"
 229"       copy    %%r0, %1\n"
 230"3:     \n"
 231"       .section .fixup,\"ax\"\n"
 232"4:     ldi     -2, %1\n"
 233        FIXUP_BRANCH(3b)
 234"       .previous\n"
 235        ASM_EXCEPTIONTABLE_ENTRY(1b,4b)
 236        ASM_EXCEPTIONTABLE_ENTRY(2b,4b)
 237        : "=r" (val), "=r" (ret)
 238        : "0" (val), "r" (saddr), "r" (regs->isr)
 239        : "r19", "r20", FIXUP_BRANCH_CLOBBER );
 240#else
 241    {
 242        unsigned long valh=0,vall=0;
 243        __asm__ __volatile__  (
 244"       zdep    %5,29,2,%%r19\n"                /* r19=(ofs&3)*8 */
 245"       mtsp    %6, %%sr1\n"
 246"       dep     %%r0,31,2,%5\n"
 247"1:     ldw     0(%%sr1,%5),%0\n"
 248"2:     ldw     4(%%sr1,%5),%1\n"
 249"3:     ldw     8(%%sr1,%5),%%r20\n"
 250"       subi    32,%%r19,%%r19\n"
 251"       mtsar   %%r19\n"
 252"       vshd    %0,%1,%0\n"
 253"       vshd    %1,%%r20,%1\n"
 254"       copy    %%r0, %2\n"
 255"4:     \n"
 256"       .section .fixup,\"ax\"\n"
 257"5:     ldi     -2, %2\n"
 258        FIXUP_BRANCH(4b)
 259"       .previous\n"
 260        ASM_EXCEPTIONTABLE_ENTRY(1b,5b)
 261        ASM_EXCEPTIONTABLE_ENTRY(2b,5b)
 262        ASM_EXCEPTIONTABLE_ENTRY(3b,5b)
 263        : "=r" (valh), "=r" (vall), "=r" (ret)
 264        : "0" (valh), "1" (vall), "r" (saddr), "r" (regs->isr)
 265        : "r19", "r20", FIXUP_BRANCH_CLOBBER );
 266        val=((__u64)valh<<32)|(__u64)vall;
 267    }
 268#endif
 269
 270        DPRINTF("val = 0x%llx\n", val);
 271
 272        if (flop)
 273                regs->fr[toreg] = val;
 274        else if (toreg)
 275                regs->gr[toreg] = val;
 276
 277        return ret;
 278}
 279
 280static int emulate_sth(struct pt_regs *regs, int frreg)
 281{
 282        unsigned long val = regs->gr[frreg];
 283        int ret;
 284
 285        if (!frreg)
 286                val = 0;
 287
 288        DPRINTF("store r%d (0x" RFMT ") to " RFMT ":" RFMT " for 2 bytes\n", frreg, 
 289                val, regs->isr, regs->ior);
 290
 291        __asm__ __volatile__ (
 292"       mtsp %3, %%sr1\n"
 293"       extrw,u %1, 23, 8, %%r19\n"
 294"1:     stb %1, 1(%%sr1, %2)\n"
 295"2:     stb %%r19, 0(%%sr1, %2)\n"
 296"       copy    %%r0, %0\n"
 297"3:     \n"
 298"       .section .fixup,\"ax\"\n"
 299"4:     ldi     -2, %0\n"
 300        FIXUP_BRANCH(3b)
 301"       .previous\n"
 302        ASM_EXCEPTIONTABLE_ENTRY(1b,4b)
 303        ASM_EXCEPTIONTABLE_ENTRY(2b,4b)
 304        : "=r" (ret)
 305        : "r" (val), "r" (regs->ior), "r" (regs->isr)
 306        : "r19", FIXUP_BRANCH_CLOBBER );
 307
 308        return ret;
 309}
 310
 311static int emulate_stw(struct pt_regs *regs, int frreg, int flop)
 312{
 313        unsigned long val;
 314        int ret;
 315
 316        if (flop)
 317                val = ((__u32*)(regs->fr))[frreg];
 318        else if (frreg)
 319                val = regs->gr[frreg];
 320        else
 321                val = 0;
 322
 323        DPRINTF("store r%d (0x" RFMT ") to " RFMT ":" RFMT " for 4 bytes\n", frreg, 
 324                val, regs->isr, regs->ior);
 325
 326
 327        __asm__ __volatile__ (
 328"       mtsp %3, %%sr1\n"
 329"       zdep    %2, 28, 2, %%r19\n"
 330"       dep     %%r0, 31, 2, %2\n"
 331"       mtsar   %%r19\n"
 332"       depwi,z -2, %%sar, 32, %%r19\n"
 333"1:     ldw     0(%%sr1,%2),%%r20\n"
 334"2:     ldw     4(%%sr1,%2),%%r21\n"
 335"       vshd    %%r0, %1, %%r22\n"
 336"       vshd    %1, %%r0, %%r1\n"
 337"       and     %%r20, %%r19, %%r20\n"
 338"       andcm   %%r21, %%r19, %%r21\n"
 339"       or      %%r22, %%r20, %%r20\n"
 340"       or      %%r1, %%r21, %%r21\n"
 341"       stw     %%r20,0(%%sr1,%2)\n"
 342"       stw     %%r21,4(%%sr1,%2)\n"
 343"       copy    %%r0, %0\n"
 344"3:     \n"
 345"       .section .fixup,\"ax\"\n"
 346"4:     ldi     -2, %0\n"
 347        FIXUP_BRANCH(3b)
 348"       .previous\n"
 349        ASM_EXCEPTIONTABLE_ENTRY(1b,4b)
 350        ASM_EXCEPTIONTABLE_ENTRY(2b,4b)
 351        : "=r" (ret)
 352        : "r" (val), "r" (regs->ior), "r" (regs->isr)
 353        : "r19", "r20", "r21", "r22", "r1", FIXUP_BRANCH_CLOBBER );
 354
 355        return 0;
 356}
 357static int emulate_std(struct pt_regs *regs, int frreg, int flop)
 358{
 359        __u64 val;
 360        int ret;
 361
 362        if (flop)
 363                val = regs->fr[frreg];
 364        else if (frreg)
 365                val = regs->gr[frreg];
 366        else
 367                val = 0;
 368
 369        DPRINTF("store r%d (0x%016llx) to " RFMT ":" RFMT " for 8 bytes\n", frreg, 
 370                val,  regs->isr, regs->ior);
 371
 372#ifdef CONFIG_PA20
 373#ifndef CONFIG_64BIT
 374        if (!flop)
 375                return -1;
 376#endif
 377        __asm__ __volatile__ (
 378"       mtsp %3, %%sr1\n"
 379"       depd,z  %2, 60, 3, %%r19\n"
 380"       depd    %%r0, 63, 3, %2\n"
 381"       mtsar   %%r19\n"
 382"       depdi,z -2, %%sar, 64, %%r19\n"
 383"1:     ldd     0(%%sr1,%2),%%r20\n"
 384"2:     ldd     8(%%sr1,%2),%%r21\n"
 385"       shrpd   %%r0, %1, %%sar, %%r22\n"
 386"       shrpd   %1, %%r0, %%sar, %%r1\n"
 387"       and     %%r20, %%r19, %%r20\n"
 388"       andcm   %%r21, %%r19, %%r21\n"
 389"       or      %%r22, %%r20, %%r20\n"
 390"       or      %%r1, %%r21, %%r21\n"
 391"3:     std     %%r20,0(%%sr1,%2)\n"
 392"4:     std     %%r21,8(%%sr1,%2)\n"
 393"       copy    %%r0, %0\n"
 394"5:     \n"
 395"       .section .fixup,\"ax\"\n"
 396"6:     ldi     -2, %0\n"
 397        FIXUP_BRANCH(5b)
 398"       .previous\n"
 399        ASM_EXCEPTIONTABLE_ENTRY(1b,6b)
 400        ASM_EXCEPTIONTABLE_ENTRY(2b,6b)
 401        ASM_EXCEPTIONTABLE_ENTRY(3b,6b)
 402        ASM_EXCEPTIONTABLE_ENTRY(4b,6b)
 403        : "=r" (ret)
 404        : "r" (val), "r" (regs->ior), "r" (regs->isr)
 405        : "r19", "r20", "r21", "r22", "r1", FIXUP_BRANCH_CLOBBER );
 406#else
 407    {
 408        unsigned long valh=(val>>32),vall=(val&0xffffffffl);
 409        __asm__ __volatile__ (
 410"       mtsp    %4, %%sr1\n"
 411"       zdep    %2, 29, 2, %%r19\n"
 412"       dep     %%r0, 31, 2, %2\n"
 413"       mtsar   %%r19\n"
 414"       zvdepi  -2, 32, %%r19\n"
 415"1:     ldw     0(%%sr1,%3),%%r20\n"
 416"2:     ldw     8(%%sr1,%3),%%r21\n"
 417"       vshd    %1, %2, %%r1\n"
 418"       vshd    %%r0, %1, %1\n"
 419"       vshd    %2, %%r0, %2\n"
 420"       and     %%r20, %%r19, %%r20\n"
 421"       andcm   %%r21, %%r19, %%r21\n"
 422"       or      %1, %%r20, %1\n"
 423"       or      %2, %%r21, %2\n"
 424"3:     stw     %1,0(%%sr1,%1)\n"
 425"4:     stw     %%r1,4(%%sr1,%3)\n"
 426"5:     stw     %2,8(%%sr1,%3)\n"
 427"       copy    %%r0, %0\n"
 428"6:     \n"
 429"       .section .fixup,\"ax\"\n"
 430"7:     ldi     -2, %0\n"
 431        FIXUP_BRANCH(6b)
 432"       .previous\n"
 433        ASM_EXCEPTIONTABLE_ENTRY(1b,7b)
 434        ASM_EXCEPTIONTABLE_ENTRY(2b,7b)
 435        ASM_EXCEPTIONTABLE_ENTRY(3b,7b)
 436        ASM_EXCEPTIONTABLE_ENTRY(4b,7b)
 437        ASM_EXCEPTIONTABLE_ENTRY(5b,7b)
 438        : "=r" (ret)
 439        : "r" (valh), "r" (vall), "r" (regs->ior), "r" (regs->isr)
 440        : "r19", "r20", "r21", "r1", FIXUP_BRANCH_CLOBBER );
 441    }
 442#endif
 443
 444        return ret;
 445}
 446
 447void handle_unaligned(struct pt_regs *regs)
 448{
 449        static unsigned long unaligned_count = 0;
 450        static unsigned long last_time = 0;
 451        unsigned long newbase = R1(regs->iir)?regs->gr[R1(regs->iir)]:0;
 452        int modify = 0;
 453        int ret = ERR_NOTHANDLED;
 454        struct siginfo si;
 455        register int flop=0;    /* true if this is a flop */
 456
 457        /* log a message with pacing */
 458        if (user_mode(regs)) {
 459                if (current->thread.flags & PARISC_UAC_SIGBUS) {
 460                        goto force_sigbus;
 461                }
 462
 463                if (unaligned_count > 5 &&
 464                                time_after(jiffies, last_time + 5 * HZ)) {
 465                        unaligned_count = 0;
 466                        last_time = jiffies;
 467                }
 468
 469                if (!(current->thread.flags & PARISC_UAC_NOPRINT) 
 470                    && ++unaligned_count < 5) {
 471                        char buf[256];
 472                        sprintf(buf, "%s(%d): unaligned access to 0x" RFMT " at ip=0x" RFMT "\n",
 473                                current->comm, task_pid_nr(current), regs->ior, regs->iaoq[0]);
 474                        printk(KERN_WARNING "%s", buf);
 475#ifdef DEBUG_UNALIGNED
 476                        show_regs(regs);
 477#endif          
 478                }
 479
 480                if (!unaligned_enabled)
 481                        goto force_sigbus;
 482        }
 483
 484        /* handle modification - OK, it's ugly, see the instruction manual */
 485        switch (MAJOR_OP(regs->iir))
 486        {
 487        case 0x03:
 488        case 0x09:
 489        case 0x0b:
 490                if (regs->iir&0x20)
 491                {
 492                        modify = 1;
 493                        if (regs->iir&0x1000)           /* short loads */
 494                                if (regs->iir&0x200)
 495                                        newbase += IM5_3(regs->iir);
 496                                else
 497                                        newbase += IM5_2(regs->iir);
 498                        else if (regs->iir&0x2000)      /* scaled indexed */
 499                        {
 500                                int shift=0;
 501                                switch (regs->iir & OPCODE1_MASK)
 502                                {
 503                                case OPCODE_LDH_I:
 504                                        shift= 1; break;
 505                                case OPCODE_LDW_I:
 506                                        shift= 2; break;
 507                                case OPCODE_LDD_I:
 508                                case OPCODE_LDDA_I:
 509                                        shift= 3; break;
 510                                }
 511                                newbase += (R2(regs->iir)?regs->gr[R2(regs->iir)]:0)<<shift;
 512                        } else                          /* simple indexed */
 513                                newbase += (R2(regs->iir)?regs->gr[R2(regs->iir)]:0);
 514                }
 515                break;
 516        case 0x13:
 517        case 0x1b:
 518                modify = 1;
 519                newbase += IM14(regs->iir);
 520                break;
 521        case 0x14:
 522        case 0x1c:
 523                if (regs->iir&8)
 524                {
 525                        modify = 1;
 526                        newbase += IM14(regs->iir&~0xe);
 527                }
 528                break;
 529        case 0x16:
 530        case 0x1e:
 531                modify = 1;
 532                newbase += IM14(regs->iir&6);
 533                break;
 534        case 0x17:
 535        case 0x1f:
 536                if (regs->iir&4)
 537                {
 538                        modify = 1;
 539                        newbase += IM14(regs->iir&~4);
 540                }
 541                break;
 542        }
 543
 544        /* TODO: make this cleaner... */
 545        switch (regs->iir & OPCODE1_MASK)
 546        {
 547        case OPCODE_LDH_I:
 548        case OPCODE_LDH_S:
 549                ret = emulate_ldh(regs, R3(regs->iir));
 550                break;
 551
 552        case OPCODE_LDW_I:
 553        case OPCODE_LDWA_I:
 554        case OPCODE_LDW_S:
 555        case OPCODE_LDWA_S:
 556                ret = emulate_ldw(regs, R3(regs->iir),0);
 557                break;
 558
 559        case OPCODE_STH:
 560                ret = emulate_sth(regs, R2(regs->iir));
 561                break;
 562
 563        case OPCODE_STW:
 564        case OPCODE_STWA:
 565                ret = emulate_stw(regs, R2(regs->iir),0);
 566                break;
 567
 568#ifdef CONFIG_PA20
 569        case OPCODE_LDD_I:
 570        case OPCODE_LDDA_I:
 571        case OPCODE_LDD_S:
 572        case OPCODE_LDDA_S:
 573                ret = emulate_ldd(regs, R3(regs->iir),0);
 574                break;
 575
 576        case OPCODE_STD:
 577        case OPCODE_STDA:
 578                ret = emulate_std(regs, R2(regs->iir),0);
 579                break;
 580#endif
 581
 582        case OPCODE_FLDWX:
 583        case OPCODE_FLDWS:
 584        case OPCODE_FLDWXR:
 585        case OPCODE_FLDWSR:
 586                flop=1;
 587                ret = emulate_ldw(regs,FR3(regs->iir),1);
 588                break;
 589
 590        case OPCODE_FLDDX:
 591        case OPCODE_FLDDS:
 592                flop=1;
 593                ret = emulate_ldd(regs,R3(regs->iir),1);
 594                break;
 595
 596        case OPCODE_FSTWX:
 597        case OPCODE_FSTWS:
 598        case OPCODE_FSTWXR:
 599        case OPCODE_FSTWSR:
 600                flop=1;
 601                ret = emulate_stw(regs,FR3(regs->iir),1);
 602                break;
 603
 604        case OPCODE_FSTDX:
 605        case OPCODE_FSTDS:
 606                flop=1;
 607                ret = emulate_std(regs,R3(regs->iir),1);
 608                break;
 609
 610        case OPCODE_LDCD_I:
 611        case OPCODE_LDCW_I:
 612        case OPCODE_LDCD_S:
 613        case OPCODE_LDCW_S:
 614                ret = ERR_NOTHANDLED;   /* "undefined", but lets kill them. */
 615                break;
 616        }
 617#ifdef CONFIG_PA20
 618        switch (regs->iir & OPCODE2_MASK)
 619        {
 620        case OPCODE_FLDD_L:
 621                flop=1;
 622                ret = emulate_ldd(regs,R2(regs->iir),1);
 623                break;
 624        case OPCODE_FSTD_L:
 625                flop=1;
 626                ret = emulate_std(regs, R2(regs->iir),1);
 627                break;
 628
 629#ifdef CONFIG_PA20
 630        case OPCODE_LDD_L:
 631                ret = emulate_ldd(regs, R2(regs->iir),0);
 632                break;
 633        case OPCODE_STD_L:
 634                ret = emulate_std(regs, R2(regs->iir),0);
 635                break;
 636#endif
 637        }
 638#endif
 639        switch (regs->iir & OPCODE3_MASK)
 640        {
 641        case OPCODE_FLDW_L:
 642                flop=1;
 643                ret = emulate_ldw(regs, R2(regs->iir),0);
 644                break;
 645        case OPCODE_LDW_M:
 646                ret = emulate_ldw(regs, R2(regs->iir),1);
 647                break;
 648
 649        case OPCODE_FSTW_L:
 650                flop=1;
 651                ret = emulate_stw(regs, R2(regs->iir),1);
 652                break;
 653        case OPCODE_STW_M:
 654                ret = emulate_stw(regs, R2(regs->iir),0);
 655                break;
 656        }
 657        switch (regs->iir & OPCODE4_MASK)
 658        {
 659        case OPCODE_LDH_L:
 660                ret = emulate_ldh(regs, R2(regs->iir));
 661                break;
 662        case OPCODE_LDW_L:
 663        case OPCODE_LDWM:
 664                ret = emulate_ldw(regs, R2(regs->iir),0);
 665                break;
 666        case OPCODE_STH_L:
 667                ret = emulate_sth(regs, R2(regs->iir));
 668                break;
 669        case OPCODE_STW_L:
 670        case OPCODE_STWM:
 671                ret = emulate_stw(regs, R2(regs->iir),0);
 672                break;
 673        }
 674
 675        if (modify && R1(regs->iir))
 676                regs->gr[R1(regs->iir)] = newbase;
 677
 678
 679        if (ret == ERR_NOTHANDLED)
 680                printk(KERN_CRIT "Not-handled unaligned insn 0x%08lx\n", regs->iir);
 681
 682        DPRINTF("ret = %d\n", ret);
 683
 684        if (ret)
 685        {
 686                printk(KERN_CRIT "Unaligned handler failed, ret = %d\n", ret);
 687                die_if_kernel("Unaligned data reference", regs, 28);
 688
 689                if (ret == ERR_PAGEFAULT)
 690                {
 691                        si.si_signo = SIGSEGV;
 692                        si.si_errno = 0;
 693                        si.si_code = SEGV_MAPERR;
 694                        si.si_addr = (void __user *)regs->ior;
 695                        force_sig_info(SIGSEGV, &si, current);
 696                }
 697                else
 698                {
 699force_sigbus:
 700                        /* couldn't handle it ... */
 701                        si.si_signo = SIGBUS;
 702                        si.si_errno = 0;
 703                        si.si_code = BUS_ADRALN;
 704                        si.si_addr = (void __user *)regs->ior;
 705                        force_sig_info(SIGBUS, &si, current);
 706                }
 707                
 708                return;
 709        }
 710
 711        /* else we handled it, let life go on. */
 712        regs->gr[0]|=PSW_N;
 713}
 714
 715/*
 716 * NB: check_unaligned() is only used for PCXS processors right
 717 * now, so we only check for PA1.1 encodings at this point.
 718 */
 719
 720int
 721check_unaligned(struct pt_regs *regs)
 722{
 723        unsigned long align_mask;
 724
 725        /* Get alignment mask */
 726
 727        align_mask = 0UL;
 728        switch (regs->iir & OPCODE1_MASK) {
 729
 730        case OPCODE_LDH_I:
 731        case OPCODE_LDH_S:
 732        case OPCODE_STH:
 733                align_mask = 1UL;
 734                break;
 735
 736        case OPCODE_LDW_I:
 737        case OPCODE_LDWA_I:
 738        case OPCODE_LDW_S:
 739        case OPCODE_LDWA_S:
 740        case OPCODE_STW:
 741        case OPCODE_STWA:
 742                align_mask = 3UL;
 743                break;
 744
 745        default:
 746                switch (regs->iir & OPCODE4_MASK) {
 747                case OPCODE_LDH_L:
 748                case OPCODE_STH_L:
 749                        align_mask = 1UL;
 750                        break;
 751                case OPCODE_LDW_L:
 752                case OPCODE_LDWM:
 753                case OPCODE_STW_L:
 754                case OPCODE_STWM:
 755                        align_mask = 3UL;
 756                        break;
 757                }
 758                break;
 759        }
 760
 761        return (int)(regs->ior & align_mask);
 762}
 763
 764