Jolix
/joh'liks/ n.,adj. 386BSD

PORTING UNIX TO THE 386: A PRACTICAL APPROACH


William & Lynne Jolitz


We coded stub routines to connect trap handlers to descriptor table entry points, wiring the kernel to receive processor traps.




Trap Handling
Listing 12:
/* [excerpted from i386.c] */
 ...
init386() {
 ...
   outb(0xf1,0);      /* clear coprocessor to cover all bases */

   /* initialize 8259 ICU's in preperation for device interrupts */

   outb(ICU1,0x11);   /* reset the unit */
   outb(ICU1+1,32);   /* start with idt 32 */
   outb(ICU1+1,4);    /* master please */
   outb(ICU1+1,1);
   outb(ICU1+1,0xff); /* all disabled */

   outb(ICU2,0x11);
   outb(ICU2+1,40);   /* start with idt 40 */
   outb(ICU2+1,2);    /* just a slave */
   outb(ICU2+1,1);
   outb(ICU2+1,0xff); /* all disabled */

   /* initialize 8253 timer on interrupt #0 */

   outb (0x43, 0x36);
   outb (0x40, 193182/60);
   outb (0x40, (193182/60)/256);
}
test386(){
 ...
   /* test interrupts for a while */
   printf("inton"); getchar();
   outb(ICU1+1,0);   /* unmask all interrupts */
   outb(ICU2+1,0);
   inton();

   timeout = 0x8000000;
   do nothing(); while (timeout-- );

   intoff();
 ...

# excerpted from srt.s
 ...
#define   INTR(a) \
   pushal ; \
   push %ds ; \
   push %es ; \
   movw $0x10, %ax ; \
   movw %ax, %ds ; \
   movw %ax,%es ; \
   pushl $##a ; \
   call _intr ; \
   pop %eax ; \
   pop %es ; \
   pop %ds ; \
   popal ; \
   iret

   /* hardware 32 - 47 */
IDTVEC(intr0)
   INTR(0)
IDTVEC(intr1)
   INTR(1)
IDTVEC(intr2)
   INTR(2)
IDTVEC(intr3)
   INTR(3)
IDTVEC(intr4)
   INTR(4)
IDTVEC(intr5)
   INTR(5)
IDTVEC(intr6)
   INTR(6)
IDTVEC(intr7)
   INTR(7)
IDTVEC(intr8)
   INTR(8)
IDTVEC(intr9)
   INTR(9)
IDTVEC(intr10)
   INTR(10)
IDTVEC(intr11)
   INTR(11)
IDTVEC(intr12)
   INTR(12)
IDTVEC(intr13)
   INTR(13)
IDTVEC(intr14)
   INTR(14)
IDTVEC(intr15)
   INTR(15)

   .globl   _inton
_inton:
   sti
   ret

   .globl   _intoff
_intoff:
   cli
   ret
 ...

/* back to i386.c */
 ...
/* Interrupt vector processing code  */
intr(ivec) {
   static clk;
   int omsk1, omsk2;

   /* mask off interrupt being serviced, save old mask */
   if (ivec > 7) {
      omsk2 = inb(ICU2+1);
      outb(ICU2+1, 1<<(ivec-8));
   } else {
      omsk1 = inb(ICU1+1);
      outb(ICU1+1, 1<<ivec);
   }

   /* re-enable processor's interrupts, allowing others in */
   inton();

   /* if we are the clock, count clock tick */
   if (ivec == 0) clk++;
   /* if we are the keyboard, show data incoming */
   else if (ivec == 1) printf("kbd data %x, clk %d\n", inb(0x60), clk);
   /* otherwise print message stating source and time */
   else {
      printf("intr %d, clk %d \n", ivec, clk);
      getchar();
   }

   /* turn off interrupts, re-enable old mask, do interrupt acknowledge */
   intoff();
   if (ivec > 7) {
      outb(ICU2+1,omsk2);
      outb(ICU2,0x20);
   } else
      outb(ICU1+1,omsk1);
   outb(ICU1,0x20);
   /* return to interrupt stub */
}
 ...
Earlier in this article we alluded to the 386 exception/trap's assembler stubs routines. Now that we have enough 386 support in place, we can describe trap handling and the mechanism by which these stubs reflect the trap event into the C trap() handling function. Listing Twelve contains code for a sample trap, in this case a breakpoint or INT3 instruction. Assembly language stubs in module srt.s are executed by the processor in response to receiving a trap or interrupt that selects the corresponding IDT entry.

Listing 12:
/* [excerpted from i386.c] */
 ...
init386() {
 ...
   outb(0xf1,0);      /* clear coprocessor to cover all bases */

   /* initialize 8259 ICU's in preperation for device interrupts */

   outb(ICU1,0x11);   /* reset the unit */
   outb(ICU1+1,32);   /* start with idt 32 */
   outb(ICU1+1,4);    /* master please */
   outb(ICU1+1,1);
   outb(ICU1+1,0xff); /* all disabled */

   outb(ICU2,0x11);
   outb(ICU2+1,40);   /* start with idt 40 */
   outb(ICU2+1,2);    /* just a slave */
   outb(ICU2+1,1);
   outb(ICU2+1,0xff); /* all disabled */

   /* initialize 8253 timer on interrupt #0 */

   outb (0x43, 0x36);
   outb (0x40, 193182/60);
   outb (0x40, (193182/60)/256);
}
test386(){
 ...
   /* test interrupts for a while */
   printf("inton"); getchar();
   outb(ICU1+1,0);   /* unmask all interrupts */
   outb(ICU2+1,0);
   inton();

   timeout = 0x8000000;
   do nothing(); while (timeout-- );

   intoff();
 ...

# excerpted from srt.s
 ...
#define   INTR(a) \
   pushal ; \
   push %ds ; \
   push %es ; \
   movw $0x10, %ax ; \
   movw %ax, %ds ; \
   movw %ax,%es ; \
   pushl $##a ; \
   call _intr ; \
   pop %eax ; \
   pop %es ; \
   pop %ds ; \
   popal ; \
   iret

   /* hardware 32 - 47 */
IDTVEC(intr0)
   INTR(0)
IDTVEC(intr1)
   INTR(1)
IDTVEC(intr2)
   INTR(2)
IDTVEC(intr3)
   INTR(3)
IDTVEC(intr4)
   INTR(4)
IDTVEC(intr5)
   INTR(5)
IDTVEC(intr6)
   INTR(6)
IDTVEC(intr7)
   INTR(7)
IDTVEC(intr8)
   INTR(8)
IDTVEC(intr9)
   INTR(9)
IDTVEC(intr10)
   INTR(10)
IDTVEC(intr11)
   INTR(11)
IDTVEC(intr12)
   INTR(12)
IDTVEC(intr13)
   INTR(13)
IDTVEC(intr14)
   INTR(14)
IDTVEC(intr15)
   INTR(15)

   .globl   _inton
_inton:
   sti
   ret

   .globl   _intoff
_intoff:
   cli
   ret
 ...

/* back to i386.c */
 ...
/* Interrupt vector processing code  */
intr(ivec) {
   static clk;
   int omsk1, omsk2;

   /* mask off interrupt being serviced, save old mask */
   if (ivec > 7) {
      omsk2 = inb(ICU2+1);
      outb(ICU2+1, 1<<(ivec-8));
   } else {
      omsk1 = inb(ICU1+1);
      outb(ICU1+1, 1<<ivec);
   }

   /* re-enable processor's interrupts, allowing others in */
   inton();

   /* if we are the clock, count clock tick */
   if (ivec == 0) clk++;
   /* if we are the keyboard, show data incoming */
   else if (ivec == 1) printf("kbd data %x, clk %d\n", inb(0x60), clk);
   /* otherwise print message stating source and time */
   else {
      printf("intr %d, clk %d \n", ivec, clk);
      getchar();
   }

   /* turn off interrupts, re-enable old mask, do interrupt acknowledge */
   intoff();
   if (ivec > 7) {
      outb(ICU2+1,omsk2);
      outb(ICU2,0x20);
   } else
      outb(ICU1+1,omsk1);
   outb(ICU1,0x20);
   /* return to interrupt stub */
}
 ...
These stubs are the minimal glue that index each kind of trap with a manifest constant. This constant is always of the form T_XXXX and is obtained from the file trap.h (see Listing Fourteen). Some traps on the 386 also place an error code word on the stack, in order to transfer additional information about the cause of the trap. Because we need to ultimately remove this error code before returning from the trap, we first make all traps appear alike by pushing a dummy error code of zero on the stack for traps without error codes. Then, the common code that returns after the trap has only to remove both the trap constant and error code, regardless of which trap occurred.

After saving the processor state, all trap stubs call common code, which calls the C language trap handler; they also have code following, which restores the state and returns to whatever code was running when the trap occurred. The C language handler merely notifies us of the processor state and exception type and then returns. Since our test function will test different traps in a sequence, we prefer to bypass faults that don't move the program counter. We do this by manually incrementing the program counter, knowing that all faults we intend to encounter happen to be 1 byte in size. Obviously, this convenience doesn't hold for the BSD kernel, but it is satisfactory for the moment.




<<BACK NEXT >>



Copyright 1989, 1990, 2006 TeleMuse Partners, William Jolitz and Lynne Jolitz