Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | /*
* Single-step support.
*
* Copyright (C) 2004 Paul Mackerras <paulus@au.ibm.com>, IBM
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/ptrace.h>
#include <linux/config.h>
#include <asm/sstep.h>
#include <asm/processor.h>
extern char system_call_common[];
#ifdef CONFIG_PPC64
/* Bits in SRR1 that are copied from MSR */
#define MSR_MASK 0xffffffff87c0ffff
#else
#define MSR_MASK 0x87c0ffff
#endif
/*
* Determine whether a conditional branch instruction would branch.
*/
static int branch_taken(unsigned int instr, struct pt_regs *regs)
{
unsigned int bo = (instr >> 21) & 0x1f;
unsigned int bi;
if ((bo & 4) == 0) {
/* decrement counter */
--regs->ctr;
if (((bo >> 1) & 1) ^ (regs->ctr == 0))
return 0;
}
if ((bo & 0x10) == 0) {
/* check bit from CR */
bi = (instr >> 16) & 0x1f;
if (((regs->ccr >> (31 - bi)) & 1) != ((bo >> 3) & 1))
return 0;
}
return 1;
}
/*
* Emulate instructions that cause a transfer of control.
* Returns 1 if the step was emulated, 0 if not,
* or -1 if the instruction is one that should not be stepped,
* such as an rfid, or a mtmsrd that would clear MSR_RI.
*/
int emulate_step(struct pt_regs *regs, unsigned int instr)
{
unsigned int opcode, rd;
unsigned long int imm;
opcode = instr >> 26;
switch (opcode) {
case 16: /* bc */
imm = (signed short)(instr & 0xfffc);
if ((instr & 2) == 0)
imm += regs->nip;
regs->nip += 4;
if ((regs->msr & MSR_SF) == 0)
regs->nip &= 0xffffffffUL;
if (instr & 1)
regs->link = regs->nip;
if (branch_taken(instr, regs))
regs->nip = imm;
return 1;
#ifdef CONFIG_PPC64
case 17: /* sc */
/*
* N.B. this uses knowledge about how the syscall
* entry code works. If that is changed, this will
* need to be changed also.
*/
regs->gpr[9] = regs->gpr[13];
regs->gpr[11] = regs->nip + 4;
regs->gpr[12] = regs->msr & MSR_MASK;
regs->gpr[13] = (unsigned long) get_paca();
regs->nip = (unsigned long) &system_call_common;
regs->msr = MSR_KERNEL;
return 1;
#endif
case 18: /* b */
imm = instr & 0x03fffffc;
if (imm & 0x02000000)
imm -= 0x04000000;
if ((instr & 2) == 0)
imm += regs->nip;
if (instr & 1) {
regs->link = regs->nip + 4;
if ((regs->msr & MSR_SF) == 0)
regs->link &= 0xffffffffUL;
}
if ((regs->msr & MSR_SF) == 0)
imm &= 0xffffffffUL;
regs->nip = imm;
return 1;
case 19:
switch (instr & 0x7fe) {
case 0x20: /* bclr */
case 0x420: /* bcctr */
imm = (instr & 0x400)? regs->ctr: regs->link;
regs->nip += 4;
if ((regs->msr & MSR_SF) == 0) {
regs->nip &= 0xffffffffUL;
imm &= 0xffffffffUL;
}
if (instr & 1)
regs->link = regs->nip;
if (branch_taken(instr, regs))
regs->nip = imm;
return 1;
case 0x24: /* rfid, scary */
return -1;
}
case 31:
rd = (instr >> 21) & 0x1f;
switch (instr & 0x7fe) {
case 0xa6: /* mfmsr */
regs->gpr[rd] = regs->msr & MSR_MASK;
regs->nip += 4;
if ((regs->msr & MSR_SF) == 0)
regs->nip &= 0xffffffffUL;
return 1;
case 0x124: /* mtmsr */
imm = regs->gpr[rd];
if ((imm & MSR_RI) == 0)
/* can't step mtmsr that would clear MSR_RI */
return -1;
regs->msr = imm;
regs->nip += 4;
return 1;
#ifdef CONFIG_PPC64
case 0x164: /* mtmsrd */
/* only MSR_EE and MSR_RI get changed if bit 15 set */
/* mtmsrd doesn't change MSR_HV and MSR_ME */
imm = (instr & 0x10000)? 0x8002: 0xefffffffffffefffUL;
imm = (regs->msr & MSR_MASK & ~imm)
| (regs->gpr[rd] & imm);
if ((imm & MSR_RI) == 0)
/* can't step mtmsrd that would clear MSR_RI */
return -1;
regs->msr = imm;
regs->nip += 4;
if ((imm & MSR_SF) == 0)
regs->nip &= 0xffffffffUL;
return 1;
#endif
}
}
return 0;
}
|