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