79 |
|
#if (defined(powerpc) || defined(__powerpc__)) |
80 |
|
#include <sys/ucontext.h> |
81 |
|
#define SIGSEGV_FAULT_INSTRUCTION (((ucontext_t *)scp)->uc_mcontext.regs->nip) |
82 |
+ |
#define SIGSEGV_REGISTER_FILE (unsigned long *)(((ucontext_t *)scp)->uc_mcontext.regs) |
83 |
+ |
#define SIGSEGV_SKIP_INSTRUCTION powerpc_skip_instruction |
84 |
|
#endif |
85 |
|
#endif |
86 |
|
#endif |
107 |
|
#define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, struct sigcontext *scp |
108 |
|
#define SIGSEGV_FAULT_ADDRESS scp->regs->dar |
109 |
|
#define SIGSEGV_FAULT_INSTRUCTION scp->regs->nip |
110 |
+ |
#define SIGSEGV_REGISTER_FILE (unsigned long *)(scp->regs) |
111 |
+ |
#define SIGSEGV_SKIP_INSTRUCTION powerpc_skip_instruction |
112 |
|
#endif |
113 |
|
#if (defined(alpha) || defined(__alpha__)) |
114 |
|
#include <asm/sigcontext.h> |
449 |
|
} |
450 |
|
|
451 |
|
#if DEBUG |
452 |
< |
printf("%08x: %s %s access", regs[X86_REG_EIP], |
452 |
> |
printf("%08x: %s %s access", fault_instruction, |
453 |
|
transfer_size == SIZE_BYTE ? "byte" : transfer_size == SIZE_WORD ? "word" : "long", |
454 |
|
transfer_type == TYPE_LOAD ? "read" : "write"); |
455 |
|
|
467 |
|
return true; |
468 |
|
} |
469 |
|
#endif |
470 |
+ |
// Decode and skip PPC instruction |
471 |
+ |
#if (defined(powerpc) || defined(__powerpc__)) |
472 |
+ |
#if defined(__linux__) |
473 |
+ |
enum { |
474 |
+ |
POWERPC_REG_GPR = 0, |
475 |
+ |
POWERPC_REG_NIP = 32 |
476 |
+ |
}; |
477 |
+ |
#endif |
478 |
+ |
static bool powerpc_skip_instruction(sigsegv_address_t fault_instruction, unsigned long * regs) |
479 |
+ |
{ |
480 |
+ |
// Get opcode and divide into fields |
481 |
+ |
unsigned int opcode = *((unsigned int *)fault_instruction); |
482 |
+ |
unsigned int primop = opcode >> 26; |
483 |
+ |
unsigned int exop = (opcode >> 1) & 0x3ff; |
484 |
+ |
unsigned int ra = (opcode >> 16) & 0x1f; |
485 |
+ |
unsigned int rb = (opcode >> 11) & 0x1f; |
486 |
+ |
unsigned int rd = (opcode >> 21) & 0x1f; |
487 |
+ |
signed int imm = (signed short)(opcode & 0xffff); |
488 |
+ |
|
489 |
+ |
// Analyze opcode |
490 |
+ |
enum { |
491 |
+ |
TYPE_UNKNOWN, |
492 |
+ |
TYPE_LOAD, |
493 |
+ |
TYPE_STORE |
494 |
+ |
} transfer_type = TYPE_UNKNOWN; |
495 |
+ |
enum { |
496 |
+ |
SIZE_UNKNOWN, |
497 |
+ |
SIZE_BYTE, |
498 |
+ |
SIZE_HALFWORD, |
499 |
+ |
SIZE_WORD |
500 |
+ |
} transfer_size = SIZE_UNKNOWN; |
501 |
+ |
enum { |
502 |
+ |
MODE_UNKNOWN, |
503 |
+ |
MODE_NORM, |
504 |
+ |
MODE_U, |
505 |
+ |
MODE_X, |
506 |
+ |
MODE_UX |
507 |
+ |
} addr_mode = MODE_UNKNOWN; |
508 |
+ |
switch (primop) { |
509 |
+ |
case 31: |
510 |
+ |
switch (exop) { |
511 |
+ |
case 23: // lwzx |
512 |
+ |
transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_X; break; |
513 |
+ |
case 55: // lwzux |
514 |
+ |
transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_UX; break; |
515 |
+ |
case 87: // lbzx |
516 |
+ |
transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_X; break; |
517 |
+ |
case 119: // lbzux |
518 |
+ |
transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_UX; break; |
519 |
+ |
case 151: // stwx |
520 |
+ |
transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_X; break; |
521 |
+ |
case 183: // stwux |
522 |
+ |
transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_UX; break; |
523 |
+ |
case 215: // stbx |
524 |
+ |
transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_X; break; |
525 |
+ |
case 247: // stbux |
526 |
+ |
transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_UX; break; |
527 |
+ |
case 279: // lhzx |
528 |
+ |
transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_X; break; |
529 |
+ |
case 311: // lhzux |
530 |
+ |
transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_UX; break; |
531 |
+ |
case 343: // lhax |
532 |
+ |
transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_X; break; |
533 |
+ |
case 375: // lhaux |
534 |
+ |
transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_UX; break; |
535 |
+ |
case 407: // sthx |
536 |
+ |
transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_X; break; |
537 |
+ |
case 439: // sthux |
538 |
+ |
transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_UX; break; |
539 |
+ |
} |
540 |
+ |
break; |
541 |
+ |
|
542 |
+ |
case 32: // lwz |
543 |
+ |
transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_NORM; break; |
544 |
+ |
case 33: // lwzu |
545 |
+ |
transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_U; break; |
546 |
+ |
case 34: // lbz |
547 |
+ |
transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_NORM; break; |
548 |
+ |
case 35: // lbzu |
549 |
+ |
transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_U; break; |
550 |
+ |
case 36: // stw |
551 |
+ |
transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_NORM; break; |
552 |
+ |
case 37: // stwu |
553 |
+ |
transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_U; break; |
554 |
+ |
case 38: // stb |
555 |
+ |
transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_NORM; break; |
556 |
+ |
case 39: // stbu |
557 |
+ |
transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_U; break; |
558 |
+ |
case 40: // lhz |
559 |
+ |
transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_NORM; break; |
560 |
+ |
case 41: // lhzu |
561 |
+ |
transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_U; break; |
562 |
+ |
case 42: // lha |
563 |
+ |
transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_NORM; break; |
564 |
+ |
case 43: // lhau |
565 |
+ |
transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_U; break; |
566 |
+ |
case 44: // sth |
567 |
+ |
transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_NORM; break; |
568 |
+ |
case 45: // sthu |
569 |
+ |
transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_U; break; |
570 |
+ |
} |
571 |
+ |
|
572 |
+ |
// Calculate effective address |
573 |
+ |
unsigned int addr = 0; |
574 |
+ |
switch (addr_mode) { |
575 |
+ |
case MODE_X: |
576 |
+ |
case MODE_UX: |
577 |
+ |
if (ra == 0) |
578 |
+ |
addr = regs[POWERPC_REG_GPR + rb]; |
579 |
+ |
else |
580 |
+ |
addr = regs[POWERPC_REG_GPR + ra] + regs[POWERPC_REG_GPR + rb]; |
581 |
+ |
break; |
582 |
+ |
case MODE_NORM: |
583 |
+ |
case MODE_U: |
584 |
+ |
if (ra == 0) |
585 |
+ |
addr = (signed int)(signed short)imm; |
586 |
+ |
else |
587 |
+ |
addr = regs[POWERPC_REG_GPR + ra] + (signed int)(signed short)imm; |
588 |
+ |
break; |
589 |
+ |
default: |
590 |
+ |
break; |
591 |
+ |
} |
592 |
+ |
|
593 |
+ |
if (transfer_type == TYPE_UNKNOWN) { |
594 |
+ |
// Unknown machine code, let it crash. Then patch the decoder |
595 |
+ |
return false; |
596 |
+ |
} |
597 |
+ |
|
598 |
+ |
#if DEBUG |
599 |
+ |
printf("%08x: %s %s access", fault_instruction, |
600 |
+ |
transfer_size == SIZE_BYTE ? "byte" : transfer_size == SIZE_HALFWORD ? "word" : "long", |
601 |
+ |
transfer_type == TYPE_LOAD ? "read" : "write"); |
602 |
+ |
|
603 |
+ |
if (addr_mode == MODE_U || addr_mode == MODE_UX) |
604 |
+ |
printf(" r%d (ra = %08x)\n", ra, addr); |
605 |
+ |
if (transfer_type == TYPE_LOAD) |
606 |
+ |
printf(" r%d (rd = 0)\n", rd); |
607 |
+ |
#endif |
608 |
+ |
|
609 |
+ |
if (addr_mode == MODE_U || addr_mode == MODE_UX) |
610 |
+ |
regs[POWERPC_REG_GPR + ra] = addr; |
611 |
+ |
if (transfer_type == TYPE_LOAD) |
612 |
+ |
regs[POWERPC_REG_GPR + rd] = 0; |
613 |
+ |
|
614 |
+ |
regs[POWERPC_REG_NIP] += 4; |
615 |
+ |
return true; |
616 |
+ |
} |
617 |
+ |
#endif |
618 |
|
#endif |
619 |
|
|
620 |
|
// Fallbacks |