diff options
Diffstat (limited to 'arch/powerpc/kernel/align.c')
| -rw-r--r-- | arch/powerpc/kernel/align.c | 189 |
1 files changed, 118 insertions, 71 deletions
diff --git a/arch/powerpc/kernel/align.c b/arch/powerpc/kernel/align.c index faaec9c6f78f..4734b5de599d 100644 --- a/arch/powerpc/kernel/align.c +++ b/arch/powerpc/kernel/align.c | |||
| @@ -35,17 +35,19 @@ struct aligninfo { | |||
| 35 | 35 | ||
| 36 | #define INVALID { 0, 0 } | 36 | #define INVALID { 0, 0 } |
| 37 | 37 | ||
| 38 | #define LD 1 /* load */ | 38 | /* Bits in the flags field */ |
| 39 | #define ST 2 /* store */ | 39 | #define LD 0 /* load */ |
| 40 | #define SE 4 /* sign-extend value */ | 40 | #define ST 1 /* store */ |
| 41 | #define F 8 /* to/from fp regs */ | 41 | #define SE 2 /* sign-extend value */ |
| 42 | #define U 0x10 /* update index register */ | 42 | #define F 4 /* to/from fp regs */ |
| 43 | #define M 0x20 /* multiple load/store */ | 43 | #define U 8 /* update index register */ |
| 44 | #define SW 0x40 /* byte swap int or ... */ | 44 | #define M 0x10 /* multiple load/store */ |
| 45 | #define S 0x40 /* ... single-precision fp */ | 45 | #define SW 0x20 /* byte swap */ |
| 46 | #define SX 0x40 /* byte count in XER */ | 46 | #define S 0x40 /* single-precision fp or... */ |
| 47 | #define SX 0x40 /* ... byte count in XER */ | ||
| 47 | #define HARD 0x80 /* string, stwcx. */ | 48 | #define HARD 0x80 /* string, stwcx. */ |
| 48 | 49 | ||
| 50 | /* DSISR bits reported for a DCBZ instruction: */ | ||
| 49 | #define DCBZ 0x5f /* 8xx/82xx dcbz faults when cache not enabled */ | 51 | #define DCBZ 0x5f /* 8xx/82xx dcbz faults when cache not enabled */ |
| 50 | 52 | ||
| 51 | #define SWAP(a, b) (t = (a), (a) = (b), (b) = t) | 53 | #define SWAP(a, b) (t = (a), (a) = (b), (b) = t) |
| @@ -256,12 +258,16 @@ static int emulate_dcbz(struct pt_regs *regs, unsigned char __user *addr) | |||
| 256 | #define REG_BYTE(rp, i) *((u8 *)(rp) + (i)) | 258 | #define REG_BYTE(rp, i) *((u8 *)(rp) + (i)) |
| 257 | #endif | 259 | #endif |
| 258 | 260 | ||
| 261 | #define SWIZ_PTR(p) ((unsigned char __user *)((p) ^ swiz)) | ||
| 262 | |||
| 259 | static int emulate_multiple(struct pt_regs *regs, unsigned char __user *addr, | 263 | static int emulate_multiple(struct pt_regs *regs, unsigned char __user *addr, |
| 260 | unsigned int reg, unsigned int nb, | 264 | unsigned int reg, unsigned int nb, |
| 261 | unsigned int flags, unsigned int instr) | 265 | unsigned int flags, unsigned int instr, |
| 266 | unsigned long swiz) | ||
| 262 | { | 267 | { |
| 263 | unsigned long *rptr; | 268 | unsigned long *rptr; |
| 264 | unsigned int nb0, i; | 269 | unsigned int nb0, i, bswiz; |
| 270 | unsigned long p; | ||
| 265 | 271 | ||
| 266 | /* | 272 | /* |
| 267 | * We do not try to emulate 8 bytes multiple as they aren't really | 273 | * We do not try to emulate 8 bytes multiple as they aren't really |
| @@ -280,9 +286,12 @@ static int emulate_multiple(struct pt_regs *regs, unsigned char __user *addr, | |||
| 280 | if (nb == 0) | 286 | if (nb == 0) |
| 281 | return 1; | 287 | return 1; |
| 282 | } else { | 288 | } else { |
| 283 | if (__get_user(instr, | 289 | unsigned long pc = regs->nip ^ (swiz & 4); |
| 284 | (unsigned int __user *)regs->nip)) | 290 | |
| 291 | if (__get_user(instr, (unsigned int __user *)pc)) | ||
| 285 | return -EFAULT; | 292 | return -EFAULT; |
| 293 | if (swiz == 0 && (flags & SW)) | ||
| 294 | instr = cpu_to_le32(instr); | ||
| 286 | nb = (instr >> 11) & 0x1f; | 295 | nb = (instr >> 11) & 0x1f; |
| 287 | if (nb == 0) | 296 | if (nb == 0) |
| 288 | nb = 32; | 297 | nb = 32; |
| @@ -300,7 +309,10 @@ static int emulate_multiple(struct pt_regs *regs, unsigned char __user *addr, | |||
| 300 | return -EFAULT; /* bad address */ | 309 | return -EFAULT; /* bad address */ |
| 301 | 310 | ||
| 302 | rptr = ®s->gpr[reg]; | 311 | rptr = ®s->gpr[reg]; |
| 303 | if (flags & LD) { | 312 | p = (unsigned long) addr; |
| 313 | bswiz = (flags & SW)? 3: 0; | ||
| 314 | |||
| 315 | if (!(flags & ST)) { | ||
| 304 | /* | 316 | /* |
| 305 | * This zeroes the top 4 bytes of the affected registers | 317 | * This zeroes the top 4 bytes of the affected registers |
| 306 | * in 64-bit mode, and also zeroes out any remaining | 318 | * in 64-bit mode, and also zeroes out any remaining |
| @@ -311,26 +323,28 @@ static int emulate_multiple(struct pt_regs *regs, unsigned char __user *addr, | |||
| 311 | memset(®s->gpr[0], 0, | 323 | memset(®s->gpr[0], 0, |
| 312 | ((nb0 + 3) / 4) * sizeof(unsigned long)); | 324 | ((nb0 + 3) / 4) * sizeof(unsigned long)); |
| 313 | 325 | ||
| 314 | for (i = 0; i < nb; ++i) | 326 | for (i = 0; i < nb; ++i, ++p) |
| 315 | if (__get_user(REG_BYTE(rptr, i), addr + i)) | 327 | if (__get_user(REG_BYTE(rptr, i ^ bswiz), SWIZ_PTR(p))) |
| 316 | return -EFAULT; | 328 | return -EFAULT; |
| 317 | if (nb0 > 0) { | 329 | if (nb0 > 0) { |
| 318 | rptr = ®s->gpr[0]; | 330 | rptr = ®s->gpr[0]; |
| 319 | addr += nb; | 331 | addr += nb; |
| 320 | for (i = 0; i < nb0; ++i) | 332 | for (i = 0; i < nb0; ++i, ++p) |
| 321 | if (__get_user(REG_BYTE(rptr, i), addr + i)) | 333 | if (__get_user(REG_BYTE(rptr, i ^ bswiz), |
| 334 | SWIZ_PTR(p))) | ||
| 322 | return -EFAULT; | 335 | return -EFAULT; |
| 323 | } | 336 | } |
| 324 | 337 | ||
| 325 | } else { | 338 | } else { |
| 326 | for (i = 0; i < nb; ++i) | 339 | for (i = 0; i < nb; ++i, ++p) |
| 327 | if (__put_user(REG_BYTE(rptr, i), addr + i)) | 340 | if (__put_user(REG_BYTE(rptr, i ^ bswiz), SWIZ_PTR(p))) |
| 328 | return -EFAULT; | 341 | return -EFAULT; |
| 329 | if (nb0 > 0) { | 342 | if (nb0 > 0) { |
| 330 | rptr = ®s->gpr[0]; | 343 | rptr = ®s->gpr[0]; |
| 331 | addr += nb; | 344 | addr += nb; |
| 332 | for (i = 0; i < nb0; ++i) | 345 | for (i = 0; i < nb0; ++i, ++p) |
| 333 | if (__put_user(REG_BYTE(rptr, i), addr + i)) | 346 | if (__put_user(REG_BYTE(rptr, i ^ bswiz), |
| 347 | SWIZ_PTR(p))) | ||
| 334 | return -EFAULT; | 348 | return -EFAULT; |
| 335 | } | 349 | } |
| 336 | } | 350 | } |
| @@ -352,7 +366,7 @@ int fix_alignment(struct pt_regs *regs) | |||
| 352 | unsigned int reg, areg; | 366 | unsigned int reg, areg; |
| 353 | unsigned int dsisr; | 367 | unsigned int dsisr; |
| 354 | unsigned char __user *addr; | 368 | unsigned char __user *addr; |
| 355 | unsigned char __user *p; | 369 | unsigned long p, swiz; |
| 356 | int ret, t; | 370 | int ret, t; |
| 357 | union { | 371 | union { |
| 358 | u64 ll; | 372 | u64 ll; |
| @@ -380,11 +394,15 @@ int fix_alignment(struct pt_regs *regs) | |||
| 380 | * let's make one up from the instruction | 394 | * let's make one up from the instruction |
| 381 | */ | 395 | */ |
| 382 | if (cpu_has_feature(CPU_FTR_NODSISRALIGN)) { | 396 | if (cpu_has_feature(CPU_FTR_NODSISRALIGN)) { |
| 383 | unsigned int real_instr; | 397 | unsigned long pc = regs->nip; |
| 384 | if (unlikely(__get_user(real_instr, | 398 | |
| 385 | (unsigned int __user *)regs->nip))) | 399 | if (cpu_has_feature(CPU_FTR_PPC_LE) && (regs->msr & MSR_LE)) |
| 400 | pc ^= 4; | ||
| 401 | if (unlikely(__get_user(instr, (unsigned int __user *)pc))) | ||
| 386 | return -EFAULT; | 402 | return -EFAULT; |
| 387 | dsisr = make_dsisr(real_instr); | 403 | if (cpu_has_feature(CPU_FTR_REAL_LE) && (regs->msr & MSR_LE)) |
| 404 | instr = cpu_to_le32(instr); | ||
| 405 | dsisr = make_dsisr(instr); | ||
| 388 | } | 406 | } |
| 389 | 407 | ||
| 390 | /* extract the operation and registers from the dsisr */ | 408 | /* extract the operation and registers from the dsisr */ |
| @@ -397,6 +415,24 @@ int fix_alignment(struct pt_regs *regs) | |||
| 397 | nb = aligninfo[instr].len; | 415 | nb = aligninfo[instr].len; |
| 398 | flags = aligninfo[instr].flags; | 416 | flags = aligninfo[instr].flags; |
| 399 | 417 | ||
| 418 | /* Byteswap little endian loads and stores */ | ||
| 419 | swiz = 0; | ||
| 420 | if (regs->msr & MSR_LE) { | ||
| 421 | flags ^= SW; | ||
| 422 | /* | ||
| 423 | * So-called "PowerPC little endian" mode works by | ||
| 424 | * swizzling addresses rather than by actually doing | ||
| 425 | * any byte-swapping. To emulate this, we XOR each | ||
| 426 | * byte address with 7. We also byte-swap, because | ||
| 427 | * the processor's address swizzling depends on the | ||
| 428 | * operand size (it xors the address with 7 for bytes, | ||
| 429 | * 6 for halfwords, 4 for words, 0 for doublewords) but | ||
| 430 | * we will xor with 7 and load/store each byte separately. | ||
| 431 | */ | ||
| 432 | if (cpu_has_feature(CPU_FTR_PPC_LE)) | ||
| 433 | swiz = 7; | ||
| 434 | } | ||
| 435 | |||
| 400 | /* DAR has the operand effective address */ | 436 | /* DAR has the operand effective address */ |
| 401 | addr = (unsigned char __user *)regs->dar; | 437 | addr = (unsigned char __user *)regs->dar; |
| 402 | 438 | ||
| @@ -412,7 +448,8 @@ int fix_alignment(struct pt_regs *regs) | |||
| 412 | * function | 448 | * function |
| 413 | */ | 449 | */ |
| 414 | if (flags & M) | 450 | if (flags & M) |
| 415 | return emulate_multiple(regs, addr, reg, nb, flags, instr); | 451 | return emulate_multiple(regs, addr, reg, nb, |
| 452 | flags, instr, swiz); | ||
| 416 | 453 | ||
| 417 | /* Verify the address of the operand */ | 454 | /* Verify the address of the operand */ |
| 418 | if (unlikely(user_mode(regs) && | 455 | if (unlikely(user_mode(regs) && |
| @@ -431,51 +468,71 @@ int fix_alignment(struct pt_regs *regs) | |||
| 431 | /* If we are loading, get the data from user space, else | 468 | /* If we are loading, get the data from user space, else |
| 432 | * get it from register values | 469 | * get it from register values |
| 433 | */ | 470 | */ |
| 434 | if (flags & LD) { | 471 | if (!(flags & ST)) { |
| 435 | data.ll = 0; | 472 | data.ll = 0; |
| 436 | ret = 0; | 473 | ret = 0; |
| 437 | p = addr; | 474 | p = (unsigned long) addr; |
| 438 | switch (nb) { | 475 | switch (nb) { |
| 439 | case 8: | 476 | case 8: |
| 440 | ret |= __get_user(data.v[0], p++); | 477 | ret |= __get_user(data.v[0], SWIZ_PTR(p++)); |
| 441 | ret |= __get_user(data.v[1], p++); | 478 | ret |= __get_user(data.v[1], SWIZ_PTR(p++)); |
| 442 | ret |= __get_user(data.v[2], p++); | 479 | ret |= __get_user(data.v[2], SWIZ_PTR(p++)); |
| 443 | ret |= __get_user(data.v[3], p++); | 480 | ret |= __get_user(data.v[3], SWIZ_PTR(p++)); |
| 444 | case 4: | 481 | case 4: |
| 445 | ret |= __get_user(data.v[4], p++); | 482 | ret |= __get_user(data.v[4], SWIZ_PTR(p++)); |
| 446 | ret |= __get_user(data.v[5], p++); | 483 | ret |= __get_user(data.v[5], SWIZ_PTR(p++)); |
| 447 | case 2: | 484 | case 2: |
| 448 | ret |= __get_user(data.v[6], p++); | 485 | ret |= __get_user(data.v[6], SWIZ_PTR(p++)); |
| 449 | ret |= __get_user(data.v[7], p++); | 486 | ret |= __get_user(data.v[7], SWIZ_PTR(p++)); |
| 450 | if (unlikely(ret)) | 487 | if (unlikely(ret)) |
| 451 | return -EFAULT; | 488 | return -EFAULT; |
| 452 | } | 489 | } |
| 453 | } else if (flags & F) | 490 | } else if (flags & F) { |
| 454 | data.dd = current->thread.fpr[reg]; | 491 | data.dd = current->thread.fpr[reg]; |
| 455 | else | 492 | if (flags & S) { |
| 493 | /* Single-precision FP store requires conversion... */ | ||
| 494 | #ifdef CONFIG_PPC_FPU | ||
| 495 | preempt_disable(); | ||
| 496 | enable_kernel_fp(); | ||
| 497 | cvt_df(&data.dd, (float *)&data.v[4], ¤t->thread); | ||
| 498 | preempt_enable(); | ||
| 499 | #else | ||
| 500 | return 0; | ||
| 501 | #endif | ||
| 502 | } | ||
| 503 | } else | ||
| 456 | data.ll = regs->gpr[reg]; | 504 | data.ll = regs->gpr[reg]; |
| 457 | 505 | ||
| 458 | /* Perform other misc operations like sign extension, byteswap, | 506 | if (flags & SW) { |
| 507 | switch (nb) { | ||
| 508 | case 8: | ||
| 509 | SWAP(data.v[0], data.v[7]); | ||
| 510 | SWAP(data.v[1], data.v[6]); | ||
| 511 | SWAP(data.v[2], data.v[5]); | ||
| 512 | SWAP(data.v[3], data.v[4]); | ||
| 513 | break; | ||
| 514 | case 4: | ||
| 515 | SWAP(data.v[4], data.v[7]); | ||
| 516 | SWAP(data.v[5], data.v[6]); | ||
| 517 | break; | ||
| 518 | case 2: | ||
| 519 | SWAP(data.v[6], data.v[7]); | ||
| 520 | break; | ||
| 521 | } | ||
| 522 | } | ||
| 523 | |||
| 524 | /* Perform other misc operations like sign extension | ||
| 459 | * or floating point single precision conversion | 525 | * or floating point single precision conversion |
| 460 | */ | 526 | */ |
| 461 | switch (flags & ~U) { | 527 | switch (flags & ~(U|SW)) { |
| 462 | case LD+SE: /* sign extend */ | 528 | case LD+SE: /* sign extend */ |
| 463 | if ( nb == 2 ) | 529 | if ( nb == 2 ) |
| 464 | data.ll = data.x16.low16; | 530 | data.ll = data.x16.low16; |
| 465 | else /* nb must be 4 */ | 531 | else /* nb must be 4 */ |
| 466 | data.ll = data.x32.low32; | 532 | data.ll = data.x32.low32; |
| 467 | break; | 533 | break; |
| 468 | case LD+S: /* byte-swap */ | ||
| 469 | case ST+S: | ||
| 470 | if (nb == 2) { | ||
| 471 | SWAP(data.v[6], data.v[7]); | ||
| 472 | } else { | ||
| 473 | SWAP(data.v[4], data.v[7]); | ||
| 474 | SWAP(data.v[5], data.v[6]); | ||
| 475 | } | ||
| 476 | break; | ||
| 477 | 534 | ||
| 478 | /* Single-precision FP load and store require conversions... */ | 535 | /* Single-precision FP load requires conversion... */ |
| 479 | case LD+F+S: | 536 | case LD+F+S: |
| 480 | #ifdef CONFIG_PPC_FPU | 537 | #ifdef CONFIG_PPC_FPU |
| 481 | preempt_disable(); | 538 | preempt_disable(); |
| @@ -486,34 +543,24 @@ int fix_alignment(struct pt_regs *regs) | |||
| 486 | return 0; | 543 | return 0; |
| 487 | #endif | 544 | #endif |
| 488 | break; | 545 | break; |
| 489 | case ST+F+S: | ||
| 490 | #ifdef CONFIG_PPC_FPU | ||
| 491 | preempt_disable(); | ||
| 492 | enable_kernel_fp(); | ||
| 493 | cvt_df(&data.dd, (float *)&data.v[4], ¤t->thread); | ||
| 494 | preempt_enable(); | ||
| 495 | #else | ||
| 496 | return 0; | ||
| 497 | #endif | ||
| 498 | break; | ||
| 499 | } | 546 | } |
| 500 | 547 | ||
| 501 | /* Store result to memory or update registers */ | 548 | /* Store result to memory or update registers */ |
| 502 | if (flags & ST) { | 549 | if (flags & ST) { |
| 503 | ret = 0; | 550 | ret = 0; |
| 504 | p = addr; | 551 | p = (unsigned long) addr; |
| 505 | switch (nb) { | 552 | switch (nb) { |
| 506 | case 8: | 553 | case 8: |
| 507 | ret |= __put_user(data.v[0], p++); | 554 | ret |= __put_user(data.v[0], SWIZ_PTR(p++)); |
| 508 | ret |= __put_user(data.v[1], p++); | 555 | ret |= __put_user(data.v[1], SWIZ_PTR(p++)); |
| 509 | ret |= __put_user(data.v[2], p++); | 556 | ret |= __put_user(data.v[2], SWIZ_PTR(p++)); |
| 510 | ret |= __put_user(data.v[3], p++); | 557 | ret |= __put_user(data.v[3], SWIZ_PTR(p++)); |
| 511 | case 4: | 558 | case 4: |
| 512 | ret |= __put_user(data.v[4], p++); | 559 | ret |= __put_user(data.v[4], SWIZ_PTR(p++)); |
| 513 | ret |= __put_user(data.v[5], p++); | 560 | ret |= __put_user(data.v[5], SWIZ_PTR(p++)); |
| 514 | case 2: | 561 | case 2: |
| 515 | ret |= __put_user(data.v[6], p++); | 562 | ret |= __put_user(data.v[6], SWIZ_PTR(p++)); |
| 516 | ret |= __put_user(data.v[7], p++); | 563 | ret |= __put_user(data.v[7], SWIZ_PTR(p++)); |
| 517 | } | 564 | } |
| 518 | if (unlikely(ret)) | 565 | if (unlikely(ret)) |
| 519 | return -EFAULT; | 566 | return -EFAULT; |
