TX51 pre-release
[karo-tx-redboot.git] / packages / hal / common / v2_0 / src / thread-packets.c
1 //========================================================================
2 //
3 //      thread-packets.c
4 //
5 //      Provides multi-threaded debug support
6 //
7 //========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
12 //
13 // eCos is free software; you can redistribute it and/or modify it under
14 // the terms of the GNU General Public License as published by the Free
15 // Software Foundation; either version 2 or (at your option) any later version.
16 //
17 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
18 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20 // for more details.
21 //
22 // You should have received a copy of the GNU General Public License along
23 // with eCos; if not, write to the Free Software Foundation, Inc.,
24 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 //
26 // As a special exception, if other files instantiate templates or use macros
27 // or inline functions from this file, or you compile this file and link it
28 // with other works to produce a work based on this file, this file does not
29 // by itself cause the resulting work to be covered by the GNU General Public
30 // License. However the source code for this file must still be made available
31 // in accordance with section (3) of the GNU General Public License.
32 //
33 // This exception does not invalidate any other reasons why a work based on
34 // this file might be covered by the GNU General Public License.
35 //
36 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
37 // at http://sources.redhat.com/ecos/ecos-license/
38 // -------------------------------------------
39 //####ECOSGPLCOPYRIGHTEND####
40 //========================================================================
41 //#####DESCRIPTIONBEGIN####
42 //
43 // Author(s):     Red Hat, nickg
44 // Contributors:  Red Hat, nickg
45 // Date:          1998-08-25
46 // Purpose:
47 // Description:   Provides multi-threaded debug support
48 // Usage:
49 //
50 //####DESCRIPTIONEND####
51 //
52 //========================================================================
53
54 // Define __ECOS__; allows all eCos specific additions to be easily identified.
55 #define __ECOS__
56
57
58 // #ifdef __ECOS__
59 #include <pkgconf/hal.h>
60
61 #if defined(CYGDBG_HAL_DEBUG_GDB_THREAD_SUPPORT) && \
62         defined(CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS)
63 // #endif // __ECOS__
64
65 /* FIXME: Scan this module for correct sizes of fields in packets */
66
67 #ifdef __ECOS__
68 #include <cyg/hal/dbg-threads-api.h>
69 #else  // __ECOS__
70 #include "dbg-threads-api.h"
71 #endif // __ECOS__
72
73 /* This file should ALWAYS define debug thread support */
74 /* Dont include the object in the link if you dont need the support */
75 /* This is NOT the internal unit debug flag, it is a feature control */
76 #if defined(DEBUG_THREADS)
77 #undef DEBUG_THREADS
78 #endif
79
80 #define DEBUG_THREADS 1
81 #define UNIT_TEST         0
82 #define GDB_MOCKUP        0
83
84
85 #define STUB_BUF_MAX 300 /* for range checking of packet lengths */
86
87 #include "thread-pkts.h"
88
89 #ifdef __ECOS__
90 // Use HAL rather than board.h in eCos
91 #include <cyg/hal/hal_stub.h>
92 #else  // __ECOS__
93 #include "board.h"
94 #endif // __ECOS__
95
96 /*
97  * Export the continue and "general" (context) thread IDs from GDB.
98  */
99 int _gdb_cont_thread;
100 int _gdb_general_thread;
101
102 #if !defined(PKT_DEBUG)
103 #define PKT_DEBUG 0
104 #endif
105 extern void output_string(char *message);
106
107 #if PKT_DEBUG
108 void output_threadid(char *title, threadref *ref);
109 #warning "PKT_DEBUG macros engaged"
110 #define PKT_TRACE(title, packet) \
111 { output_string(title); output_string(packet); output_string("\n");}
112 #else
113 #define PKT_TRACE(title, packet) {}
114 #endif
115
116
117 /* This is going to be irregular because the various implementations
118    have adopted different names for registers.
119    It would be nice to fix them to have a common convention
120      _stub_registers
121      stub_registers
122      alt_stub_registers
123  */
124
125 extern target_register_t *_registers;
126         /* A pointer to the current set of registers */
127 extern target_register_t registers[]; /* The current saved registers */
128 extern target_register_t alt_registers[];
129         /* Thread or saved process state */
130
131
132 static void stub_copy_registers(target_register_t *dest,
133                                                                 target_register_t *src)
134 {
135         target_register_t *limit;
136         limit = dest + NUMREGS;
137
138         while (dest < limit)  *dest++ = *src++;
139 }
140
141 #ifdef __ECOS__
142 void __stub_copy_registers(target_register_t *dest,
143                                                 target_register_t *src)
144 {
145         stub_copy_registers(dest, src);
146 }
147 #endif // __ECOS__
148
149 extern int stubhex(char ch);
150
151 /* ----- STUB_PACK_NAK ----------------------------------- */
152 /* Pack an error response into the response packet */
153
154 char *stub_pack_nak(char *outbuf)
155 {
156         *outbuf++ = 'E';
157         *outbuf++ = '0';
158         *outbuf++ = '2';
159         return outbuf;
160 } /* stub_pack_nak */
161
162 /* ----- STUB_PACK_ACK -------------------------- */
163 /* Pack an OK achnowledgement */
164 char *stub_pack_ack(char *outbuf)
165 {
166         *outbuf++ = 'O';
167         *outbuf++ = 'K';
168         return outbuf;
169 } /* stub_pack_ack */
170
171 /* ------- STUB_UNPACK_INT ------------------------------- */
172 /* Unpack a few bytes and return its integer value         */
173 /* This is where I wish functions could return several values
174    I would also advance the buffer pointer */
175
176 int stub_unpack_int(char *buff, int fieldlength)
177 {
178         int retval = 0;
179         int nibble;
180         while (fieldlength)
181         { nibble = stubhex(*buff++);
182                 retval |= nibble;
183                 fieldlength--;
184                 if (fieldlength) retval = retval << 4;
185         }
186         return retval;
187 } /* stub_unpack_int */
188
189 static char *unpack_byte(char *buf, int *value)
190 {
191         *value = stub_unpack_int(buf, 2);
192         return buf + 2;
193 }
194
195 static char *unpack_int(char *buf, int *value)
196 {
197         *value = stub_unpack_int(buf, 8);
198         return buf + 8;
199 }
200
201 /* We are NOT depending upon extensive libraries */
202 static int ishex(char ch, int *val)
203 {
204         if ((ch >= 'a') && (ch <= 'f')) {
205                 *val = ch - 'a' + 10;
206                 return 1;
207         }
208         if ((ch >= 'A') && (ch <= 'F')) {
209                 *val = ch - 'A' + 10;
210                 return 1;
211         }
212         if ((ch >= '0') && (ch <= '9')) {
213                 *val = ch - '0';
214                 return 1;
215         }
216         return 0;
217 } /* ishex */
218
219 static char *unpack_nibble(char *buf, int *val)
220 {
221         ishex(*buf++, val);
222         return buf;
223 }
224
225
226 static const char hexchars[] = "0123456789abcdef";
227
228 static char *pack_hex_byte(char *pkt, unsigned char byte)
229 {
230         *pkt++ = hexchars[(byte >> 4) & 0xf];
231         *pkt++ = hexchars[(byte & 0xf)];
232         return pkt;
233 } /* pack_hex_byte */
234
235 #ifndef __ECOS__
236 /* ---- STUB_PACK_VARLEN_HEX ------------------------------------- */
237 /* Format a variable length stream of hex bytes */
238
239 static char *pack_varlen_hex(
240         char *pkt,
241         unsigned int value)
242 {
243         int i;
244         static unsigned char n[8];
245         if (value == 0)
246         {
247                 *pkt++ = '0';
248                 return pkt;
249         }
250         else
251         {
252                 i = 8;
253                 while (i-- >= 0 )  /* unpack nibbles into a char array */
254                 {
255                         n[i] = value & 0x0f;
256                         value = value >> 4;
257                 }
258                 i = 0;                  /* we had decrmented it to -1 */
259                 while (n[i] == 0 ) i++; /* drop leading zeroes */
260                 while (i++ < 8) *pkt++ = hexchars[n[i]]; /* pack the number */
261         }
262         return pkt;
263 } /* pack_varlen_hex */
264 #endif // !__ECOS__
265
266
267 /* ------ STUB_UNPACK_VARLEN_HEX --------------------------------  */
268 /* Parse a stream of hex bytes which may be of variable length     */
269 /* return the pointer to the next char                             */
270 /* modify a varparm containing the result                          */
271 /* A failure would look like a non-increment of the buffer pointer */
272
273 /* This unpacks hex strings that may have been packed using sprintf(%x) */
274 /* We assume some non-hex delimits them */
275
276 char *unpack_varlen_hex(
277         char *buff,    /* packet to parse */
278         int *result)
279 {
280         int nibble;
281         int retval;
282         retval = 0;
283
284         while (ishex(*buff, &nibble))
285         {
286                 buff++;
287                 retval = retval  << 4;
288                 retval |= nibble & 0x0f;
289         }
290         *result = retval;
291         return buff;
292 } /* stub_unpack_varlen_int */
293
294
295 /* ------ UNPACK_THREADID ------------------------------- */
296 /* A threadid is a 64 bit quantity                        */
297
298 #define BUFTHREADIDSIZ 16 /* encode 64 bits in 16 chars of hex */
299
300 static char *unpack_threadid(char *inbuf, threadref *id)
301 {
302         char *altref;
303         char *limit  = inbuf + BUFTHREADIDSIZ;
304         int x, y;
305         altref = (char *)id;
306
307         while (inbuf < limit)
308         {
309                 x = stubhex(*inbuf++);
310                 y = stubhex(*inbuf++);
311                 *altref++ = (x << 4) | y;
312         }
313         return inbuf;
314 } /* unpack_threadid */
315
316
317
318 /* Pack an integer use leading zeroes */
319 static char *pack_int(char *buf, int value)
320 {
321         buf = pack_hex_byte(buf, (value >> 24) & 0xff);
322         buf = pack_hex_byte(buf, (value >> 16) & 0xff);
323         buf = pack_hex_byte(buf, (value >> 8) & 0x0ff);
324         buf = pack_hex_byte(buf, value & 0xff);
325         return buf;
326 } /* pack_int */
327
328
329 /* -------- PACK_STRING ---------------------------------------------- */
330 /* This stupid string better not contain any funny characters */
331 /* Also, the GDB protocol will not cope with NULLs in the
332    string or at the end of it.
333    While is is posable to encapsulate the protocol in ays that
334    preclude filtering for # I am assuming this is a constraint.
335 */
336
337 static char *pack_raw_string(char *pkt, char *string)
338 {
339         char ch;
340         while (0 != (ch = *string++)) *pkt++ = ch;
341         return pkt;
342 }
343
344 static char *pack_string(
345         char *pkt,
346         char *string)
347 {
348         char ch;
349 #ifdef __ECOS__
350         int len = 0;
351         char *s = string;
352         while( *s++ ) len++;
353 #else  // __ECOS__
354         int len;
355         len = strlen(string);
356 #endif // __ECOS
357         if (len > 200 ) len = 200; /* Bigger than most GDB packets, junk??? */
358         pkt = pack_hex_byte(pkt, len);
359         while (len-- > 0)
360         {
361                 ch = *string++;
362                 if ((ch == '\0') || (ch == '#')) ch = '*'; /* Protect encapsulation */
363                 *pkt++ = ch;
364         }
365         return pkt;
366 } /* pack_string */
367
368
369 /* ----- STUB_PACK_THREADID  --------------------------------------------- */
370 /* Convert a binary 64 bit threadid  and pack it into a xmit buffer */
371 /* Return the advanced buffer pointer */
372
373 static char *pack_threadid(char *pkt, threadref *id)
374 {
375         char *limit;
376         unsigned char *altid;
377         altid = (unsigned char *)id;
378         limit = pkt + BUFTHREADIDSIZ;
379         while (pkt < limit) pkt = pack_hex_byte(pkt, *altid++);
380         return pkt;
381 } /* stub_pack_threadid */
382
383 /* UNFORTUNATELY, not all of the extended debugging system has yet been
384    converted to 64 but thread references and process identifiers.
385    These routines do the conversion.
386    An array of bytes is the correct treatment of an opaque identifier.
387    ints have endian issues.
388 */
389
390 static void int_to_threadref(threadref *id, int value)
391 {
392         unsigned char *scan;
393         scan = (unsigned char *)id;
394         {
395                 int i = 4;
396                 while (i--) *scan++  = 0;
397         }
398         *scan++ = (value >> 24) & 0xff;
399         *scan++ = (value >> 16) & 0xff;
400         *scan++ = (value >> 8) & 0xff;
401         *scan++ = (value & 0xff);
402 }
403
404 static int threadref_to_int(threadref *ref)
405 {
406         int value = 0;
407         unsigned char *scan;
408         int i;
409
410         scan = (char *)ref;
411         scan += 4;
412         i = 4;
413         while (i-- > 0) value = (value << 8) | ((*scan++) & 0xff);
414         return value;
415 } /* threadref_to_int */
416
417 void copy_threadref(threadref *dest, threadref *src)
418 {
419         int i;
420         unsigned char *csrc, *cdest;
421         csrc = (unsigned char *)src;
422         cdest = (unsigned char *)dest;
423         i = 8;
424         while (i--) *cdest++ = *csrc++;
425 }
426
427
428 int threadmatch(
429         threadref *dest,
430         threadref *src
431         )
432 {
433         unsigned char *srcp, *destp;
434         int i, result;
435         srcp = (char *)src;
436         destp = (char *)dest;
437         i = 8;
438         result = 1;
439         while (i-- > 0 ) result &= (*srcp++ == *destp++) ? 1 : 0;
440         return result;
441 } /* threadmatch */
442
443
444
445 static char *Tpkt_threadtag = "thread:";
446
447 /* ----- STUB_PACK_TPKT_THREADID ------------------------------------ */
448 /* retreive, tag and insert a thread identifier into a T packet. */
449 /* Insert nothing if the thread identifier is not available */
450
451 char *stub_pack_Tpkt_threadid(char *pkt)
452 {
453         static threadref thread;
454         int fmt = 0; /* old format */
455         PKT_TRACE("Tpkt-id", "---");
456         if (dbg_currthread(&thread))
457         {
458                 pkt = pack_raw_string(pkt, Tpkt_threadtag);
459                 if (fmt)
460                         pkt = pack_threadid(pkt, &thread);
461                 else
462                         /* Until GDB lengthens its thread ids, we have to MASH
463                            the threadid into somthing shorter. PLEASE FIX GDB */
464                         pkt = pack_int(pkt, threadref_to_int(&thread));
465                 *pkt++ = ';' ; /* terminate variable length int */
466                 *pkt = '\0'; /* Null terminate to allow string to be printed, no++ */
467         }
468         PKT_TRACE("packedTpkt", "--");
469         return pkt;
470 } /* stub_pack_Tpkt_threadid */
471
472 long stub_get_currthread (void)
473 {
474         threadref thread;
475
476         if (dbg_currthread(&thread))
477                 return threadref_to_int(&thread);
478         else
479                 return 0;
480 }
481
482 void stub_pkt_currthread(
483         char *inbuf,
484         char *outbuf,
485         int bufmax)
486 {
487         threadref thread;
488         char *base_out;
489         base_out = outbuf;
490
491         if (dbg_currthread(&thread))
492         {
493                 *outbuf++ = 'Q';
494                 *outbuf++ = 'C'; /* FIXME: Is this a reasonable code */
495                 outbuf = pack_int(outbuf, threadref_to_int(&thread)); /* Short form */
496         }
497         else outbuf = stub_pack_nak(outbuf);
498         *outbuf = '\0'; /* terminate response packet */
499         PKT_TRACE("stub_pkt_currthread(resp) ", base_out);
500 } /* stub_pkt_currthread */
501
502 /* ----- STUB_PKT_THREAD_ALIVE --------------------------------- */
503 /* Answer the thread alive query */
504
505 static int thread_alive (int id)
506 {
507         threadref thread;
508         struct cygmon_thread_debug_info info;
509
510         int_to_threadref(&thread, id);
511         if (dbg_threadinfo(&thread, &info) &&
512                 info.context_exists)
513                 return 1;
514         else
515                 return 0;
516 }
517
518 void stub_pkt_thread_alive(char *inbuf,
519                                                 char *outbuf,
520                                                 int bufmax)
521 {
522         char *prebuf = inbuf;
523         int result;
524
525         if (prebuf != (inbuf = unpack_varlen_hex(inbuf, &result)))
526         {
527                 if (thread_alive(result))
528                 {
529                         outbuf = stub_pack_ack(outbuf);
530                         *outbuf = '\0';
531                         return;
532                 }
533         }
534         outbuf = stub_pack_nak(outbuf);
535         *outbuf = '\0'; /* terminate the response message */
536 } /* stub_pkt_thread_alive */
537
538
539
540 /* ----- STUB_PKT_CHANGETHREAD ------------------------------- */
541 /* Switch the display of registers to that of a saved context */
542
543 /* Changing the context makes NO sense, although the packets define the
544    capability. Therefore, the option to change the context back does
545    call the function to change registers. Also, there is no
546    forced context switch.
547    'p' - New format, long long threadid, no special cases
548    'c' - Old format, id for continue, 32 bit threadid max, possably less
549    -1 means continue all threads
550    'g' - Old Format, id for general use (other than continue)
551
552    replies:
553    OK for success
554    ENN for error
555 */
556
557 void stub_pkt_changethread(
558         char *inbuf,
559         char *outbuf,
560         int bufmax)
561 {
562         threadref id;
563         int idefined = -1;
564         char ch;
565         PKT_TRACE("setthread-pkt ", inbuf);
566
567         /* Parse the incoming packet for a thread identifier */
568         switch (ch = *inbuf++ )  /* handle various packet formats */
569         {
570         case 'p' : /* New format: mode:8, threadid:64 */
571                 inbuf = unpack_nibble(inbuf, &idefined);
572                 inbuf = unpack_threadid(inbuf, &id); /* even if startflag */
573                 break;
574         case 'c' : /* old format, specify thread for continue */
575                 if (inbuf[0] == '-' && inbuf[1] == '1')   /* Hc-1 */
576                         _gdb_cont_thread = 0;
577                 else
578                         inbuf = unpack_varlen_hex(inbuf, &_gdb_cont_thread);
579
580                 if (_gdb_cont_thread == 0 ||                      /* revert to any old thread */
581                         thread_alive(_gdb_cont_thread))           /* specified thread is alive */
582                         outbuf = stub_pack_ack(outbuf);
583                 else
584                         outbuf = stub_pack_nak(outbuf);
585                 break;
586         case 'g' : /* old format, specify thread for general operations */
587                 /* OLD format: parse a variable length hex string */
588                 /* OLD format consider special thread ids */
589         {
590                 inbuf = unpack_varlen_hex(inbuf, &_gdb_general_thread);
591                 int_to_threadref(&id, _gdb_general_thread);
592                 switch (_gdb_general_thread)
593                 {
594                 case  0 : /* pick a thread, any thread */
595                         idefined = 2; /* select original interrupted context */
596                         break;
597                 case -1 : /* all threads */
598                         idefined = 2;
599                         break;
600                 default :
601                         idefined = 1; /* select the specified thread */
602                         break;
603                 }
604         }
605         break;
606         default:
607                 outbuf = stub_pack_nak(outbuf);
608                 break;
609         } /* handle various packet formats */
610
611         switch (idefined)
612         {
613         case -1 :
614                 /* Packet not supported, already NAKed, no further action */
615                 break;
616         case 0 :
617                 /* Switch back to interrupted context */
618                 _registers = &registers[0];
619                 break;
620         case 1 :
621                 /* copy the saved registers into the backup registers */
622                 stub_copy_registers(alt_registers, registers);
623                 /* The OS will now update the values it has in a saved process context*/
624                 if (dbg_getthreadreg(&id, NUMREGS, &alt_registers[0]))
625                 {
626                         /* switch the registers pointer */
627                         _registers = &alt_registers[0];
628                         outbuf = stub_pack_ack(outbuf);
629                 }
630                 else
631                         outbuf = stub_pack_nak(outbuf);
632                 break;
633         case 2 :
634                 /* switch to interrupted context */
635                 outbuf = stub_pack_ack(outbuf);
636                 break;
637         default:
638                 outbuf = stub_pack_nak(outbuf);
639                 break;
640         }
641         *outbuf = '\0'; /* Terminate response pkt */
642 } /* stub_pkt_changethread */
643
644
645 /* ---- STUB_PKT_GETTHREADLIST ------------------------------- */
646 /* Get a portion of the threadlist  or process list            */
647 /* This may be part of a multipacket transaction               */
648 /* It would be hard to tell in the response packet the difference
649    between the end of list and an error in threadid encoding.
650 */
651
652 void stub_pkt_getthreadlist(char *inbuf,
653                                                         char *outbuf,
654                                                         int bufmax)
655 {
656         char *count_ptr;
657         char *done_ptr;
658         char *limit;
659         int start_flag = 0, batchsize, result, count;
660         static threadref lastthread, nextthread;
661
662 #if PKT_DEBUG
663         char *r_base = outbuf;
664 #endif
665         PKT_TRACE("pkt_getthreadlist: ", inbuf);
666
667         count = 0;
668         inbuf = unpack_nibble(inbuf, &start_flag);
669         inbuf = unpack_byte(inbuf, &batchsize);
670         inbuf = unpack_threadid(inbuf, &lastthread); /* even if startflag */
671
672         /*   Start building response packet    */
673         limit = outbuf + (bufmax - BUFTHREADIDSIZ - 10); /* id output packing limit */
674         *outbuf++ = 'Q' ;
675         *outbuf++ = 'M' ;
676
677         /* Default values for count and done fields, save ptr to repatch */
678         count_ptr = outbuf; /* save to repatch count */
679         outbuf = pack_hex_byte(outbuf, 0);
680         done_ptr = outbuf;  /* Backpatched later */
681         *outbuf++ = '0';    /* Done = 0 by default */
682         outbuf = pack_threadid(outbuf, &lastthread);
683
684         /* Loop through the threadid packing */
685         while ((outbuf < limit) && (count < batchsize)) {
686                 result = dbg_threadlist(start_flag, &lastthread, &nextthread);
687                 start_flag = 0; /* redundant but effective */
688                 if (!result) {
689                         *done_ptr = '1';   /* pack the done flag */
690                         break;
691                 }
692 #if 0 /* DEBUG */
693                 if (threadmatch(&lastthread, &nextthread)) {
694                         output_string("FAIL: Threadlist, not incrementing\n");
695                         *done_ptr = '1';
696                         break;
697                 }
698 #endif
699                 count++;
700                 outbuf = pack_threadid(outbuf, &nextthread);
701                 copy_threadref(&lastthread, &nextthread);
702         }
703         pack_hex_byte(count_ptr, count);/* backpatch, Pack the count field */
704         *outbuf = '\0';
705         PKT_TRACE("pkt_getthreadlist(resp) ", r_base);
706 } /* pkt_getthreadlist */
707
708
709
710
711 /* ----- STUB_PKT_GETTHREADINFO ---------------------------------------- */
712 /* Get the detailed information about a specific thread or process */
713
714 /*
715   Encoding:
716   'Q':8, 'P':8, mask:16
717
718   Mask Fields
719   threadid:1,        # always request threadid
720   context_exists:2,
721   display:4,
722   unique_name:8,
723   more_display:16
724 */
725
726 void stub_pkt_getthreadinfo(
727         char *inbuf,
728         char *outbuf,
729         int bufmax)
730 {
731         int mask;
732         int result;
733         threadref thread;
734         struct cygmon_thread_debug_info info;
735
736         info.context_exists = 0;
737         info.thread_display = 0;
738         info.unique_thread_name = 0;
739         info.more_display = 0;
740
741         /* Assume the packet identification chars have already been
742            discarded by the packet demultiples routines */
743         PKT_TRACE("PKT getthreadinfo", inbuf);
744
745         inbuf = unpack_int(inbuf, &mask);
746         inbuf = unpack_threadid(inbuf, &thread);
747
748         result = dbg_threadinfo(&thread, &info); /* Make system call */
749
750         if (result)
751         {
752                 *outbuf++ = 'q';
753                 *outbuf++ = 'p';
754                 outbuf = pack_int(outbuf, mask);
755                 outbuf = pack_threadid(outbuf, &info.thread_id); /* echo threadid */
756                 if (mask & 2)   /* context-exists */
757                 {
758                         outbuf = pack_int(outbuf, 2); /* tag */
759                         outbuf = pack_hex_byte(outbuf, 2); /* length */
760                         outbuf = pack_hex_byte(outbuf, info.context_exists);
761                 }
762                 if ((mask & 4)  && info.thread_display)/* display */
763                 {
764                         outbuf = pack_int(outbuf, 4); /* tag */
765                         outbuf = pack_string(outbuf, info.thread_display);
766                 }
767                 if ((mask & 8) && info.unique_thread_name) /* unique_name */
768                 {
769                         outbuf = pack_int(outbuf, 8);
770                         outbuf = pack_string(outbuf, info.unique_thread_name);
771                 }
772                 if ((mask & 16) && info.more_display)  /* more display */
773                 {
774                         outbuf = pack_int(outbuf, 16); /* tag 16 */
775                         outbuf = pack_string(outbuf, info.more_display);
776                 }
777         }
778         else
779         {
780                 PKT_TRACE("FAIL: dbg_threadinfo\n", "");
781                 outbuf = stub_pack_nak(outbuf);
782         }
783         *outbuf = '\0';
784 } /* stub_pkt_getthreadinfo */
785
786 int stub_lock_scheduler(int lock,       /* 0 to unlock, 1 to lock */
787                                                 int mode,       /* 0 for step,  1 for continue */
788                                                 long id)        /* current thread */
789 {
790         threadref thread;
791
792         int_to_threadref(&thread, id);
793         return dbg_scheduler(&thread, lock, mode);
794 }
795
796
797 #if GDB_MOCKUP
798 /* ------ MATCHED GDB SIDE PACKET ENCODING AND PARSING ----------------- */
799
800 char *pack_nibble(char *buf, int nibble)
801 {
802         *buf++ =  hexchars[(nibble & 0x0f)];
803         return buf;
804 } /* pack_nibble */
805
806 #if 0
807 static char *unpack_short(char *buf, int *value)
808 {
809         *value = stub_unpack_int(buf, 4);
810         return buf + 4;
811 }
812
813 static char *pack_short(
814         char *buf,
815         unsigned int value)
816 {
817         buf = pack_hex_byte(buf, (value >> 8) & 0xff);
818         buf = pack_hex_byte(buf, (value & 0xff));
819         return buf;
820 } /* pack_short */
821 #endif
822
823
824 /* Generally, I dont bury xmit and receive calls inside packet formatters
825    and parsers
826 */
827
828
829
830
831 /* ----- PACK_SETTHREAD_REQUEST ------------------------------------- */
832 /*      Encoding: ??? decode gdb/remote.c
833                 'Q':8, 'p':8, idefined:8, threadid:32;
834 */
835
836 char *pack_setthread_request(
837         char *buf,
838         char fmt,   /* c, g or, p */
839         int idformat,
840         threadref * threadid )
841 {
842         *buf++ = fmt;
843
844         if (fmt == 'p')
845         {  /* pack the long form */
846                 buf = pack_nibble(buf, idformat);
847                 buf = pack_threadid(buf, threadid) ;
848         }
849         else
850         {  /* pack the shorter form - Serious truncation */
851                 /* There are reserved identifieds 0, -1 */
852                 int quickref = threadref_to_int(threadid);
853                 buf = pack_varlen_hex(buf, quickref);
854         }
855         *buf++ = '\0'; /* end_of_packet  */
856         return buf;
857 } /* pack_setthread_request */
858
859
860
861 /* -------- PACK_THREADLIST-REQUEST --------------------------------- */
862 /*    Format: i'Q':8, i"L":8, initflag:8, batchsize:16, lastthreadid:32   */
863
864
865 char *pack_threadlist_request(
866         char *pkt,
867         int startflag,
868         int threadcount,
869         threadref *nextthread
870         )
871 {
872         *pkt++ = 'q';
873         *pkt++ = 'L';
874         pkt = pack_nibble(pkt, startflag);     /* initflag 1 bytes */
875         pkt = pack_hex_byte(pkt, threadcount);   /* threadcount 2 bytes */
876         pkt = pack_threadid(pkt, nextthread);        /* 64 bit thread identifier */
877         *pkt = '\0';
878         return pkt;
879 } /* remote_threadlist_request */
880
881
882
883
884 /* ---------- PARSE_THREADLIST_RESPONSE ------------------------------------ */
885 /* Encoding:   'q':8, 'M':8, count:16, done:8, argthreadid:64, (threadid:64)* */
886
887 int parse_threadlist_response(
888         char *pkt,
889         threadref *original_echo,
890         threadref *resultlist,
891         int *doneflag)
892 {
893         char *limit;
894         int count, resultcount, done;
895         resultcount = 0;
896
897         /* assume the 'q' and 'M chars have been stripped */
898         PKT_TRACE("parse-threadlist-response ", pkt);
899         limit = pkt + (STUB_BUF_MAX - BUFTHREADIDSIZ); /* done parse past here */
900         pkt = unpack_byte(pkt, &count) ;                /* count field */
901         pkt = unpack_nibble(pkt, &done);
902         /* The first threadid is the argument threadid */
903         pkt = unpack_threadid(pkt, original_echo); /* should match query packet */
904         while ((count-- > 0) && (pkt < limit))
905         {
906                 pkt = unpack_threadid(pkt, resultlist++);
907                 resultcount++;
908         }
909         if (doneflag) *doneflag = done;
910         return resultcount; /* successvalue */
911 } /* parse_threadlist_response */
912
913 struct gdb_ext_thread_info
914 {
915         threadref threadid;
916         int active;
917         char display[256];
918         char shortname[32];
919         char more_display[256];
920 };
921
922
923 /* ----- PACK_THREAD_INFO_REQUEST -------------------------------- */
924
925 /*
926   threadid:1,        # always request threadid
927   context_exists:2,
928   display:4,
929   unique_name:8,
930   more_display:16
931 */
932
933 /* Encoding:  'Q':8, 'P':8, mask:32, threadid:64 */
934
935 char *pack_threadinfo_request(char *pkt,
936                                                         int mode,
937                                                         threadref *id
938         )
939 {
940         *pkt++ = 'Q';
941         *pkt++ = 'P';
942         pkt = pack_int(pkt, mode); /* mode */
943         pkt = pack_threadid(pkt, id); /* threadid */
944         *pkt = '\0'; /* terminate */
945         return pkt;
946 } /* pack_thread_info_request */
947
948
949
950 static char *unpack_string(
951         char *src,
952         char *dest,
953         int length)
954 {
955         while (length--) *dest++ = *src++;
956         *dest = '\0';
957         return src;
958 } /* unpack_string */
959
960
961 void output_threadid(char *title, threadref *ref)
962 {
963         char hexid[20];
964         pack_threadid(&hexid[0], ref); /* Convert threead id into hex */
965         hexid[16] = 0;
966         output_string(title);
967         output_string(&hexid[0]);
968         output_string("\n");
969 }
970
971 /* ------ REMOTE_UPK_THREAD_INFO_RESPONSE ------------------------------- */
972 /* Unpack the response of a detailed thread info packet */
973 /* Encoding:  i'Q':8, i'R':8, argmask:16, threadid:64, (tag:8, length:16, data:x)* */
974
975 #define TAG_THREADID 1
976 #define TAG_EXISTS 2
977 #define TAG_DISPLAY 4
978 #define TAG_THREADNAME 8
979 #define TAG_MOREDISPLAY 16
980
981
982 int remote_upk_thread_info_response(
983         char *pkt,
984         threadref *expectedref,
985         struct gdb_ext_thread_info *info)
986 {
987         int mask, length;
988         unsigned int tag;
989         threadref ref;
990         char *limit = pkt + 500; /* plausable parsing limit */
991         int retval = 1;
992
993         PKT_TRACE("upk-threadinfo ", pkt);
994
995         /* info->threadid = 0; FIXME: implement zero_threadref */
996         info->active = 0;
997         info->display[0] = '\0';
998         info->shortname[0] = '\0';
999         info->more_display[0] = '\0';
1000
1001         /* Assume the characters indicating the packet type have been stripped */
1002         pkt = unpack_int(pkt, &mask);  /* arg mask */
1003         pkt = unpack_threadid(pkt, &ref);
1004
1005         if (! threadmatch(&ref, expectedref))
1006         { /* This is an answer to a different request */
1007                 output_string("FAIL Thread mismatch\n");
1008                 output_threadid("ref ", &ref);
1009                 output_threadid("expected ", expectedref);
1010                 return 0;
1011         }
1012         copy_threadref(&info->threadid, &ref);
1013
1014         /* Loop on tagged fields, try to bail if somthing goes wrong */
1015         if (mask==0)  output_string("OOPS NO MASK \n");
1016
1017         while ((pkt < limit) && mask && *pkt)  /* packets are terminated with nulls */
1018         {
1019                 pkt = unpack_int(pkt, &tag);            /* tag */
1020                 pkt = unpack_byte(pkt, &length);   /* length */
1021                 if (! (tag & mask))  /* tags out of synch with mask */
1022                 {
1023                         output_string("FAIL: threadinfo tag mismatch\n");
1024                         retval = 0;
1025                         break;
1026                 }
1027                 if (tag == TAG_THREADID)
1028                 {
1029                         output_string("unpack THREADID\n");
1030                         if (length != 16)
1031                         {
1032                                 output_string("FAIL: length of threadid is not 16\n");
1033                                 retval = 0;
1034                                 break;
1035                         }
1036                         pkt = unpack_threadid(pkt, &ref);
1037                         mask = mask & ~ TAG_THREADID;
1038                         continue;
1039                 }
1040                 if (tag == TAG_EXISTS)
1041                 {
1042                         info->active = stub_unpack_int(pkt, length);
1043                         pkt += length;
1044                         mask = mask & ~(TAG_EXISTS);
1045                         if (length > 8)
1046                         {
1047                                 output_string("FAIL: 'exists' length too long\n");
1048                                 retval = 0;
1049                                 break;
1050                         }
1051                         continue;
1052                 }
1053                 if (tag == TAG_THREADNAME)
1054                 {
1055                         pkt = unpack_string(pkt, &info->shortname[0], length);
1056                         mask = mask & ~TAG_THREADNAME;
1057                         continue;
1058                 }
1059                 if (tag == TAG_DISPLAY)
1060                 {
1061                         pkt = unpack_string(pkt, &info->display[0], length);
1062                         mask = mask & ~TAG_DISPLAY;
1063                         continue;
1064                 }
1065                 if (tag == TAG_MOREDISPLAY)
1066                 {
1067                         pkt = unpack_string(pkt, &info->more_display[0], length);
1068                         mask = mask & ~TAG_MOREDISPLAY;
1069                         continue;
1070                 }
1071                 output_string("FAIL: unknown info tag\n");
1072                 break; /* Not a tag we know about */
1073         }
1074         return retval ;
1075 } /* parse-thread_info_response */
1076
1077
1078 /* ---- REMOTE_PACK_CURRTHREAD_REQUEST ---------------------------- */
1079 /* This is a request to emit the T packet */
1080
1081 /* FORMAT: 'q':8, 'C' */
1082
1083 char *remote_pack_currthread_request(char *pkt )
1084 {
1085         *pkt++ = 'q';
1086         *pkt++ = 'C';
1087         *pkt = '\0';
1088         return pkt;
1089 } /* remote_pack_currthread_request */
1090
1091
1092 /* ------- REMOTE_UPK_CURTHREAD_RESPONSE ----------------------- */
1093 /* Unpack the interesting part of a T packet */
1094
1095
1096 int remote_upk_currthread_response(
1097         char *pkt,
1098         int *thr )  /* Parse a T packet */
1099 {
1100         int retval = 0;
1101         PKT_TRACE("upk-currthreadresp ", pkt);
1102
1103 #if 0
1104         {
1105                 static char threadtag[8] =  "thread";
1106                 int retval = 0;
1107                 int i, found;
1108                 char ch;
1109                 int quickid;
1110
1111                 /* Unpack as a t packet */
1112                 while (((ch = *pkt++) != ':')    /* scan for : thread */
1113                         && (ch != '\0'))          /* stop at end of packet */
1114
1115                 {
1116                         found = 0;
1117                         i = 0;
1118                         while ((ch = *pkt++) == threadtag[i++]);
1119                         if (i == 8) /* string match "thread" */
1120                         {
1121                                 pkt = unpack_varlen_hex(pkt, &quickid);
1122                                 retval = 1;
1123                                 break;
1124                         }
1125                         retval = 0;
1126                 }
1127         }
1128 #else
1129         pkt = unpack_threadid(pkt, thr);
1130         retval = 1;
1131 #endif
1132         return retval;
1133 } /* remote_upk_currthread_response */
1134
1135
1136 /* -------- REMOTE_UPK-SIMPLE_ACK --------------------------------- */
1137 /* Decode a response which is eother "OK" or "Enn"
1138    fillin error code,  fillin pkfag-1== undef, 0==nak, 1 == ack;
1139    return advanced packet pointer */
1140
1141
1142 char *remote_upk_simple_ack(
1143         char *buf,
1144         int *pkflag,
1145         int *errcode)
1146 {
1147         int lclerr = 0;
1148         char ch = *buf++;
1149         int retval = -1;  /* Undefined ACK, a protocol error */
1150         if (ch == 'E')     /* NAK */
1151         {
1152                 buf = unpack_byte(buf, &lclerr);
1153                 retval = 0;   /* transaction failed, explicitly */
1154         }
1155         else
1156                 if ((ch == 'O') && (*buf++ == 'K')) /* ACK */
1157                         retval = 1; /* transaction succeeded */
1158         *pkflag = retval;
1159         *errcode = lclerr;
1160         return buf;
1161 } /* remote-upk_simple_ack */
1162
1163
1164 /* -------- PACK_THREADALIVE_REQUEST ------------------------------- */
1165
1166 char *pack_threadalive_request(
1167         char *buf,
1168         threadref *threadid)
1169 {
1170         *buf++ = 'T';
1171         buf = pack_threadid(buf, threadid);
1172         *buf = '\0';
1173         return buf;
1174 } /* pack_threadalive_request */
1175
1176 #endif /* GDB_MOCKUP */
1177
1178 /* ---------------------------------------------------------------------- */
1179 /* UNIT_TESTS SUBSECTION                                                  */
1180 /* ---------------------------------------------------------------------- */
1181
1182
1183 #if UNIT_TEST
1184 extern void output_string(char *message);
1185 static char test_req[400];
1186 static char t_response[400];
1187
1188
1189
1190 /* ----- DISPLAY_THREAD_INFO ---------------------------------------------- */
1191 /*  Use local cygmon string output utiities */
1192
1193 void display_thread_info(struct gdb_ext_thread_info *info)
1194 {
1195
1196         output_threadid("Threadid: ", &info->threadid);
1197         /* short name */
1198         output_string("Name: "); output_string(info->shortname); output_string("\n");
1199         /* format display state */
1200         output_string("State: "); output_string(info->display); output_string("\n");
1201         /* additional data */
1202         output_string("other: ");output_string(info->more_display);
1203         output_string("\n\n");
1204 } /* display_thread_info */
1205
1206
1207 /* --- CURRTHREAD-TEST -------------------------------------------- */
1208 static int currthread_test(threadref *thread)
1209 {
1210         int result;
1211         int threadid;
1212         output_string("TEST: currthread\n");
1213         remote_pack_currthread_request(test_req);
1214         stub_pkt_currthread(test_req+2, t_response, STUB_BUF_MAX);
1215         result = remote_upk_currthread_response(t_response+2, &threadid);
1216         if (result)
1217         {
1218                 output_string("PASS getcurthread\n");
1219                 /* FIXME: print the thread */
1220         }
1221         else
1222                 output_string("FAIL getcurrthread\n");
1223         return result;
1224 } /* currthread_test */
1225
1226 /* ------ SETTHREAD_TEST ------------------------------------------- */
1227 /* use a known thread from previous test */
1228
1229 static int setthread_test(threadref *thread)
1230 {
1231         int result, errcode;
1232         output_string("TEST: setthread\n");
1233
1234         pack_setthread_request(test_req, 'p', 1, thread);
1235         stub_pkt_changethread(test_req, t_response, STUB_BUF_MAX);
1236         remote_upk_simple_ack(t_response, &result, &errcode);
1237         switch (result)
1238         {
1239         case 0 :
1240                 output_string("FAIL setthread\n");
1241                 break;
1242         case 1 :
1243                 output_string("PASS setthread\n");
1244                 break;
1245         default :
1246                 output_string("FAIL setthread -unrecognized response\n");
1247                 break;
1248         }
1249         return result;
1250 } /* setthread_test */
1251
1252
1253 /* ------ THREADACTIVE_TEST ---------------------- */
1254 /* use known thread */
1255 /* pack threadactive packet */
1256 /* process threadactive packet */
1257 /* parse threadactive response */
1258 /* check results */
1259
1260
1261 int threadactive_test(threadref *thread)
1262 {
1263         int result;
1264         int errcode;
1265         output_string("TEST: threadactive\n");
1266         pack_threadalive_request(test_req, thread);
1267         stub_pkt_thread_alive(test_req+1, t_response, STUB_BUF_MAX);
1268         remote_upk_simple_ack(t_response, &result, &errcode);
1269         switch (result)
1270         {
1271         case 0 :
1272                 output_string("FAIL threadalive\n");
1273                 break;
1274         case 1 :
1275                 output_string("PASS threadalive\n");
1276                 break;
1277         default :
1278                 output_string("FAIL threadalive -unrecognized response\n");
1279                 break;
1280         }
1281         return result;
1282 } /* threadactive_test */
1283
1284 /* ------ REMOTE_GET_THREADINFO -------------------------------------- */
1285 int remote_get_threadinfo(
1286         threadref *threadid,
1287         int fieldset, /* TAG mask */
1288         struct gdb_ext_thread_info *info
1289         )
1290 {
1291         int result;
1292         pack_threadinfo_request(test_req, fieldset, threadid);
1293         stub_pkt_getthreadinfo(test_req+2, t_response, STUB_BUF_MAX);
1294         result = remote_upk_thread_info_response(t_response+2, threadid, info);
1295         return result;
1296 } /* remote_get-thrreadinfo */
1297
1298
1299 static struct gdb_ext_thread_info test_info;
1300
1301 static int get_and_display_threadinfo(threadref *thread)
1302 {
1303         int mode;
1304         int result;
1305         /* output_string("TEST: get and display threadinfo\n"); */
1306
1307         mode = TAG_THREADID | TAG_EXISTS | TAG_THREADNAME
1308                 | TAG_MOREDISPLAY | TAG_DISPLAY;
1309         result = remote_get_threadinfo(thread, mode, &test_info);
1310         if (result) display_thread_info(&test_info);
1311 #if 0  /* silent subtest */
1312         if (result)
1313                 output_string("PASS: get_and_display threadinfo\n");
1314         else
1315                 output_string("FAIL: get_and_display threadinfo\n");
1316 #endif
1317         return result;
1318 } /* get-and-display-threadinfo */
1319
1320
1321
1322 /* ----- THREADLIST_TEST ------------------------------------------ */
1323 #define TESTLISTSIZE 16
1324 #define TLRSIZ 2
1325 static threadref test_threadlist[TESTLISTSIZE];
1326
1327 static int threadlist_test(void)
1328 {
1329         int done, i, result_count;
1330         int startflag = 1;
1331         int result = 1;
1332         int loopcount = 0;
1333         static threadref nextthread;
1334         static threadref echo_nextthread;
1335
1336         output_string("TEST: threadlist\n");
1337
1338         done = 0;
1339         while (! done) {
1340                 if (loopcount++ > 10) {
1341                         result = 0;
1342                         output_string("FAIL: Threadlist test -infinite loop-\n");
1343                         break;
1344                 }
1345                 pack_threadlist_request(test_req, startflag, TLRSIZ, &nextthread);
1346                 startflag = 0; /* clear for later iterations */
1347                 stub_pkt_getthreadlist(test_req+2, t_response, STUB_BUF_MAX);
1348                 result_count = parse_threadlist_response(t_response+2,
1349                                                                                                 &echo_nextthread,
1350                                                                                                 &test_threadlist[0], &done);
1351                 if (! threadmatch(&echo_nextthread, &nextthread)) {
1352                         output_string("FAIL: threadlist did not echo arg thread\n");
1353                         result = 0;
1354                         break;
1355                 }
1356                 if (result_count <= 0) {
1357                         if (done != 0)
1358                         { output_string("FAIL threadlist_test, failed to get list");
1359                                 result = 0;
1360                         }
1361                         break;
1362                 }
1363                 if (result_count > TLRSIZ) {
1364                         output_string("FAIL: threadlist response longer than requested\n");
1365                         result = 0;
1366                         break;
1367                 }
1368                 /* Setup to resume next batch of thread references, set nestthread */
1369                 copy_threadref(&nextthread, &test_threadlist[result_count-1]);
1370                 /* output_threadid("last-of-batch", &nextthread); */
1371                 i = 0;
1372                 while (result_count--) {
1373                         if (0)  /* two display alternatives */
1374                                 output_threadid("truncatedisplay", &test_threadlist[i++]);
1375                         else
1376                                 get_and_display_threadinfo(&test_threadlist[i++]);
1377                 }
1378
1379         }
1380         if (!result)
1381                 output_string("FAIL: threadlist test\n");
1382         else output_string("PASS: Threadlist test\n");
1383         return result;
1384 } /* threadlist_test */
1385
1386
1387 static threadref testthread;
1388
1389
1390 int test_thread_support(void)
1391 {
1392         int result = 1;
1393         output_string("TESTING Thread support infrastructure\n");
1394         stub_pack_Tpkt_threadid(test_req);
1395         PKT_TRACE("packing the threadid  -> ", test_req);
1396         result &= currthread_test(&testthread);
1397         result &= get_and_display_threadinfo(&testthread);
1398         result &= threadlist_test();
1399         result &= setthread_test(&testthread);
1400         if (result)
1401                 output_string("PASS: UNITTEST Thread support\n");
1402         else
1403                 output_string("FAIL: UNITTEST Thread support\n");
1404         return result;
1405 } /* test-thread_support */
1406 #endif /* UNIT_TEST */
1407
1408 // #ifdef __ECOS__
1409 #endif // ifdef CYGDBG_HAL_DEBUG_GDB_THREAD_SUPPORT...
1410 // #endif // __ECOS__