The XBRA Protocol

A generic XBRA element looks like:

struct xbra { 

long xbra_magic;
long my_magic;
void (*next)();
〈 exception code here 〉
};

xbra_magic is initialized to ``XBRA'' (0x58425241L hex). my_magic is a 32 bit value that you choose. In gcc compiled applications a default value is ``GNUC'' (0x474E5543L hex) but of course it may be changed. next holds the address of the next exception handler in the chain. It is initialized when the handler is initially linked into the chain, just past its head. The code for your handler follows the next field. You will land at the address of the first word of this code when control is passed to this handler. Usually we do not put our handler right into the structure, but instead we put a jump instruction to our handler routine in the XBRA structure. This is a convenience for C programs and not a requirement of the XBRA protocol. In C there is no portable way to ensure that the code for the handler is contiguous to the XBRA header and in the same section (text or data section) Here is an example of how to install a handler into the event timer handoff vector (exception 0x100).

void my_handler()
{
  /* handler code */
}

#define _XBRA_MAGIC     0x58425241L /* "XBRA" */
#define _GNUC_MAGIC     0x474E5543L /* "GNUC" */
#define _JMP_OPCODE     0x4EF9
#define _TIME_VEC       0x100

struct __xbra {
   long             xbra_magic;
   long             gnu_magic;
   void             (*next)();
   short            jump;
   void             (*handler_address)();
} xbra = { _XBRA_MAGIC,  /* XBRA magic                     */
           _GNUC_MAGIC,  /* application magic              */
           0L,           /* pointer to next handler        */
           _JMP_OPCODE,  /* jump instruction               */
           my_handler    /* address of our handler routine */
         };

void install_handler()
{
    xbra.next = Setexc(_TIME_VEC, &xbra.jump);
    /*
     * Setexc returns the address of the old handler that we stash
     * away in xbra.next. In its place we put the address of the
     * jump instruction in the XBRA structure. When the processor
     * picks up this address, it executes the jump instruction to
     * the entry point of our handler function.
     */
}

After our handler is installed, when the exception occurs it finally gets control. It must do what it needs to, then branch to the next exception handler in the chain (after it has cleaned up anything it put on the stack)3. Please note that a piece of an assembler code in the example below is using gcc syntax. A keyword volatile is needed as a guard against an overzealous optimizer.

void my_handler()
{
  /* my handler code goes here */
  __asm__ volatile("unlk a6          /* clean up stack */
                    movl _xbra+8, a0 /* address of next handler in A0 */
}                   jmp  a0@");      /* jump to it */

To unlink our handler from the exception chain we first need to locate it in memory, and then make its predecessor point to its successor. This is where the XBRA protocol really comes into its own. We begin the search at the exception vector looking for our XBRA elements address. If our address is found we authenticate it by testing the two magic numbers. Otherwise we travel down the chain by following the XBRA elements next pointer.

/*
 * unlink a handler in a xbra friendly manner from the exc chain
 */
static void unlink_handler(me, exc)
xbra_struct *me;
int exc;
{
    xbra_struct *this, *prev;
    long save_ssp;
    
    this = (xbra_struct *)      /* get head of chain */
        ((unsigned long)Setexc(exc, -1L) - offsetof(xbra_struct, jump));
    if(this == me)
    {   /* at the head, just unlink */
        Setexc(exc, me->next);
        return;
    }
    /* otherwise find me in the chain and unlink */
    save_ssp = Super(0L);
    for(prev = this; this && (this != me); prev = this,
        this = (xbra_struct *)((this->next)
             ? (((char *)(this->next)) - offsetof(xbra_struct, jump)) : 0))
    {
        /* validate the xbra */
        if(this->xbra_magic != _XBRA_MAGIC) 
        {       /* a non XBRA handler in the chain -- shame on you */
            Super(save_ssp);
            Setexc(exc, me->next); /* nuke it, otherwise it may call ME */
            return;                /* after I am deinstalled */
        }
    }
    
    if( (this == me) && (this->gnuc_magic == _GNUC_MAGIC) )
    {   /* unlink me from middle of the chain */
        prev->next = this->next;
        Super(save_ssp);
        return;
    }
    /* we are screwed */
    Super(save_ssp);
    Cconws("\r\nHandler not found!\r\n\n");
}

/* this routine may be called as unlink_handler(&xbra, _TIME_VEC) */