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