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

Porting Unix to the 386: A Practical Approach



William & Lynne Jolitz


We coded specialized trap handlers for interrupt traps, as these were initimate with the systems interrupt control hardware, not part of the processor.




Interrupt Handling
Interrupts on the 386 are a kind of trap that function much like the exceptions we discussed. In Listing Twelve, the AT's interrupt control units and interrupt timer are initialized to allow hardware interrupts to be signaled from AT devices, such as timer to processor. As a minor point, we clear the coprocessor's exception interrupt to avoid spotting a possibly spurious interrupt from some preexisting condition formed in MS-DOS mode prior to running the standalone program.

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 */
}
 ...
Next, our interrupt test enables the processor and interrupt control unit for a brief period of time, allowing hardware interrupts to be processed by the 386. Any interrupts occurring during that interval will cause the 386 to extract the appropriate IDT entry from the table and cause one of the assembly stubs in Listing Twelve to be executed. These stubs, like the trap stubs, save the state, record the interrupt index on the stack, and call the common C function intr().

In intr(), the present interrupt is masked off and the interrupts are then reenabled so other interrupts can be active while the received interrupt is processed. Note that both the stubs and this function are fully reentrant, as this is not an uncommon occurrence. The example code also provides some trivial interrupt actions for our timer, keyboard, and any other device that generates an interrupt. At this point, we dispatch to a specific device driver's interrupt routine.

After responding to the interrupt, we restore our old mask in an uninterruptable "critical section," and signal the interrupt control unit that this interrupt is to be acknowledged as "finished." Our interrupt stub then unwinds the stack, restores the state as needed, and returns us to the exact state we were in prior to processing the interrupt signaled to the 386.


<<BACK NEXT >>



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