linux-old/arch/ppc/kernel/ppc-stub.c
<<
>>
Prefs
   1/* $Id: ppc-stub.c,v 1.4.2.1 1999/07/20 05:04:42 paulus Exp $
   2 * ppc-stub.c:  KGDB support for the Linux kernel.
   3 *
   4 * adapted from arch/sparc/kernel/sparc-stub.c for the PowerPC
   5 * some stuff borrowed from Paul Mackerras' xmon
   6 * Copyright (C) 1998 Michael AK Tesch (tesch@cs.wisc.edu)
   7 *
   8 * Modifications to run under Linux
   9 * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
  10 *
  11 * This file originally came from the gdb sources, and the
  12 * copyright notices have been retained below.
  13 */
  14
  15/****************************************************************************
  16
  17                THIS SOFTWARE IS NOT COPYRIGHTED
  18
  19   HP offers the following for use in the public domain.  HP makes no
  20   warranty with regard to the software or its performance and the
  21   user accepts the software "AS IS" with all faults.
  22
  23   HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD
  24   TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  25   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  26
  27****************************************************************************/
  28
  29/****************************************************************************
  30 *  Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $
  31 *
  32 *  Module name: remcom.c $
  33 *  Revision: 1.34 $
  34 *  Date: 91/03/09 12:29:49 $
  35 *  Contributor:     Lake Stevens Instrument Division$
  36 *
  37 *  Description:     low level support for gdb debugger. $
  38 *
  39 *  Considerations:  only works on target hardware $
  40 *
  41 *  Written by:      Glenn Engel $
  42 *  ModuleState:     Experimental $
  43 *
  44 *  NOTES:           See Below $
  45 *
  46 *  Modified for SPARC by Stu Grossman, Cygnus Support.
  47 *
  48 *  This code has been extensively tested on the Fujitsu SPARClite demo board.
  49 *
  50 *  To enable debugger support, two things need to happen.  One, a
  51 *  call to set_debug_traps() is necessary in order to allow any breakpoints
  52 *  or error conditions to be properly intercepted and reported to gdb.
  53 *  Two, a breakpoint needs to be generated to begin communication.  This
  54 *  is most easily accomplished by a call to breakpoint().  Breakpoint()
  55 *  simulates a breakpoint by executing a trap #1.
  56 *
  57 *************
  58 *
  59 *    The following gdb commands are supported:
  60 *
  61 * command          function                               Return value
  62 *
  63 *    g             return the value of the CPU registers  hex data or ENN
  64 *    G             set the value of the CPU registers     OK or ENN
  65 *    qOffsets      Get section offsets.  Reply is Text=xxx;Data=yyy;Bss=zzz
  66 *
  67 *    mAA..AA,LLLL  Read LLLL bytes at address AA..AA      hex data or ENN
  68 *    MAA..AA,LLLL: Write LLLL bytes at address AA.AA      OK or ENN
  69 *
  70 *    c             Resume at current address              SNN   ( signal NN)
  71 *    cAA..AA       Continue at address AA..AA             SNN
  72 *
  73 *    s             Step one instruction                   SNN
  74 *    sAA..AA       Step one instruction from AA..AA       SNN
  75 *
  76 *    k             kill
  77 *
  78 *    ?             What was the last sigval ?             SNN   (signal NN)
  79 *
  80 *    bBB..BB       Set baud rate to BB..BB                OK or BNN, then sets
  81 *                                                         baud rate
  82 *
  83 * All commands and responses are sent with a packet which includes a
  84 * checksum.  A packet consists of
  85 *
  86 * $<packet info>#<checksum>.
  87 *
  88 * where
  89 * <packet info> :: <characters representing the command or response>
  90 * <checksum>    :: <two hex digits computed as modulo 256 sum of <packetinfo>>
  91 *
  92 * When a packet is received, it is first acknowledged with either '+' or '-'.
  93 * '+' indicates a successful transfer.  '-' indicates a failed transfer.
  94 *
  95 * Example:
  96 *
  97 * Host:                  Reply:
  98 * $m0,10#2a               +$00010203040506070809101112131415#42
  99 *
 100 ****************************************************************************/
 101
 102#include <linux/kernel.h>
 103#include <linux/string.h>
 104#include <linux/mm.h>
 105#include <linux/smp.h>
 106#include <linux/smp_lock.h>
 107
 108#include <asm/system.h>
 109#include <asm/signal.h>
 110#include <asm/kgdb.h>
 111#include <asm/pgtable.h>
 112#include <asm/ptrace.h>
 113
 114void breakinst(void);
 115
 116/*
 117 * BUFMAX defines the maximum number of characters in inbound/outbound buffers
 118 * at least NUMREGBYTES*2 are needed for register packets
 119 */
 120#define BUFMAX 2048
 121static char remcomInBuffer[BUFMAX];
 122static char remcomOutBuffer[BUFMAX];
 123
 124static int initialized = 0;
 125static int kgdb_active = 0;
 126static u_int fault_jmp_buf[100];
 127static int kdebug;
 128
 129static const char hexchars[]="0123456789abcdef";
 130
 131/* Place where we save old trap entries for restoration - sparc*/
 132/* struct tt_entry kgdb_savettable[256]; */
 133/* typedef void (*trapfunc_t)(void); */
 134
 135#if 0
 136/* Install an exception handler for kgdb */
 137static void exceptionHandler(int tnum, unsigned int *tfunc)
 138{
 139        /* We are dorking with a live trap table, all irqs off */
 140}
 141#endif
 142
 143int
 144kgdb_setjmp(long *buf)
 145{
 146        asm ("mflr 0; stw 0,0(%0);"
 147             "stw 1,4(%0); stw 2,8(%0);"
 148             "mfcr 0; stw 0,12(%0);"
 149             "stmw 13,16(%0)"
 150             : : "r" (buf));
 151        /* XXX should save fp regs as well */
 152        return 0;
 153}
 154void
 155kgdb_longjmp(long *buf, int val)
 156{
 157        if (val == 0)
 158                val = 1;
 159        asm ("lmw 13,16(%0);"
 160             "lwz 0,12(%0); mtcrf 0x38,0;"
 161             "lwz 0,0(%0); lwz 1,4(%0); lwz 2,8(%0);"
 162             "mtlr 0; mr 3,%1"
 163             : : "r" (buf), "r" (val));
 164}
 165/* Convert ch from a hex digit to an int */
 166static int
 167hex(unsigned char ch)
 168{
 169        if (ch >= 'a' && ch <= 'f')
 170                return ch-'a'+10;
 171        if (ch >= '0' && ch <= '9')
 172                return ch-'0';
 173        if (ch >= 'A' && ch <= 'F')
 174                return ch-'A'+10;
 175        return -1;
 176}
 177
 178/* Convert the memory pointed to by mem into hex, placing result in buf.
 179 * Return a pointer to the last char put in buf (null), in case of mem fault,
 180 * return 0.
 181 */
 182static unsigned char *
 183mem2hex(char *mem, char *buf, int count)
 184{
 185        unsigned char ch;
 186
 187        if (kgdb_setjmp((long*)fault_jmp_buf) == 0) {
 188                debugger_fault_handler = kgdb_fault_handler;
 189                while (count-- > 0) {
 190                        ch = *mem++;
 191                        *buf++ = hexchars[ch >> 4];
 192                        *buf++ = hexchars[ch & 0xf];
 193                }
 194        } else {
 195                /* error condition */
 196        }
 197        debugger_fault_handler = 0;
 198        *buf = 0;
 199        return buf;
 200}
 201
 202/* convert the hex array pointed to by buf into binary to be placed in mem
 203 * return a pointer to the character AFTER the last byte written.
 204*/
 205static char *
 206hex2mem(char *buf, char *mem, int count)
 207{
 208        int i;
 209        unsigned char ch;
 210
 211        if (kgdb_setjmp((long*)fault_jmp_buf) == 0) {
 212                debugger_fault_handler = kgdb_fault_handler;
 213                for (i=0; i<count; i++) {
 214                        ch = hex(*buf++) << 4;
 215                        ch |= hex(*buf++);
 216                        *mem++ = ch;
 217                }
 218                flush_icache_range((int)mem, (int)mem+count);
 219        } else {
 220                /* error condition */
 221        }
 222        debugger_fault_handler = 0;
 223        return mem;
 224}
 225
 226/*
 227 * While we find nice hex chars, build an int.
 228 * Return number of chars processed.
 229 */
 230static int
 231hexToInt(char **ptr, int *intValue)
 232{
 233        int numChars = 0;
 234        int hexValue;
 235
 236        *intValue = 0;
 237
 238        if (kgdb_setjmp((long*)fault_jmp_buf) == 0) {
 239                debugger_fault_handler = kgdb_fault_handler;
 240                while (**ptr) {
 241                        hexValue = hex(**ptr);
 242                        if (hexValue < 0)
 243                                break;
 244
 245                        *intValue = (*intValue << 4) | hexValue;
 246                        numChars ++;
 247
 248                        (*ptr)++;
 249                }
 250        } else {
 251             /* error condition */
 252        }
 253        debugger_fault_handler = 0;
 254
 255        return (numChars);
 256}
 257
 258/* scan for the sequence $<data>#<checksum>     */
 259static void
 260getpacket(char *buffer)
 261{
 262        unsigned char checksum;
 263        unsigned char xmitcsum;
 264        int i;
 265        int count;
 266        unsigned char ch;
 267
 268        do {
 269                /* wait around for the start character, ignore all other
 270                 * characters */
 271                while ((ch = (getDebugChar() & 0x7f)) != '$') ;
 272
 273                checksum = 0;
 274                xmitcsum = -1;
 275
 276                count = 0;
 277
 278                /* now, read until a # or end of buffer is found */
 279                while (count < BUFMAX) {
 280                        ch = getDebugChar() & 0x7f;
 281                        if (ch == '#')
 282                                break;
 283                        checksum = checksum + ch;
 284                        buffer[count] = ch;
 285                        count = count + 1;
 286                }
 287
 288                if (count >= BUFMAX)
 289                        continue;
 290
 291                buffer[count] = 0;
 292
 293                if (ch == '#') {
 294                        xmitcsum = hex(getDebugChar() & 0x7f) << 4;
 295                        xmitcsum |= hex(getDebugChar() & 0x7f);
 296                        if (checksum != xmitcsum)
 297                                putDebugChar('-');      /* failed checksum */
 298                        else {
 299                                putDebugChar('+'); /* successful transfer */
 300                                /* if a sequence char is present, reply the ID */
 301                                if (buffer[2] == ':') {
 302                                        putDebugChar(buffer[0]);
 303                                        putDebugChar(buffer[1]);
 304                                        /* remove sequence chars from buffer */
 305                                        count = strlen(buffer);
 306                                        for (i=3; i <= count; i++)
 307                                                buffer[i-3] = buffer[i];
 308                                }
 309                        }
 310                }
 311        } while (checksum != xmitcsum);
 312}
 313
 314/* send the packet in buffer.  */
 315static void putpacket(unsigned char *buffer)
 316{
 317        unsigned char checksum;
 318        int count;
 319        unsigned char ch, recv;
 320
 321        /*  $<packet info>#<checksum>. */
 322        do {
 323                putDebugChar('$');
 324                checksum = 0;
 325                count = 0;
 326
 327                while ((ch = buffer[count])) {
 328                        putDebugChar(ch);
 329                        checksum += ch;
 330                        count += 1;
 331                }
 332
 333                putDebugChar('#');
 334                putDebugChar(hexchars[checksum >> 4]);
 335                putDebugChar(hexchars[checksum & 0xf]);
 336                recv = getDebugChar();
 337        } while ((recv & 0x7f) != '+');
 338}
 339
 340static void kgdb_flush_cache_all(void)
 341{
 342        flush_instruction_cache();
 343}
 344
 345static inline int get_msr()
 346{
 347        int msr;
 348        asm volatile("mfmsr %0" : "=r" (msr):);
 349        return msr;
 350}
 351
 352static inline void set_msr(int msr)
 353{
 354        asm volatile("mtmsr %0" : : "r" (msr));
 355}
 356
 357/* Set up exception handlers for tracing and breakpoints
 358 * [could be called kgdb_init()]
 359 */
 360void set_debug_traps(void)
 361{
 362#if 0
 363        unsigned char c;
 364
 365        save_and_cli(flags);
 366
 367        /* In case GDB is started before us, ack any packets (presumably
 368         * "$?#xx") sitting there.
 369         *
 370         * I've found this code causes more problems than it solves,
 371         * so that's why it's commented out.  GDB seems to work fine
 372         * now starting either before or after the kernel   -bwb
 373         */
 374
 375        while((c = getDebugChar()) != '$');
 376        while((c = getDebugChar()) != '#');
 377        c = getDebugChar(); /* eat first csum byte */
 378        c = getDebugChar(); /* eat second csum byte */
 379        putDebugChar('+'); /* ack it */
 380#endif
 381        debugger = kgdb;
 382        debugger_bpt = kgdb_bpt;
 383        debugger_sstep = kgdb_sstep;
 384        debugger_iabr_match = kgdb_iabr_match;
 385        debugger_dabr_match = kgdb_dabr_match;
 386
 387        initialized = 1;
 388}
 389
 390static void kgdb_fault_handler(struct pt_regs *regs)
 391{
 392        kgdb_longjmp((long*)fault_jmp_buf, 1);
 393}
 394
 395int kgdb_bpt(struct pt_regs *regs)
 396{
 397        handle_exception(regs);
 398        return 1;
 399}
 400
 401int kgdb_sstep(struct pt_regs *regs)
 402{
 403        handle_exception(regs);
 404        return 1;
 405}
 406
 407void kgdb(struct pt_regs *regs)
 408{
 409        handle_exception(regs);
 410}
 411
 412int kgdb_iabr_match(struct pt_regs *regs)
 413{
 414        printk("kgdb doesn't support iabr, what?!?\n");
 415        handle_exception(regs);
 416        return 1;
 417}
 418
 419int kgdb_dabr_match(struct pt_regs *regs)
 420{
 421        printk("kgdb doesn't support dabr, what?!?\n");
 422        handle_exception(regs);
 423        return 1;
 424}
 425
 426/* Convert the SPARC hardware trap type code to a unix signal number. */
 427/*
 428 * This table contains the mapping between PowerPC hardware trap types, and
 429 * signals, which are primarily what GDB understands.
 430 */
 431static struct hard_trap_info
 432{
 433        unsigned int tt;                /* Trap type code for powerpc */
 434        unsigned char signo;            /* Signal that we map this trap into */
 435} hard_trap_info[] = {
 436        { 0x200, SIGSEGV },                     /* machine check */
 437        { 0x300, SIGSEGV },                     /* address error (store) */
 438        { 0x400, SIGBUS },                      /* instruction bus error */
 439        { 0x500, SIGINT },                      /* interrupt */
 440        { 0x600, SIGBUS },                      /* alingment */
 441        { 0x700, SIGILL },                      /* reserved instruction or sumpin' */
 442        { 0x800, SIGFPE },                      /* fpu unavail */
 443        { 0x900, SIGALRM },                     /* decrementer */
 444        { 0xa00, SIGILL },                      /* reserved */
 445        { 0xb00, SIGILL },                      /* reserved */
 446        { 0xc00, SIGCHLD },                     /* syscall */
 447        { 0xd00, SIGINT },                      /* watch */
 448        { 0xe00, SIGFPE },                      /* fp assist */
 449        { 0, 0}                         /* Must be last */
 450};
 451
 452static int computeSignal(unsigned int tt)
 453{
 454        struct hard_trap_info *ht;
 455
 456        for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
 457                if (ht->tt == tt)
 458                        return ht->signo;
 459
 460        return SIGHUP;         /* default for things we don't know about */
 461}
 462
 463/*
 464 * This function does all command processing for interfacing to gdb.
 465 */
 466static void
 467handle_exception (struct pt_regs *regs)
 468{
 469        int sigval;
 470        int addr;
 471        int length;
 472        char *ptr;
 473        unsigned int msr;
 474
 475        if (debugger_fault_handler) {
 476                debugger_fault_handler(regs);
 477                panic("kgdb longjump failed!\n");
 478        }
 479        if (kgdb_active) {
 480                printk("interrupt while in kgdb, returning\n");
 481                return;
 482        }
 483        kgdb_active = 1;
 484
 485        printk("kgdb: entering handle_exception; trap [0x%x]\n",
 486               (unsigned int)regs->trap);
 487
 488        kgdb_interruptible(0);
 489        lock_kernel();
 490        msr = get_msr();
 491        set_msr(msr & ~MSR_EE); /* disable interrupts */
 492
 493        if (regs->nip == (unsigned long)breakinst) {
 494                /* Skip over breakpoint trap insn */
 495                regs->nip += 4;
 496        }
 497
 498        /* reply to host that an exception has occurred */
 499        sigval = computeSignal(regs->trap);
 500        ptr = remcomOutBuffer;
 501
 502        *ptr++ = 'S';
 503        *ptr++ = hexchars[sigval >> 4];
 504        *ptr++ = hexchars[sigval & 0xf];
 505
 506        *ptr++ = 0;
 507
 508        putpacket(remcomOutBuffer);
 509
 510        /* XXX We may want to add some features dealing with poking the
 511         * XXX page tables, ... (look at sparc-stub.c for more info)
 512         * XXX also required hacking to the gdb sources directly...
 513         */
 514
 515        while (1) {
 516                remcomOutBuffer[0] = 0;
 517
 518                getpacket(remcomInBuffer);
 519                switch (remcomInBuffer[0]) {
 520                case '?':               /* report most recent signal */
 521                        remcomOutBuffer[0] = 'S';
 522                        remcomOutBuffer[1] = hexchars[sigval >> 4];
 523                        remcomOutBuffer[2] = hexchars[sigval & 0xf];
 524                        remcomOutBuffer[3] = 0;
 525                        break;
 526#if 0
 527                case 'q': /* this screws up gdb for some reason...*/
 528                {
 529                        extern long _start, sdata, __bss_start;
 530
 531                        ptr = &remcomInBuffer[1];
 532                        if (strncmp(ptr, "Offsets", 7) != 0)
 533                                break;
 534
 535                        ptr = remcomOutBuffer;
 536                        sprintf(ptr, "Text=%8.8x;Data=%8.8x;Bss=%8.8x",
 537                                &_start, &sdata, &__bss_start);
 538                        break;
 539                }
 540#endif
 541                case 'd':
 542                        /* toggle debug flag */
 543                        kdebug ^= 1;
 544                        break;
 545
 546                case 'g':       /* return the value of the CPU registers.
 547                                 * some of them are non-PowerPC names :(
 548                                 * they are stored in gdb like:
 549                                 * struct {
 550                                 *     u32 gpr[32];
 551                                 *     f64 fpr[32];
 552                                 *     u32 pc, ps, cnd, lr; (ps=msr)
 553                                 *     u32 cnt, xer, mq;
 554                                 * }
 555                                 */
 556                {
 557                        int i;
 558                        ptr = remcomOutBuffer;
 559                        /* General Purpose Regs */
 560                        ptr = mem2hex((char *)regs, ptr, 32 * 4);
 561                        /* Floating Point Regs - FIXME */
 562                        /*ptr = mem2hex((char *), ptr, 32 * 8);*/
 563                        for(i=0; i<(32*8*2); i++) { /* 2chars/byte */
 564                                ptr[i] = '0';
 565                        }
 566                        ptr += 32*8*2;
 567                        /* pc, msr, cr, lr, ctr, xer, (mq is unused) */
 568                        ptr = mem2hex((char *)&regs->nip, ptr, 4);
 569                        ptr = mem2hex((char *)&regs->msr, ptr, 4);
 570                        ptr = mem2hex((char *)&regs->ccr, ptr, 4);
 571                        ptr = mem2hex((char *)&regs->link, ptr, 4);
 572                        ptr = mem2hex((char *)&regs->ctr, ptr, 4);
 573                        ptr = mem2hex((char *)&regs->xer, ptr, 4);
 574                }
 575                        break;
 576
 577                case 'G':   /* set the value of the CPU registers */
 578                {
 579                        ptr = &remcomInBuffer[1];
 580
 581                        /*
 582                         * If the stack pointer has moved, you should pray.
 583                         * (cause only god can help you).
 584                         */
 585
 586                        /* General Purpose Regs */
 587                        hex2mem(ptr, (char *)regs, 32 * 4);
 588
 589                        /* Floating Point Regs - FIXME?? */
 590                        /*ptr = hex2mem(ptr, ??, 32 * 8);*/
 591                        ptr += 32*8*2;
 592
 593                        /* pc, msr, cr, lr, ctr, xer, (mq is unused) */
 594                        ptr = hex2mem(ptr, (char *)&regs->nip, 4);
 595                        ptr = hex2mem(ptr, (char *)&regs->msr, 4);
 596                        ptr = hex2mem(ptr, (char *)&regs->ccr, 4);
 597                        ptr = hex2mem(ptr, (char *)&regs->link, 4);
 598                        ptr = hex2mem(ptr, (char *)&regs->ctr, 4);
 599                        ptr = hex2mem(ptr, (char *)&regs->xer, 4);
 600
 601                        strcpy(remcomOutBuffer,"OK");
 602                }
 603                        break;
 604                case 'H':
 605                        /* don't do anything, yet, just acknowledge */
 606                        hexToInt(&ptr, &addr);
 607                        strcpy(remcomOutBuffer,"OK");
 608                        break;
 609
 610                case 'm':       /* mAA..AA,LLLL  Read LLLL bytes at address AA..AA */
 611                                /* Try to read %x,%x.  */
 612
 613                        ptr = &remcomInBuffer[1];
 614
 615                        if (hexToInt(&ptr, &addr)
 616                            && *ptr++ == ','
 617                            && hexToInt(&ptr, &length)) {
 618                                if (mem2hex((char *)addr, remcomOutBuffer,length))
 619                                        break;
 620                                strcpy (remcomOutBuffer, "E03");
 621                        } else {
 622                                strcpy(remcomOutBuffer,"E01");
 623                        }
 624                        break;
 625
 626                case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
 627                        /* Try to read '%x,%x:'.  */
 628
 629                        ptr = &remcomInBuffer[1];
 630
 631                        if (hexToInt(&ptr, &addr)
 632                            && *ptr++ == ','
 633                            && hexToInt(&ptr, &length)
 634                            && *ptr++ == ':') {
 635                                if (hex2mem(ptr, (char *)addr, length)) {
 636                                        strcpy(remcomOutBuffer, "OK");
 637                                } else {
 638                                        strcpy(remcomOutBuffer, "E03");
 639                                }
 640                        } else {
 641                                strcpy(remcomOutBuffer, "E02");
 642                        }
 643                        break;
 644
 645
 646                case 'k':    /* kill the program, actually just continue */
 647                case 'c':    /* cAA..AA  Continue; address AA..AA optional */
 648                        /* try to read optional parameter, pc unchanged if no parm */
 649
 650                        ptr = &remcomInBuffer[1];
 651                        if (hexToInt(&ptr, &addr)) {
 652                                regs->nip = addr;
 653                        }
 654
 655/* Need to flush the instruction cache here, as we may have deposited a
 656 * breakpoint, and the icache probably has no way of knowing that a data ref to
 657 * some location may have changed something that is in the instruction cache.
 658 */
 659                        kgdb_flush_cache_all();
 660                        set_msr(msr);
 661                        kgdb_interruptible(1);
 662                        unlock_kernel();
 663                        kgdb_active = 0;
 664                        return;
 665
 666                case 's':
 667                        kgdb_flush_cache_all();
 668                        regs->msr |= MSR_SE;
 669                        set_msr(msr | MSR_SE);
 670                        unlock_kernel();
 671                        kgdb_active = 0;
 672                        return;
 673
 674                case 'r':               /* Reset (if user process..exit ???)*/
 675                        panic("kgdb reset.");
 676                        break;
 677                }                       /* switch */
 678                if (remcomOutBuffer[0] && kdebug) {
 679                        printk("remcomInBuffer: %s\n", remcomInBuffer);
 680                        printk("remcomOutBuffer: %s\n", remcomOutBuffer);
 681                }
 682                /* reply to the request */
 683                putpacket(remcomOutBuffer);
 684        } /* while(1) */
 685}
 686
 687/* This function will generate a breakpoint exception.  It is used at the
 688   beginning of a program to sync up with a debugger and can be used
 689   otherwise as a quick means to stop program execution and "break" into
 690   the debugger. */
 691
 692void
 693breakpoint(void)
 694{
 695        if (!initialized) {
 696                printk("breakpoint() called b4 kgdb init\n");
 697                return;
 698        }
 699
 700        asm("   .globl breakinst
 701             breakinst: trap
 702            ");
 703}
 704
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.