root/arch/powerpc/kernel/align.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. emulate_spe
  2. fix_alignment

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /* align.c - handle alignment exceptions for the Power PC.
   3  *
   4  * Copyright (c) 1996 Paul Mackerras <paulus@cs.anu.edu.au>
   5  * Copyright (c) 1998-1999 TiVo, Inc.
   6  *   PowerPC 403GCX modifications.
   7  * Copyright (c) 1999 Grant Erickson <grant@lcse.umn.edu>
   8  *   PowerPC 403GCX/405GP modifications.
   9  * Copyright (c) 2001-2002 PPC64 team, IBM Corp
  10  *   64-bit and Power4 support
  11  * Copyright (c) 2005 Benjamin Herrenschmidt, IBM Corp
  12  *                    <benh@kernel.crashing.org>
  13  *   Merge ppc32 and ppc64 implementations
  14  */
  15 
  16 #include <linux/kernel.h>
  17 #include <linux/mm.h>
  18 #include <asm/processor.h>
  19 #include <linux/uaccess.h>
  20 #include <asm/cache.h>
  21 #include <asm/cputable.h>
  22 #include <asm/emulated_ops.h>
  23 #include <asm/switch_to.h>
  24 #include <asm/disassemble.h>
  25 #include <asm/cpu_has_feature.h>
  26 #include <asm/sstep.h>
  27 
  28 struct aligninfo {
  29         unsigned char len;
  30         unsigned char flags;
  31 };
  32 
  33 
  34 #define INVALID { 0, 0 }
  35 
  36 /* Bits in the flags field */
  37 #define LD      0       /* load */
  38 #define ST      1       /* store */
  39 #define SE      2       /* sign-extend value, or FP ld/st as word */
  40 #define SW      0x20    /* byte swap */
  41 #define E4      0x40    /* SPE endianness is word */
  42 #define E8      0x80    /* SPE endianness is double word */
  43 
  44 #ifdef CONFIG_SPE
  45 
  46 static struct aligninfo spe_aligninfo[32] = {
  47         { 8, LD+E8 },           /* 0 00 00: evldd[x] */
  48         { 8, LD+E4 },           /* 0 00 01: evldw[x] */
  49         { 8, LD },              /* 0 00 10: evldh[x] */
  50         INVALID,                /* 0 00 11 */
  51         { 2, LD },              /* 0 01 00: evlhhesplat[x] */
  52         INVALID,                /* 0 01 01 */
  53         { 2, LD },              /* 0 01 10: evlhhousplat[x] */
  54         { 2, LD+SE },           /* 0 01 11: evlhhossplat[x] */
  55         { 4, LD },              /* 0 10 00: evlwhe[x] */
  56         INVALID,                /* 0 10 01 */
  57         { 4, LD },              /* 0 10 10: evlwhou[x] */
  58         { 4, LD+SE },           /* 0 10 11: evlwhos[x] */
  59         { 4, LD+E4 },           /* 0 11 00: evlwwsplat[x] */
  60         INVALID,                /* 0 11 01 */
  61         { 4, LD },              /* 0 11 10: evlwhsplat[x] */
  62         INVALID,                /* 0 11 11 */
  63 
  64         { 8, ST+E8 },           /* 1 00 00: evstdd[x] */
  65         { 8, ST+E4 },           /* 1 00 01: evstdw[x] */
  66         { 8, ST },              /* 1 00 10: evstdh[x] */
  67         INVALID,                /* 1 00 11 */
  68         INVALID,                /* 1 01 00 */
  69         INVALID,                /* 1 01 01 */
  70         INVALID,                /* 1 01 10 */
  71         INVALID,                /* 1 01 11 */
  72         { 4, ST },              /* 1 10 00: evstwhe[x] */
  73         INVALID,                /* 1 10 01 */
  74         { 4, ST },              /* 1 10 10: evstwho[x] */
  75         INVALID,                /* 1 10 11 */
  76         { 4, ST+E4 },           /* 1 11 00: evstwwe[x] */
  77         INVALID,                /* 1 11 01 */
  78         { 4, ST+E4 },           /* 1 11 10: evstwwo[x] */
  79         INVALID,                /* 1 11 11 */
  80 };
  81 
  82 #define EVLDD           0x00
  83 #define EVLDW           0x01
  84 #define EVLDH           0x02
  85 #define EVLHHESPLAT     0x04
  86 #define EVLHHOUSPLAT    0x06
  87 #define EVLHHOSSPLAT    0x07
  88 #define EVLWHE          0x08
  89 #define EVLWHOU         0x0A
  90 #define EVLWHOS         0x0B
  91 #define EVLWWSPLAT      0x0C
  92 #define EVLWHSPLAT      0x0E
  93 #define EVSTDD          0x10
  94 #define EVSTDW          0x11
  95 #define EVSTDH          0x12
  96 #define EVSTWHE         0x18
  97 #define EVSTWHO         0x1A
  98 #define EVSTWWE         0x1C
  99 #define EVSTWWO         0x1E
 100 
 101 /*
 102  * Emulate SPE loads and stores.
 103  * Only Book-E has these instructions, and it does true little-endian,
 104  * so we don't need the address swizzling.
 105  */
 106 static int emulate_spe(struct pt_regs *regs, unsigned int reg,
 107                        unsigned int instr)
 108 {
 109         int ret;
 110         union {
 111                 u64 ll;
 112                 u32 w[2];
 113                 u16 h[4];
 114                 u8 v[8];
 115         } data, temp;
 116         unsigned char __user *p, *addr;
 117         unsigned long *evr = &current->thread.evr[reg];
 118         unsigned int nb, flags;
 119 
 120         instr = (instr >> 1) & 0x1f;
 121 
 122         /* DAR has the operand effective address */
 123         addr = (unsigned char __user *)regs->dar;
 124 
 125         nb = spe_aligninfo[instr].len;
 126         flags = spe_aligninfo[instr].flags;
 127 
 128         /* Verify the address of the operand */
 129         if (unlikely(user_mode(regs) &&
 130                      !access_ok(addr, nb)))
 131                 return -EFAULT;
 132 
 133         /* userland only */
 134         if (unlikely(!user_mode(regs)))
 135                 return 0;
 136 
 137         flush_spe_to_thread(current);
 138 
 139         /* If we are loading, get the data from user space, else
 140          * get it from register values
 141          */
 142         if (flags & ST) {
 143                 data.ll = 0;
 144                 switch (instr) {
 145                 case EVSTDD:
 146                 case EVSTDW:
 147                 case EVSTDH:
 148                         data.w[0] = *evr;
 149                         data.w[1] = regs->gpr[reg];
 150                         break;
 151                 case EVSTWHE:
 152                         data.h[2] = *evr >> 16;
 153                         data.h[3] = regs->gpr[reg] >> 16;
 154                         break;
 155                 case EVSTWHO:
 156                         data.h[2] = *evr & 0xffff;
 157                         data.h[3] = regs->gpr[reg] & 0xffff;
 158                         break;
 159                 case EVSTWWE:
 160                         data.w[1] = *evr;
 161                         break;
 162                 case EVSTWWO:
 163                         data.w[1] = regs->gpr[reg];
 164                         break;
 165                 default:
 166                         return -EINVAL;
 167                 }
 168         } else {
 169                 temp.ll = data.ll = 0;
 170                 ret = 0;
 171                 p = addr;
 172 
 173                 switch (nb) {
 174                 case 8:
 175                         ret |= __get_user_inatomic(temp.v[0], p++);
 176                         ret |= __get_user_inatomic(temp.v[1], p++);
 177                         ret |= __get_user_inatomic(temp.v[2], p++);
 178                         ret |= __get_user_inatomic(temp.v[3], p++);
 179                         /* fall through */
 180                 case 4:
 181                         ret |= __get_user_inatomic(temp.v[4], p++);
 182                         ret |= __get_user_inatomic(temp.v[5], p++);
 183                         /* fall through */
 184                 case 2:
 185                         ret |= __get_user_inatomic(temp.v[6], p++);
 186                         ret |= __get_user_inatomic(temp.v[7], p++);
 187                         if (unlikely(ret))
 188                                 return -EFAULT;
 189                 }
 190 
 191                 switch (instr) {
 192                 case EVLDD:
 193                 case EVLDW:
 194                 case EVLDH:
 195                         data.ll = temp.ll;
 196                         break;
 197                 case EVLHHESPLAT:
 198                         data.h[0] = temp.h[3];
 199                         data.h[2] = temp.h[3];
 200                         break;
 201                 case EVLHHOUSPLAT:
 202                 case EVLHHOSSPLAT:
 203                         data.h[1] = temp.h[3];
 204                         data.h[3] = temp.h[3];
 205                         break;
 206                 case EVLWHE:
 207                         data.h[0] = temp.h[2];
 208                         data.h[2] = temp.h[3];
 209                         break;
 210                 case EVLWHOU:
 211                 case EVLWHOS:
 212                         data.h[1] = temp.h[2];
 213                         data.h[3] = temp.h[3];
 214                         break;
 215                 case EVLWWSPLAT:
 216                         data.w[0] = temp.w[1];
 217                         data.w[1] = temp.w[1];
 218                         break;
 219                 case EVLWHSPLAT:
 220                         data.h[0] = temp.h[2];
 221                         data.h[1] = temp.h[2];
 222                         data.h[2] = temp.h[3];
 223                         data.h[3] = temp.h[3];
 224                         break;
 225                 default:
 226                         return -EINVAL;
 227                 }
 228         }
 229 
 230         if (flags & SW) {
 231                 switch (flags & 0xf0) {
 232                 case E8:
 233                         data.ll = swab64(data.ll);
 234                         break;
 235                 case E4:
 236                         data.w[0] = swab32(data.w[0]);
 237                         data.w[1] = swab32(data.w[1]);
 238                         break;
 239                 /* Its half word endian */
 240                 default:
 241                         data.h[0] = swab16(data.h[0]);
 242                         data.h[1] = swab16(data.h[1]);
 243                         data.h[2] = swab16(data.h[2]);
 244                         data.h[3] = swab16(data.h[3]);
 245                         break;
 246                 }
 247         }
 248 
 249         if (flags & SE) {
 250                 data.w[0] = (s16)data.h[1];
 251                 data.w[1] = (s16)data.h[3];
 252         }
 253 
 254         /* Store result to memory or update registers */
 255         if (flags & ST) {
 256                 ret = 0;
 257                 p = addr;
 258                 switch (nb) {
 259                 case 8:
 260                         ret |= __put_user_inatomic(data.v[0], p++);
 261                         ret |= __put_user_inatomic(data.v[1], p++);
 262                         ret |= __put_user_inatomic(data.v[2], p++);
 263                         ret |= __put_user_inatomic(data.v[3], p++);
 264                         /* fall through */
 265                 case 4:
 266                         ret |= __put_user_inatomic(data.v[4], p++);
 267                         ret |= __put_user_inatomic(data.v[5], p++);
 268                         /* fall through */
 269                 case 2:
 270                         ret |= __put_user_inatomic(data.v[6], p++);
 271                         ret |= __put_user_inatomic(data.v[7], p++);
 272                 }
 273                 if (unlikely(ret))
 274                         return -EFAULT;
 275         } else {
 276                 *evr = data.w[0];
 277                 regs->gpr[reg] = data.w[1];
 278         }
 279 
 280         return 1;
 281 }
 282 #endif /* CONFIG_SPE */
 283 
 284 /*
 285  * Called on alignment exception. Attempts to fixup
 286  *
 287  * Return 1 on success
 288  * Return 0 if unable to handle the interrupt
 289  * Return -EFAULT if data address is bad
 290  * Other negative return values indicate that the instruction can't
 291  * be emulated, and the process should be given a SIGBUS.
 292  */
 293 
 294 int fix_alignment(struct pt_regs *regs)
 295 {
 296         unsigned int instr;
 297         struct instruction_op op;
 298         int r, type;
 299 
 300         /*
 301          * We require a complete register set, if not, then our assembly
 302          * is broken
 303          */
 304         CHECK_FULL_REGS(regs);
 305 
 306         if (unlikely(__get_user(instr, (unsigned int __user *)regs->nip)))
 307                 return -EFAULT;
 308         if ((regs->msr & MSR_LE) != (MSR_KERNEL & MSR_LE)) {
 309                 /* We don't handle PPC little-endian any more... */
 310                 if (cpu_has_feature(CPU_FTR_PPC_LE))
 311                         return -EIO;
 312                 instr = swab32(instr);
 313         }
 314 
 315 #ifdef CONFIG_SPE
 316         if ((instr >> 26) == 0x4) {
 317                 int reg = (instr >> 21) & 0x1f;
 318                 PPC_WARN_ALIGNMENT(spe, regs);
 319                 return emulate_spe(regs, reg, instr);
 320         }
 321 #endif
 322 
 323 
 324         /*
 325          * ISA 3.0 (such as P9) copy, copy_first, paste and paste_last alignment
 326          * check.
 327          *
 328          * Send a SIGBUS to the process that caused the fault.
 329          *
 330          * We do not emulate these because paste may contain additional metadata
 331          * when pasting to a co-processor. Furthermore, paste_last is the
 332          * synchronisation point for preceding copy/paste sequences.
 333          */
 334         if ((instr & 0xfc0006fe) == (PPC_INST_COPY & 0xfc0006fe))
 335                 return -EIO;
 336 
 337         r = analyse_instr(&op, regs, instr);
 338         if (r < 0)
 339                 return -EINVAL;
 340 
 341         type = GETTYPE(op.type);
 342         if (!OP_IS_LOAD_STORE(type)) {
 343                 if (op.type != CACHEOP + DCBZ)
 344                         return -EINVAL;
 345                 PPC_WARN_ALIGNMENT(dcbz, regs);
 346                 r = emulate_dcbz(op.ea, regs);
 347         } else {
 348                 if (type == LARX || type == STCX)
 349                         return -EIO;
 350                 PPC_WARN_ALIGNMENT(unaligned, regs);
 351                 r = emulate_loadstore(regs, &op);
 352         }
 353 
 354         if (!r)
 355                 return 1;
 356         return r;
 357 }

/* [<][>][^][v][top][bottom][index][help] */