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