From fd67814e06a734cd0d78274d61fc04c7af79b5fc Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Mon, 23 Apr 2018 12:06:14 +0200 Subject: bsps: Move GDB stubs to bsps This patch is a part of the BSP source reorganization. Update #3285. --- bsps/mips/shared/gdbstub/README | 151 +++ bsps/mips/shared/gdbstub/gdb_if.h | 194 ++++ bsps/mips/shared/gdbstub/memlimits.h | 100 ++ bsps/mips/shared/gdbstub/mips-stub.c | 1589 ++++++++++++++++++++++++++++++++ bsps/mips/shared/gdbstub/mips_opcode.h | 336 +++++++ 5 files changed, 2370 insertions(+) create mode 100644 bsps/mips/shared/gdbstub/README create mode 100644 bsps/mips/shared/gdbstub/gdb_if.h create mode 100644 bsps/mips/shared/gdbstub/memlimits.h create mode 100644 bsps/mips/shared/gdbstub/mips-stub.c create mode 100644 bsps/mips/shared/gdbstub/mips_opcode.h (limited to 'bsps/mips/shared') diff --git a/bsps/mips/shared/gdbstub/README b/bsps/mips/shared/gdbstub/README new file mode 100644 index 0000000000..e61d0f0aa5 --- /dev/null +++ b/bsps/mips/shared/gdbstub/README @@ -0,0 +1,151 @@ +/*****************************************************/ + +Debugged this stub against the MongooseV bsp. Relies on putting break +instructions on breakpoints and step targets- normal stuff, and does not +employ hardware breakpoint support at this time. As written, a single +breakpoint in a loop will not be reasserted unless the user steps or has +a 2nd one, since breakpoints are only reset when the gdb stub is +re-entered. A useful enhancement would be to fix the break instruction +management so the stub could invisibly put a 2nd break after the 1st +"official" one so it can silently reset breakpoints. Shouldn't be too +hard, mostly a matter of working it out. + +This was tested only against an R3000 MIPS. It should work OK on a +R4000. Needs to be tested at some point. + +This stub supports threads as implemented by gdb 5 and doesn't have any +bugs I'm aware of. + +Greg Menke +3/5/2002 + +/*****************************************************/ + + +The contents of this directory are based upon the "r46kstub.tar.gz" package +released to the net by + + C. M. Heard + VVNET, Inc. phone: +1 408 247 9376 + 4040 Moorpark Ave. Suite 206 fax: +1 408 244 3651 + San Jose, CA 95117 USA e-mail: heard@vvnet.com + +This package was released in the September 1996 time frame for use +with gdb 4.16 and an IDT R4600 Orion. The stub was modified to support +R3000 class CPUs and to work within the mips-rtems exeception processing +framework. + +THe file memlimits.h could end up being target board dependent. If +this is the case, copy it to your BSP directory and modify as necessary. + +--joel +8 February 2002 + +Original README +=============== + +The r46kstub directory and its compressed archive (r46kstub.tar.gz) contain +the 9/29/96 source code snapshot for a ROM-resident gdb-4.16 debug agent +(aka stub) for the IDT R4600 Orion processor. It is based on the stub for +the Hitachi SH processor written by Ben Lee and Steve Chamberlain and +supplied with the gdb-4.16 distribution; that stub in turn was "originally +based on an m68k software stub written by Glenn Engel at HP, but has changed +quite a bit". The modifications for the R4600 were contributed by C. M. +Heard of VVNET, Inc. and were based in part on the Algorithmics R4000 version +of Phil Bunce's PMON program. + +The distribution consists of the following files: + +-rw-r--r-- 1 1178 Sep 29 16:34 ChangeLog +-rw-r--r-- 1 748 Jul 26 01:18 Makefile +-rw-r--r-- 1 6652 Sep 29 16:34 README +-rw-r--r-- 1 1829 May 21 02:02 gdb_if.h +-rw-r--r-- 1 3745 Sep 29 14:03 ioaddr.h +-rw-r--r-- 1 2906 Sep 29 14:39 limits.h +-rw-r--r-- 1 6552 May 23 00:17 mips_opcode.h +-rw-r--r-- 1 14017 May 21 02:04 r4600.h +-rw-r--r-- 1 23874 Jul 21 20:31 r46kstub.c +-rw-r--r-- 1 1064 Jul 3 12:35 r46kstub.ld +-rw-r--r-- 1 13299 Sep 29 16:24 stubinit.S + +With the exception of mips_opcode.h, which is a slightly modified version +of a header file contributed by Ralph Campbell to 4.4 BSD and is therefore +copyrighted by the UC Regents, all of the source files have been dedicated +by their authors to the public domain. Use them as you wish, but do so +at your own risk! The authors accept _no_ responsibility for any errors. + +The debug agent contained herein is at this writing in active use at VVNET +supporting initial hardware debug and board bring-up of an OC-12 ATM probe +board. It uses polled I/O on a 16C450 UART. We had originally intended to +add support for interrupts to allow gdb to break in on a running program, +but we have found that this is not really necessary since the reset button +will accomplish the same purpose (thanks to the MIPS feature of saving the +program counter in the ErrorEPC register when a reset exception occurs). + +Be aware that this stub handles ALL interrupts and exceptions except for +reset (or NMI) in the same way -- by passing control to the debug command +loop. It of course uses the ROM exception vectors to do so. In order to +support code that actally needs to use interrupts we use use a more elaborate +stub that is linked with the downloaded program. It hooks the RAM exception +vectors and clears the BEV status bit to gain control. The ROM-based stub +is still used in this case for initial program loading. + +In order to port this stub to a different platform you will at a minimum +need to customize the macros in limits.h (which define the limits of readable, +writeable, and steppable address space) and the I/O addresses in ioaddr.h +(which define the 16C450 MMIO addresses). If you use something other than +a 16C450 UART you will probably also need to modify the portions of stubinit.S +which deal with the serial port. I've tried to be careful to respect all the +architecturally-defined hazards as described in Appendix F of Kane and +Heinrich, MIPS RISC Architecture, in order to minimize the work in porting +to 4000-series processors other than the R4600, but no guarantees are offered. +Support is presently restricted to big-endian addressing, and I've not even +considered what changes would be needed for little-endian support. + +When this stub is built with gcc-2.7.2 and binutils-2.6 you will see a few +warning messages from the single-step support routine where a cast is used +to sign-extend a pointer (the next instruction address) into a long long +(the PC image). Those warnings are expected; I've checked the generated +code and it is doing what I had intended. But you should not see any other +warnings or errors. Here is a log of the build: + +mips64orion-idt-elf-gcc -g -Wa,-ahld -Wall -membedded-data \ + -O3 -c r46kstub.c >r46kstub.L +r46kstub.c: In function `doSStep': +r46kstub.c:537: warning: cast to pointer from integer of different size +r46kstub.c:539: warning: cast to pointer from integer of different size +r46kstub.c:547: warning: cast to pointer from integer of different size +r46kstub.c:561: warning: cast to pointer from integer of different size +r46kstub.c:563: warning: cast to pointer from integer of different size +r46kstub.c:572: warning: cast to pointer from integer of different size +r46kstub.c:574: warning: cast to pointer from integer of different size +r46kstub.c:582: warning: cast to pointer from integer of different size +r46kstub.c:589: warning: cast to pointer from integer of different size +r46kstub.c:591: warning: cast to pointer from integer of different size +r46kstub.c:597: warning: cast to pointer from integer of different size +r46kstub.c:599: warning: cast to pointer from integer of different size +r46kstub.c:605: warning: cast to pointer from integer of different size +r46kstub.c:607: warning: cast to pointer from integer of different size +r46kstub.c:613: warning: cast to pointer from integer of different size +r46kstub.c:615: warning: cast to pointer from integer of different size +r46kstub.c:624: warning: cast to pointer from integer of different size +r46kstub.c:628: warning: cast to pointer from integer of different size +r46kstub.c:635: warning: cast to pointer from integer of different size +r46kstub.c:637: warning: cast to pointer from integer of different size +mips64orion-idt-elf-gcc -g -Wa,-ahld -Wall -membedded-data \ + -O3 -c stubinit.S >stubinit.L +mips64orion-idt-elf-ld -t -s -T r46kstub.ld -Map r46kstub.map -o r46kstub.out +mips64orion-idt-elf-ld: mode elf32bmip +stubinit.o +r46kstub.o +mips64orion-idt-elf-objcopy -S -R .bss -R .data -R .reginfo \ + -O srec r46kstub.out r46kstub.hex + +Limitations: stubinit.S deliberately forces the PC (which is a 64-bit +register) to contain a legitimate sign-extended 32-bit value. This was +done to cope with a bug in gdb-4.16, which does _not_ properly sign-extend +the initial PC when it loads a program. This means that you cannot use +the "set" command to load an unmapped sixty-four bit virtual address into +the PC, as you can for all other registers. + +Please send bug reports, comments, or suggestions for improvement to: diff --git a/bsps/mips/shared/gdbstub/gdb_if.h b/bsps/mips/shared/gdbstub/gdb_if.h new file mode 100644 index 0000000000..ba4f0eb757 --- /dev/null +++ b/bsps/mips/shared/gdbstub/gdb_if.h @@ -0,0 +1,194 @@ +/** + * @file + * @ingroup mips_gdb + * @brief Definition of the interface between stub and gdb + */ + +/* + * gdb_if.h - definition of the interface between the stub and gdb + * + * THIS SOFTWARE IS NOT COPYRIGHTED + * + * The following software is offered for use in the public domain. + * There is no warranty with regard to this software or its performance + * and the user must accept the software "AS IS" with all faults. + * + * THE CONTRIBUTORS DISCLAIM ANY WARRANTIES, EXPRESS OR IMPLIED, WITH + * REGARD TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/** + * @defgroup mips_gdb GDB Interface + * @ingroup mips_shared + * @brief GDB Interface + * @{ + */ + +#ifndef _GDB_IF_H +#define _GDB_IF_H + +/** @brief Max number of threads in qM response */ +#define QM_MAX_THREADS (20) + +struct rtems_gdb_stub_thread_info { + char display[256]; + char name[256]; + char more_display[256]; +}; + +/** + * @name Prototypes + * @{ + */ + +int parse_zbreak(const char *in, int *type, unsigned char **addr, int *len); + +char* mem2hstr(char *buf, const unsigned char *mem, int count); +int hstr2mem(unsigned char *mem, const char *buf, int count); +void set_mem_err(void); +unsigned char get_byte(const unsigned char *ptr); +void set_byte(unsigned char *ptr, int val); +char* thread2vhstr(char *buf, int thread); +char* thread2fhstr(char *buf, int thread); +const char* fhstr2thread(const char *buf, int *thread); +const char* vhstr2thread(const char *buf, int *thread); +char* int2fhstr(char *buf, int val); +char* int2vhstr(char *buf, int vali); +const char* fhstr2int(const char *buf, int *ival); +const char* vhstr2int(const char *buf, int *ival); +int hstr2byte(const char *buf, int *bval); +int hstr2nibble(const char *buf, int *nibble); + +Thread_Control *rtems_gdb_index_to_stub_id(int); +int rtems_gdb_stub_thread_support_ok(void); +int rtems_gdb_stub_get_current_thread(void); +int rtems_gdb_stub_get_next_thread(int); +int rtems_gdb_stub_get_offsets( + unsigned char **text_addr, + unsigned char **data_addr, + unsigned char **bss_addr +); +int rtems_gdb_stub_get_thread_regs( + int thread, + unsigned int *registers +); +int rtems_gdb_stub_set_thread_regs( + int thread, + unsigned int *registers +); +void rtems_gdb_process_query( + char *inbuffer, + char *outbuffer, + int do_threads, + int thread +); + +/** @} */ + +/** + * @name MIPS registers + * @brief Numbered in the order in which gdb expects to see them. + * @{ + */ + +#define ZERO 0 +#define AT 1 +#define V0 2 +#define V1 3 +#define A0 4 +#define A1 5 +#define A2 6 +#define A3 7 + +#define T0 8 +#define T1 9 +#define T2 10 +#define T3 11 +#define T4 12 +#define T5 13 +#define T6 14 +#define T7 15 + +#define S0 16 +#define S1 17 +#define S2 18 +#define S3 19 +#define S4 20 +#define S5 21 +#define S6 22 +#define S7 23 + +#define T8 24 +#define T9 25 +#define K0 26 +#define K1 27 +#define GP 28 +#define SP 29 +#define S8 30 +#define RA 31 + +#define SR 32 +#define LO 33 +#define HI 34 +#define BAD_VA 35 +#define CAUSE 36 +#define PC 37 + +#define F0 38 +#define F1 39 +#define F2 40 +#define F3 41 +#define F4 42 +#define F5 43 +#define F6 44 +#define F7 45 + +#define F8 46 +#define F9 47 +#define F10 48 +#define F11 49 +#define F12 50 +#define F13 51 +#define F14 52 +#define F15 53 + +#define F16 54 +#define F17 55 +#define F18 56 +#define F19 57 +#define F20 58 +#define F21 59 +#define F22 60 +#define F23 61 + +#define F24 62 +#define F25 63 +#define F26 64 +#define F27 65 +#define F28 66 +#define F29 67 +#define F30 68 +#define F31 69 + +#define FCSR 70 +#define FIRR 71 + +#define NUM_REGS 72 + +/** @} */ + +void mips_gdb_stub_install(int enableThreads) ; + +#define MEMOPT_READABLE 1 +#define MEMOPT_WRITEABLE 2 + +#ifndef NUM_MEMSEGS +#define NUM_MEMSEGS 10 +#endif + +int gdbstub_add_memsegment(unsigned,unsigned,int); + +/** @} */ + +#endif /* _GDB_IF_H */ diff --git a/bsps/mips/shared/gdbstub/memlimits.h b/bsps/mips/shared/gdbstub/memlimits.h new file mode 100644 index 0000000000..c60ca12111 --- /dev/null +++ b/bsps/mips/shared/gdbstub/memlimits.h @@ -0,0 +1,100 @@ +/** + * @file + * @ingroup mips_limits + * @brief Definition of machine and system dependent address limits. + */ + +/* + * limits.h - definition of machine & system dependent address limits + * + * THIS SOFTWARE IS NOT COPYRIGHTED + * + * The following software is offered for use in the public domain. + * There is no warranty with regard to this software or its performance + * and the user must accept the software "AS IS" with all faults. + * + * THE CONTRIBUTORS DISCLAIM ANY WARRANTIES, EXPRESS OR IMPLIED, WITH + * REGARD TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef _MEMLIMITS_H_ +#define _MEMLIMITS_H_ + +/* + * The macros in this file are specific to a given implementation. + * The general rules for their construction are as follows: + * + * 1.) is_readable(addr,length) should be true if and only if the + * region starting at the given virtual address can be read + * _without_ causing an exception or other fatal error. Note + * that the stub will use the strictest alignment satisfied + * by _both_ addr and length (e.g., if both are divisible by + * 8 then the region will be read in double-word chunks). + * + * 2.) is_writeable(addr,length) should be true if and only if the + * region starting at the given virtual address can be written + * _without_ causing an exception or other fatal error. Note + * that the stub will use the strictest alignment satisfied + * by _both_ addr and length (e.g., if both are divisible by + * 8 then the region will be written in double-word chunks). + * + * 3.) is-steppable(ptr) whould be true if and only if ptr is the + * address of a writeable region of memory which may contain + * an executable instruction. At a minimum this requires that + * ptr be word-aligned (divisible by 4) and not point to EPROM + * or memory-mapped I/O. + * + * Note: in order to satisfy constraints related to cacheability + * of certain memory subsystems it may be necessary for regions + * of kseg0 and kseg1 which map to the same physical addresses + * to have different readability and/or writeability attributes. + */ + +/** + * @defgroup mips_limits Address Limits + * @ingroup mips_shared + * @brief Address Limits + */ + + +/* +#define K0_LIMIT_FOR_READ (K0BASE+0x18000000) +#define K1_LIMIT_FOR_READ (K1BASE+K1SIZE) + +#define is_readable(addr,length) \ + (((K0BASE <= addr) && ((addr + length) <= K0_LIMIT_FOR_READ)) \ + || ((K1BASE <= addr) && ((addr + length) <= K1_LIMIT_FOR_READ))) + +#define K0_LIMIT_FOR_WRITE (K0BASE+0x08000000) +#define K1_LIMIT_FOR_WRITE (K1BASE+0x1e000000) + +#define is_writeable(addr,length) \ + (((K0BASE <= addr) && ((addr + length) <= K0_LIMIT_FOR_WRITE)) \ + || ((K1BASE <= addr) && ((addr + length) <= K1_LIMIT_FOR_WRITE))) + +#define K0_LIMIT_FOR_STEP (K0BASE+0x08000000) +#define K1_LIMIT_FOR_STEP (K1BASE+0x08000000) + +#define is_steppable(ptr) \ + ((((int)ptr & 0x3) == 0) \ + && (((K0BASE <= (int)ptr) && ((int)ptr < K0_LIMIT_FOR_STEP)) \ + || ((K1BASE <= (int)ptr) && ((int)ptr < K1_LIMIT_FOR_STEP)))) + +struct memseg +{ + unsigned begin, end, opts; +}; + +#define MEMOPT_READABLE 1 +#define MEMOPT_WRITEABLE 2 + +#define NUM_MEMSEGS 10 + +int add_memsegment(unsigned,unsigned,int); +int is_readable(unsigned,unsigned); +int is_writeable(unsigned,unsigned); +int is_steppable(unsigned); +*/ + +#endif /* _MEMLIMITS_H_ */ diff --git a/bsps/mips/shared/gdbstub/mips-stub.c b/bsps/mips/shared/gdbstub/mips-stub.c new file mode 100644 index 0000000000..8320eb66a9 --- /dev/null +++ b/bsps/mips/shared/gdbstub/mips-stub.c @@ -0,0 +1,1589 @@ +#define GDB_STUB_ENABLE_THREAD_SUPPORT 1 +/******************************************************************************* + + THIS SOFTWARE IS NOT COPYRIGHTED + + The following software is offered for use in the public domain. + There is no warranty with regard to this software or its performance + and the user must accept the software "AS IS" with all faults. + + THE CONTRIBUTORS DISCLAIM ANY WARRANTIES, EXPRESS OR IMPLIED, WITH + REGARD TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +******************************************************************************** +* +* r46kstub.c -- target debugging stub for the IDT R4600 Orion processor +* +* This module is based on the stub for the Hitachi SH processor written by +* Ben Lee and Steve Chamberlain and supplied with gdb 4.16. The latter +* in turn "is originally based on an m68k software stub written by Glenn +* Engel at HP, but has changed quite a bit." The changes for the R4600 +* were written by C. M. Heard at VVNET. They were based in part on the +* Algorithmics R4000 version of Phil Bunce's PMON program. +* +* Remote communication protocol: +* +* A debug packet whose contents are +* is encapsulated for transmission in the form: +* +* $ # CSUM1 CSUM2 +* +* must be ASCII alphanumeric and cannot include characters +* '$' or '#'. If starts with two characters followed by +* ':', then the existing stubs interpret this as a sequence number. +* +* CSUM1 and CSUM2 are ascii hex representation of an 8-bit +* checksum of , the most significant nibble is sent first. +* the hex digits 0-9,a-f are used. +* +* Receiver responds with: +* +* + if CSUM is correct +* - if CSUM is incorrect +* +* is as follows. All values are encoded in ascii hex digits. +* +* Request Packet +* +* read registers g +* reply XX....X Each byte of register data +* is described by two hex digits. +* Registers are in the internal order +* for GDB, and the bytes in a register +* are in the same order the machine uses. +* or ENN for an error. +* +* write regs GXX..XX Each byte of register data +* is described by two hex digits. +* reply OK for success +* ENN for an error +* +* write reg Pn...=r... Write register n... with value r.... +* reply OK for success +* ENN for an error +* +* read mem mAA..AA,LLLL AA..AA is address, LLLL is length. +* reply XX..XX XX..XX is mem contents +* Can be fewer bytes than requested +* if able to read only part of the data. +* or ENN NN is errno +* +* write mem MAA..AA,LLLL:XX..XX +* AA..AA is address, +* LLLL is number of bytes, +* XX..XX is data +* reply OK for success +* ENN for an error (this includes the case +* where only part of the data was +* written). +* +* cont cAA..AA AA..AA is address to resume +* If AA..AA is omitted, +* resume at same address. +* +* step sAA..AA AA..AA is address to resume +* If AA..AA is omitted, +* resume at same address. +* +* There is no immediate reply to step or cont. +* The reply comes when the machine stops. +* It is SAA AA is the "signal number" +* +* last signal ? Reply with the reason for stopping. +* This is the same reply as is generated +* for step or cont: SAA where AA is the +* signal number. +* +* detach D Host is detaching. Reply OK and +* end remote debugging session. +* +* reserved On other requests, the stub should +* ignore the request and send an empty +* response ($#). This way +* we can extend the protocol and GDB +* can tell whether the stub it is +* talking to uses the old or the new. +* +* Responses can be run-length encoded to save space. A '*' means that +* the next character is an ASCII encoding giving a repeat count which +* stands for that many repetitions of the character preceding the '*'. +* The encoding is n+29, yielding a printable character when n >=3 +* (which is where rle starts to win). Don't use n > 99 since gdb +* masks each character is receives with 0x7f in order to strip off +* the parity bit. +* +* As an example, "0* " means the same thing as "0000". +* +*******************************************************************************/ + + +#include +#include +#include "mips_opcode.h" +/* #include "memlimits.h" */ +#include +#include +#include "gdb_if.h" + +/* Change it to something meaningful when debugging */ +#undef ASSERT +#define ASSERT(x) if(!(x)) printk("ASSERT: stub: %d\n", __LINE__) + +/***************/ +/* Exception Codes */ +#define EXC_INT 0 /* External interrupt */ +#define EXC_MOD 1 /* TLB modification exception */ +#define EXC_TLBL 2 /* TLB miss (Load or Ifetch) */ +#define EXC_TLBS 3 /* TLB miss (Store) */ +#define EXC_ADEL 4 /* Address error (Load or Ifetch) */ +#define EXC_ADES 5 /* Address error (Store) */ +#define EXC_IBE 6 /* Bus error (Ifetch) */ +#define EXC_DBE 7 /* Bus error (data load or store) */ +#define EXC_SYS 8 /* System call */ +#define EXC_BP 9 /* Break point */ +#define EXC_RI 10 /* Reserved instruction */ +#define EXC_CPU 11 /* Coprocessor unusable */ +#define EXC_OVF 12 /* Arithmetic overflow */ +#define EXC_TRAP 13 /* Trap exception */ +#define EXC_FPE 15 /* Floating Point Exception */ + +/* FPU Control/Status register fields */ +#define CSR_FS 0x01000000 /* Set to flush denormals to zero */ +#define CSR_C 0x00800000 /* Condition bit (set by FP compare) */ + +#define CSR_CMASK (0x3f<<12) +#define CSR_CE 0x00020000 +#define CSR_CV 0x00010000 +#define CSR_CZ 0x00008000 +#define CSR_CO 0x00004000 +#define CSR_CU 0x00002000 +#define CSR_CI 0x00001000 + +#define CSR_EMASK (0x1f<<7) +#define CSR_EV 0x00000800 +#define CSR_EZ 0x00000400 +#define CSR_EO 0x00000200 +#define CSR_EU 0x00000100 +#define CSR_EI 0x00000080 + +#define CSR_FMASK (0x1f<<2) +#define CSR_FV 0x00000040 +#define CSR_FZ 0x00000020 +#define CSR_FO 0x00000010 +#define CSR_FU 0x00000008 +#define CSR_FI 0x00000004 + +#define CSR_RMODE_MASK (0x3<<0) +#define CSR_RM 0x00000003 +#define CSR_RP 0x00000002 +#define CSR_RZ 0x00000001 +#define CSR_RN 0x00000000 + +/***************/ + +/* + * Saved register information. Must be prepared by the exception + * preprocessor before handle_exception is invoked. + */ +#if (__mips == 3) +typedef long long mips_register_t; +#define R_SZ 8 +#elif (__mips == 1) +typedef unsigned int mips_register_t; +#define R_SZ 4 +#else +#error "unknown MIPS ISA" +#endif +static mips_register_t *registers; + +#if defined(GDB_STUB_ENABLE_THREAD_SUPPORT) +static char do_threads; /* != 0 means we are supporting threads */ +#endif + +/* + * The following external functions provide character input and output. + */ +extern char getDebugChar (void); +extern void putDebugChar (char); + +/* + * Exception handler + */ +void handle_exception (rtems_vector_number vector, CPU_Interrupt_frame *frame); + +/* + * Prototype needed by this code and to keep it self contained. + */ +void rtems_interrupt_catch( rtems_isr_entry, int, rtems_isr_entry *); + +/* + * The following definitions are used for the gdb stub memory map + */ +struct memseg +{ + unsigned begin, end, opts; +}; + +static int is_readable(unsigned,unsigned); +static int is_writeable(unsigned,unsigned); +static int is_steppable(unsigned); + +/* + * BUFMAX defines the maximum number of characters in the inbound & outbound + * packet buffers. At least 4+(sizeof registers)*2 bytes will be needed for + * register packets. Memory dump packets can profitably use even more. + */ +#define BUFMAX 1500 + +static char inBuffer[BUFMAX]; +static char outBuffer[BUFMAX]; + +/* Structure to keep info on a z-breaks */ +#define BREAKNUM 32 + +struct z0break +{ + /* List support */ + struct z0break *next; + struct z0break *prev; + + /* Location, preserved data */ + + /* the address pointer, really, really must be a pointer to + ** a 32 bit quantity (likely 64 on the R4k), so the full instruction is read & + ** written. Making it a char * as on the i386 will cause + ** the zbreaks to mess up the breakpoint instructions + */ + unsigned char *address; + unsigned instr; +}; + +static struct z0break z0break_arr[BREAKNUM]; +static struct z0break *z0break_avail = NULL; +static struct z0break *z0break_list = NULL; + + +/* + * Convert an int to hex. + */ +const char gdb_hexchars[] = "0123456789abcdef"; + +#define highhex(x) gdb_hexchars [(x >> 4) & 0xf] +#define lowhex(x) gdb_hexchars [x & 0xf] + +/* + * Convert length bytes of data starting at addr into hex, placing the + * result in buf. Return a pointer to the last (null) char in buf. + */ +static char * +mem2hex (void *_addr, int length, char *buf) +{ + unsigned int addr = (unsigned int) _addr; + + if (((addr & 0x7) == 0) && ((length & 0x7) == 0)) /* dword aligned */ + { + long long *source = (long long *) (addr); + long long *limit = (long long *) (addr + length); + + while (source < limit) + { + int i; + long long k = *source++; + + for (i = 15; i >= 0; i--) + *buf++ = gdb_hexchars [(k >> (i*4)) & 0xf]; + } + } + else if (((addr & 0x3) == 0) && ((length & 0x3) == 0)) /* word aligned */ + { + int *source = (int *) (addr); + int *limit = (int *) (addr + length); + + while (source < limit) + { + int i; + int k = *source++; + + for (i = 7; i >= 0; i--) + *buf++ = gdb_hexchars [(k >> (i*4)) & 0xf]; + } + } + else if (((addr & 0x1) == 0) && ((length & 0x1) == 0)) /* halfword aligned */ + { + short *source = (short *) (addr); + short *limit = (short *) (addr + length); + + while (source < limit) + { + int i; + short k = *source++; + + for (i = 3; i >= 0; i--) + *buf++ = gdb_hexchars [(k >> (i*4)) & 0xf]; + } + } + else /* byte aligned */ + { + char *source = (char *) (addr); + char *limit = (char *) (addr + length); + + while (source < limit) + { + int i; + char k = *source++; + + for (i = 1; i >= 0; i--) + *buf++ = gdb_hexchars [(k >> (i*4)) & 0xf]; + } + } + + *buf = '\0'; + return (buf); +} + + +/* + * Convert a hex character to an int. + */ +static int +hex (char ch) +{ + if ((ch >= 'a') && (ch <= 'f')) + return (ch - 'a' + 10); + if ((ch >= '0') && (ch <= '9')) + return (ch - '0'); + if ((ch >= 'A') && (ch <= 'F')) + return (ch - 'A' + 10); + return (-1); +} + +/* + * Convert a string from hex to int until a non-hex digit + * is found. Return the number of characters processed. + */ +static int +hexToInt (char **ptr, int *intValue) +{ + int numChars = 0; + int hexValue; + + *intValue = 0; + + while (**ptr) + { + hexValue = hex (**ptr); + if (hexValue >= 0) + { + *intValue = (*intValue << 4) | hexValue; + numChars++; + } + else + break; + + (*ptr)++; + } + + return (numChars); +} + +/* + * Convert a string from hex to long long until a non-hex + * digit is found. Return the number of characters processed. + */ +static int +hexToLongLong (char **ptr, long long *intValue) +{ + int numChars = 0; + int hexValue; + + *intValue = 0; + + while (**ptr) + { + hexValue = hex (**ptr); + if (hexValue >= 0) + { + *intValue = (*intValue << 4) | hexValue; + numChars++; + } + else + break; + + (*ptr)++; + } + + return (numChars); +} + +/* + * Convert the hex array buf into binary, placing the result at the + * specified address. If the conversion fails at any point (i.e., + * if fewer bytes are written than indicated by the size parameter) + * then return 0; otherwise return 1. + */ +static int +hex2mem (char *buf, void *_addr, int length) +{ + unsigned int addr = (unsigned int) _addr; + if (((addr & 0x7) == 0) && ((length & 0x7) == 0)) /* dword aligned */ + { + long long *target = (long long *) (addr); + long long *limit = (long long *) (addr + length); + + while (target < limit) + { + int i, j; + long long k = 0; + + for (i = 0; i < 16; i++) + if ((j = hex(*buf++)) < 0) + return 0; + else + k = (k << 4) + j; + *target++ = k; + } + } + else if (((addr & 0x3) == 0) && ((length & 0x3) == 0)) /* word aligned */ + { + int *target = (int *) (addr); + int *limit = (int *) (addr + length); + + while (target < limit) + { + int i, j; + int k = 0; + + for (i = 0; i < 8; i++) + if ((j = hex(*buf++)) < 0) + return 0; + else + k = (k << 4) + j; + *target++ = k; + } + } + else if (((addr & 0x1) == 0) && ((length & 0x1) == 0)) /* halfword aligned */ + { + short *target = (short *) (addr); + short *limit = (short *) (addr + length); + + while (target < limit) + { + int i, j; + short k = 0; + + for (i = 0; i < 4; i++) + if ((j = hex(*buf++)) < 0) + return 0; + else + k = (k << 4) + j; + *target++ = k; + } + } + else /* byte aligned */ + { + char *target = (char *) (addr); + char *limit = (char *) (addr + length); + + while (target < limit) + { + int i, j; + char k = 0; + + for (i = 0; i < 2; i++) + if ((j = hex(*buf++)) < 0) + return 0; + else + k = (k << 4) + j; + *target++ = k; + } + } + + return 1; +} + +/* Convert the binary stream in BUF to memory. + + Gdb will escape $, #, and the escape char (0x7d). + COUNT is the total number of bytes to write into + memory. */ +static unsigned char * +bin2mem ( + char *buf, + unsigned char *mem, + int count +) +{ + int i; + + for (i = 0; i < count; i++) { + /* Check for any escaped characters. Be paranoid and + only unescape chars that should be escaped. */ + if (*buf == 0x7d) { + switch (*(buf+1)) { + case 0x3: /* # */ + case 0x4: /* $ */ + case 0x5d: /* escape char */ + buf++; + *buf |= 0x20; + break; + default: + /* nothing */ + break; + } + } + + *mem++ = *buf++; + } + + return mem; +} + + +/* + * Scan the input stream for a sequence for the form $#. + */ +static void +getpacket (char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + char ch; + do + { + /* wait around for the start character, ignore all other characters */ + while ((ch = getDebugChar ()) != '$'); + checksum = 0; + xmitcsum = -1; + + count = 0; + + /* now, read until a # or end of buffer is found */ + while ( (count < BUFMAX-1) && ((ch = getDebugChar ()) != '#') ) + checksum += (buffer[count++] = ch); + + /* make sure that the buffer is null-terminated */ + buffer[count] = '\0'; + + if (ch == '#') + { + xmitcsum = hex (getDebugChar ()) << 4; + xmitcsum += hex (getDebugChar ()); + if (checksum != xmitcsum) + putDebugChar ('-'); /* failed checksum */ + else + { + putDebugChar ('+'); /* successful transfer */ + /* if a sequence char is present, reply the sequence ID */ + if (buffer[2] == ':') + { + putDebugChar (buffer[0]); + putDebugChar (buffer[1]); + /* remove sequence chars from buffer */ + for (i = 3; i <= count; i++) + buffer[i - 3] = buffer[i]; + } + } + } + } + while (checksum != xmitcsum); +} + +/* + * Get a positive/negative acknowledgment for a transmitted packet. + */ +static char +getAck (void) +{ + char c; + + do + { + c = getDebugChar (); + } + while ((c != '+') && (c != '-')); + + return c; +} + +/* + * Send the packet in buffer and wait for a positive acknowledgement. + */ +static void +putpacket (char *buffer) +{ + int checksum; + + /* $# */ + do + { + char *src = buffer; + putDebugChar ('$'); + checksum = 0; + + while (*src != '\0') + { + int runlen = 0; + + /* Do run length encoding */ + while ((src[runlen] == src[0]) && (runlen < 99)) + runlen++; + if (runlen > 3) + { + int encode; + /* Got a useful amount */ + putDebugChar (*src); + checksum += *src; + putDebugChar ('*'); + checksum += '*'; + checksum += (encode = (runlen - 4) + ' '); + putDebugChar (encode); + src += runlen; + } + else + { + putDebugChar (*src); + checksum += *src; + src++; + } + } + + putDebugChar ('#'); + putDebugChar (highhex (checksum)); + putDebugChar (lowhex (checksum)); + } + while (getAck () != '+'); +} + + +/* + * Saved instruction data for single step support + */ +static struct + { + unsigned *targetAddr; + unsigned savedInstr; + } +instrBuffer; + +/* + * If a step breakpoint was planted restore the saved instruction. + */ +static void +undoSStep (void) +{ + if (instrBuffer.targetAddr != NULL) + { + *instrBuffer.targetAddr = instrBuffer.savedInstr; + instrBuffer.targetAddr = NULL; + } + instrBuffer.savedInstr = NOP_INSTR; +} + +/* + * If a single step is requested put a temporary breakpoint at the instruction + * which logically follows the next one to be executed. If the next instruction + * is a branch instruction then skip the instruction in the delay slot. NOTE: + * ERET instructions are NOT handled, as it is impossible to single-step through + * the exit code in an exception handler. In addition, no attempt is made to + * do anything about BC0T and BC0F, since a condition bit for coprocessor 0 + * is not defined on the R4600. Finally, BC2T and BC2F are ignored since there + * is no coprocessor 2 on a 4600. + */ +static void +doSStep (void) +{ + InstFmt inst; + + instrBuffer.targetAddr = (unsigned *)(registers[PC]+4); /* set default */ + + inst.word = *(unsigned *)registers[PC]; /* read the next instruction */ + + switch (inst.RType.op) { /* override default if branch */ + case OP_SPECIAL: + switch (inst.RType.func) { + case OP_JR: + case OP_JALR: + instrBuffer.targetAddr = + (unsigned *)registers[inst.RType.rs]; + break; + }; + break; + + case OP_REGIMM: + switch (inst.IType.rt) { + case OP_BLTZ: + case OP_BLTZL: + case OP_BLTZAL: + case OP_BLTZALL: + if (registers[inst.IType.rs] < 0 ) + instrBuffer.targetAddr = + (unsigned *)(((signed short)inst.IType.imm<<2) + + (registers[PC]+4)); + else + instrBuffer.targetAddr = (unsigned*)(registers[PC]+8); + break; + case OP_BGEZ: + case OP_BGEZL: + case OP_BGEZAL: + case OP_BGEZALL: + if (registers[inst.IType.rs] >= 0 ) + instrBuffer.targetAddr = + (unsigned *)(((signed short)inst.IType.imm<<2) + + (registers[PC]+4)); + else + instrBuffer.targetAddr = (unsigned*)(registers[PC]+8); + break; + }; + break; + + case OP_J: + case OP_JAL: + instrBuffer.targetAddr = + (unsigned *)((inst.JType.target<<2) + ((registers[PC]+4)&0xf0000000)); + break; + + case OP_BEQ: + case OP_BEQL: + if (registers[inst.IType.rs] == registers[inst.IType.rt]) + instrBuffer.targetAddr = + (unsigned *)(((signed short)inst.IType.imm<<2) + (registers[PC]+4)); + else + instrBuffer.targetAddr = (unsigned*)(registers[PC]+8); + break; + case OP_BNE: + case OP_BNEL: + if (registers[inst.IType.rs] != registers[inst.IType.rt]) + instrBuffer.targetAddr = + (unsigned *)(((signed short)inst.IType.imm<<2) + (registers[PC]+4)); + else + instrBuffer.targetAddr = (unsigned*)(registers[PC]+8); + break; + case OP_BLEZ: + case OP_BLEZL: + if (registers[inst.IType.rs] <= 0) + instrBuffer.targetAddr = + (unsigned *)(((signed short)inst.IType.imm<<2) + (registers[PC]+4)); + else + instrBuffer.targetAddr = (unsigned*)(registers[PC]+8); + break; + case OP_BGTZ: + case OP_BGTZL: + if (registers[inst.IType.rs] > 0) + instrBuffer.targetAddr = + (unsigned *)(((signed short)inst.IType.imm<<2) + (registers[PC]+4)); + else + instrBuffer.targetAddr = (unsigned*)(registers[PC]+8); + break; + + case OP_COP1: + if (inst.RType.rs == OP_BC) + switch (inst.RType.rt) { + case COPz_BCF: + case COPz_BCFL: + if (registers[FCSR] & CSR_C) + instrBuffer.targetAddr = (unsigned*)(registers[PC]+8); + else + instrBuffer.targetAddr = + (unsigned *)(((signed short)inst.IType.imm<<2) + + (registers[PC]+4)); + break; + case COPz_BCT: + case COPz_BCTL: + if (registers[FCSR] & CSR_C) + instrBuffer.targetAddr = + (unsigned *)(((signed short)inst.IType.imm<<2) + + (registers[PC]+4)); + else + instrBuffer.targetAddr = (unsigned*)(registers[PC]+8); + break; + }; + break; + } + + if( is_steppable((unsigned)instrBuffer.targetAddr) && *(instrBuffer.targetAddr) != BREAK_INSTR ) + { + instrBuffer.savedInstr = *instrBuffer.targetAddr; + *instrBuffer.targetAddr = BREAK_INSTR; + } + else + { + instrBuffer.targetAddr = NULL; + instrBuffer.savedInstr = NOP_INSTR; + } + return; +} + + +/* + * Translate the R4600 exception code into a Unix-compatible signal. + */ +static int +computeSignal (void) +{ + int exceptionCode = (registers[CAUSE] & CAUSE_EXCMASK) >> CAUSE_EXCSHIFT; + + switch (exceptionCode) + { + case EXC_INT: + /* External interrupt */ + return SIGINT; + + case EXC_RI: + /* Reserved instruction */ + case EXC_CPU: + /* Coprocessor unusable */ + return SIGILL; + + case EXC_BP: + /* Break point */ + return SIGTRAP; + + case EXC_OVF: + /* Arithmetic overflow */ + case EXC_TRAP: + /* Trap exception */ + case EXC_FPE: + /* Floating Point Exception */ + return SIGFPE; + + case EXC_IBE: + /* Bus error (Ifetch) */ + case EXC_DBE: + /* Bus error (data load or store) */ + return SIGBUS; + + case EXC_MOD: + /* TLB modification exception */ + case EXC_TLBL: + /* TLB miss (Load or Ifetch) */ + case EXC_TLBS: + /* TLB miss (Store) */ + case EXC_ADEL: + /* Address error (Load or Ifetch) */ + case EXC_ADES: + /* Address error (Store) */ + return SIGSEGV; + + case EXC_SYS: + /* System call */ + return SIGSYS; + + default: + return SIGTERM; + } +} + +/* + * This support function prepares and sends the message containing the + * basic information about this exception. + */ +static void gdb_stub_report_exception_info( + rtems_vector_number vector, + CPU_Interrupt_frame *frame, + int thread +) +{ + char *optr; + int sigval; + + optr = outBuffer; + *optr++ = 'T'; + sigval = computeSignal (); + *optr++ = highhex (sigval); + *optr++ = lowhex (sigval); + + *optr++ = highhex(SP); /*gdb_hexchars[SP]; */ + *optr++ = lowhex(SP); + *optr++ = ':'; + optr = mem2hstr(optr, (unsigned char *)&frame->sp, R_SZ ); + *optr++ = ';'; + + *optr++ = highhex(PC); /*gdb_hexchars[PC]; */ + *optr++ = lowhex(PC); + *optr++ = ':'; + optr = mem2hstr(optr, (unsigned char *)&frame->epc, R_SZ ); + *optr++ = ';'; + +#if defined(GDB_STUB_ENABLE_THREAD_SUPPORT) + if (do_threads) + { + *optr++ = 't'; + *optr++ = 'h'; + *optr++ = 'r'; + *optr++ = 'e'; + *optr++ = 'a'; + *optr++ = 'd'; + *optr++ = ':'; + optr = thread2vhstr(optr, thread); + *optr++ = ';'; + } +#endif + *optr++ = '\0'; +} + + + +/* + * Scratch frame used to retrieve contexts for different threads, so as + * not to disrupt our current context on the stack + */ +CPU_Interrupt_frame current_thread_registers; + +/* + * This function handles all exceptions. It only does two things: + * it figures out why it was activated and tells gdb, and then it + * reacts to gdb's requests. + */ + +extern void clear_cache(void); +void handle_exception (rtems_vector_number vector, CPU_Interrupt_frame *frame) +{ + int host_has_detached = 0; + int regno, addr, length; + char *ptr; + int current_thread; /* current generic thread */ + int thread; /* stopped thread: context exception happened in */ + + long long regval; + void *regptr; + int binary; + + registers = (mips_register_t *)frame; + + thread = 0; +#if defined(GDB_STUB_ENABLE_THREAD_SUPPORT) + if (do_threads) { + thread = rtems_gdb_stub_get_current_thread(); + } +#endif + current_thread = thread; + + { + /* reapply all breakpoints regardless of how we came in */ + struct z0break *z0, *zother; + + for (zother=z0break_list; zother!=NULL; zother=zother->next) + { + if( zother->instr == 0xffffffff ) + { + /* grab the instruction */ + zother->instr = *(zother->address); + /* and insert the breakpoint */ + *(zother->address) = BREAK_INSTR; + } + } + + /* see if we're coming from a breakpoint */ + if( *((unsigned *)frame->epc) == BREAK_INSTR ) + { + /* see if its one of our zbreaks */ + for (z0=z0break_list; z0!=NULL; z0=z0->next) + { + if( (unsigned)z0->address == frame->epc) + break; + } + if( z0 ) + { + /* restore the original instruction */ + *(z0->address) = z0->instr; + /* flag the breakpoint */ + z0->instr = 0xffffffff; + + /* + now when we return, we'll execute the original code in + the original state. This leaves our breakpoint inactive + since the break instruction isn't there, but we'll reapply + it the next time we come in via step or breakpoint + */ + } + else + { + /* not a zbreak, see if its our trusty stepping code */ + + /* + * Restore the saved instruction at + * the single-step target address. + */ + undoSStep(); + } + } + } + + /* reply to host that an exception has occurred with some basic info */ + gdb_stub_report_exception_info(vector, frame, thread); + putpacket (outBuffer); + + while (!(host_has_detached)) { + outBuffer[0] = '\0'; + getpacket (inBuffer); + binary = 0; + + switch (inBuffer[0]) { + case '?': + gdb_stub_report_exception_info(vector, frame, thread); + break; + + case 'd': /* toggle debug flag */ + /* can print ill-formed commands in valid packets & checksum errors */ + break; + + case 'D': + /* remote system is detaching - return OK and exit from debugger */ + strcpy (outBuffer, "OK"); + host_has_detached = 1; + break; + + case 'g': /* return the values of the CPU registers */ + regptr = registers; +#if defined(GDB_STUB_ENABLE_THREAD_SUPPORT) + if (do_threads && current_thread != thread ) + regptr = ¤t_thread_registers; +#endif + mem2hex (regptr, NUM_REGS * (sizeof registers), outBuffer); + break; + + case 'G': /* set the values of the CPU registers - return OK */ + regptr = registers; +#if defined(GDB_STUB_ENABLE_THREAD_SUPPORT) + if (do_threads && current_thread != thread ) + regptr = ¤t_thread_registers; +#endif + if (hex2mem (&inBuffer[1], regptr, NUM_REGS * (sizeof registers))) + strcpy (outBuffer, "OK"); + else + strcpy (outBuffer, "E00"); /* E00 = bad "set register" command */ + break; + + case 'P': + /* Pn...=r... Write register n... with value r... - return OK */ + ptr = &inBuffer[1]; + if (hexToInt(&ptr, ®no) && + *ptr++ == '=' && + hexToLongLong(&ptr, ®val)) + { + registers[regno] = regval; + strcpy (outBuffer, "OK"); + } + else + strcpy (outBuffer, "E00"); /* E00 = bad "set register" command */ + break; + + case 'm': + /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ + ptr = &inBuffer[1]; + if (hexToInt (&ptr, &addr) + && *ptr++ == ',' + && hexToInt (&ptr, &length) + && is_readable (addr, length) + && (length < (BUFMAX - 4)/2)) + mem2hex ((void *)addr, length, outBuffer); + else + strcpy (outBuffer, "E01"); /* E01 = bad 'm' command */ + break; + + case 'X': /* XAA..AA,LLLL:#cs */ + binary = 1; + case 'M': + /* MAA..AA,LLLL: Write LLLL bytes at address AA..AA - return OK */ + ptr = &inBuffer[1]; + if (hexToInt (&ptr, &addr) + && *ptr++ == ',' + && hexToInt (&ptr, &length) + && *ptr++ == ':' + && is_writeable (addr, length) ) { + if ( binary ) + hex2mem (ptr, (void *)addr, length); + else + bin2mem (ptr, (void *)addr, length); + strcpy (outBuffer, "OK"); + } + else + strcpy (outBuffer, "E02"); /* E02 = bad 'M' command */ + break; + + case 'c': + /* cAA..AA Continue at address AA..AA(optional) */ + case 's': + /* sAA..AA Step one instruction from AA..AA(optional) */ + { + /* try to read optional parameter, pc unchanged if no parm */ + ptr = &inBuffer[1]; + if (hexToInt (&ptr, &addr)) + registers[PC] = addr; + + if (inBuffer[0] == 's') + doSStep (); + } + goto stubexit; + + case 'k': /* remove all zbreaks if any */ + dumpzbreaks: + { + { + /* Unlink the entire list */ + struct z0break *z0, *znxt; + + while( (z0= z0break_list) ) + { + + /* put back the instruction */ + if( z0->instr != 0xffffffff ) + *(z0->address) = z0->instr; + + /* pop off the top entry */ + znxt = z0->next; + if( znxt ) znxt->prev = NULL; + z0break_list = znxt; + + /* and put it on the free list */ + z0->prev = NULL; + z0->next = z0break_avail; + z0break_avail = z0; + } + } + + strcpy(outBuffer, "OK"); + } + break; + + case 'q': /* queries */ +#if defined(GDB_STUB_ENABLE_THREAD_SUPPORT) + rtems_gdb_process_query( inBuffer, outBuffer, do_threads, thread ); +#endif + break; + +#if defined(GDB_STUB_ENABLE_THREAD_SUPPORT) + case 'T': + { + int testThread; + + if( vhstr2thread(&inBuffer[1], &testThread) == NULL ) + { + strcpy(outBuffer, "E01"); + break; + } + + if( rtems_gdb_index_to_stub_id(testThread) == NULL ) + { + strcpy(outBuffer, "E02"); + } + else + { + strcpy(outBuffer, "OK"); + } + } + break; +#endif + + case 'H': /* set new thread */ +#if defined(GDB_STUB_ENABLE_THREAD_SUPPORT) + if (inBuffer[1] != 'g') { + break; + } + + if (!do_threads) { + break; + } + + { + int tmp, ret; + + /* Set new generic thread */ + if (vhstr2thread(&inBuffer[2], &tmp) == NULL) { + strcpy(outBuffer, "E01"); + break; + } + + /* 0 means `thread' */ + if (tmp == 0) { + tmp = thread; + } + + if (tmp == current_thread) { + /* No changes */ + strcpy(outBuffer, "OK"); + break; + } + + /* Save current thread registers if necessary */ + if (current_thread != thread) { + ret = rtems_gdb_stub_set_thread_regs( + current_thread, + (unsigned int *) (void *)¤t_thread_registers); + ASSERT(ret); + } + + /* Read new registers if necessary */ + if (tmp != thread) { + ret = rtems_gdb_stub_get_thread_regs( + tmp, (unsigned int *) (void *)¤t_thread_registers); + + if (!ret) { + /* Thread does not exist */ + strcpy(outBuffer, "E02"); + break; + } + } + + current_thread = tmp; + strcpy(outBuffer, "OK"); + } +#endif + break; + + case 'Z': /* Add breakpoint */ + { + int ret, type, len; + unsigned char *address; + struct z0break *z0; + + ret = parse_zbreak(inBuffer, &type, &address, &len); + if (!ret) { + strcpy(outBuffer, "E01"); + break; + } + + if (type != 0) { + /* We support only software break points so far */ + strcpy(outBuffer, "E02"); + break; + } + + if (len != R_SZ) { /* was 1 */ + strcpy(outBuffer, "E03"); + break; + } + + /* Let us check whether this break point already set */ + for (z0=z0break_list; z0!=NULL; z0=z0->next) { + if (z0->address == address) { + break; + } + } + + if (z0 != NULL) { + /* we already have a breakpoint for this address */ + strcpy(outBuffer, "E04"); + break; + } + + /* Let us allocate new break point */ + if (z0break_avail == NULL) { + strcpy(outBuffer, "E05"); + break; + } + + /* Get entry */ + z0 = z0break_avail; + z0break_avail = z0break_avail->next; + + /* Let us copy memory from address add stuff the break point in */ + /* + *if (mem2hstr(z0->buf, address, 1) == NULL || + !hstr2mem(address, "cc" , 1)) { + + * Memory error * + z0->next = z0break_avail; + z0break_avail = z0; + strcpy(outBuffer, "E05"); + break; + }*/ + + /* Fill it */ + z0->address = address; + + if( z0->address == (unsigned char *) frame->epc ) + { + /* re-asserting the breakpoint that put us in here, so + we'll add the breakpoint but leave the code in place + since we'll be returning to it when the user continues */ + z0->instr = 0xffffffff; + } + else + { + /* grab the instruction */ + z0->instr = *(z0->address); + /* and insert the break */ + *(z0->address) = BREAK_INSTR; + } + + /* Add to the list */ + { + struct z0break *znxt = z0break_list; + + z0->prev = NULL; + z0->next = znxt; + + if( znxt ) znxt->prev = z0; + z0break_list = z0; + } + + strcpy(outBuffer, "OK"); + } + break; + + case 'z': /* remove breakpoint */ + if (inBuffer[1] == 'z') + { + goto dumpzbreaks; + + /* + * zz packet - remove all breaks * + z0last = NULL; + + for (z0=z0break_list; z0!=NULL; z0=z0->next) + { + if(!hstr2mem(z0->address, z0->buf, R_SZ)) + { + ret = 0; + } + z0last = z0; + } + + * Free entries if any * + if (z0last != NULL) { + z0last->next = z0break_avail; + z0break_avail = z0break_list; + z0break_list = NULL; + } + + if (ret) { + strcpy(outBuffer, "OK"); + } else { + strcpy(outBuffer, "E04"); + } + break; + */ + } + else + { + int ret, type, len; + unsigned char *address; + struct z0break *z0; + + ret = parse_zbreak(inBuffer, &type, &address, &len); + if (!ret) { + strcpy(outBuffer, "E01"); + break; + } + + if (type != 0) { + /* We support only software break points so far */ + break; + } + + if (len != R_SZ) { + strcpy(outBuffer, "E02"); + break; + } + + /* Let us check whether this break point set */ + for (z0=z0break_list; z0!=NULL; z0=z0->next) { + if (z0->address == address) { + break; + } + } + + if (z0 == NULL) { + /* Unknown breakpoint */ + strcpy(outBuffer, "E03"); + break; + } + + /* + if (!hstr2mem(z0->address, z0->buf, R_SZ)) { + strcpy(outBuffer, "E04"); + break; + }*/ + + if( z0->instr != 0xffffffff ) + { + /* put the old instruction back */ + *(z0->address) = z0->instr; + } + + /* Unlink entry */ + { + struct z0break *zprv = z0->prev, *znxt = z0->next; + + if( zprv ) zprv->next = znxt; + if( znxt ) znxt->prev = zprv; + + if( !zprv ) z0break_list = znxt; + + znxt = z0break_avail; + + z0break_avail = z0; + z0->prev = NULL; + z0->next = znxt; + } + + strcpy(outBuffer, "OK"); + } + break; + + default: /* do nothing */ + break; + } + + /* reply to the request */ + putpacket (outBuffer); + } + + stubexit: + + /* + * The original code did this in the assembly wrapper. We should consider + * doing it here before we return. + * + * On exit from the exception handler invalidate each line in the I-cache + * and write back each dirty line in the D-cache. This needs to be done + * before the target program is resumed in order to ensure that software + * breakpoints and downloaded code will actually take effect. This + * is because modifications to code in ram will affect the D-cache, + * but not necessarily the I-cache. + */ + + clear_cache(); +} + +static int numsegs; +static struct memseg memsegments[NUM_MEMSEGS]; + +int gdbstub_add_memsegment( unsigned base, unsigned end, int opts ) +{ + if( numsegs == NUM_MEMSEGS ) return -1; + + memsegments[numsegs].begin = base; + memsegments[numsegs].end = end; + memsegments[numsegs].opts = opts; + + ++numsegs; + return RTEMS_SUCCESSFUL; +} + +static int is_readable(unsigned ptr, unsigned len) +{ + struct memseg *ms; + int i; + + if( (ptr & 0x3) ) return -1; + + for(i=0; ibegin <= ptr && ptr+len <= ms->end && (ms->opts & MEMOPT_READABLE) ) + return -1; + } + return 0; +} + +static int is_writeable(unsigned ptr, unsigned len) +{ + struct memseg *ms; + int i; + + if( (ptr & 0x3) ) return -1; + + for(i=0; ibegin <= ptr && ptr+len <= ms->end && (ms->opts & MEMOPT_WRITEABLE) ) + return -1; + } + return 0; +} + +static int is_steppable(unsigned ptr) +{ + struct memseg *ms; + int i; + + if( (ptr & 0x3) ) return -1; + + for(i=0; ibegin <= ptr && ptr <= ms->end && (ms->opts & MEMOPT_WRITEABLE) ) + return -1; + } + return 0; +} + +static char initialized = 0; /* 0 means we are not initialized */ + +void mips_gdb_stub_install(int enableThreads) +{ + /* + These are the RTEMS-defined vectors for all the MIPS exceptions + */ + int exceptionVector[]= { MIPS_EXCEPTION_MOD, \ + MIPS_EXCEPTION_TLBL, \ + MIPS_EXCEPTION_TLBS, \ + MIPS_EXCEPTION_ADEL, \ + MIPS_EXCEPTION_ADES, \ + MIPS_EXCEPTION_IBE, \ + MIPS_EXCEPTION_DBE, \ + MIPS_EXCEPTION_SYSCALL, \ + MIPS_EXCEPTION_BREAK, \ + MIPS_EXCEPTION_RI, \ + MIPS_EXCEPTION_CPU, \ + MIPS_EXCEPTION_OVERFLOW, \ + MIPS_EXCEPTION_TRAP, \ + MIPS_EXCEPTION_VCEI, \ + MIPS_EXCEPTION_FPE, \ + MIPS_EXCEPTION_C2E, \ + MIPS_EXCEPTION_WATCH, \ + MIPS_EXCEPTION_VCED, \ + -1 }; + int i; + rtems_isr_entry old; + + if (initialized) + { + ASSERT(0); + return; + } + + memset( memsegments,0,sizeof(struct memseg)*NUM_MEMSEGS ); + numsegs = 0; + +#if defined(GDB_STUB_ENABLE_THREAD_SUPPORT) + if( enableThreads ) + do_threads = 1; + else + do_threads = 0; +#endif + + { + struct z0break *z0; + + z0break_avail = NULL; + z0break_list = NULL; + + /* z0breaks list init, now we'll do it so it makes sense... */ + for (i=0; inext = z0break_avail; + z0break_avail = z0; + } + } + + for(i=0; exceptionVector[i] > -1; i++) + { + rtems_interrupt_catch( (rtems_isr_entry) handle_exception, exceptionVector[i], &old ); + } + + initialized = 1; + + /* get the attention of gdb */ + /* mips_break(1); disabled so user code can choose to invoke it or not */ +} diff --git a/bsps/mips/shared/gdbstub/mips_opcode.h b/bsps/mips/shared/gdbstub/mips_opcode.h new file mode 100644 index 0000000000..883b1f174b --- /dev/null +++ b/bsps/mips/shared/gdbstub/mips_opcode.h @@ -0,0 +1,336 @@ +/** + * @file + * @ingroup + * @brief Instruction formats and opcode values for MIPS + */ + +/* + * Copyright (c) 1992 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ralph Campbell. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)mips_opcode.h 7.1 (Berkeley) 3/19/92 + * via: mips_opcode.h,v 1.1 1994/03/10 16:15:10 (algorithmics) + */ + +/* + * Define the instruction formats and opcode values for the + * MIPS instruction set. + */ + +#ifndef _MIPS_OPCODE_H +#define _MIPS_OPCODE_H + +/** + * @defgroup mips_ops MIPS Opcodes + * @ingroup mips_shared + * @brief MIPS Instruction Formats and Opcode Values + * @{ + */ + +/** + * @name Instruction formats + * @{ + */ + +typedef union { + unsigned word; + +#ifdef MIPSEL + struct { + unsigned imm: 16; + unsigned rt: 5; + unsigned rs: 5; + unsigned op: 6; + } IType; + + struct { + unsigned target: 26; + unsigned op: 6; + } JType; + + struct { + unsigned func: 6; + unsigned shamt: 5; + unsigned rd: 5; + unsigned rt: 5; + unsigned rs: 5; + unsigned op: 6; + } RType; + + struct { + unsigned func: 6; + unsigned fd: 5; + unsigned fs: 5; + unsigned ft: 5; + unsigned fmt: 4; + unsigned : 1; /* always '1' */ + unsigned op: 6; /* always '0x11' */ + } FRType; +#else + struct { + unsigned op: 6; + unsigned rs: 5; + unsigned rt: 5; + unsigned imm: 16; + } IType; + + struct { + unsigned op: 6; + unsigned target: 26; + } JType; + + struct { + unsigned op: 6; + unsigned rs: 5; + unsigned rt: 5; + unsigned rd: 5; + unsigned shamt: 5; + unsigned func: 6; + } RType; + + struct { + unsigned op: 6; /* always '0x11' */ + unsigned : 1; /* always '1' */ + unsigned fmt: 4; + unsigned func: 6; + unsigned ft: 5; + unsigned fs: 5; + unsigned fd: 5; + } FRType; +#endif +} InstFmt; + +/** @} */ + +/** + * @name 'op' field values + * @{ + */ + +#define OP_SPECIAL 000 +#define OP_REGIMM 001 +#define OP_J 002 +#define OP_JAL 003 +#define OP_BEQ 004 +#define OP_BNE 005 +#define OP_BLEZ 006 +#define OP_BGTZ 007 + +#define OP_ADDI 010 +#define OP_ADDIU 011 +#define OP_SLTI 012 +#define OP_SLTIU 013 +#define OP_ANDI 014 +#define OP_ORI 015 +#define OP_XORI 016 +#define OP_LUI 017 + +#define OP_COP0 020 +#define OP_COP1 021 +#define OP_COP2 022 +#define OP_BEQL 024 +#define OP_BNEL 025 +#define OP_BLEZL 026 +#define OP_BGTZL 027 + +#define OP_DADDI 030 +#define OP_DADDIU 031 +#define OP_LDL 032 +#define OP_LDR 033 + +#define OP_LB 040 +#define OP_LH 041 +#define OP_LWL 042 +#define OP_LW 043 +#define OP_LBU 044 +#define OP_LHU 045 +#define OP_LWR 046 +#define OP_LWU 047 + +#define OP_SB 050 +#define OP_SH 051 +#define OP_SWL 052 +#define OP_SW 053 +#define OP_SDL 054 +#define OP_SDR 055 +#define OP_SWR 056 +#define OP_CACHE 057 + +#define OP_LL 060 +#define OP_LWC1 061 +#define OP_LWC2 062 +#define OP_LLD 064 +#define OP_LDC1 065 +#define OP_LDC2 066 +#define OP_LD 067 + +#define OP_SC 070 +#define OP_SWC1 071 +#define OP_SWC2 072 +#define OP_SCD 074 +#define OP_SDC1 075 +#define OP_SDC2 076 +#define OP_SD 077 + +/** + * @name 'func' field values when 'op' == OP_SPECIAL. + * @{ + */ + +#define OP_SLL 000 +#define OP_SRL 002 +#define OP_SRA 003 +#define OP_SLLV 004 +#define OP_SRLV 006 +#define OP_SRAV 007 + +#define OP_JR 010 +#define OP_JALR 011 +#define OP_SYSCALL 014 +#define OP_BREAK 015 +#define OP_SYNC 017 + +#define OP_MFHI 020 +#define OP_MTHI 021 +#define OP_MFLO 022 +#define OP_MTLO 023 +#define OP_DSLLV 024 +#define OP_DSRLV 026 +#define OP_DSRAV 027 + +#define OP_MULT 030 +#define OP_MULTU 031 +#define OP_DIV 032 +#define OP_DIVU 033 +#define OP_DMULT 034 +#define OP_DMULTU 035 +#define OP_DDIV 036 +#define OP_DDIVU 037 + +#define OP_ADD 040 +#define OP_ADDU 041 +#define OP_SUB 042 +#define OP_SUBU 043 +#define OP_AND 044 +#define OP_OR 045 +#define OP_XOR 046 +#define OP_NOR 047 + +#define OP_SLT 052 +#define OP_SLTU 053 +#define OP_DADD 054 +#define OP_DADDU 055 +#define OP_DSUB 056 +#define OP_DSUBU 057 + +#define OP_TGE 060 +#define OP_TGEU 061 +#define OP_TLT 062 +#define OP_TLTU 063 +#define OP_TEQ 064 +#define OP_TNE 066 + +#define OP_DSLL 070 +#define OP_DSRL 072 +#define OP_DSRA 073 +#define OP_DSLL32 074 +#define OP_DSRL32 076 +#define OP_DSRA32 077 + +/** @} */ + +/** + * 'func' field values when 'op' == OP_REGIMM. + * @{ + */ + +#define OP_BLTZ 000 +#define OP_BGEZ 001 +#define OP_BLTZL 002 +#define OP_BGEZL 003 + +#define OP_TGEI 010 +#define OP_TGEIU 011 +#define OP_TLTI 012 +#define OP_TLTIU 013 +#define OP_TEQI 014 +#define OP_TNEI 016 + +#define OP_BLTZAL 020 +#define OP_BGEZAL 021 +#define OP_BLTZALL 022 +#define OP_BGEZALL 023 + +/** @} */ + +/** + * @name 'rs' field values when 'op' == OP_COPz. + * @{ + */ + +#define OP_MF 000 +#define OP_DMF 001 +#define OP_CF 002 +#define OP_MT 004 +#define OP_DMT 005 +#define OP_CT 006 +#define OP_BC 010 + +/** @} */ + +/** + * @name 'rt' field values when 'op' == OP_COPz and 'rt' == OP_BC. + * @{ + */ + +#define COPz_BCF 0x00 +#define COPz_BCT 0x01 +#define COPz_BCFL 0x02 +#define COPz_BCTL 0x03 + +/** @} */ + +/** + * @name Instructions with specal significance to debuggers. + * @{ + */ + +#define BREAK_INSTR 0x0000000d ///< @brief instruction code for break +#define NOP_INSTR 0x00000000 ///< @brief instruction code for no-op + +/** @} */ + +/** @} */ + +#endif /* _MIPS_OPCODE_H */ -- cgit v1.2.3