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