gdbio.c
14.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
/*---------------------------------------------------------------------*
* Copyright (C) 2004 BroadOn Communications Corp.
*
* $RCSfile: gdbio.c,v $ (derived from rmonsio.c)
* $Revision: 1.4 $
* $Date: 2004/02/18 03:01:13 $
*---------------------------------------------------------------------*/
/*
* This file contains functions for communicating with GDB via the RDB,
* using the GDB Remote Serial Protocol (RSP).
*
* The BBplayer communicates with GDB through the following chain:
*
* TCP/IP USB Shared variables
* GDB <----------> MUX <----------> RDB Slave <---------> GDB Stub
* 1 2 3 4
*
*
* 1. GDB sents the GDB RSP Request to Mux. The maximum size of a
* GDB packet is >= 400 bytes and it set so that it can accommodate
* the largest packet (a 'G' packet) in the protocol. For the MIPS-32
* architecture that would be 72 (# regs) * 8 (bytes to hex encode 32-bit)
* + 32 (for padding) = 608 bytes.
*
* 2. MUX reads from the GDB TCP socket and sends the information to the
* BBPlayer via USB. MUX wraps the GDB data in RDB packets with
* type RDB_TYPE_HtoG_DEBUG and sends it through USB. Each packet
* has a command byte where the upper 8 bit is used for the RDB type.
* The length is specified as follows for a RDB_TYPE_HtoG_DEBUG packet:
* It is assumed that the RDB_TYPE_HtoG_DEBUG packet will only
* be sent if the length of the debug data is > 0.
* If the length is <= 3 bytes, then the length is stored in
* the lower 2 bits. The number of bytes used for sending
* n bytes of data will be 1+n. This is good for sending GDB
* single character acknowledgments ('+').
* If the length is > 3 bytes and < 254 bytes, the lower 2 bits are
* set to 0. The length is specified in the next byte. The
* number of bytes used for sending n bytes of data will be 2+n.
* If the data is longer than 254 bytes, it is chopped up into
* 254 size blocks and set. This is because the USB interface
* can only handle up to 256 bytes at a time.
*
* After all data from this read is sent, MUX will sent a RDB packet of
* type RDB_TYPE_HtoG_DEBUG_DONE (consuming 1 byte) to RDB, indicating
* that there is no more data from the debuggin stream.
*
* At this point, MUX will also block the debug stream until it gets a
* RDB_TYPE_GtoH_DEBUG_READY signal from the RDB. If GDB still attempts
* to communicate with MUX, all incoming data on the debug stream is
* dropped. GDB will have to retransmit the data.
*
* 3. The RDB will read the data from the USB/RDB buffer (with a maximumum
* size of RIP_BULK_BUFSZ = 8K) and copy data that has a RDB type
* of RDB_TYPE_HtoG_DEBUG into the shared debug buffer pointed to by
* __osRdb_DbgRead_Buf. The size of the buffer is specified by
* __osRdb_DbgRead_BufSize. It is set to GDB_DBG_BUF_SIZE = 2048 bytes.
* The number of bytes read is kept in __osRdb_DbgRead_Cnt. The RDB will
* continue to copy data into the buffer until it becomes full or it gets
* a RDB packet with RDB type RDB_TYPE_HtoG_DEBUG_DONE. Once it receives
* the signal that the debug data has finish, it will send the event
* OS_EVENT_RDB_DBG_DONE, waking up the GDB IO thread. If the buffer becomes
* full before it receives this signal, the RDB will drop any additional
* data for the debugger.
*
* 4. The GDB IO thread waits in a loop for input from the RDB. It will
* initially set the __osRdb_DbgRead_Buf to point to its buffer
* gdbRdbReadBuf (with maximum buffer size GDB_DBG_BUF_SIZE = 2048 bytes)
* and set__osRdb_DbgRead_Cnt to 0. Once it gets the OS_EVENT_RDB_DBG_DONE
* message, it will proceed to process the next GDB packet, character by
* character, removing the GDB packet headers/trailers and copying it into
* the __gdbInBuffer (with maximum buffer size GDB_DBG_BUF_SIZE = 2048
* bytes). When it has consumed all the characters in its buffer, it will
* resets the shared variables __osRdb_DbgRead_Buf and __osRdb_DbgRead_Cnt
* and send a message back to MUX (via RDB/USB) with the RDB packet type
* of RDB_TYPE_GtoH_DEBUG_READY, indicating that it is ready for addtional
* GDB input. At that point, MUX will unblock the debug stream, and will
* be willing to copy data from the GDB debugger to the BBPlayer again.
*/
#include "ultragdb.h"
#include "rdb.h"
#include "os_bb.h"
#include <sys/signal.h>
#ifndef _FINALROM
static OSMesgQueue IOmq;
static OSMesg IOmsgs;
OSMesgQueue BKmq;
OSMesg BKmsgs;
u8 gdbRdbReadBuf[GDB_DBG_BUF_SIZE];
u32 gdbRdbReadBufSize = 0;
extern u8 *__osRdb_DbgRead_Buf;
extern u32 __osRdb_DbgRead_Ct;
extern u32 __osRdb_DbgRead_BufSize;
extern char hexchars[];
void __gdbSendFault( OSThread * thread )
{
volatile float f;
u8 *tPtr;
u32 sent = 0;
/*
* Touch a floating point register to become the owner of the floating
* point unit (and force the previous owner to save its registers).
*/
f = 0.0F;
tPtr = (u8*)thread;
while(sent < sizeof(OSThread))
{
sent += __osRdbSend(&tPtr[sent], sizeof(OSThread)-sent, RDB_TYPE_GtoH_FAULT);
}
}
void __gdbIOflush( void )
{
int sent = 0;
unsigned char tstr[4];
while(sent < 1)
sent += __osRdbSend(&tstr[0], 1, RDB_TYPE_GtoH_DEBUG_DONE);
}
int __gdbSend(char *s, int n)
{
int sent = 0;
while(sent < n)
sent += __osRdbSend(&s[sent], n-sent, RDB_TYPE_GtoH_DEBUG);
return sent;
}
char __gdbPutDebugChar(char c)
{
__gdbSend(&c, 1);
return c;
}
/* This functions gets the next character for the debug stream from the RDB. */
int __gdbGetDebugChar(void)
{
u32 saveMask;
char c = 0;
static int cnt = 0;
static int sent;
unsigned char tstr[4]; /* junk, but needed for osRdbSend */
while (cnt >= gdbRdbReadBufSize) {
/* Make sure we are not interrupted when resetting the shared
* variables for the debug buffer */
/* Disable interrupts */
saveMask = __osDisableInt();
__osRdb_DbgRead_Buf = gdbRdbReadBuf; /* reset the buffer pointer */
__osRdb_DbgRead_Ct = 0;
/* Restore interrupts */
__osRestoreInt(saveMask);
sent = 0;
while(sent < 1)
sent += __osRdbSend(&tstr[0],1,RDB_TYPE_GtoH_DEBUG_READY);
osRecvMesg(&IOmq, NULL, OS_MESG_BLOCK);
/* Make sure we are not interrupted when reading the shared variable
* __osRdb_DbgRead_Ct */
/* For the MIPS R4300, the store is an atomic operation so we don't
* need to disable interrupts */
/* Disable interrupts */
/* saveMask = __osDisableInt(); */
gdbRdbReadBufSize = __osRdb_DbgRead_Ct;
/* Restore interrupts */
/*__osRestoreInt(saveMask); */
if (gdbDebug & GDB_DEBUG_SHOW_INFO)
{
GDB_PRINTF("%s: Debug Message Received: %d bytes\n",
gdb_module_name, gdbRdbReadBufSize);
}
cnt = 0;
}
if (gdbRdbReadBufSize)
c = gdbRdbReadBuf[cnt++];
return c;
}
/************************************************************************
Function: __gdbGetPacket
Args: char* buffer - The buffer to store the packet into.
u32 bufsize - The size of the buffer.
int* psig - Pointer to signal value.
Purpose: This function retrieves a GDB packet by scanning for the
sequence $<data>#<checksum>. This function will not return
until it has retrieved a GDB packet with the correct checksum
(it will request GDB retransmit the packet if the checksum is
incorrect). The <data> portion of the GDB packet is stored
in buffer along with a null-termination character at the end.
If a CTRL-C is received out-side of a packet, then *psig
is set to SIGINT, and the function returns immediately.
Otherwise, *psig is set to 0.
Returns: u32 - The number of characters in the packet.
TODO: Improve handling for buffer overflow.
************************************************************************/
u32 __gdbGetPacket(char *buffer, u32 bufsize, int* psig)
{
static int badchars = 0;
unsigned char checksum;
unsigned char xmitcsum;
int i;
u32 count;
char ch;
if (psig != NULL) *psig = 0;
if (buffer == NULL) return 0;
if (bufsize == 0) return 0;
do {
/* wait around for the start character, ignore all other characters */
while ((ch = __gdbGetDebugChar() & 0x7f) != '$')
{
if (ch == 0x03) /* Control-C */
{
/* Signal to stop program */
if (psig != NULL) *psig = SIGINT;
return 0;
}
if (ch != '+' && ch != '-')
{
if (gdbDebug & GDB_DEBUG_SHOW_CHAR)
{
GDB_PRINTF("%s: Got char (outside of packet): %c (0x%x)\n",
gdb_module_name, ch, ch);
}
/* Hmm, we are seeing some funny characters */
/* let's send a request for retransmission */
if ((badchars % 10) == 0)
{
__gdbPutDebugChar('-');
}
badchars++;
}
}
checksum = 0;
xmitcsum = -1;
count = 0;
/* now, read until a # or end of buffer is found */
/* save one char in buffer for null terminator */
while (count < bufsize-1) {
ch = __gdbGetDebugChar() & 0x7f;
if (ch == '#') break;
checksum = checksum + ch;
buffer[count] = ch;
count++;
}
buffer[count] = 0; /* NULL-terminate */
if (ch == '#') {
/* Got end of packet - everything is good */
/* Let's check the checksum */
xmitcsum = hex(__gdbGetDebugChar() & 0x7f) << 4;
xmitcsum += hex(__gdbGetDebugChar() & 0x7f);
if ((gdbDebug & GDB_DEBUG_SHOW_ERROR) && (checksum != xmitcsum)) {
GDB_PRINTF("%s: Packet failed checksum - requesting retransmission\n",
gdb_module_name);
/* error */
}
if (checksum != xmitcsum)
__gdbPutDebugChar('-'); /* failed checksum */
else {
__gdbPutDebugChar('+'); /* successful transfer */
/* if a sequence char is present, reply the sequence ID */
if (buffer[2] == ':') {
__gdbPutDebugChar( buffer[0] );
__gdbPutDebugChar( buffer[1] );
/* remove sequence chars from buffer */
count = strlen(buffer);
for (i=3; i <= count; i++) buffer[i-3] = buffer[i];
}
}
}
else {
/* TODO */
/* We must have terminated the loop because we've reached
the maximum size of the buffer and haven't gotten
the end of the packet yet. What should we do????? */
/* We don't want to allocate more memory for the messages, but
we really cannot handle this message - is there a way to respond to
GDB saying the message is too big and to break it down? */
if (gdbDebug & GDB_DEBUG_SHOW_ERROR)
{
GDB_PRINTF("%s: GDB packet too big for buffer size %d\n",
gdb_module_name, bufsize);
}
/* Right now, we are just going to ignore this sequence of characters
and continue looping around until we get a packet we can handle */
}
} while (checksum != xmitcsum);
return count;
}
/************************************************************************
Function: __gdbPutPacket
Args: char* buffer - The packet info to send to GDB.
Assumes: buffer is null-terminated
Purpose: This function wraps up the contents of the buffer in the format
$<packet info>$<checksum> by adding a leading '$', the trailing
'#', and calculating the checksum and sending the resulting
packet to GDB.
This function returns immediately after sending the packet
and does not check to see if GDB has recieved it.
TODO: Wait and get acknowledge from GDB ('+') and resend packet
if needed?????
************************************************************************/
void __gdbPutPacket(char *buffer)
{
unsigned char checksum;
int count;
char ch;
if (buffer == NULL) return;
/* Buffer is assumed to be null-terminated */
if (gdbDebug & GDB_DEBUG_SHOW_PACKET)
GDB_PRINTF("%s: sending packet: %s\n", gdb_module_name, buffer);
/* $<packet info>#<checksum>. */
do {
__gdbPutDebugChar('$');
checksum = 0;
count = 0;
while ((ch=buffer[count])) {
/* For efficiency, we will send the entire buffer at once */
/* if (! __gdbPutDebugChar(ch)) return; */
checksum += ch;
count += 1;
}
if (count > 0)
__gdbSend(buffer, count);
__gdbPutDebugChar('#');
__gdbPutDebugChar(hexchars[checksum >> 4]);
__gdbPutDebugChar(hexchars[checksum % 16]);
} while (1 == 0); /* (__gdbGetDebugChar() != '+'); */
}
/************************************************************************
Function: __gdbIOhandler
Purpose: This function is the entry point of the thread responsible
for communicating with GDB.
************************************************************************/
void __gdbIOhandler(void)
{
u32 saveMask;
osCreateMesgQueue(&IOmq, &IOmsgs, 1);
osSetEventMesg(OS_EVENT_RDB_DBG_DONE, &IOmq, 0);
osCreateMesgQueue(&BKmq, &BKmsgs, 1);
osSetEventMesg(OS_EVENT_DBG_WAIT_BRK, &BKmq, 0);
saveMask = __osDisableInt();
__osRdb_DbgRead_BufSize = GDB_DBG_BUF_SIZE;
__osRdb_DbgRead_Buf = gdbRdbReadBuf;
__osRdb_DbgRead_Ct = 0;
__osRestoreInt(saveMask);
while(1)
{
/* Use GDB Serial Protocol */
gdbProcPacket();
}
}
#endif /* #ifndef _FINALROM */