|
2
|
1 //============================================================================= |
|
|
2 // |
|
|
3 // hal_stub.c |
|
|
4 // |
|
|
5 // Helper functions for stub, specific to eCos HAL |
|
|
6 // |
|
|
7 //============================================================================= |
|
|
8 //####COPYRIGHTBEGIN#### |
|
|
9 // |
|
|
10 // ------------------------------------------- |
|
|
11 // The contents of this file are subject to the Cygnus eCos Public License |
|
|
12 // Version 1.0 (the "License"); you may not use this file except in |
|
|
13 // compliance with the License. You may obtain a copy of the License at |
|
|
14 // http://sourceware.cygnus.com/ecos |
|
|
15 // |
|
|
16 // Software distributed under the License is distributed on an "AS IS" |
|
|
17 // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the |
|
|
18 // License for the specific language governing rights and limitations under |
|
|
19 // the License. |
|
|
20 // |
|
|
21 // The Original Code is eCos - Embedded Cygnus Operating System, released |
|
|
22 // September 30, 1998. |
|
|
23 // |
|
|
24 // The Initial Developer of the Original Code is Cygnus. Portions created |
|
|
25 // by Cygnus are Copyright (C) 1998, 1999 Cygnus Solutions. |
|
|
26 // All Rights Reserved. |
|
|
27 // ------------------------------------------- |
|
|
28 // |
|
|
29 //####COPYRIGHTEND#### |
|
|
30 //============================================================================= |
|
|
31 //#####DESCRIPTIONBEGIN#### |
|
|
32 // |
|
|
33 // Author(s): jskov (based on powerpc/cogent hal_stub.c) |
|
|
34 // Contributors:jskov |
|
|
35 // Date: 1999-02-12 |
|
|
36 // Purpose: Helper functions for stub, specific to eCos HAL |
|
|
37 // Description: Parts of the GDB stub requirements are provided by |
|
|
38 // the eCos HAL, rather than target and/or board specific |
|
|
39 // code. |
|
|
40 // |
|
|
41 //####DESCRIPTIONEND#### |
|
|
42 // |
|
|
43 //============================================================================= |
|
|
44 |
|
|
45 #include <pkgconf/hal.h> |
|
|
46 |
|
|
47 #ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS |
|
|
48 |
|
|
49 #include <cyg/hal/hal_stub.h> // Our header |
|
|
50 |
|
|
51 #include <cyg/hal/hal_arch.h> // HAL_BREAKINST |
|
|
52 #include <cyg/hal/hal_cache.h> // HAL_xCACHE_x |
|
|
53 #include <cyg/hal/hal_intr.h> // interrupt disable/restore |
|
|
54 |
|
|
55 #ifdef CYGDBG_HAL_DEBUG_GDB_THREAD_SUPPORT |
|
|
56 #include <cyg/hal/dbg-threads-api.h> // dbg_currthread_id |
|
|
57 #endif |
|
|
58 |
|
|
59 |
|
|
60 //----------------------------------------------------------------------------- |
|
|
61 // Extra eCos data. |
|
|
62 |
|
|
63 // Saved registers. |
|
|
64 HAL_SavedRegisters *_hal_registers; |
|
|
65 target_register_t * _registers; // Pointer to current set of registers |
|
|
66 target_register_t registers[NUMREGS]; |
|
|
67 target_register_t alt_registers[NUMREGS] ; // Thread or saved process state |
|
|
68 |
|
|
69 // Interrupt control. |
|
|
70 static volatile __PFI __interruptible_control; |
|
|
71 |
|
|
72 //----------------------------------------------------------------------------- |
|
|
73 // Register access |
|
|
74 |
|
|
75 // Return the currently-saved value corresponding to register REG of |
|
|
76 // the exception context. |
|
|
77 target_register_t |
|
|
78 get_register (regnames_t reg) |
|
|
79 { |
|
|
80 return _registers[reg]; |
|
|
81 } |
|
|
82 |
|
|
83 // Store VALUE in the register corresponding to WHICH in the exception |
|
|
84 // context. |
|
|
85 void |
|
|
86 put_register (regnames_t which, target_register_t value) |
|
|
87 { |
|
|
88 _registers[which] = value; |
|
|
89 } |
|
|
90 |
|
|
91 //----------------------------------------------------------------------------- |
|
|
92 // Serial stuff |
|
|
93 |
|
|
94 // Write C to the current serial port. |
|
|
95 void |
|
|
96 putDebugChar (int c) |
|
|
97 { |
|
|
98 HAL_STUB_PLATFORM_PUT_CHAR(c); |
|
|
99 } |
|
|
100 |
|
|
101 // Read one character from the current serial port. |
|
|
102 int |
|
|
103 getDebugChar (void) |
|
|
104 { |
|
|
105 return HAL_STUB_PLATFORM_GET_CHAR(); |
|
|
106 } |
|
|
107 |
|
|
108 // Set the baud rate for the current serial port. |
|
|
109 void |
|
|
110 __set_baud_rate (int baud) |
|
|
111 { |
|
|
112 HAL_STUB_PLATFORM_SET_BAUD_RATE(baud); |
|
|
113 } |
|
|
114 |
|
|
115 //----------------------------------------------------------------------------- |
|
|
116 // GDB interrupt (BREAK) support. |
|
|
117 |
|
|
118 #ifdef CYGDBG_HAL_DEBUG_GDB_BREAK_SUPPORT |
|
|
119 |
|
|
120 typedef unsigned long t_inst; |
|
|
121 |
|
|
122 typedef struct |
|
|
123 { |
|
|
124 t_inst *targetAddr; |
|
|
125 t_inst savedInstr; |
|
|
126 } instrBuffer; |
|
|
127 |
|
|
128 static instrBuffer break_buffer; |
|
|
129 |
|
|
130 void |
|
|
131 cyg_hal_gdb_interrupt (target_register_t pc) |
|
|
132 { |
|
|
133 if (NULL == break_buffer.targetAddr) { |
|
|
134 break_buffer.targetAddr = (t_inst*) pc; |
|
|
135 break_buffer.savedInstr = *(t_inst*)pc; |
|
|
136 *(t_inst*)pc = (t_inst)HAL_BREAKINST; |
|
|
137 |
|
|
138 __data_cache(CACHE_FLUSH); |
|
|
139 __instruction_cache(CACHE_FLUSH); |
|
|
140 } |
|
|
141 } |
|
|
142 |
|
|
143 int |
|
|
144 cyg_hal_gdb_remove_break (target_register_t pc) |
|
|
145 { |
|
|
146 if ((t_inst*)pc == break_buffer.targetAddr) { |
|
|
147 *(t_inst*)pc = break_buffer.savedInstr; |
|
|
148 break_buffer.targetAddr = NULL; |
|
|
149 |
|
|
150 __data_cache(CACHE_FLUSH); |
|
|
151 __instruction_cache(CACHE_FLUSH); |
|
|
152 return 1; |
|
|
153 } |
|
|
154 return 0; |
|
|
155 } |
|
|
156 |
|
|
157 #endif // CYGDBG_HAL_DEBUG_GDB_BREAK_SUPPORT |
|
|
158 |
|
|
159 // Use this function to disable serial interrupts whenever reply |
|
|
160 // characters are expected from GDB. The reason we want to control |
|
|
161 // whether the target can be interrupted or not is simply that GDB on |
|
|
162 // the host will be sending acknowledge characters/commands while the |
|
|
163 // stub is running - if serial interrupts were still active, the |
|
|
164 // characters would never reach the (polling) getDebugChar. |
|
|
165 static void |
|
|
166 interruptible(int state) |
|
|
167 { |
|
|
168 static int __interrupts_suspended = 0; |
|
|
169 |
|
|
170 if (__interruptible_control) { |
|
|
171 if (state) { |
|
|
172 __interrupts_suspended--; |
|
|
173 if (0 >= __interrupts_suspended) { |
|
|
174 __interrupts_suspended = 0; |
|
|
175 __interruptible_control(1); |
|
|
176 } |
|
|
177 } else { |
|
|
178 __interrupts_suspended++; |
|
|
179 if (1 == __interrupts_suspended) |
|
|
180 __interruptible_control(0); |
|
|
181 } |
|
|
182 } |
|
|
183 } |
|
|
184 |
|
|
185 //----------------------------------------------------------------------------- |
|
|
186 // eCos stub entry and exit magic. |
|
|
187 |
|
|
188 #ifdef CYGDBG_HAL_DEBUG_GDB_BREAK_SUPPORT |
|
|
189 int cyg_hal_gdb_break; |
|
|
190 #endif |
|
|
191 |
|
|
192 // Called at stub *entry* |
|
|
193 static void |
|
|
194 handle_exception_cleanup( void ) |
|
|
195 { |
|
|
196 interruptible(0); |
|
|
197 |
|
|
198 // Expand the HAL_SavedRegisters structure into the GDB register |
|
|
199 // array format. |
|
|
200 HAL_GET_GDB_REGISTERS(®isters[0], _hal_registers); |
|
|
201 _registers = ®isters[0]; |
|
|
202 |
|
|
203 #ifdef CYGDBG_HAL_DEBUG_GDB_BREAK_SUPPORT |
|
|
204 // FIXME: (there may be a better way to do this) |
|
|
205 // If we hit a breakpoint set by the gdb interrupt stub, make it |
|
|
206 // seem like an interrupt rather than having hit a breakpoint. |
|
|
207 if (cyg_hal_gdb_remove_break(get_register (PC))) |
|
|
208 cyg_hal_gdb_break = 1; |
|
|
209 else |
|
|
210 cyg_hal_gdb_break = 0; |
|
|
211 #endif |
|
|
212 } |
|
|
213 |
|
|
214 // Called at stub *exit* |
|
|
215 static void |
|
|
216 handle_exception_init( void ) |
|
|
217 { |
|
|
218 // Compact register array again. |
|
|
219 HAL_SET_GDB_REGISTERS(_hal_registers, ®isters[0]); |
|
|
220 |
|
|
221 interruptible(1); |
|
|
222 } |
|
|
223 |
|
|
224 |
|
|
225 //----------------------------------------------------------------------------- |
|
|
226 // Initialization. |
|
|
227 |
|
|
228 // Signal handler. |
|
|
229 int |
|
|
230 cyg_hal_process_signal (int signal) |
|
|
231 { |
|
|
232 // We don't care about the signal (atm). |
|
|
233 return 0; |
|
|
234 } |
|
|
235 |
|
|
236 // Install the standard set of trap handlers for the stub. |
|
|
237 void |
|
|
238 __install_traps (void) |
|
|
239 { |
|
|
240 // Set signal handling vector so we can treat 'C<signum>' as 'c'. |
|
|
241 __process_signal_vec = &cyg_hal_process_signal; |
|
|
242 |
|
|
243 __cleanup_vec = &handle_exception_cleanup; |
|
|
244 __init_vec = &handle_exception_init; |
|
|
245 |
|
|
246 #ifdef CYGDBG_HAL_DEBUG_GDB_BREAK_SUPPORT |
|
|
247 // Control of GDB interrupts. |
|
|
248 __interruptible_control = HAL_STUB_PLATFORM_INTERRUPTIBLE; |
|
|
249 #endif |
|
|
250 |
|
|
251 // Nothing further to do, handle_exception will be called when an |
|
|
252 // exception occurs. |
|
|
253 } |
|
|
254 |
|
|
255 // Initialize the hardware. |
|
|
256 void |
|
|
257 initHardware (void) |
|
|
258 { |
|
|
259 static int initialized = 0; |
|
|
260 |
|
|
261 if (!initialized) { |
|
|
262 initialized = 1; |
|
|
263 |
|
|
264 // Get serial port initialized. |
|
|
265 HAL_STUB_PLATFORM_INIT_SERIAL(); |
|
|
266 |
|
|
267 #ifdef CYGDBG_HAL_DEBUG_GDB_BREAK_SUPPORT |
|
|
268 // Get interrupt handler initialized. |
|
|
269 HAL_STUB_PLATFORM_INIT_BREAK_IRQ(); |
|
|
270 #endif |
|
|
271 |
|
|
272 #ifdef CYG_HAL_STARTUP_STUBS |
|
|
273 // There may be extra initialization required when configured |
|
|
274 // for stubs startup. |
|
|
275 HAL_STUB_PLATFORM_STUBS_INIT(); |
|
|
276 #endif |
|
|
277 } |
|
|
278 } |
|
|
279 |
|
|
280 // Reset the board. |
|
|
281 void |
|
|
282 __reset (void) |
|
|
283 { |
|
|
284 HAL_STUB_PLATFORM_RESET(); |
|
|
285 } |
|
|
286 |
|
|
287 //----------------------------------------------------------------------------- |
|
|
288 // Breakpoint support. |
|
|
289 |
|
|
290 #ifndef CYGPKG_HAL_ARM |
|
|
291 // This function will generate a breakpoint exception. It is used at |
|
|
292 // the beginning of a program to sync up with a debugger and can be |
|
|
293 // used otherwise as a quick means to stop program execution and |
|
|
294 // "break" into the debugger. |
|
|
295 void |
|
|
296 breakpoint() |
|
|
297 { |
|
|
298 HAL_BREAKPOINT(_breakinst); |
|
|
299 } |
|
|
300 |
|
|
301 // This function returns the opcode for a 'trap' instruction. |
|
|
302 unsigned long |
|
|
303 __break_opcode () |
|
|
304 { |
|
|
305 return HAL_BREAKINST; |
|
|
306 } |
|
|
307 #endif |
|
|
308 |
|
|
309 // FIXME: Add support for multiple breakpoints (libstub/bplist-dynamic.c) |
|
|
310 int __set_breakpoint (target_register_t addr) { return 0; } |
|
|
311 int __remove_breakpoint (target_register_t addr) { return 0; } |
|
|
312 |
|
|
313 //----------------------------------------------------------------------------- |
|
|
314 // Write the 'T' packet in BUFFER. SIGVAL is the signal the program received. |
|
|
315 void |
|
|
316 __build_t_packet (int sigval, char *buf) |
|
|
317 { |
|
|
318 target_register_t addr; |
|
|
319 char *ptr = buf; |
|
|
320 target_register_t *sp; |
|
|
321 |
|
|
322 sp = (target_register_t *) get_register (SP); |
|
|
323 |
|
|
324 *ptr++ = 'T'; |
|
|
325 *ptr++ = __tohex (sigval >> 4); |
|
|
326 *ptr++ = __tohex (sigval); |
|
|
327 |
|
|
328 #ifdef CYGDBG_HAL_DEBUG_GDB_THREAD_SUPPORT |
|
|
329 // Include thread ID if thread manipulation is required. |
|
|
330 { |
|
|
331 int id; |
|
|
332 |
|
|
333 *ptr++ = 't'; |
|
|
334 *ptr++ = 'h'; |
|
|
335 *ptr++ = 'r'; |
|
|
336 *ptr++ = 'e'; |
|
|
337 *ptr++ = 'a'; |
|
|
338 *ptr++ = 'd'; |
|
|
339 *ptr++ = ':'; |
|
|
340 |
|
|
341 #if (CYG_BYTEORDER == CYG_MSBFIRST) |
|
|
342 id = dbg_currthread_id (); |
|
|
343 #else |
|
|
344 // FIXME: Temporary workaround for PR 18903. Thread ID must be |
|
|
345 // big-endian in the T packet. |
|
|
346 { |
|
|
347 unsigned char* bep = (unsigned char*)&id; |
|
|
348 int be_id; |
|
|
349 |
|
|
350 be_id = dbg_currthread_id (); |
|
|
351 *bep++ = (be_id >> 24) & 0xff ; |
|
|
352 *bep++ = (be_id >> 16) & 0xff ; |
|
|
353 *bep++ = (be_id >> 8) & 0xff ; |
|
|
354 *bep++ = (be_id & 0xff) ; |
|
|
355 } |
|
|
356 #endif |
|
|
357 ptr = __mem2hex((char *)&id, ptr, sizeof(id), 0); |
|
|
358 *ptr++ = ';'; |
|
|
359 } |
|
|
360 #endif |
|
|
361 |
|
|
362 *ptr++ = __tohex (PC >> 4); |
|
|
363 *ptr++ = __tohex (PC); |
|
|
364 *ptr++ = ':'; |
|
|
365 addr = get_register (PC); |
|
|
366 ptr = __mem2hex((char *)&addr, ptr, sizeof(addr), 0); |
|
|
367 *ptr++ = ';'; |
|
|
368 |
|
|
369 *ptr++ = __tohex (SP >> 4); |
|
|
370 *ptr++ = __tohex (SP); |
|
|
371 *ptr++ = ':'; |
|
|
372 ptr = __mem2hex((char *)&sp, ptr, sizeof(sp), 0); |
|
|
373 *ptr++ = ';'; |
|
|
374 |
|
|
375 *ptr++ = 0; |
|
|
376 } |
|
|
377 |
|
|
378 |
|
|
379 //----------------------------------------------------------------------------- |
|
|
380 // Cache functions. |
|
|
381 |
|
|
382 // Perform the specified operation on the instruction cache. |
|
|
383 // Returns 1 if the cache is enabled, 0 otherwise. |
|
|
384 int |
|
|
385 __instruction_cache (cache_control_t request) |
|
|
386 { |
|
|
387 int state = 1; |
|
|
388 |
|
|
389 switch (request) { |
|
|
390 case CACHE_ENABLE: |
|
|
391 HAL_ICACHE_ENABLE(); |
|
|
392 break; |
|
|
393 case CACHE_DISABLE: |
|
|
394 HAL_ICACHE_DISABLE(); |
|
|
395 state = 0; |
|
|
396 break; |
|
|
397 case CACHE_FLUSH: |
|
|
398 HAL_ICACHE_SYNC(); |
|
|
399 HAL_ICACHE_INVALIDATE_ALL(); |
|
|
400 break; |
|
|
401 case CACHE_NOOP: |
|
|
402 /* fall through */ |
|
|
403 default: |
|
|
404 break; |
|
|
405 } |
|
|
406 |
|
|
407 #ifdef HAL_ICACHE_IS_ENABLED |
|
|
408 HAL_ICACHE_IS_ENABLED(state); |
|
|
409 #endif |
|
|
410 |
|
|
411 return state; |
|
|
412 } |
|
|
413 |
|
|
414 // Perform the specified operation on the data cache. |
|
|
415 // Returns 1 if the cache is enabled, 0 otherwise. |
|
|
416 int |
|
|
417 __data_cache (cache_control_t request) |
|
|
418 { |
|
|
419 int state = 1; |
|
|
420 |
|
|
421 switch (request) { |
|
|
422 case CACHE_ENABLE: |
|
|
423 HAL_DCACHE_ENABLE(); |
|
|
424 break; |
|
|
425 case CACHE_DISABLE: |
|
|
426 HAL_DCACHE_DISABLE(); |
|
|
427 state = 0; |
|
|
428 break; |
|
|
429 case CACHE_FLUSH: |
|
|
430 HAL_DCACHE_SYNC(); |
|
|
431 HAL_DCACHE_INVALIDATE_ALL(); |
|
|
432 break; |
|
|
433 case CACHE_NOOP: |
|
|
434 /* fall through */ |
|
|
435 default: |
|
|
436 break; |
|
|
437 } |
|
|
438 |
|
|
439 #ifdef HAL_DCACHE_IS_ENABLED |
|
|
440 HAL_DCACHE_IS_ENABLED(state); |
|
|
441 #endif |
|
|
442 |
|
|
443 return state; |
|
|
444 } |
|
|
445 |
|
|
446 //----------------------------------------------------------------------------- |
|
|
447 // Memory accessor functions. |
|
|
448 |
|
|
449 void *__mem_fault_handler = (void *)0; |
|
|
450 |
|
|
451 /* These are the "arguments" to __do_read_mem and __do_write_mem, |
|
|
452 which are passed as globals to avoid squeezing them thru |
|
|
453 __set_mem_fault_trap. */ |
|
|
454 |
|
|
455 static volatile target_register_t memCount; |
|
|
456 |
|
|
457 static void |
|
|
458 __do_copy_mem (unsigned char* src, unsigned char* dst) |
|
|
459 { |
|
|
460 __mem_fault = 1; // Defaults to 'fail'. Is cleared |
|
|
461 // when the copy loop completes. |
|
|
462 __mem_fault_handler = &&err; |
|
|
463 |
|
|
464 while (memCount) |
|
|
465 { |
|
|
466 *dst++ = *src++; |
|
|
467 memCount--; |
|
|
468 } |
|
|
469 |
|
|
470 __mem_fault = 0; |
|
|
471 |
|
|
472 err: |
|
|
473 __mem_fault_handler = (void *)0; |
|
|
474 } |
|
|
475 |
|
|
476 /* |
|
|
477 * __read_mem_safe: |
|
|
478 * Get contents of target memory, abort on error. |
|
|
479 */ |
|
|
480 |
|
|
481 int |
|
|
482 __read_mem_safe (unsigned char *dst, target_register_t src, int count) |
|
|
483 { |
|
|
484 memCount = count; |
|
|
485 __do_copy_mem((unsigned char*) src, (unsigned char*) dst); |
|
|
486 return count - memCount; // return number of bytes successfully read |
|
|
487 } |
|
|
488 |
|
|
489 /* |
|
|
490 * __write_mem_safe: |
|
|
491 * Set contents of target memory, abort on error. |
|
|
492 */ |
|
|
493 |
|
|
494 int |
|
|
495 __write_mem_safe (unsigned char *src, target_register_t dst, int count) |
|
|
496 { |
|
|
497 memCount = count; |
|
|
498 __do_copy_mem((unsigned char*) src, (unsigned char*) dst); |
|
|
499 return count - memCount; // return number of bytes successfully written |
|
|
500 } |
|
|
501 |
|
|
502 //----------------------------------------------------------------------------- |
|
|
503 // Target extras?! |
|
|
504 int |
|
|
505 __process_target_query(char * pkt, char * out, int maxOut) |
|
|
506 { return 0 ; } |
|
|
507 int |
|
|
508 __process_target_set(char * pkt, char * out, int maxout) |
|
|
509 { return 0 ; } |
|
|
510 int |
|
|
511 __process_target_packet(char * pkt, char * out, int maxout) |
|
|
512 { return 0 ; } |
|
|
513 |
|
|
514 // GDB string output, making sure interrupts are disabled. |
|
|
515 // This function gets used by some diag output functions. |
|
|
516 void |
|
|
517 hal_output_gdb_string(target_register_t str, int string_len) |
|
|
518 { |
|
|
519 unsigned long __state; |
|
|
520 HAL_DISABLE_INTERRUPTS(__state) |
|
|
521 __output_gdb_string(str, string_len); |
|
|
522 HAL_RESTORE_INTERRUPTS(__state); |
|
|
523 } |
|
|
524 |
|
|
525 #endif // CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS |