]>
Commit | Line | Data |
---|---|---|
1dda0b1f WD |
1 | /* |
2 | * Bedbug Functions specific to the PPC405 chip | |
3 | */ | |
4 | ||
5 | #include <common.h> | |
6 | #include <command.h> | |
7 | #include <linux/ctype.h> | |
8bde7f77 | 8 | #include <bedbug/type.h> |
1dda0b1f WD |
9 | #include <bedbug/bedbug.h> |
10 | #include <bedbug/regs.h> | |
11 | #include <bedbug/ppc.h> | |
12 | ||
13 | #if (CONFIG_COMMANDS & CFG_CMD_BEDBUG) && defined(CONFIG_4xx) | |
14 | ||
15 | #define MAX_BREAK_POINTS 4 | |
16 | ||
17 | extern CPU_DEBUG_CTX bug_ctx; | |
18 | ||
19 | void bedbug405_init __P ((void)); | |
20 | void bedbug405_do_break __P ((cmd_tbl_t *, int, int, char *[])); | |
21 | void bedbug405_break_isr __P ((struct pt_regs *)); | |
22 | int bedbug405_find_empty __P ((void)); | |
23 | int bedbug405_set __P ((int, unsigned long)); | |
24 | int bedbug405_clear __P ((int)); | |
25 | \f | |
26 | ||
27 | /* ====================================================================== | |
28 | * Initialize the global bug_ctx structure for the IBM PPC405. Clear all | |
29 | * of the breakpoints. | |
30 | * ====================================================================== */ | |
31 | ||
32 | void bedbug405_init (void) | |
33 | { | |
34 | int i; | |
35 | ||
36 | /* -------------------------------------------------- */ | |
37 | ||
38 | bug_ctx.hw_debug_enabled = 0; | |
39 | bug_ctx.stopped = 0; | |
40 | bug_ctx.current_bp = 0; | |
41 | bug_ctx.regs = NULL; | |
42 | ||
43 | bug_ctx.do_break = bedbug405_do_break; | |
44 | bug_ctx.break_isr = bedbug405_break_isr; | |
45 | bug_ctx.find_empty = bedbug405_find_empty; | |
46 | bug_ctx.set = bedbug405_set; | |
47 | bug_ctx.clear = bedbug405_clear; | |
48 | ||
49 | for (i = 1; i <= MAX_BREAK_POINTS; ++i) | |
50 | (*bug_ctx.clear) (i); | |
51 | ||
52 | puts ("BEDBUG:ready\n"); | |
53 | return; | |
54 | } /* bedbug_init_breakpoints */ | |
55 | \f | |
56 | ||
57 | ||
58 | /* ====================================================================== | |
59 | * Set/clear/show one of the hardware breakpoints for the 405. The "off" | |
60 | * string will disable a specific breakpoint. The "show" string will | |
61 | * display the current breakpoints. Otherwise an address will set a | |
62 | * breakpoint at that address. Setting a breakpoint uses the CPU-specific | |
63 | * set routine which will assign a breakpoint number. | |
64 | * ====================================================================== */ | |
65 | ||
66 | void bedbug405_do_break (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) | |
67 | { | |
68 | long addr = 0; /* Address to break at */ | |
69 | int which_bp; /* Breakpoint number */ | |
70 | ||
71 | /* -------------------------------------------------- */ | |
72 | ||
73 | if (argc < 2) { | |
74 | printf ("Usage:\n%s\n", cmdtp->usage); | |
75 | return; | |
76 | } | |
77 | ||
78 | /* Turn off a breakpoint */ | |
79 | ||
80 | if (strcmp (argv[1], "off") == 0) { | |
81 | if (bug_ctx.hw_debug_enabled == 0) { | |
82 | printf ("No breakpoints enabled\n"); | |
83 | return; | |
84 | } | |
85 | ||
86 | which_bp = simple_strtoul (argv[2], NULL, 10); | |
87 | ||
88 | if (bug_ctx.clear) | |
89 | (*bug_ctx.clear) (which_bp); | |
90 | ||
91 | printf ("Breakpoint %d removed\n", which_bp); | |
92 | return; | |
93 | } | |
94 | ||
95 | /* Show a list of breakpoints */ | |
96 | ||
97 | if (strcmp (argv[1], "show") == 0) { | |
98 | for (which_bp = 1; which_bp <= MAX_BREAK_POINTS; ++which_bp) { | |
99 | ||
100 | switch (which_bp) { | |
101 | case 1: | |
102 | addr = GET_IAC1 (); | |
103 | break; | |
104 | case 2: | |
105 | addr = GET_IAC2 (); | |
106 | break; | |
107 | case 3: | |
108 | addr = GET_IAC3 (); | |
109 | break; | |
110 | case 4: | |
111 | addr = GET_IAC4 (); | |
112 | break; | |
113 | } | |
114 | ||
115 | printf ("Breakpoint [%d]: ", which_bp); | |
116 | if (addr == 0) | |
117 | printf ("NOT SET\n"); | |
118 | else | |
119 | disppc ((unsigned char *) addr, 0, 1, bedbug_puts, | |
120 | F_RADHEX); | |
121 | } | |
122 | return; | |
123 | } | |
124 | ||
125 | /* Set a breakpoint at the address */ | |
126 | ||
127 | if (!isdigit (argv[1][0])) { | |
128 | printf ("Usage:\n%s\n", cmdtp->usage); | |
129 | return; | |
130 | } | |
131 | ||
132 | addr = simple_strtoul (argv[1], NULL, 16) & 0xfffffffc; | |
133 | ||
134 | if ((bug_ctx.set) && (which_bp = (*bug_ctx.set) (0, addr)) > 0) { | |
135 | printf ("Breakpoint [%d]: ", which_bp); | |
136 | disppc ((unsigned char *) addr, 0, 1, bedbug_puts, F_RADHEX); | |
137 | } | |
138 | ||
139 | return; | |
140 | } /* bedbug405_do_break */ | |
141 | \f | |
142 | ||
143 | ||
144 | /* ====================================================================== | |
145 | * Handle a breakpoint. First determine which breakpoint was hit by | |
146 | * looking at the DeBug Status Register (DBSR), clear the breakpoint | |
147 | * and enter a mini main loop. Stay in the loop until the stopped flag | |
148 | * in the debug context is cleared. | |
149 | * ====================================================================== */ | |
150 | ||
151 | void bedbug405_break_isr (struct pt_regs *regs) | |
152 | { | |
153 | unsigned long dbsr_val; /* Value of the DBSR */ | |
154 | unsigned long addr = 0; /* Address stopped at */ | |
155 | ||
156 | /* -------------------------------------------------- */ | |
157 | ||
158 | dbsr_val = GET_DBSR (); | |
159 | ||
160 | if (dbsr_val & DBSR_IA1) { | |
161 | bug_ctx.current_bp = 1; | |
162 | addr = GET_IAC1 (); | |
163 | SET_DBSR (DBSR_IA1); /* Write a 1 to clear */ | |
164 | } else if (dbsr_val & DBSR_IA2) { | |
165 | bug_ctx.current_bp = 2; | |
166 | addr = GET_IAC2 (); | |
167 | SET_DBSR (DBSR_IA2); /* Write a 1 to clear */ | |
168 | } else if (dbsr_val & DBSR_IA3) { | |
169 | bug_ctx.current_bp = 3; | |
170 | addr = GET_IAC3 (); | |
171 | SET_DBSR (DBSR_IA3); /* Write a 1 to clear */ | |
172 | } else if (dbsr_val & DBSR_IA4) { | |
173 | bug_ctx.current_bp = 4; | |
174 | addr = GET_IAC4 (); | |
175 | SET_DBSR (DBSR_IA4); /* Write a 1 to clear */ | |
176 | } | |
177 | ||
178 | bedbug_main_loop (addr, regs); | |
179 | return; | |
180 | } /* bedbug405_break_isr */ | |
181 | \f | |
182 | ||
183 | ||
184 | /* ====================================================================== | |
185 | * Look through all of the hardware breakpoints available to see if one | |
186 | * is unused. | |
187 | * ====================================================================== */ | |
188 | ||
189 | int bedbug405_find_empty (void) | |
190 | { | |
191 | /* -------------------------------------------------- */ | |
192 | ||
193 | if (GET_IAC1 () == 0) | |
194 | return 1; | |
195 | ||
196 | if (GET_IAC2 () == 0) | |
197 | return 2; | |
198 | ||
199 | if (GET_IAC3 () == 0) | |
200 | return 3; | |
201 | ||
202 | if (GET_IAC4 () == 0) | |
203 | return 4; | |
204 | ||
205 | return 0; | |
206 | } /* bedbug405_find_empty */ | |
207 | \f | |
208 | ||
209 | ||
210 | /* ====================================================================== | |
211 | * Set a breakpoint. If 'which_bp' is zero then find an unused breakpoint | |
212 | * number, otherwise reassign the given breakpoint. If hardware debugging | |
213 | * is not enabled, then turn it on via the MSR and DBCR0. Set the break | |
214 | * address in the appropriate IACx register and enable proper address | |
215 | * beakpoint in DBCR0. | |
216 | * ====================================================================== */ | |
217 | ||
218 | int bedbug405_set (int which_bp, unsigned long addr) | |
219 | { | |
220 | /* -------------------------------------------------- */ | |
221 | ||
222 | /* Only look if which_bp == 0, else use which_bp */ | |
223 | if ((bug_ctx.find_empty) && (!which_bp) && | |
224 | (which_bp = (*bug_ctx.find_empty) ()) == 0) { | |
225 | printf ("All breakpoints in use\n"); | |
226 | return 0; | |
227 | } | |
228 | ||
229 | if (which_bp < 1 || which_bp > MAX_BREAK_POINTS) { | |
230 | printf ("Invalid break point # %d\n", which_bp); | |
231 | return 0; | |
232 | } | |
233 | ||
234 | if (!bug_ctx.hw_debug_enabled) { | |
235 | SET_MSR (GET_MSR () | 0x200); /* set MSR[ DE ] */ | |
236 | SET_DBCR0 (GET_DBCR0 () | DBCR0_IDM); | |
237 | bug_ctx.hw_debug_enabled = 1; | |
238 | } | |
239 | ||
240 | switch (which_bp) { | |
241 | case 1: | |
242 | SET_IAC1 (addr); | |
243 | SET_DBCR0 (GET_DBCR0 () | DBCR0_IA1); | |
244 | break; | |
245 | ||
246 | case 2: | |
247 | SET_IAC2 (addr); | |
248 | SET_DBCR0 (GET_DBCR0 () | DBCR0_IA2); | |
249 | break; | |
250 | ||
251 | case 3: | |
252 | SET_IAC3 (addr); | |
253 | SET_DBCR0 (GET_DBCR0 () | DBCR0_IA3); | |
254 | break; | |
255 | ||
256 | case 4: | |
257 | SET_IAC4 (addr); | |
258 | SET_DBCR0 (GET_DBCR0 () | DBCR0_IA4); | |
259 | break; | |
260 | } | |
261 | ||
262 | return which_bp; | |
263 | } /* bedbug405_set */ | |
264 | \f | |
265 | ||
266 | ||
267 | /* ====================================================================== | |
268 | * Disable a specific breakoint by setting the appropriate IACx register | |
269 | * to zero and claring the instruction address breakpoint in DBCR0. | |
270 | * ====================================================================== */ | |
271 | ||
272 | int bedbug405_clear (int which_bp) | |
273 | { | |
274 | /* -------------------------------------------------- */ | |
275 | ||
276 | if (which_bp < 1 || which_bp > MAX_BREAK_POINTS) { | |
277 | printf ("Invalid break point # (%d)\n", which_bp); | |
278 | return -1; | |
279 | } | |
280 | ||
281 | switch (which_bp) { | |
282 | case 1: | |
283 | SET_IAC1 (0); | |
284 | SET_DBCR0 (GET_DBCR0 () & ~DBCR0_IA1); | |
285 | break; | |
286 | ||
287 | case 2: | |
288 | SET_IAC2 (0); | |
289 | SET_DBCR0 (GET_DBCR0 () & ~DBCR0_IA2); | |
290 | break; | |
291 | ||
292 | case 3: | |
293 | SET_IAC3 (0); | |
294 | SET_DBCR0 (GET_DBCR0 () & ~DBCR0_IA3); | |
295 | break; | |
296 | ||
297 | case 4: | |
298 | SET_IAC4 (0); | |
299 | SET_DBCR0 (GET_DBCR0 () & ~DBCR0_IA4); | |
300 | break; | |
301 | } | |
302 | ||
303 | return 0; | |
304 | } /* bedbug405_clear */ | |
305 | ||
306 | ||
307 | /* ====================================================================== */ | |
308 | #endif |