File: /usr/src/linux/arch/sparc/kernel/sparc-stub.c

1     /* $Id: sparc-stub.c,v 1.27 2000/10/03 07:28:49 anton 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     /*
113      *
114      * external low-level support routines
115      */
116     
117     extern void putDebugChar(char);   /* write a single character      */
118     extern char getDebugChar(void);   /* read and return a single char */
119     
120     /*
121      * BUFMAX defines the maximum number of characters in inbound/outbound buffers
122      * at least NUMREGBYTES*2 are needed for register packets
123      */
124     #define BUFMAX 2048
125     
126     static int initialized;	/* !0 means we've been initialized */
127     
128     static const char hexchars[]="0123456789abcdef";
129     
130     #define NUMREGS 72
131     
132     /* Number of bytes of registers.  */
133     #define NUMREGBYTES (NUMREGS * 4)
134     enum regnames {G0, G1, G2, G3, G4, G5, G6, G7,
135     		 O0, O1, O2, O3, O4, O5, SP, O7,
136     		 L0, L1, L2, L3, L4, L5, L6, L7,
137     		 I0, I1, I2, I3, I4, I5, FP, I7,
138     
139     		 F0, F1, F2, F3, F4, F5, F6, F7,
140     		 F8, F9, F10, F11, F12, F13, F14, F15,
141     		 F16, F17, F18, F19, F20, F21, F22, F23,
142     		 F24, F25, F26, F27, F28, F29, F30, F31,
143     		 Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR };
144     
145     
146     extern void trap_low(void);  /* In arch/sparc/kernel/entry.S */
147     
148     unsigned long get_sun4cpte(unsigned long addr)
149     {
150     	unsigned long entry;
151     
152     	__asm__ __volatile__("\n\tlda [%1] %2, %0\n\t" : 
153     			     "=r" (entry) :
154     			     "r" (addr), "i" (ASI_PTE));
155     	return entry;
156     }
157     
158     unsigned long get_sun4csegmap(unsigned long addr)
159     {
160     	unsigned long entry;
161     
162     	__asm__ __volatile__("\n\tlduba [%1] %2, %0\n\t" : 
163     			     "=r" (entry) :
164     			     "r" (addr), "i" (ASI_SEGMAP));
165     	return entry;
166     }
167     
168     #if 0
169     /* Have to sort this out. This cannot be done after initialization. */
170     static void flush_cache_all_nop(void) {}
171     #endif
172     
173     /* Place where we save old trap entries for restoration */
174     struct tt_entry kgdb_savettable[256];
175     typedef void (*trapfunc_t)(void);
176     
177     /* Helper routine for manipulation of kgdb_savettable */
178     static inline void copy_ttentry(struct tt_entry *src, struct tt_entry *dest)
179     {
180     	dest->inst_one = src->inst_one;
181     	dest->inst_two = src->inst_two;
182     	dest->inst_three = src->inst_three;
183     	dest->inst_four = src->inst_four;
184     }
185     
186     /* Initialize the kgdb_savettable so that debugging can commence */
187     static void eh_init(void)
188     {
189     	int i, flags;
190     
191     	save_and_cli(flags);
192     	for(i=0; i < 256; i++)
193     		copy_ttentry(&sparc_ttable[i], &kgdb_savettable[i]);
194     	restore_flags(flags);
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     	int flags;
202     
203     	/* We are dorking with a live trap table, all irqs off */
204     	save_and_cli(flags);
205     
206     	/* Make new vector */
207     	sparc_ttable[tnum].inst_one =
208     		SPARC_BRANCH((unsigned long) te_addr,
209     			     (unsigned long) &sparc_ttable[tnum].inst_one);
210     	sparc_ttable[tnum].inst_two = SPARC_RD_PSR_L0;
211     	sparc_ttable[tnum].inst_three = SPARC_NOP;
212     	sparc_ttable[tnum].inst_four = SPARC_NOP;
213     
214     	restore_flags(flags);
215     }
216     
217     /* Convert ch from a hex digit to an int */
218     static int
219     hex(unsigned char ch)
220     {
221     	if (ch >= 'a' && ch <= 'f')
222     		return ch-'a'+10;
223     	if (ch >= '0' && ch <= '9')
224     		return ch-'0';
225     	if (ch >= 'A' && ch <= 'F')
226     		return ch-'A'+10;
227     	return -1;
228     }
229     
230     /* scan for the sequence $<data>#<checksum>     */
231     static void
232     getpacket(char *buffer)
233     {
234     	unsigned char checksum;
235     	unsigned char xmitcsum;
236     	int i;
237     	int count;
238     	unsigned char ch;
239     
240     	do {
241     		/* wait around for the start character, ignore all other characters */
242     		while ((ch = (getDebugChar() & 0x7f)) != '$') ;
243     
244     		checksum = 0;
245     		xmitcsum = -1;
246     
247     		count = 0;
248     
249     		/* now, read until a # or end of buffer is found */
250     		while (count < BUFMAX) {
251     			ch = getDebugChar() & 0x7f;
252     			if (ch == '#')
253     				break;
254     			checksum = checksum + ch;
255     			buffer[count] = ch;
256     			count = count + 1;
257     		}
258     
259     		if (count >= BUFMAX)
260     			continue;
261     
262     		buffer[count] = 0;
263     
264     		if (ch == '#') {
265     			xmitcsum = hex(getDebugChar() & 0x7f) << 4;
266     			xmitcsum |= hex(getDebugChar() & 0x7f);
267     			if (checksum != xmitcsum)
268     				putDebugChar('-');	/* failed checksum */
269     			else {
270     				putDebugChar('+'); /* successful transfer */
271     				/* if a sequence char is present, reply the ID */
272     				if (buffer[2] == ':') {
273     					putDebugChar(buffer[0]);
274     					putDebugChar(buffer[1]);
275     					/* remove sequence chars from buffer */
276     					count = strlen(buffer);
277     					for (i=3; i <= count; i++)
278     						buffer[i-3] = buffer[i];
279     				}
280     			}
281     		}
282     	} while (checksum != xmitcsum);
283     }
284     
285     /* send the packet in buffer.  */
286     
287     static void
288     putpacket(unsigned char *buffer)
289     {
290     	unsigned char checksum;
291     	int count;
292     	unsigned char ch, recv;
293     
294     	/*  $<packet info>#<checksum>. */
295     	do {
296     		putDebugChar('$');
297     		checksum = 0;
298     		count = 0;
299     
300     		while ((ch = buffer[count])) {
301     			putDebugChar(ch);
302     			checksum += ch;
303     			count += 1;
304     		}
305     
306     		putDebugChar('#');
307     		putDebugChar(hexchars[checksum >> 4]);
308     		putDebugChar(hexchars[checksum & 0xf]);
309     		recv = getDebugChar();
310     	} while ((recv & 0x7f) != '+');
311     }
312     
313     static char remcomInBuffer[BUFMAX];
314     static char remcomOutBuffer[BUFMAX];
315     
316     /* Convert the memory pointed to by mem into hex, placing result in buf.
317      * Return a pointer to the last char put in buf (null), in case of mem fault,
318      * return 0.
319      */
320     
321     static unsigned char *
322     mem2hex(char *mem, char *buf, int count)
323     {
324     	unsigned char ch;
325     
326     	while (count-- > 0) {
327     		/* This assembler code is basically:  ch = *mem++;
328     		 * except that we use the SPARC/Linux exception table
329     		 * mechanism (see how "fixup" works in kernel_mna_trap_fault)
330     		 * to arrange for a "return 0" upon a memory fault
331     		 */
332     		__asm__(
333     			"1:	ldub [%0], %1
334     				inc %0
335     				.section .fixup,#alloc,#execinstr
336     				.align 4
337     			 2:	retl
338     				 mov 0, %%o0
339     				.section __ex_table, #alloc
340     				.align 4
341     				.word 1b, 2b
342     				.text"
343     					: "=r" (mem), "=r" (ch) : "0" (mem));
344     		*buf++ = hexchars[ch >> 4];
345     		*buf++ = hexchars[ch & 0xf];
346     	}
347     
348     	*buf = 0;
349     	return buf;
350     }
351     
352     /* convert the hex array pointed to by buf into binary to be placed in mem
353      * return a pointer to the character AFTER the last byte written.
354     */
355     static char *
356     hex2mem(char *buf, char *mem, int count)
357     {
358     	int i;
359     	unsigned char ch;
360     
361     	for (i=0; i<count; i++) {
362     
363     		ch = hex(*buf++) << 4;
364     		ch |= hex(*buf++);
365     		/* Assembler code is   *mem++ = ch;   with return 0 on fault */
366     		__asm__(
367     			"1:	stb %1, [%0]
368     				inc %0
369     				.section .fixup,#alloc,#execinstr
370     				.align 4
371     			 2:	retl
372     				 mov 0, %%o0
373     				.section __ex_table, #alloc
374     				.align 4
375     				.word 1b, 2b
376     				.text"
377     					: "=r" (mem) : "r" (ch) , "0" (mem));
378     	}
379     	return mem;
380     }
381     
382     /* This table contains the mapping between SPARC hardware trap types, and
383        signals, which are primarily what GDB understands.  It also indicates
384        which hardware traps we need to commandeer when initializing the stub. */
385     
386     static struct hard_trap_info
387     {
388       unsigned char tt;		/* Trap type code for SPARC */
389       unsigned char signo;		/* Signal that we map this trap into */
390     } hard_trap_info[] = {
391       {SP_TRAP_SBPT, SIGTRAP},      /* ta 1 - Linux/KGDB software breakpoint */
392       {0, 0}			/* Must be last */
393     };
394     
395     /* Set up exception handlers for tracing and breakpoints */
396     
397     void
398     set_debug_traps(void)
399     {
400     	struct hard_trap_info *ht;
401     	unsigned long flags;
402     
403     	save_and_cli(flags);
404     #if 0	
405     /* Have to sort this out. This cannot be done after initialization. */
406     	BTFIXUPSET_CALL(flush_cache_all, flush_cache_all_nop, BTFIXUPCALL_NOP);
407     #endif
408     
409     	/* Initialize our copy of the Linux Sparc trap table */
410     	eh_init();
411     
412     	for (ht = hard_trap_info; ht->tt && ht->signo; ht++) {
413     		/* Only if it doesn't destroy our fault handlers */
414     		if((ht->tt != SP_TRAP_TFLT) && 
415     		   (ht->tt != SP_TRAP_DFLT))
416     			exceptionHandler(ht->tt, trap_low);
417     	}
418     
419     	/* In case GDB is started before us, ack any packets (presumably
420     	 * "$?#xx") sitting there.
421     	 *
422     	 * I've found this code causes more problems than it solves,
423     	 * so that's why it's commented out.  GDB seems to work fine
424     	 * now starting either before or after the kernel   -bwb
425     	 */
426     #if 0
427     	while((c = getDebugChar()) != '$');
428     	while((c = getDebugChar()) != '#');
429     	c = getDebugChar(); /* eat first csum byte */
430     	c = getDebugChar(); /* eat second csum byte */
431     	putDebugChar('+'); /* ack it */
432     #endif
433     
434     	initialized = 1; /* connect! */
435     	restore_flags(flags);
436     }
437     
438     /* Convert the SPARC hardware trap type code to a unix signal number. */
439     
440     static int
441     computeSignal(int tt)
442     {
443     	struct hard_trap_info *ht;
444     
445     	for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
446     		if (ht->tt == tt)
447     			return ht->signo;
448     
449     	return SIGHUP;         /* default for things we don't know about */
450     }
451     
452     /*
453      * While we find nice hex chars, build an int.
454      * Return number of chars processed.
455      */
456     
457     static int
458     hexToInt(char **ptr, int *intValue)
459     {
460     	int numChars = 0;
461     	int hexValue;
462     
463     	*intValue = 0;
464     
465     	while (**ptr) {
466     		hexValue = hex(**ptr);
467     		if (hexValue < 0)
468     			break;
469     
470     		*intValue = (*intValue << 4) | hexValue;
471     		numChars ++;
472     
473     		(*ptr)++;
474     	}
475     
476     	return (numChars);
477     }
478     
479     /*
480      * This function does all command processing for interfacing to gdb.  It
481      * returns 1 if you should skip the instruction at the trap address, 0
482      * otherwise.
483      */
484     
485     extern void breakinst(void);
486     
487     void
488     handle_exception (unsigned long *registers)
489     {
490     	int tt;       /* Trap type */
491     	int sigval;
492     	int addr;
493     	int length;
494     	char *ptr;
495     	unsigned long *sp;
496     
497     	/* First, we must force all of the windows to be spilled out */
498     
499     	asm("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     	    "save %sp, -64, %sp\n\t"
506     	    "save %sp, -64, %sp\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     	    "restore\n\t"
514     	    "restore\n\t");
515     
516     	lock_kernel();
517     	if (registers[PC] == (unsigned long)breakinst) {
518     		/* Skip over breakpoint trap insn */
519     		registers[PC] = registers[NPC];
520     		registers[NPC] += 4;
521     	}
522     
523     	sp = (unsigned long *)registers[SP];
524     
525     	tt = (registers[TBR] >> 4) & 0xff;
526     
527     	/* reply to host that an exception has occurred */
528     	sigval = computeSignal(tt);
529     	ptr = remcomOutBuffer;
530     
531     	*ptr++ = 'T';
532     	*ptr++ = hexchars[sigval >> 4];
533     	*ptr++ = hexchars[sigval & 0xf];
534     
535     	*ptr++ = hexchars[PC >> 4];
536     	*ptr++ = hexchars[PC & 0xf];
537     	*ptr++ = ':';
538     	ptr = mem2hex((char *)&registers[PC], ptr, 4);
539     	*ptr++ = ';';
540     
541     	*ptr++ = hexchars[FP >> 4];
542     	*ptr++ = hexchars[FP & 0xf];
543     	*ptr++ = ':';
544     	ptr = mem2hex((char *) (sp + 8 + 6), ptr, 4); /* FP */
545     	*ptr++ = ';';
546     
547     	*ptr++ = hexchars[SP >> 4];
548     	*ptr++ = hexchars[SP & 0xf];
549     	*ptr++ = ':';
550     	ptr = mem2hex((char *)&sp, ptr, 4);
551     	*ptr++ = ';';
552     
553     	*ptr++ = hexchars[NPC >> 4];
554     	*ptr++ = hexchars[NPC & 0xf];
555     	*ptr++ = ':';
556     	ptr = mem2hex((char *)&registers[NPC], ptr, 4);
557     	*ptr++ = ';';
558     
559     	*ptr++ = hexchars[O7 >> 4];
560     	*ptr++ = hexchars[O7 & 0xf];
561     	*ptr++ = ':';
562     	ptr = mem2hex((char *)&registers[O7], ptr, 4);
563     	*ptr++ = ';';
564     
565     	*ptr++ = 0;
566     
567     	putpacket(remcomOutBuffer);
568     
569     	/* XXX We may want to add some features dealing with poking the
570     	 * XXX page tables, the real ones on the srmmu, and what is currently
571     	 * XXX loaded in the sun4/sun4c tlb at this point in time.  But this
572     	 * XXX also required hacking to the gdb sources directly...
573     	 */
574     
575     	while (1) {
576     		remcomOutBuffer[0] = 0;
577     
578     		getpacket(remcomInBuffer);
579     		switch (remcomInBuffer[0]) {
580     		case '?':
581     			remcomOutBuffer[0] = 'S';
582     			remcomOutBuffer[1] = hexchars[sigval >> 4];
583     			remcomOutBuffer[2] = hexchars[sigval & 0xf];
584     			remcomOutBuffer[3] = 0;
585     			break;
586     
587     		case 'd':
588     			/* toggle debug flag */
589     			break;
590     
591     		case 'g':		/* return the value of the CPU registers */
592     		{
593     			ptr = remcomOutBuffer;
594     			/* G & O regs */
595     			ptr = mem2hex((char *)registers, ptr, 16 * 4);
596     			/* L & I regs */
597     			ptr = mem2hex((char *) (sp + 0), ptr, 16 * 4);
598     			/* Floating point */
599     			memset(ptr, '0', 32 * 8);
600     			/* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
601     			mem2hex((char *)&registers[Y], (ptr + 32 * 4 * 2), (8 * 4));
602     		}
603     			break;
604     
605     		case 'G':	   /* set the value of the CPU registers - return OK */
606     		{
607     			unsigned long *newsp, psr;
608     
609     			psr = registers[PSR];
610     
611     			ptr = &remcomInBuffer[1];
612     			/* G & O regs */
613     			hex2mem(ptr, (char *)registers, 16 * 4);
614     			/* L & I regs */
615     			hex2mem(ptr + 16 * 4 * 2, (char *) (sp + 0), 16 * 4);
616     			/* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
617     			hex2mem(ptr + 64 * 4 * 2, (char *)&registers[Y], 8 * 4);
618     
619     			/* See if the stack pointer has moved.  If so,
620     			 * then copy the saved locals and ins to the
621     			 * new location.  This keeps the window
622     			 * overflow and underflow routines happy.
623     			 */
624     
625     			newsp = (unsigned long *)registers[SP];
626     			if (sp != newsp)
627     				sp = memcpy(newsp, sp, 16 * 4);
628     
629     			/* Don't allow CWP to be modified. */
630     
631     			if (psr != registers[PSR])
632     				registers[PSR] = (psr & 0x1f) | (registers[PSR] & ~0x1f);
633     
634     			strcpy(remcomOutBuffer,"OK");
635     		}
636     			break;
637     
638     		case 'm':	  /* mAA..AA,LLLL  Read LLLL bytes at address AA..AA */
639     			/* Try to read %x,%x.  */
640     
641     			ptr = &remcomInBuffer[1];
642     
643     			if (hexToInt(&ptr, &addr)
644     			    && *ptr++ == ','
645     			    && hexToInt(&ptr, &length))	{
646     				if (mem2hex((char *)addr, remcomOutBuffer, length))
647     					break;
648     
649     				strcpy (remcomOutBuffer, "E03");
650     			} else {
651     				strcpy(remcomOutBuffer,"E01");
652     			}
653     			break;
654     
655     		case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
656     			/* Try to read '%x,%x:'.  */
657     
658     			ptr = &remcomInBuffer[1];
659     
660     			if (hexToInt(&ptr, &addr)
661     			    && *ptr++ == ','
662     			    && hexToInt(&ptr, &length)
663     			    && *ptr++ == ':') {
664     				if (hex2mem(ptr, (char *)addr, length)) {
665     					strcpy(remcomOutBuffer, "OK");
666     				} else {
667     					strcpy(remcomOutBuffer, "E03");
668     				}
669     			} else {
670     				strcpy(remcomOutBuffer, "E02");
671     			}
672     			break;
673     
674     		case 'c':    /* cAA..AA    Continue at address AA..AA(optional) */
675     			/* try to read optional parameter, pc unchanged if no parm */
676     
677     			ptr = &remcomInBuffer[1];
678     			if (hexToInt(&ptr, &addr)) {
679     				registers[PC] = addr;
680     				registers[NPC] = addr + 4;
681     			}
682     
683     /* Need to flush the instruction cache here, as we may have deposited a
684      * breakpoint, and the icache probably has no way of knowing that a data ref to
685      * some location may have changed something that is in the instruction cache.
686      */
687     			flush_cache_all();
688     			unlock_kernel();
689     			return;
690     
691     			/* kill the program */
692     		case 'k' :		/* do nothing */
693     			break;
694     		case 'r':		/* Reset */
695     			asm ("call 0\n\t"
696     			     "nop\n\t");
697     			break;
698     		}			/* switch */
699     
700     		/* reply to the request */
701     		putpacket(remcomOutBuffer);
702     	} /* while(1) */
703     }
704     
705     /* This function will generate a breakpoint exception.  It is used at the
706        beginning of a program to sync up with a debugger and can be used
707        otherwise as a quick means to stop program execution and "break" into
708        the debugger. */
709     
710     void
711     breakpoint(void)
712     {
713     	if (!initialized)
714     		return;
715     
716     	/* Again, watch those c-prefixes for ELF kernels */
717     #if defined(__svr4__) || defined(__ELF__)
718     	asm("	.globl breakinst
719     
720     	     breakinst: ta 1
721                 ");
722     #else
723     	asm("	.globl _breakinst
724     
725     	     _breakinst: ta 1
726                 ");
727     #endif
728     }
729