linux/arch/mn10300/mm/misalignment.c
<<
>>
Prefs
   1/* MN10300 Misalignment fixup handler
   2 *
   3 * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
   4 * Written by David Howells (dhowells@redhat.com)
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public Licence
   8 * as published by the Free Software Foundation; either version
   9 * 2 of the Licence, or (at your option) any later version.
  10 */
  11#include <linux/module.h>
  12#include <linux/sched.h>
  13#include <linux/kernel.h>
  14#include <linux/string.h>
  15#include <linux/errno.h>
  16#include <linux/ptrace.h>
  17#include <linux/timer.h>
  18#include <linux/mm.h>
  19#include <linux/smp.h>
  20#include <linux/smp_lock.h>
  21#include <linux/init.h>
  22#include <linux/delay.h>
  23#include <linux/spinlock.h>
  24#include <linux/interrupt.h>
  25#include <linux/pci.h>
  26#include <asm/processor.h>
  27#include <asm/system.h>
  28#include <asm/uaccess.h>
  29#include <asm/io.h>
  30#include <asm/atomic.h>
  31#include <asm/smp.h>
  32#include <asm/pgalloc.h>
  33#include <asm/cpu-regs.h>
  34#include <asm/busctl-regs.h>
  35#include <asm/fpu.h>
  36#include <asm/gdb-stub.h>
  37#include <asm/asm-offsets.h>
  38
  39#if 0
  40#define kdebug(FMT, ...) printk(KERN_DEBUG FMT, ##__VA_ARGS__)
  41#else
  42#define kdebug(FMT, ...) do {} while (0)
  43#endif
  44
  45static int misalignment_addr(unsigned long *registers, unsigned params,
  46                             unsigned opcode, unsigned disp,
  47                             void **_address, unsigned long **_postinc);
  48
  49static int misalignment_reg(unsigned long *registers, unsigned params,
  50                            unsigned opcode, unsigned disp,
  51                            unsigned long **_register);
  52
  53static inline unsigned int_log2(unsigned x)
  54{
  55        unsigned y;
  56        asm("bsch %1,%0" : "=r"(y) : "r"(x), "0"(0));
  57        return y;
  58}
  59#define log2(x) int_log2(x)
  60
  61static const unsigned Dreg_index[] = {
  62        REG_D0 >> 2, REG_D1 >> 2, REG_D2 >> 2, REG_D3 >> 2
  63};
  64
  65static const unsigned Areg_index[] = {
  66        REG_A0 >> 2, REG_A1 >> 2, REG_A2 >> 2, REG_A3 >> 2
  67};
  68
  69static const unsigned Rreg_index[] = {
  70        REG_E0 >> 2, REG_E1 >> 2, REG_E2 >> 2, REG_E3 >> 2,
  71        REG_E4 >> 2, REG_E5 >> 2, REG_E6 >> 2, REG_E7 >> 2,
  72        REG_A0 >> 2, REG_A1 >> 2, REG_A2 >> 2, REG_A3 >> 2,
  73        REG_D0 >> 2, REG_D1 >> 2, REG_D2 >> 2, REG_D3 >> 2
  74};
  75
  76enum format_id {
  77        FMT_S0,
  78        FMT_S1,
  79        FMT_S2,
  80        FMT_S4,
  81        FMT_D0,
  82        FMT_D1,
  83        FMT_D2,
  84        FMT_D4,
  85        FMT_D6,
  86        FMT_D7,
  87        FMT_D8,
  88        FMT_D9,
  89};
  90
  91struct {
  92        u_int8_t opsz, dispsz;
  93} format_tbl[16] = {
  94        [FMT_S0]        = { 8,  0       },
  95        [FMT_S1]        = { 8,  8       },
  96        [FMT_S2]        = { 8,  16      },
  97        [FMT_S4]        = { 8,  32      },
  98        [FMT_D0]        = { 16, 0       },
  99        [FMT_D1]        = { 16, 8       },
 100        [FMT_D2]        = { 16, 16      },
 101        [FMT_D4]        = { 16, 32      },
 102        [FMT_D6]        = { 24, 0       },
 103        [FMT_D7]        = { 24, 8       },
 104        [FMT_D8]        = { 24, 24      },
 105        [FMT_D9]        = { 24, 32      },
 106};
 107
 108enum value_id {
 109        DM0,            /* data reg in opcode in bits 0-1 */
 110        DM1,            /* data reg in opcode in bits 2-3 */
 111        DM2,            /* data reg in opcode in bits 4-5 */
 112        AM0,            /* addr reg in opcode in bits 0-1 */
 113        AM1,            /* addr reg in opcode in bits 2-3 */
 114        AM2,            /* addr reg in opcode in bits 4-5 */
 115        RM0,            /* reg in opcode in bits 0-3 */
 116        RM1,            /* reg in opcode in bits 2-5 */
 117        RM2,            /* reg in opcode in bits 4-7 */
 118        RM4,            /* reg in opcode in bits 8-11 */
 119        RM6,            /* reg in opcode in bits 12-15 */
 120
 121        RD0,            /* reg in displacement in bits 0-3 */
 122        RD2,            /* reg in displacement in bits 4-7 */
 123
 124        SP,             /* stack pointer */
 125
 126        SD8,            /* 8-bit signed displacement */
 127        SD16,           /* 16-bit signed displacement */
 128        SD24,           /* 24-bit signed displacement */
 129        SIMM4_2,        /* 4-bit signed displacement in opcode bits 4-7 */
 130        SIMM8,          /* 8-bit signed immediate */
 131        IMM24,          /* 24-bit unsigned immediate */
 132        IMM32,          /* 32-bit unsigned immediate */
 133        IMM32_HIGH8,    /* 32-bit unsigned immediate, high 8-bits in opcode */
 134
 135        DN0     = DM0,
 136        DN1     = DM1,
 137        DN2     = DM2,
 138        AN0     = AM0,
 139        AN1     = AM1,
 140        AN2     = AM2,
 141        RN0     = RM0,
 142        RN1     = RM1,
 143        RN2     = RM2,
 144        RN4     = RM4,
 145        RN6     = RM6,
 146        DI      = DM1,
 147        RI      = RM2,
 148
 149};
 150
 151struct mn10300_opcode {
 152        const char      *name;
 153        u_int32_t       opcode;
 154        u_int32_t       opmask;
 155        unsigned        exclusion;
 156
 157        enum format_id  format;
 158
 159        unsigned        cpu_mask;
 160#define AM33    330
 161
 162        unsigned        params[2];
 163#define MEM(ADDR)               (0x80000000 | (ADDR))
 164#define MEM2(ADDR1, ADDR2)      (0x80000000 | (ADDR1) << 8 | (ADDR2))
 165#define MEMINC(ADDR)            (0x81000000 | (ADDR))
 166#define MEMINC2(ADDR, INC)      (0x81000000 | (ADDR) << 8 | (INC))
 167};
 168
 169/* LIBOPCODES EXCERPT
 170   Assemble Matsushita MN10300 instructions.
 171   Copyright 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
 172
 173   This program is free software; you can redistribute it and/or modify
 174   it under the terms of the GNU General Public Licence as published by
 175   the Free Software Foundation; either version 2 of the Licence, or
 176   (at your option) any later version.
 177
 178   This program is distributed in the hope that it will be useful,
 179   but WITHOUT ANY WARRANTY; without even the implied warranty of
 180   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 181   GNU General Public Licence for more details.
 182
 183   You should have received a copy of the GNU General Public Licence
 184   along with this program; if not, write to the Free Software
 185   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 186*/
 187static const struct mn10300_opcode mn10300_opcodes[] = {
 188{ "mov",        0x60,        0xf0,        0,    FMT_S0, 0,      {DM1, MEM(AN0)}},
 189{ "mov",        0x70,        0xf0,        0,    FMT_S0, 0,      {MEM(AM0), DN1}},
 190{ "mov",        0xf000,      0xfff0,      0,    FMT_D0, 0,      {MEM(AM0), AN1}},
 191{ "mov",        0xf010,      0xfff0,      0,    FMT_D0, 0,      {AM1, MEM(AN0)}},
 192{ "mov",        0xf300,      0xffc0,      0,    FMT_D0, 0,      {MEM2(DI, AM0), DN2}},
 193{ "mov",        0xf340,      0xffc0,      0,    FMT_D0, 0,      {DM2, MEM2(DI, AN0)}},
 194{ "mov",        0xf380,      0xffc0,      0,    FMT_D0, 0,      {MEM2(DI, AM0), AN2}},
 195{ "mov",        0xf3c0,      0xffc0,      0,    FMT_D0, 0,      {AM2, MEM2(DI, AN0)}},
 196{ "mov",        0xf80000,    0xfff000,    0,    FMT_D1, 0,      {MEM2(SD8, AM0), DN1}},
 197{ "mov",        0xf81000,    0xfff000,    0,    FMT_D1, 0,      {DM1, MEM2(SD8, AN0)}},
 198{ "mov",        0xf82000,    0xfff000,    0,    FMT_D1, 0,      {MEM2(SD8,AM0), AN1}},
 199{ "mov",        0xf83000,    0xfff000,    0,    FMT_D1, 0,      {AM1, MEM2(SD8, AN0)}},
 200{ "mov",        0xf8f000,    0xfffc00,    0,    FMT_D1, AM33,   {MEM2(SD8, AM0), SP}},
 201{ "mov",        0xf8f400,    0xfffc00,    0,    FMT_D1, AM33,   {SP, MEM2(SD8, AN0)}},
 202{ "mov",        0xf90a00,    0xffff00,    0,    FMT_D6, AM33,   {MEM(RM0), RN2}},
 203{ "mov",        0xf91a00,    0xffff00,    0,    FMT_D6, AM33,   {RM2, MEM(RN0)}},
 204{ "mov",        0xf96a00,    0xffff00,    0x12, FMT_D6, AM33,   {MEMINC(RM0), RN2}},
 205{ "mov",        0xf97a00,    0xffff00,    0,    FMT_D6, AM33,   {RM2, MEMINC(RN0)}},
 206{ "mov",        0xfa000000,  0xfff00000,  0,    FMT_D2, 0,      {MEM2(SD16, AM0), DN1}},
 207{ "mov",        0xfa100000,  0xfff00000,  0,    FMT_D2, 0,      {DM1, MEM2(SD16, AN0)}},
 208{ "mov",        0xfa200000,  0xfff00000,  0,    FMT_D2, 0,      {MEM2(SD16, AM0), AN1}},
 209{ "mov",        0xfa300000,  0xfff00000,  0,    FMT_D2, 0,      {AM1, MEM2(SD16, AN0)}},
 210{ "mov",        0xfb0a0000,  0xffff0000,  0,    FMT_D7, AM33,   {MEM2(SD8, RM0), RN2}},
 211{ "mov",        0xfb1a0000,  0xffff0000,  0,    FMT_D7, AM33,   {RM2, MEM2(SD8, RN0)}},
 212{ "mov",        0xfb6a0000,  0xffff0000,  0x22, FMT_D7, AM33,   {MEMINC2 (RM0, SIMM8), RN2}},
 213{ "mov",        0xfb7a0000,  0xffff0000,  0,    FMT_D7, AM33,   {RM2, MEMINC2 (RN0, SIMM8)}},
 214{ "mov",        0xfb8e0000,  0xffff000f,  0,    FMT_D7, AM33,   {MEM2(RI, RM0), RD2}},
 215{ "mov",        0xfb9e0000,  0xffff000f,  0,    FMT_D7, AM33,   {RD2, MEM2(RI, RN0)}},
 216{ "mov",        0xfc000000,  0xfff00000,  0,    FMT_D4, 0,      {MEM2(IMM32,AM0), DN1}},
 217{ "mov",        0xfc100000,  0xfff00000,  0,    FMT_D4, 0,      {DM1, MEM2(IMM32,AN0)}},
 218{ "mov",        0xfc200000,  0xfff00000,  0,    FMT_D4, 0,      {MEM2(IMM32,AM0), AN1}},
 219{ "mov",        0xfc300000,  0xfff00000,  0,    FMT_D4, 0,      {AM1, MEM2(IMM32,AN0)}},
 220{ "mov",        0xfd0a0000,  0xffff0000,  0,    FMT_D8, AM33,   {MEM2(SD24, RM0), RN2}},
 221{ "mov",        0xfd1a0000,  0xffff0000,  0,    FMT_D8, AM33,   {RM2, MEM2(SD24, RN0)}},
 222{ "mov",        0xfd6a0000,  0xffff0000,  0x22, FMT_D8, AM33,   {MEMINC2 (RM0, IMM24), RN2}},
 223{ "mov",        0xfd7a0000,  0xffff0000,  0,    FMT_D8, AM33,   {RM2, MEMINC2 (RN0, IMM24)}},
 224{ "mov",        0xfe0a0000,  0xffff0000,  0,    FMT_D9, AM33,   {MEM2(IMM32_HIGH8,RM0), RN2}},
 225{ "mov",        0xfe1a0000,  0xffff0000,  0,    FMT_D9, AM33,   {RM2, MEM2(IMM32_HIGH8, RN0)}},
 226{ "mov",        0xfe6a0000,  0xffff0000,  0x22, FMT_D9, AM33,   {MEMINC2 (RM0, IMM32_HIGH8), RN2}},
 227{ "mov",        0xfe7a0000,  0xffff0000,  0,    FMT_D9, AM33,   {RN2, MEMINC2 (RM0, IMM32_HIGH8)}},
 228
 229{ "movhu",      0xf060,      0xfff0,      0,    FMT_D0, 0,      {MEM(AM0), DN1}},
 230{ "movhu",      0xf070,      0xfff0,      0,    FMT_D0, 0,      {DM1, MEM(AN0)}},
 231{ "movhu",      0xf480,      0xffc0,      0,    FMT_D0, 0,      {MEM2(DI, AM0), DN2}},
 232{ "movhu",      0xf4c0,      0xffc0,      0,    FMT_D0, 0,      {DM2, MEM2(DI, AN0)}},
 233{ "movhu",      0xf86000,    0xfff000,    0,    FMT_D1, 0,      {MEM2(SD8, AM0), DN1}},
 234{ "movhu",      0xf87000,    0xfff000,    0,    FMT_D1, 0,      {DM1, MEM2(SD8, AN0)}},
 235{ "movhu",      0xf94a00,    0xffff00,    0,    FMT_D6, AM33,   {MEM(RM0), RN2}},
 236{ "movhu",      0xf95a00,    0xffff00,    0,    FMT_D6, AM33,   {RM2, MEM(RN0)}},
 237{ "movhu",      0xf9ea00,    0xffff00,    0x12, FMT_D6, AM33,   {MEMINC(RM0), RN2}},
 238{ "movhu",      0xf9fa00,    0xffff00,    0,    FMT_D6, AM33,   {RM2, MEMINC(RN0)}},
 239{ "movhu",      0xfa600000,  0xfff00000,  0,    FMT_D2, 0,      {MEM2(SD16, AM0), DN1}},
 240{ "movhu",      0xfa700000,  0xfff00000,  0,    FMT_D2, 0,      {DM1, MEM2(SD16, AN0)}},
 241{ "movhu",      0xfb4a0000,  0xffff0000,  0,    FMT_D7, AM33,   {MEM2(SD8, RM0), RN2}},
 242{ "movhu",      0xfb5a0000,  0xffff0000,  0,    FMT_D7, AM33,   {RM2, MEM2(SD8, RN0)}},
 243{ "movhu",      0xfbce0000,  0xffff000f,  0,    FMT_D7, AM33,   {MEM2(RI, RM0), RD2}},
 244{ "movhu",      0xfbde0000,  0xffff000f,  0,    FMT_D7, AM33,   {RD2, MEM2(RI, RN0)}},
 245{ "movhu",      0xfbea0000,  0xffff0000,  0x22, FMT_D7, AM33,   {MEMINC2 (RM0, SIMM8), RN2}},
 246{ "movhu",      0xfbfa0000,  0xffff0000,  0,    FMT_D7, AM33,   {RM2, MEMINC2 (RN0, SIMM8)}},
 247{ "movhu",      0xfc600000,  0xfff00000,  0,    FMT_D4, 0,      {MEM2(IMM32,AM0), DN1}},
 248{ "movhu",      0xfc700000,  0xfff00000,  0,    FMT_D4, 0,      {DM1, MEM2(IMM32,AN0)}},
 249{ "movhu",      0xfd4a0000,  0xffff0000,  0,    FMT_D8, AM33,   {MEM2(SD24, RM0), RN2}},
 250{ "movhu",      0xfd5a0000,  0xffff0000,  0,    FMT_D8, AM33,   {RM2, MEM2(SD24, RN0)}},
 251{ "movhu",      0xfdea0000,  0xffff0000,  0x22, FMT_D8, AM33,   {MEMINC2 (RM0, IMM24), RN2}},
 252{ "movhu",      0xfdfa0000,  0xffff0000,  0,    FMT_D8, AM33,   {RM2, MEMINC2 (RN0, IMM24)}},
 253{ "movhu",      0xfe4a0000,  0xffff0000,  0,    FMT_D9, AM33,   {MEM2(IMM32_HIGH8,RM0), RN2}},
 254{ "movhu",      0xfe5a0000,  0xffff0000,  0,    FMT_D9, AM33,   {RM2, MEM2(IMM32_HIGH8, RN0)}},
 255{ "movhu",      0xfeea0000,  0xffff0000,  0x22, FMT_D9, AM33,   {MEMINC2 (RM0, IMM32_HIGH8), RN2}},
 256{ "movhu",      0xfefa0000,  0xffff0000,  0,    FMT_D9, AM33,   {RN2, MEMINC2 (RM0, IMM32_HIGH8)}},
 257{ 0, 0, 0, 0, 0, 0, {0}},
 258};
 259
 260/*
 261 * fix up misalignment problems where possible
 262 */
 263asmlinkage void misalignment(struct pt_regs *regs, enum exception_code code)
 264{
 265        const struct exception_table_entry *fixup;
 266        const struct mn10300_opcode *pop;
 267        unsigned long *registers = (unsigned long *) regs;
 268        unsigned long data, *store, *postinc;
 269        mm_segment_t seg;
 270        siginfo_t info;
 271        uint32_t opcode, disp, noc, xo, xm;
 272        uint8_t *pc, byte;
 273        void *address;
 274        unsigned tmp, npop;
 275
 276        kdebug("MISALIGN at %lx\n", regs->pc);
 277
 278        if (in_interrupt())
 279                die("Misalignment trap in interrupt context", regs, code);
 280
 281        if (regs->epsw & EPSW_IE)
 282                asm volatile("or %0,epsw" : : "i"(EPSW_IE));
 283
 284        seg = get_fs();
 285        set_fs(KERNEL_DS);
 286
 287        fixup = search_exception_tables(regs->pc);
 288
 289        /* first thing to do is to match the opcode */
 290        pc = (u_int8_t *) regs->pc;
 291
 292        if (__get_user(byte, pc) != 0)
 293                goto fetch_error;
 294        opcode = byte;
 295        noc = 8;
 296
 297        for (pop = mn10300_opcodes; pop->name; pop++) {
 298                npop = log2(pop->opcode | pop->opmask);
 299                if (npop <= 0 || npop > 31)
 300                        continue;
 301                npop = (npop + 8) & ~7;
 302
 303        got_more_bits:
 304                if (npop == noc) {
 305                        if ((opcode & pop->opmask) == pop->opcode)
 306                                goto found_opcode;
 307                } else if (npop > noc) {
 308                        xo = pop->opcode >> (npop - noc);
 309                        xm = pop->opmask >> (npop - noc);
 310
 311                        if ((opcode & xm) != xo)
 312                                continue;
 313
 314                        /* we've got a partial match (an exact match on the
 315                         * first N bytes), so we need to get some more data */
 316                        pc++;
 317                        if (__get_user(byte, pc) != 0)
 318                                goto fetch_error;
 319                        opcode = opcode << 8 | byte;
 320                        noc += 8;
 321                        goto got_more_bits;
 322                } else {
 323                        /* there's already been a partial match as long as the
 324                         * complete match we're now considering, so this one
 325                         * should't match */
 326                        continue;
 327                }
 328        }
 329
 330        /* didn't manage to find a fixup */
 331        if (!user_mode(regs))
 332                printk(KERN_CRIT "MISALIGN: %lx: unsupported instruction %x\n",
 333                       regs->pc, opcode);
 334
 335failed:
 336        set_fs(seg);
 337        if (die_if_no_fixup("misalignment error", regs, code))
 338                return;
 339
 340        info.si_signo   = SIGBUS;
 341        info.si_errno   = 0;
 342        info.si_code    = BUS_ADRALN;
 343        info.si_addr    = (void *) regs->pc;
 344        force_sig_info(SIGBUS, &info, current);
 345        return;
 346
 347        /* error reading opcodes */
 348fetch_error:
 349        if (!user_mode(regs))
 350                printk(KERN_CRIT
 351                       "MISALIGN: %p: fault whilst reading instruction data\n",
 352                       pc);
 353        goto failed;
 354
 355bad_addr_mode:
 356        if (!user_mode(regs))
 357                printk(KERN_CRIT
 358                       "MISALIGN: %lx: unsupported addressing mode %x\n",
 359                       regs->pc, opcode);
 360        goto failed;
 361
 362bad_reg_mode:
 363        if (!user_mode(regs))
 364                printk(KERN_CRIT
 365                       "MISALIGN: %lx: unsupported register mode %x\n",
 366                       regs->pc, opcode);
 367        goto failed;
 368
 369unsupported_instruction:
 370        if (!user_mode(regs))
 371                printk(KERN_CRIT
 372                       "MISALIGN: %lx: unsupported instruction %x (%s)\n",
 373                       regs->pc, opcode, pop->name);
 374        goto failed;
 375
 376transfer_failed:
 377        set_fs(seg);
 378        if (fixup) {
 379                regs->pc = fixup->fixup;
 380                return;
 381        }
 382        if (die_if_no_fixup("misalignment fixup", regs, code))
 383                return;
 384
 385        info.si_signo   = SIGSEGV;
 386        info.si_errno   = 0;
 387        info.si_code    = 0;
 388        info.si_addr    = (void *) regs->pc;
 389        force_sig_info(SIGSEGV, &info, current);
 390        return;
 391
 392        /* we matched the opcode */
 393found_opcode:
 394        kdebug("MISALIGN: %lx: %x==%x { %x, %x }\n",
 395               regs->pc, opcode, pop->opcode, pop->params[0], pop->params[1]);
 396
 397        tmp = format_tbl[pop->format].opsz;
 398        if (tmp > noc)
 399                BUG(); /* match was less complete than it ought to have been */
 400
 401        if (tmp < noc) {
 402                tmp = noc - tmp;
 403                opcode >>= tmp;
 404                pc -= tmp >> 3;
 405        }
 406
 407        /* grab the extra displacement (note it's LSB first) */
 408        disp = 0;
 409        tmp = format_tbl[pop->format].dispsz >> 3;
 410        while (tmp > 0) {
 411                tmp--;
 412                disp <<= 8;
 413
 414                pc++;
 415                if (__get_user(byte, pc) != 0)
 416                        goto fetch_error;
 417                disp |= byte;
 418        }
 419
 420        set_fs(KERNEL_XDS);
 421        if (fixup || regs->epsw & EPSW_nSL)
 422                set_fs(seg);
 423
 424        tmp = (pop->params[0] ^ pop->params[1]) & 0x80000000;
 425        if (!tmp) {
 426                if (!user_mode(regs))
 427                        printk(KERN_CRIT
 428                               "MISALIGN: %lx:"
 429                               " insn not move to/from memory %x\n",
 430                               regs->pc, opcode);
 431                goto failed;
 432        }
 433
 434        if (pop->params[0] & 0x80000000) {
 435                /* move memory to register */
 436                if (!misalignment_addr(registers, pop->params[0], opcode, disp,
 437                                       &address, &postinc))
 438                        goto bad_addr_mode;
 439
 440                if (!misalignment_reg(registers, pop->params[1], opcode, disp,
 441                                      &store))
 442                        goto bad_reg_mode;
 443
 444                if (strcmp(pop->name, "mov") == 0) {
 445                        kdebug("FIXUP: mov (%p),DARn\n", address);
 446                        if (copy_from_user(&data, (void *) address, 4) != 0)
 447                                goto transfer_failed;
 448                        if (pop->params[0] & 0x1000000)
 449                                *postinc += 4;
 450                } else if (strcmp(pop->name, "movhu") == 0) {
 451                        kdebug("FIXUP: movhu (%p),DARn\n", address);
 452                        data = 0;
 453                        if (copy_from_user(&data, (void *) address, 2) != 0)
 454                                goto transfer_failed;
 455                        if (pop->params[0] & 0x1000000)
 456                                *postinc += 2;
 457                } else {
 458                        goto unsupported_instruction;
 459                }
 460
 461                *store = data;
 462        } else {
 463                /* move register to memory */
 464                if (!misalignment_reg(registers, pop->params[0], opcode, disp,
 465                                      &store))
 466                        goto bad_reg_mode;
 467
 468                if (!misalignment_addr(registers, pop->params[1], opcode, disp,
 469                                       &address, &postinc))
 470                        goto bad_addr_mode;
 471
 472                data = *store;
 473
 474                if (strcmp(pop->name, "mov") == 0) {
 475                        kdebug("FIXUP: mov %lx,(%p)\n", data, address);
 476                        if (copy_to_user((void *) address, &data, 4) != 0)
 477                                goto transfer_failed;
 478                        if (pop->params[1] & 0x1000000)
 479                                *postinc += 4;
 480                } else if (strcmp(pop->name, "movhu") == 0) {
 481                        kdebug("FIXUP: movhu %hx,(%p)\n",
 482                               (uint16_t) data, address);
 483                        if (copy_to_user((void *) address, &data, 2) != 0)
 484                                goto transfer_failed;
 485                        if (pop->params[1] & 0x1000000)
 486                                *postinc += 2;
 487                } else {
 488                        goto unsupported_instruction;
 489                }
 490        }
 491
 492        tmp = format_tbl[pop->format].opsz + format_tbl[pop->format].dispsz;
 493        regs->pc += tmp >> 3;
 494
 495        set_fs(seg);
 496        return;
 497}
 498
 499/*
 500 * determine the address that was being accessed
 501 */
 502static int misalignment_addr(unsigned long *registers, unsigned params,
 503                             unsigned opcode, unsigned disp,
 504                             void **_address, unsigned long **_postinc)
 505{
 506        unsigned long *postinc = NULL, address = 0, tmp;
 507
 508        params &= 0x7fffffff;
 509
 510        do {
 511                switch (params & 0xff) {
 512                case DM0:
 513                        postinc = &registers[Dreg_index[opcode & 0x03]];
 514                        address += *postinc;
 515                        break;
 516                case DM1:
 517                        postinc = &registers[Dreg_index[opcode >> 2 & 0x0c]];
 518                        address += *postinc;
 519                        break;
 520                case DM2:
 521                        postinc = &registers[Dreg_index[opcode >> 4 & 0x30]];
 522                        address += *postinc;
 523                        break;
 524                case AM0:
 525                        postinc = &registers[Areg_index[opcode & 0x03]];
 526                        address += *postinc;
 527                        break;
 528                case AM1:
 529                        postinc = &registers[Areg_index[opcode >> 2 & 0x0c]];
 530                        address += *postinc;
 531                        break;
 532                case AM2:
 533                        postinc = &registers[Areg_index[opcode >> 4 & 0x30]];
 534                        address += *postinc;
 535                        break;
 536                case RM0:
 537                        postinc = &registers[Rreg_index[opcode & 0x0f]];
 538                        address += *postinc;
 539                        break;
 540                case RM1:
 541                        postinc = &registers[Rreg_index[opcode >> 2 & 0x0f]];
 542                        address += *postinc;
 543                        break;
 544                case RM2:
 545                        postinc = &registers[Rreg_index[opcode >> 4 & 0x0f]];
 546                        address += *postinc;
 547                        break;
 548                case RM4:
 549                        postinc = &registers[Rreg_index[opcode >> 8 & 0x0f]];
 550                        address += *postinc;
 551                        break;
 552                case RM6:
 553                        postinc = &registers[Rreg_index[opcode >> 12 & 0x0f]];
 554                        address += *postinc;
 555                        break;
 556                case RD0:
 557                        postinc = &registers[Rreg_index[disp & 0x0f]];
 558                        address += *postinc;
 559                        break;
 560                case RD2:
 561                        postinc = &registers[Rreg_index[disp >> 4 & 0x0f]];
 562                        address += *postinc;
 563                        break;
 564
 565                case SD8:
 566                case SIMM8:
 567                        address += (int32_t) (int8_t) (disp & 0xff);
 568                        break;
 569                case SD16:
 570                        address += (int32_t) (int16_t) (disp & 0xffff);
 571                        break;
 572                case SD24:
 573                        tmp = disp << 8;
 574                        asm("asr 8,%0" : "=r"(tmp) : "0"(tmp));
 575                        address += tmp;
 576                        break;
 577                case SIMM4_2:
 578                        tmp = opcode >> 4 & 0x0f;
 579                        tmp <<= 28;
 580                        asm("asr 28,%0" : "=r"(tmp) : "0"(tmp));
 581                        address += tmp;
 582                        break;
 583                case IMM24:
 584                        address += disp & 0x00ffffff;
 585                        break;
 586                case IMM32:
 587                case IMM32_HIGH8:
 588                        address += disp;
 589                        break;
 590                default:
 591                        return 0;
 592                }
 593        } while ((params >>= 8));
 594
 595        *_address = (void *) address;
 596        *_postinc = postinc;
 597        return 1;
 598}
 599
 600/*
 601 * determine the register that is acting as source/dest
 602 */
 603static int misalignment_reg(unsigned long *registers, unsigned params,
 604                            unsigned opcode, unsigned disp,
 605                            unsigned long **_register)
 606{
 607        params &= 0x7fffffff;
 608
 609        if (params & 0xffffff00)
 610                return 0;
 611
 612        switch (params & 0xff) {
 613        case DM0:
 614                *_register = &registers[Dreg_index[opcode & 0x03]];
 615                break;
 616        case DM1:
 617                *_register = &registers[Dreg_index[opcode >> 2 & 0x03]];
 618                break;
 619        case DM2:
 620                *_register = &registers[Dreg_index[opcode >> 4 & 0x03]];
 621                break;
 622        case AM0:
 623                *_register = &registers[Areg_index[opcode & 0x03]];
 624                break;
 625        case AM1:
 626                *_register = &registers[Areg_index[opcode >> 2 & 0x03]];
 627                break;
 628        case AM2:
 629                *_register = &registers[Areg_index[opcode >> 4 & 0x03]];
 630                break;
 631        case RM0:
 632                *_register = &registers[Rreg_index[opcode & 0x0f]];
 633                break;
 634        case RM1:
 635                *_register = &registers[Rreg_index[opcode >> 2 & 0x0f]];
 636                break;
 637        case RM2:
 638                *_register = &registers[Rreg_index[opcode >> 4 & 0x0f]];
 639                break;
 640        case RM4:
 641                *_register = &registers[Rreg_index[opcode >> 8 & 0x0f]];
 642                break;
 643        case RM6:
 644                *_register = &registers[Rreg_index[opcode >> 12 & 0x0f]];
 645                break;
 646        case RD0:
 647                *_register = &registers[Rreg_index[disp & 0x0f]];
 648                break;
 649        case RD2:
 650                *_register = &registers[Rreg_index[disp >> 4 & 0x0f]];
 651                break;
 652        case SP:
 653                *_register = &registers[REG_SP >> 2];
 654                break;
 655
 656        default:
 657                return 0;
 658        }
 659
 660        return 1;
 661}
 662