Merge ../linux-2.6-watchdog-mm
[linux-2.6] / arch / sparc / kernel / sparc-stub.c
1 /* $Id: sparc-stub.c,v 1.28 2001/10/30 04:54:21 davem Exp $
2  * sparc-stub.c:  KGDB support for the Linux kernel.
3  *
4  * Modifications to run under Linux
5  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
6  *
7  * This file originally came from the gdb sources, and the
8  * copyright notices have been retained below.
9  */
10
11 /****************************************************************************
12
13                 THIS SOFTWARE IS NOT COPYRIGHTED
14
15    HP offers the following for use in the public domain.  HP makes no
16    warranty with regard to the software or its performance and the
17    user accepts the software "AS IS" with all faults.
18
19    HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD
20    TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES
21    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22
23 ****************************************************************************/
24
25 /****************************************************************************
26  *  Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $
27  *
28  *  Module name: remcom.c $
29  *  Revision: 1.34 $
30  *  Date: 91/03/09 12:29:49 $
31  *  Contributor:     Lake Stevens Instrument Division$
32  *
33  *  Description:     low level support for gdb debugger. $
34  *
35  *  Considerations:  only works on target hardware $
36  *
37  *  Written by:      Glenn Engel $
38  *  ModuleState:     Experimental $
39  *
40  *  NOTES:           See Below $
41  *
42  *  Modified for SPARC by Stu Grossman, Cygnus Support.
43  *
44  *  This code has been extensively tested on the Fujitsu SPARClite demo board.
45  *
46  *  To enable debugger support, two things need to happen.  One, a
47  *  call to set_debug_traps() is necessary in order to allow any breakpoints
48  *  or error conditions to be properly intercepted and reported to gdb.
49  *  Two, a breakpoint needs to be generated to begin communication.  This
50  *  is most easily accomplished by a call to breakpoint().  Breakpoint()
51  *  simulates a breakpoint by executing a trap #1.
52  *
53  *************
54  *
55  *    The following gdb commands are supported:
56  *
57  * command          function                               Return value
58  *
59  *    g             return the value of the CPU registers  hex data or ENN
60  *    G             set the value of the CPU registers     OK or ENN
61  *
62  *    mAA..AA,LLLL  Read LLLL bytes at address AA..AA      hex data or ENN
63  *    MAA..AA,LLLL: Write LLLL bytes at address AA.AA      OK or ENN
64  *
65  *    c             Resume at current address              SNN   ( signal NN)
66  *    cAA..AA       Continue at address AA..AA             SNN
67  *
68  *    s             Step one instruction                   SNN
69  *    sAA..AA       Step one instruction from AA..AA       SNN
70  *
71  *    k             kill
72  *
73  *    ?             What was the last sigval ?             SNN   (signal NN)
74  *
75  *    bBB..BB       Set baud rate to BB..BB                OK or BNN, then sets
76  *                                                         baud rate
77  *
78  * All commands and responses are sent with a packet which includes a
79  * checksum.  A packet consists of
80  *
81  * $<packet info>#<checksum>.
82  *
83  * where
84  * <packet info> :: <characters representing the command or response>
85  * <checksum>    :: < two hex digits computed as modulo 256 sum of <packetinfo>>
86  *
87  * When a packet is received, it is first acknowledged with either '+' or '-'.
88  * '+' indicates a successful transfer.  '-' indicates a failed transfer.
89  *
90  * Example:
91  *
92  * Host:                  Reply:
93  * $m0,10#2a               +$00010203040506070809101112131415#42
94  *
95  ****************************************************************************/
96
97 #include <linux/kernel.h>
98 #include <linux/string.h>
99 #include <linux/mm.h>
100 #include <linux/smp.h>
101 #include <linux/smp_lock.h>
102
103 #include <asm/system.h>
104 #include <asm/signal.h>
105 #include <asm/oplib.h>
106 #include <asm/head.h>
107 #include <asm/traps.h>
108 #include <asm/vac-ops.h>
109 #include <asm/kgdb.h>
110 #include <asm/pgalloc.h>
111 #include <asm/pgtable.h>
112 #include <asm/cacheflush.h>
113
114 /*
115  *
116  * external low-level support routines
117  */
118
119 extern void putDebugChar(char);   /* write a single character      */
120 extern char getDebugChar(void);   /* read and return a single char */
121
122 /*
123  * BUFMAX defines the maximum number of characters in inbound/outbound buffers
124  * at least NUMREGBYTES*2 are needed for register packets
125  */
126 #define BUFMAX 2048
127
128 static int initialized; /* !0 means we've been initialized */
129
130 static const char hexchars[]="0123456789abcdef";
131
132 #define NUMREGS 72
133
134 /* Number of bytes of registers.  */
135 #define NUMREGBYTES (NUMREGS * 4)
136 enum regnames {G0, G1, G2, G3, G4, G5, G6, G7,
137                  O0, O1, O2, O3, O4, O5, SP, O7,
138                  L0, L1, L2, L3, L4, L5, L6, L7,
139                  I0, I1, I2, I3, I4, I5, FP, I7,
140
141                  F0, F1, F2, F3, F4, F5, F6, F7,
142                  F8, F9, F10, F11, F12, F13, F14, F15,
143                  F16, F17, F18, F19, F20, F21, F22, F23,
144                  F24, F25, F26, F27, F28, F29, F30, F31,
145                  Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR };
146
147
148 extern void trap_low(void);  /* In arch/sparc/kernel/entry.S */
149
150 unsigned long get_sun4cpte(unsigned long addr)
151 {
152         unsigned long entry;
153
154         __asm__ __volatile__("\n\tlda [%1] %2, %0\n\t" : 
155                              "=r" (entry) :
156                              "r" (addr), "i" (ASI_PTE));
157         return entry;
158 }
159
160 unsigned long get_sun4csegmap(unsigned long addr)
161 {
162         unsigned long entry;
163
164         __asm__ __volatile__("\n\tlduba [%1] %2, %0\n\t" : 
165                              "=r" (entry) :
166                              "r" (addr), "i" (ASI_SEGMAP));
167         return entry;
168 }
169
170 #if 0
171 /* Have to sort this out. This cannot be done after initialization. */
172 static void flush_cache_all_nop(void) {}
173 #endif
174
175 /* Place where we save old trap entries for restoration */
176 struct tt_entry kgdb_savettable[256];
177 typedef void (*trapfunc_t)(void);
178
179 /* Helper routine for manipulation of kgdb_savettable */
180 static inline void copy_ttentry(struct tt_entry *src, struct tt_entry *dest)
181 {
182         dest->inst_one = src->inst_one;
183         dest->inst_two = src->inst_two;
184         dest->inst_three = src->inst_three;
185         dest->inst_four = src->inst_four;
186 }
187
188 /* Initialize the kgdb_savettable so that debugging can commence */
189 static void eh_init(void)
190 {
191         int i;
192
193         for(i=0; i < 256; i++)
194                 copy_ttentry(&sparc_ttable[i], &kgdb_savettable[i]);
195 }
196
197 /* Install an exception handler for kgdb */
198 static void exceptionHandler(int tnum, trapfunc_t trap_entry)
199 {
200         unsigned long te_addr = (unsigned long) trap_entry;
201
202         /* Make new vector */
203         sparc_ttable[tnum].inst_one =
204                 SPARC_BRANCH((unsigned long) te_addr,
205                              (unsigned long) &sparc_ttable[tnum].inst_one);
206         sparc_ttable[tnum].inst_two = SPARC_RD_PSR_L0;
207         sparc_ttable[tnum].inst_three = SPARC_NOP;
208         sparc_ttable[tnum].inst_four = SPARC_NOP;
209 }
210
211 /* Convert ch from a hex digit to an int */
212 static int
213 hex(unsigned char ch)
214 {
215         if (ch >= 'a' && ch <= 'f')
216                 return ch-'a'+10;
217         if (ch >= '0' && ch <= '9')
218                 return ch-'0';
219         if (ch >= 'A' && ch <= 'F')
220                 return ch-'A'+10;
221         return -1;
222 }
223
224 /* scan for the sequence $<data>#<checksum>     */
225 static void
226 getpacket(char *buffer)
227 {
228         unsigned char checksum;
229         unsigned char xmitcsum;
230         int i;
231         int count;
232         unsigned char ch;
233
234         do {
235                 /* wait around for the start character, ignore all other characters */
236                 while ((ch = (getDebugChar() & 0x7f)) != '$') ;
237
238                 checksum = 0;
239                 xmitcsum = -1;
240
241                 count = 0;
242
243                 /* now, read until a # or end of buffer is found */
244                 while (count < BUFMAX) {
245                         ch = getDebugChar() & 0x7f;
246                         if (ch == '#')
247                                 break;
248                         checksum = checksum + ch;
249                         buffer[count] = ch;
250                         count = count + 1;
251                 }
252
253                 if (count >= BUFMAX)
254                         continue;
255
256                 buffer[count] = 0;
257
258                 if (ch == '#') {
259                         xmitcsum = hex(getDebugChar() & 0x7f) << 4;
260                         xmitcsum |= hex(getDebugChar() & 0x7f);
261                         if (checksum != xmitcsum)
262                                 putDebugChar('-');      /* failed checksum */
263                         else {
264                                 putDebugChar('+'); /* successful transfer */
265                                 /* if a sequence char is present, reply the ID */
266                                 if (buffer[2] == ':') {
267                                         putDebugChar(buffer[0]);
268                                         putDebugChar(buffer[1]);
269                                         /* remove sequence chars from buffer */
270                                         count = strlen(buffer);
271                                         for (i=3; i <= count; i++)
272                                                 buffer[i-3] = buffer[i];
273                                 }
274                         }
275                 }
276         } while (checksum != xmitcsum);
277 }
278
279 /* send the packet in buffer.  */
280
281 static void
282 putpacket(unsigned char *buffer)
283 {
284         unsigned char checksum;
285         int count;
286         unsigned char ch, recv;
287
288         /*  $<packet info>#<checksum>. */
289         do {
290                 putDebugChar('$');
291                 checksum = 0;
292                 count = 0;
293
294                 while ((ch = buffer[count])) {
295                         putDebugChar(ch);
296                         checksum += ch;
297                         count += 1;
298                 }
299
300                 putDebugChar('#');
301                 putDebugChar(hexchars[checksum >> 4]);
302                 putDebugChar(hexchars[checksum & 0xf]);
303                 recv = getDebugChar();
304         } while ((recv & 0x7f) != '+');
305 }
306
307 static char remcomInBuffer[BUFMAX];
308 static char remcomOutBuffer[BUFMAX];
309
310 /* Convert the memory pointed to by mem into hex, placing result in buf.
311  * Return a pointer to the last char put in buf (null), in case of mem fault,
312  * return 0.
313  */
314
315 static unsigned char *
316 mem2hex(char *mem, char *buf, int count)
317 {
318         unsigned char ch;
319
320         while (count-- > 0) {
321                 /* This assembler code is basically:  ch = *mem++;
322                  * except that we use the SPARC/Linux exception table
323                  * mechanism (see how "fixup" works in kernel_mna_trap_fault)
324                  * to arrange for a "return 0" upon a memory fault
325                  */
326                 __asm__(
327                         "\n1:\n\t"
328                         "ldub [%0], %1\n\t"
329                         "inc %0\n\t"
330                         ".section .fixup,#alloc,#execinstr\n\t"
331                         ".align 4\n"
332                         "2:\n\t"
333                         "retl\n\t"
334                         " mov 0, %%o0\n\t"
335                         ".section __ex_table, #alloc\n\t"
336                         ".align 4\n\t"
337                         ".word 1b, 2b\n\t"
338                         ".text\n"
339                         : "=r" (mem), "=r" (ch) : "0" (mem));
340                 *buf++ = hexchars[ch >> 4];
341                 *buf++ = hexchars[ch & 0xf];
342         }
343
344         *buf = 0;
345         return buf;
346 }
347
348 /* convert the hex array pointed to by buf into binary to be placed in mem
349  * return a pointer to the character AFTER the last byte written.
350 */
351 static char *
352 hex2mem(char *buf, char *mem, int count)
353 {
354         int i;
355         unsigned char ch;
356
357         for (i=0; i<count; i++) {
358
359                 ch = hex(*buf++) << 4;
360                 ch |= hex(*buf++);
361                 /* Assembler code is   *mem++ = ch;   with return 0 on fault */
362                 __asm__(
363                         "\n1:\n\t"
364                         "stb %1, [%0]\n\t"
365                         "inc %0\n\t"
366                         ".section .fixup,#alloc,#execinstr\n\t"
367                         ".align 4\n"
368                         "2:\n\t"
369                         "retl\n\t"
370                         " mov 0, %%o0\n\t"
371                         ".section __ex_table, #alloc\n\t"
372                         ".align 4\n\t"
373                         ".word 1b, 2b\n\t"
374                         ".text\n"
375                         : "=r" (mem) : "r" (ch) , "0" (mem));
376         }
377         return mem;
378 }
379
380 /* This table contains the mapping between SPARC hardware trap types, and
381    signals, which are primarily what GDB understands.  It also indicates
382    which hardware traps we need to commandeer when initializing the stub. */
383
384 static struct hard_trap_info
385 {
386   unsigned char tt;             /* Trap type code for SPARC */
387   unsigned char signo;          /* Signal that we map this trap into */
388 } hard_trap_info[] = {
389   {SP_TRAP_SBPT, SIGTRAP},      /* ta 1 - Linux/KGDB software breakpoint */
390   {0, 0}                        /* Must be last */
391 };
392
393 /* Set up exception handlers for tracing and breakpoints */
394
395 void
396 set_debug_traps(void)
397 {
398         struct hard_trap_info *ht;
399         unsigned long flags;
400
401         local_irq_save(flags);
402 #if 0   
403 /* Have to sort this out. This cannot be done after initialization. */
404         BTFIXUPSET_CALL(flush_cache_all, flush_cache_all_nop, BTFIXUPCALL_NOP);
405 #endif
406
407         /* Initialize our copy of the Linux Sparc trap table */
408         eh_init();
409
410         for (ht = hard_trap_info; ht->tt && ht->signo; ht++) {
411                 /* Only if it doesn't destroy our fault handlers */
412                 if((ht->tt != SP_TRAP_TFLT) && 
413                    (ht->tt != SP_TRAP_DFLT))
414                         exceptionHandler(ht->tt, trap_low);
415         }
416
417         /* In case GDB is started before us, ack any packets (presumably
418          * "$?#xx") sitting there.
419          *
420          * I've found this code causes more problems than it solves,
421          * so that's why it's commented out.  GDB seems to work fine
422          * now starting either before or after the kernel   -bwb
423          */
424 #if 0
425         while((c = getDebugChar()) != '$');
426         while((c = getDebugChar()) != '#');
427         c = getDebugChar(); /* eat first csum byte */
428         c = getDebugChar(); /* eat second csum byte */
429         putDebugChar('+'); /* ack it */
430 #endif
431
432         initialized = 1; /* connect! */
433         local_irq_restore(flags);
434 }
435
436 /* Convert the SPARC hardware trap type code to a unix signal number. */
437
438 static int
439 computeSignal(int tt)
440 {
441         struct hard_trap_info *ht;
442
443         for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
444                 if (ht->tt == tt)
445                         return ht->signo;
446
447         return SIGHUP;         /* default for things we don't know about */
448 }
449
450 /*
451  * While we find nice hex chars, build an int.
452  * Return number of chars processed.
453  */
454
455 static int
456 hexToInt(char **ptr, int *intValue)
457 {
458         int numChars = 0;
459         int hexValue;
460
461         *intValue = 0;
462
463         while (**ptr) {
464                 hexValue = hex(**ptr);
465                 if (hexValue < 0)
466                         break;
467
468                 *intValue = (*intValue << 4) | hexValue;
469                 numChars ++;
470
471                 (*ptr)++;
472         }
473
474         return (numChars);
475 }
476
477 /*
478  * This function does all command processing for interfacing to gdb.  It
479  * returns 1 if you should skip the instruction at the trap address, 0
480  * otherwise.
481  */
482
483 extern void breakinst(void);
484
485 void
486 handle_exception (unsigned long *registers)
487 {
488         int tt;       /* Trap type */
489         int sigval;
490         int addr;
491         int length;
492         char *ptr;
493         unsigned long *sp;
494
495         /* First, we must force all of the windows to be spilled out */
496
497         asm("save %sp, -64, %sp\n\t"
498             "save %sp, -64, %sp\n\t"
499             "save %sp, -64, %sp\n\t"
500             "save %sp, -64, %sp\n\t"
501             "save %sp, -64, %sp\n\t"
502             "save %sp, -64, %sp\n\t"
503             "save %sp, -64, %sp\n\t"
504             "save %sp, -64, %sp\n\t"
505             "restore\n\t"
506             "restore\n\t"
507             "restore\n\t"
508             "restore\n\t"
509             "restore\n\t"
510             "restore\n\t"
511             "restore\n\t"
512             "restore\n\t");
513
514         lock_kernel();
515         if (registers[PC] == (unsigned long)breakinst) {
516                 /* Skip over breakpoint trap insn */
517                 registers[PC] = registers[NPC];
518                 registers[NPC] += 4;
519         }
520
521         sp = (unsigned long *)registers[SP];
522
523         tt = (registers[TBR] >> 4) & 0xff;
524
525         /* reply to host that an exception has occurred */
526         sigval = computeSignal(tt);
527         ptr = remcomOutBuffer;
528
529         *ptr++ = 'T';
530         *ptr++ = hexchars[sigval >> 4];
531         *ptr++ = hexchars[sigval & 0xf];
532
533         *ptr++ = hexchars[PC >> 4];
534         *ptr++ = hexchars[PC & 0xf];
535         *ptr++ = ':';
536         ptr = mem2hex((char *)&registers[PC], ptr, 4);
537         *ptr++ = ';';
538
539         *ptr++ = hexchars[FP >> 4];
540         *ptr++ = hexchars[FP & 0xf];
541         *ptr++ = ':';
542         ptr = mem2hex((char *) (sp + 8 + 6), ptr, 4); /* FP */
543         *ptr++ = ';';
544
545         *ptr++ = hexchars[SP >> 4];
546         *ptr++ = hexchars[SP & 0xf];
547         *ptr++ = ':';
548         ptr = mem2hex((char *)&sp, ptr, 4);
549         *ptr++ = ';';
550
551         *ptr++ = hexchars[NPC >> 4];
552         *ptr++ = hexchars[NPC & 0xf];
553         *ptr++ = ':';
554         ptr = mem2hex((char *)&registers[NPC], ptr, 4);
555         *ptr++ = ';';
556
557         *ptr++ = hexchars[O7 >> 4];
558         *ptr++ = hexchars[O7 & 0xf];
559         *ptr++ = ':';
560         ptr = mem2hex((char *)&registers[O7], ptr, 4);
561         *ptr++ = ';';
562
563         *ptr++ = 0;
564
565         putpacket(remcomOutBuffer);
566
567         /* XXX We may want to add some features dealing with poking the
568          * XXX page tables, the real ones on the srmmu, and what is currently
569          * XXX loaded in the sun4/sun4c tlb at this point in time.  But this
570          * XXX also required hacking to the gdb sources directly...
571          */
572
573         while (1) {
574                 remcomOutBuffer[0] = 0;
575
576                 getpacket(remcomInBuffer);
577                 switch (remcomInBuffer[0]) {
578                 case '?':
579                         remcomOutBuffer[0] = 'S';
580                         remcomOutBuffer[1] = hexchars[sigval >> 4];
581                         remcomOutBuffer[2] = hexchars[sigval & 0xf];
582                         remcomOutBuffer[3] = 0;
583                         break;
584
585                 case 'd':
586                         /* toggle debug flag */
587                         break;
588
589                 case 'g':               /* return the value of the CPU registers */
590                 {
591                         ptr = remcomOutBuffer;
592                         /* G & O regs */
593                         ptr = mem2hex((char *)registers, ptr, 16 * 4);
594                         /* L & I regs */
595                         ptr = mem2hex((char *) (sp + 0), ptr, 16 * 4);
596                         /* Floating point */
597                         memset(ptr, '0', 32 * 8);
598                         /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
599                         mem2hex((char *)&registers[Y], (ptr + 32 * 4 * 2), (8 * 4));
600                 }
601                         break;
602
603                 case 'G':          /* set the value of the CPU registers - return OK */
604                 {
605                         unsigned long *newsp, psr;
606
607                         psr = registers[PSR];
608
609                         ptr = &remcomInBuffer[1];
610                         /* G & O regs */
611                         hex2mem(ptr, (char *)registers, 16 * 4);
612                         /* L & I regs */
613                         hex2mem(ptr + 16 * 4 * 2, (char *) (sp + 0), 16 * 4);
614                         /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
615                         hex2mem(ptr + 64 * 4 * 2, (char *)&registers[Y], 8 * 4);
616
617                         /* See if the stack pointer has moved.  If so,
618                          * then copy the saved locals and ins to the
619                          * new location.  This keeps the window
620                          * overflow and underflow routines happy.
621                          */
622
623                         newsp = (unsigned long *)registers[SP];
624                         if (sp != newsp)
625                                 sp = memcpy(newsp, sp, 16 * 4);
626
627                         /* Don't allow CWP to be modified. */
628
629                         if (psr != registers[PSR])
630                                 registers[PSR] = (psr & 0x1f) | (registers[PSR] & ~0x1f);
631
632                         strcpy(remcomOutBuffer,"OK");
633                 }
634                         break;
635
636                 case 'm':         /* mAA..AA,LLLL  Read LLLL bytes at address AA..AA */
637                         /* Try to read %x,%x.  */
638
639                         ptr = &remcomInBuffer[1];
640
641                         if (hexToInt(&ptr, &addr)
642                             && *ptr++ == ','
643                             && hexToInt(&ptr, &length)) {
644                                 if (mem2hex((char *)addr, remcomOutBuffer, length))
645                                         break;
646
647                                 strcpy (remcomOutBuffer, "E03");
648                         } else {
649                                 strcpy(remcomOutBuffer,"E01");
650                         }
651                         break;
652
653                 case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
654                         /* Try to read '%x,%x:'.  */
655
656                         ptr = &remcomInBuffer[1];
657
658                         if (hexToInt(&ptr, &addr)
659                             && *ptr++ == ','
660                             && hexToInt(&ptr, &length)
661                             && *ptr++ == ':') {
662                                 if (hex2mem(ptr, (char *)addr, length)) {
663                                         strcpy(remcomOutBuffer, "OK");
664                                 } else {
665                                         strcpy(remcomOutBuffer, "E03");
666                                 }
667                         } else {
668                                 strcpy(remcomOutBuffer, "E02");
669                         }
670                         break;
671
672                 case 'c':    /* cAA..AA    Continue at address AA..AA(optional) */
673                         /* try to read optional parameter, pc unchanged if no parm */
674
675                         ptr = &remcomInBuffer[1];
676                         if (hexToInt(&ptr, &addr)) {
677                                 registers[PC] = addr;
678                                 registers[NPC] = addr + 4;
679                         }
680
681 /* Need to flush the instruction cache here, as we may have deposited a
682  * breakpoint, and the icache probably has no way of knowing that a data ref to
683  * some location may have changed something that is in the instruction cache.
684  */
685                         flush_cache_all();
686                         unlock_kernel();
687                         return;
688
689                         /* kill the program */
690                 case 'k' :              /* do nothing */
691                         break;
692                 case 'r':               /* Reset */
693                         asm ("call 0\n\t"
694                              "nop\n\t");
695                         break;
696                 }                       /* switch */
697
698                 /* reply to the request */
699                 putpacket(remcomOutBuffer);
700         } /* while(1) */
701 }
702
703 /* This function will generate a breakpoint exception.  It is used at the
704    beginning of a program to sync up with a debugger and can be used
705    otherwise as a quick means to stop program execution and "break" into
706    the debugger. */
707
708 void
709 breakpoint(void)
710 {
711         if (!initialized)
712                 return;
713
714         /* Again, watch those c-prefixes for ELF kernels */
715 #if defined(__svr4__) || defined(__ELF__)
716         asm(".globl breakinst\n"
717             "breakinst:\n\t"
718             "ta 1\n");
719 #else
720         asm(".globl _breakinst\n"
721             "_breakinst:\n\t"
722             "ta 1\n");
723 #endif
724 }