1/* align.c - handle alignment exceptions for the Power PC. 2 * 3 * Copyright (c) 1996 Paul Mackerras <paulus@cs.anu.edu.au> 4 * Copyright (c) 1998-1999 TiVo, Inc. 5 * PowerPC 403GCX modifications. 6 * Copyright (c) 1999 Grant Erickson <grant@lcse.umn.edu> 7 * PowerPC 403GCX/405GP modifications. 8 * Copyright (c) 2001-2002 PPC64 team, IBM Corp 9 * 64-bit and Power4 support 10 * 11 * This program is free software; you can redistribute it and/or 12 * modify it under the terms of the GNU General Public License 13 * as published by the Free Software Foundation; either version 14 * 2 of the License, or (at your option) any later version. 15 */ 16 17#include <linux/kernel.h> 18#include <linux/mm.h> 19#include <asm/ptrace.h> 20#include <asm/processor.h> 21#include <asm/uaccess.h> 22#include <asm/system.h> 23#include <asm/cache.h> 24 25struct aligninfo { 26 unsigned char len; 27 unsigned char flags; 28}; 29 30#define IS_XFORM(inst) (((inst) >> 26) == 31) 31#define IS_DSFORM(inst) (((inst) >> 26) >= 56) 32 33#define INVALID { 0, 0 } 34 35#define LD 1 /* load */ 36#define ST 2 /* store */ 37#define SE 4 /* sign-extend value */ 38#define F 8 /* to/from fp regs */ 39#define U 0x10 /* update index register */ 40#define M 0x20 /* multiple load/store */ 41#define SW 0x40 /* byte swap */ 42 43#define DCBZ 0x5f /* 8xx/82xx dcbz faults when cache not enabled */ 44 45/* 46 * The PowerPC stores certain bits of the instruction that caused the 47 * alignment exception in the DSISR register. This array maps those 48 * bits to information about the operand length and what the 49 * instruction would do. 50 */ 51static struct aligninfo aligninfo[128] = { 52 { 4, LD }, /* 00 0 0000: lwz / lwarx */ 53 INVALID, /* 00 0 0001 */ 54 { 4, ST }, /* 00 0 0010: stw */ 55 INVALID, /* 00 0 0011 */ 56 { 2, LD }, /* 00 0 0100: lhz */ 57 { 2, LD+SE }, /* 00 0 0101: lha */ 58 { 2, ST }, /* 00 0 0110: sth */ 59 { 4, LD+M }, /* 00 0 0111: lmw */ 60 { 4, LD+F }, /* 00 0 1000: lfs */ 61 { 8, LD+F }, /* 00 0 1001: lfd */ 62 { 4, ST+F }, /* 00 0 1010: stfs */ 63 { 8, ST+F }, /* 00 0 1011: stfd */ 64 INVALID, /* 00 0 1100 */ 65 { 8, LD }, /* 00 0 1101: ld */ 66 INVALID, /* 00 0 1110 */ 67 { 8, ST }, /* 00 0 1111: std */ 68 { 4, LD+U }, /* 00 1 0000: lwzu */ 69 INVALID, /* 00 1 0001 */ 70 { 4, ST+U }, /* 00 1 0010: stwu */ 71 INVALID, /* 00 1 0011 */ 72 { 2, LD+U }, /* 00 1 0100: lhzu */ 73 { 2, LD+SE+U }, /* 00 1 0101: lhau */ 74 { 2, ST+U }, /* 00 1 0110: sthu */ 75 { 4, ST+M }, /* 00 1 0111: stmw */ 76 { 4, LD+F+U }, /* 00 1 1000: lfsu */ 77 { 8, LD+F+U }, /* 00 1 1001: lfdu */ 78 { 4, ST+F+U }, /* 00 1 1010: stfsu */ 79 { 8, ST+F+U }, /* 00 1 1011: stfdu */ 80 INVALID, /* 00 1 1100 */ 81 INVALID, /* 00 1 1101 */ 82 INVALID, /* 00 1 1110 */ 83 INVALID, /* 00 1 1111 */ 84 { 8, LD }, /* 01 0 0000: ldx */ 85 INVALID, /* 01 0 0001 */ 86 { 8, ST }, /* 01 0 0010: stdx */ 87 INVALID, /* 01 0 0011 */ 88 INVALID, /* 01 0 0100 */ 89 { 4, LD+SE }, /* 01 0 0101: lwax */ 90 INVALID, /* 01 0 0110 */ 91 INVALID, /* 01 0 0111 */ 92 { 0, LD }, /* 01 0 1000: lswx */ 93 { 0, LD }, /* 01 0 1001: lswi */ 94 { 0, ST }, /* 01 0 1010: stswx */ 95 { 0, ST }, /* 01 0 1011: stswi */ 96 INVALID, /* 01 0 1100 */ 97 { 8, LD+U }, /* 01 0 1101: ldu */ 98 INVALID, /* 01 0 1110 */ 99 { 8, ST+U }, /* 01 0 1111: stdu */ 100 { 8, LD+U }, /* 01 1 0000: ldux */ 101 INVALID, /* 01 1 0001 */ 102 { 8, ST+U }, /* 01 1 0010: stdux */ 103 INVALID, /* 01 1 0011 */ 104 INVALID, /* 01 1 0100 */ 105 { 4, LD+SE+U }, /* 01 1 0101: lwaux */ 106 INVALID, /* 01 1 0110 */ 107 INVALID, /* 01 1 0111 */ 108 INVALID, /* 01 1 1000 */ 109 INVALID, /* 01 1 1001 */ 110 INVALID, /* 01 1 1010 */ 111 INVALID, /* 01 1 1011 */ 112 INVALID, /* 01 1 1100 */ 113 INVALID, /* 01 1 1101 */ 114 INVALID, /* 01 1 1110 */ 115 INVALID, /* 01 1 1111 */ 116 INVALID, /* 10 0 0000 */ 117 INVALID, /* 10 0 0001 */ 118 { 0, ST }, /* 10 0 0010: stwcx. */ 119 INVALID, /* 10 0 0011 */ 120 INVALID, /* 10 0 0100 */ 121 INVALID, /* 10 0 0101 */ 122 INVALID, /* 10 0 0110 */ 123 INVALID, /* 10 0 0111 */ 124 { 4, LD+SW }, /* 10 0 1000: lwbrx */ 125 INVALID, /* 10 0 1001 */ 126 { 4, ST+SW }, /* 10 0 1010: stwbrx */ 127 INVALID, /* 10 0 1011 */ 128 { 2, LD+SW }, /* 10 0 1100: lhbrx */ 129 { 4, LD+SE }, /* 10 0 1101 lwa */ 130 { 2, ST+SW }, /* 10 0 1110: sthbrx */ 131 INVALID, /* 10 0 1111 */ 132 INVALID, /* 10 1 0000 */ 133 INVALID, /* 10 1 0001 */ 134 INVALID, /* 10 1 0010 */ 135 INVALID, /* 10 1 0011 */ 136 INVALID, /* 10 1 0100 */ 137 INVALID, /* 10 1 0101 */ 138 INVALID, /* 10 1 0110 */ 139 INVALID, /* 10 1 0111 */ 140 INVALID, /* 10 1 1000 */ 141 INVALID, /* 10 1 1001 */ 142 INVALID, /* 10 1 1010 */ 143 INVALID, /* 10 1 1011 */ 144 INVALID, /* 10 1 1100 */ 145 INVALID, /* 10 1 1101 */ 146 INVALID, /* 10 1 1110 */ 147 { L1_CACHE_BYTES, ST }, /* 10 1 1111: dcbz */ 148 { 4, LD }, /* 11 0 0000: lwzx */ 149 INVALID, /* 11 0 0001 */ 150 { 4, ST }, /* 11 0 0010: stwx */ 151 INVALID, /* 11 0 0011 */ 152 { 2, LD }, /* 11 0 0100: lhzx */ 153 { 2, LD+SE }, /* 11 0 0101: lhax */ 154 { 2, ST }, /* 11 0 0110: sthx */ 155 INVALID, /* 11 0 0111 */ 156 { 4, LD+F }, /* 11 0 1000: lfsx */ 157 { 8, LD+F }, /* 11 0 1001: lfdx */ 158 { 4, ST+F }, /* 11 0 1010: stfsx */ 159 { 8, ST+F }, /* 11 0 1011: stfdx */ 160 INVALID, /* 11 0 1100 */ 161 { 8, LD+M }, /* 11 0 1101: lmd */ 162 INVALID, /* 11 0 1110 */ 163 { 8, ST+M }, /* 11 0 1111: stmd */ 164 { 4, LD+U }, /* 11 1 0000: lwzux */ 165 INVALID, /* 11 1 0001 */ 166 { 4, ST+U }, /* 11 1 0010: stwux */ 167 INVALID, /* 11 1 0011 */ 168 { 2, LD+U }, /* 11 1 0100: lhzux */ 169 { 2, LD+SE+U }, /* 11 1 0101: lhaux */ 170 { 2, ST+U }, /* 11 1 0110: sthux */ 171 INVALID, /* 11 1 0111 */ 172 { 4, LD+F+U }, /* 11 1 1000: lfsux */ 173 { 8, LD+F+U }, /* 11 1 1001: lfdux */ 174 { 4, ST+F+U }, /* 11 1 1010: stfsux */ 175 { 8, ST+F+U }, /* 11 1 1011: stfdux */ 176 INVALID, /* 11 1 1100 */ 177 INVALID, /* 11 1 1101 */ 178 INVALID, /* 11 1 1110 */ 179 INVALID, /* 11 1 1111 */ 180}; 181 182#define SWAP(a, b) (t = (a), (a) = (b), (b) = t) 183 184unsigned static inline make_dsisr( unsigned instr ) 185{ 186 unsigned dsisr; 187 188 /* create a DSISR value from the instruction */ 189 dsisr = (instr & 0x03ff0000) >> 16; /* bits 6:15 --> 22:31 */ 190 191 if ( IS_XFORM(instr) ) { 192 dsisr |= (instr & 0x00000006) << 14; /* bits 29:30 --> 15:16 */ 193 dsisr |= (instr & 0x00000040) << 8; /* bit 25 --> 17 */ 194 dsisr |= (instr & 0x00000780) << 3; /* bits 21:24 --> 18:21 */ 195 } 196 else { 197 dsisr |= (instr & 0x04000000) >> 12; /* bit 5 --> 17 */ 198 dsisr |= (instr & 0x78000000) >> 17; /* bits 1: 4 --> 18:21 */ 199 if ( IS_DSFORM(instr) ) { 200 dsisr |= (instr & 0x00000003) << 18; /* bits 30:31 --> 12:13 */ 201 } 202 } 203 204 return dsisr; 205} 206 207int 208fix_alignment(struct pt_regs *regs) 209{ 210 unsigned int instr, nb, flags; 211 int t; 212 unsigned long reg, areg; 213 unsigned long i; 214 int ret; 215 unsigned dsisr; 216 unsigned char *addr, *p; 217 unsigned long *lp; 218 union { 219 long ll; 220 double dd; 221 unsigned char v[8]; 222 struct { 223 unsigned hi32; 224 int low32; 225 } x32; 226 struct { 227 unsigned char hi48[6]; 228 short low16; 229 } x16; 230 } data; 231 232 /* 233 * Return 1 on success 234 * Return 0 if unable to handle the interrupt 235 * Return -EFAULT if data address is bad 236 */ 237 238 dsisr = regs->dsisr; 239 240 /* Power4 doesn't set DSISR for an alignment interrupt */ 241 if (__is_processor(PV_POWER4) || __is_processor(PV_POWER4p)) 242 dsisr = make_dsisr( *((unsigned *)regs->nip) ); 243 244 /* extract the operation and registers from the dsisr */ 245 reg = (dsisr >> 5) & 0x1f; /* source/dest register */ 246 areg = dsisr & 0x1f; /* register to update */ 247 instr = (dsisr >> 10) & 0x7f; 248 instr |= (dsisr >> 13) & 0x60; 249 250 /* Lookup the operation in our table */ 251 nb = aligninfo[instr].len; 252 flags = aligninfo[instr].flags; 253 254 /* DAR has the operand effective address */ 255 addr = (unsigned char *)regs->dar; 256 257 /* A size of 0 indicates an instruction we don't support */ 258 /* we also don't support the multiples (lmw, stmw, lmd, stmd) */ 259 if ((nb == 0) || (flags & M)) 260 return 0; /* too hard or invalid instruction */ 261 262 /* 263 * Special handling for dcbz 264 * dcbz may give an alignment exception for accesses to caching inhibited 265 * storage 266 */ 267 if (instr == DCBZ) 268 addr = (unsigned char *) ((unsigned long)addr & -L1_CACHE_BYTES); 269 270 /* Verify the address of the operand */ 271 if (user_mode(regs)) { 272 if (verify_area((flags & ST? VERIFY_WRITE: VERIFY_READ), addr, nb)) 273 return -EFAULT; /* bad address */ 274 } 275 276 /* Force the fprs into the save area so we can reference them */ 277 if ((flags & F) && (regs->msr & MSR_FP)) 278 giveup_fpu(current); 279 280 /* If we are loading, get the data from user space */ 281 if (flags & LD) { 282 data.ll = 0; 283 ret = 0; 284 p = addr; 285 switch (nb) { 286 case 8: 287 ret |= __get_user(data.v[0], p++); 288 ret |= __get_user(data.v[1], p++); 289 ret |= __get_user(data.v[2], p++); 290 ret |= __get_user(data.v[3], p++); 291 case 4: 292 ret |= __get_user(data.v[4], p++); 293 ret |= __get_user(data.v[5], p++); 294 case 2: 295 ret |= __get_user(data.v[6], p++); 296 ret |= __get_user(data.v[7], p++); 297 if (ret) 298 return -EFAULT; 299 } 300 } 301 302 /* If we are storing, get the data from the saved gpr or fpr */ 303 if (flags & ST) { 304 if (flags & F) { 305 if (nb == 4) { 306 /* Doing stfs, have to convert to single */ 307 enable_kernel_fp(); 308 cvt_df(¤t->thread.fpr[reg], (float *)&data.v[4], ¤t->thread.fpscr); 309 } 310 else 311 data.dd = current->thread.fpr[reg]; 312 } 313 else 314 data.ll = regs->gpr[reg]; 315 } 316 317 /* Swap bytes as needed */ 318 if (flags & SW) { 319 if (nb == 2) 320 SWAP(data.v[6], data.v[7]); 321 else { /* nb must be 4 */ 322 SWAP(data.v[4], data.v[7]); 323 SWAP(data.v[5], data.v[6]); 324 } 325 } 326 327 /* Sign extend as needed */ 328 if (flags & SE) { 329 if ( nb == 2 ) 330 data.ll = data.x16.low16; 331 else /* nb must be 4 */ 332 data.ll = data.x32.low32; 333 } 334 335 /* If we are loading, move the data to the gpr or fpr */ 336 if (flags & LD) { 337 if (flags & F) { 338 if (nb == 4) { 339 /* Doing lfs, have to convert to double */ 340 enable_kernel_fp(); 341 cvt_fd((float *)&data.v[4], ¤t->thread.fpr[reg], ¤t->thread.fpscr); 342 } 343 else 344 current->thread.fpr[reg] = data.dd; 345 } 346 else 347 regs->gpr[reg] = data.ll; 348 } 349 350 /* If we are storing, copy the data to the user */ 351 if (flags & ST) { 352 ret = 0; 353 p = addr; 354 switch (nb) { 355 case 128: /* Special case - must be dcbz */ 356 lp = (unsigned long *)p; 357 for (i = 0; i < L1_CACHE_BYTES / sizeof(long); ++i) 358 ret |= __put_user(0, lp++); 359 break; 360 case 8: 361 ret |= __put_user(data.v[0], p++); 362 ret |= __put_user(data.v[1], p++); 363 ret |= __put_user(data.v[2], p++); 364 ret |= __put_user(data.v[3], p++); 365 case 4: 366 ret |= __put_user(data.v[4], p++); 367 ret |= __put_user(data.v[5], p++); 368 case 2: 369 ret |= __put_user(data.v[6], p++); 370 ret |= __put_user(data.v[7], p++); 371 } 372 if (ret) 373 return -EFAULT; 374 } 375 376 /* Update RA as needed */ 377 if (flags & U) { 378 regs->gpr[areg] = regs->dar; 379 } 380 381 return 1; 382} 383 384