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