next up previous contents
Next: 4.2 Timers Up: 4.1.9 Events for ix86 Previous: 4.1.9 Events for ix86

4.1.9.1 Interrupt delivering

When an interrupt occurs, this routine is called with interrupts cleared. It receives the interrupt number and a pointer to the saved used state. Although such state looks like a trap frame only the saved segment registers and the register state frame are valid.

Delivering simply calls raise in the interrupt table for the current processor. Note that the interrupt has already been acknowledged by the assembly stub.

We always deliver an interrupt, no matter if a previous one has been already processed or not. Interrupt handlers should protect themselves against pending interrupts: portal serviced interrupts can do so by limiting the number of available stacks to just one; procedure serviced interrupts should use a ``nested interrupt'' counter to deal with missed interrupts. Delivering code offers a small protection in that interrupts will be disabled until the message has been sent (or the procedure called). Interrupts are enabled again as soon the machine dependent interrupt handler returns.

Clock interrupt handling is a little bit special, because we do not want it to travel the common interrupt delivering path. To avoid that, it is handled apart by the handler. A external routine clock_handler will be called without arguments. Such routine should be provided by whoever is in charge of handling timers.

<off_mdep_intr_handler implementation. >= (U->)
// Handles an interrupt. 
//
volatile void off_mdep_intr_handler(unsigned irq, struct trap_state *ts)
{
  extern void off_clock_handler(void);
  // < <Nest user state (w/ ts) here. > > XXX

  if (irq == OFF_MDEP_CLOCK_IRQ)
    off_clock_handler();
  else
    off_Processor::self()->get_irq(irq)->raise();
  

  // < <Unwind user state. > >
}

Where, on Intels, MDEP_CLOCK_IRQ is

<Off machine dependent interrupt numbers. >+= (U->) [<-D]
const natural_t OFF_MDEP_CLOCK_IRQ=0; // Timer interrupt.

This function must have access to Shtl::set_uctx and Shtl::set_kctx.

<Friend declaration for upcall and system call procedures. >=
friend void off_mdep_intr_handler(...); // For ev.nw trap & irq handling

<Off machine dependent event table implementation dependencies. >+= (U->) [<-D->]
#include <hw/Processor.h>       // for off_Processor

That handler is called by a small assembly glue code. First, we will initialize in the base IDT base_idt new entries for every PC hardware interrupt. Each descriptor jumps to an interrupt entry point as defined in off_mdep_intr_inittab (see below). After that, the master en slave PICs are initialized vectoring to the entries installed in the IDT. Thus we are able now to receive hardware interrupts and they will lead to our interrupt routine being invoked, though they were initially disabled in the PIC (using pic_disable_all) to avoid undesired interrupt receipts before their handlers are actually ready and initialized. This is done when the interrupt table is instantiated.

The idt is used both to vector traps and interrupts. First 32 (0x0 to 0x1f) of a total number of 256 entries will be used for Intel processor traps. Next 16 entries (0x20 to 0x2f) entries will be used for PC hardware interrupts. Remaining ones can be defined later on by the user --but for the one used for system calls, 0x30, and returns from upcalls, 0x31.

<Machine dependent interrupt hardware initialization. >= (<-U)
gate_init(base_idt, off_mdep_intr_inittab, KERNEL_CS);
pic_init(PICM_VECTBASE, PICS_VECTBASE);
pic_disable_all();

<Off machine dependent event table implementation dependencies. >+= (U->) [<-D->]
#include <flux/machine/base_gdt.h>   // for KERNEL_CS
#include <flux/machine/base_idt.h>   // for base_idt
#include <flux/machine/gate_init.h>  // for gate_init
#include <hw/mdep/intr.h>            // for off_mdep_intr_inittab

The interrupt table off_mdep_intr_inittab is maintained in a separate assembly file together with the real interrupt entry point.

<Off machine dependent interrupt inittab. >= (U-> U->)

extern struct gate_init_entry off_mdep_intr_inittab[];

We will need some includes for this code to work.

<Off hardware interrupts implementation dependencies. >= (U->)
#include <flux/machine/asm.h>
#include <flux/machine/gate_init.h>
#include <flux/machine/seg.h>

To make easy the task of interrupt entry point declaration, a macro is used to generate an interrupt entry point. Such entry point will push the interrupt number and pass control to allints. Interrupt gates will be placed on the idt at offset IDT_IRQ_BASE.

<Off hardware interrupts definitions. >= (U->)
#define INTERRUPT(irq)                  \
        GATE_ENTRY((irq) + IDT_IRQ_BASE, 0f, ACC_PL_K|ACC_INTR_GATE)    ;\
        P2ALIGN(TEXT_ALIGN)             ;\
0:                                      ;\
        cld                             ;\
        pushl   $0                      ;\
        pushl   $(irq)                  ;\
        pushal                          ;\
        movl    $(irq),%eax             ;\
        jmp     allints
Defines INTERRUPT (links are to index).

Where

<Off hardware IRQBASE. >= (U->)
#define IDT_IRQ_BASE 0x20

Now we can use the above macro and \oskit{} GATE_INITTAB_ macros to define a table of interrupt (and trap) entry points. Remember that allints must pop eax and execute an iret to return:

<Off hardware interrupt return. >= (U->)
        popal
        addl   $8,%esp
        iret

The entry point definitions are now really easy.

<Off hardware interrupt entry points. >= (U->)
GATE_INITTAB_BEGIN(off_mdep_intr_inittab)

INTERRUPT(0)
INTERRUPT(1)
INTERRUPT(2)
INTERRUPT(3)
INTERRUPT(4)
INTERRUPT(5)
INTERRUPT(6)
INTERRUPT(7)
INTERRUPT(8)
INTERRUPT(9)
INTERRUPT(10)
INTERRUPT(11)
INTERRUPT(12)
INTERRUPT(13)
INTERRUPT(14)
INTERRUPT(15)
GATE_INITTAB_END

Defines off_mdep_intr_inittab (links are to index).

The common interrupt handling routine is allints. This routine handles initial interrupt hardware details and then jumps to the C++ entry point (with C linkage). A global counter counting the number of nested interrupts is also managed here. We will switch from user to kernel segments on interrupt.

<Off hardware interrupt common handling routine. >= (U->)
        cli
        pushl   %ds                             # save segments
        pushl   %es
        pushl   %fs
        pushl   %gs
        movl    $0x18,%cx                       # set kernel segments
        movl    %cx,%ds
        movl    %cx,%es

#        incl    off_mdep_intr_count
        movl    $1,%edx                         # if this master or slave?
        movl    %eax,%ecx
        cmpl    $8,%eax
        jge     1f
        movb    $0x20,%al                       #   ack int 
        outb    %al,$0x20
        jmp     2f
1:                                              # slave PIC
        movb    $0x20,%al                       #   ack irq
        outb    %al,$0x20
        outb    %al,$0xa0
        addl    $8,%ecx                         #   adjust irq nb. 0th is 8th.
2:
        incl    off_mdep_intr_nest              # count this int
        pushl   %esp                            # and usr state ptr.
        pushl   %ecx                            # push int # 
        call    EXT(off_mdep_intr_handler)      # call interrupt handler
        popl    %ecx                            # clean handler arguments
        popl    %esp

2:
        cmpl    $1,off_mdep_intr_nest           # check for software intr
        jne     1f
        cmpl    $0,off_mdep_intr_pending
        je      1f
        movl    $0,off_mdep_intr_pending
        call    EXT(off_mdep_intr_handler)
        sti
1:
        decl    off_mdep_intr_nest
        popl    %gs                              # restore segments
        popl    %fs
        popl    %es                             
        popl    %ds
        <Off hardware interrupt return. >

        .data
        P2ALIGN(DATA_ALIGN)
        .globl off_mdep_intr_nest
off_mdep_intr_pending:
        .long   0
        .globl off_mdep_intr_pe
off_mdep_intr_nest:
        .long   0
#        .globl off_mdep_intr_count
#off_mdep_intr_count:
#        .long   0

Defines allints, off_mdep_intr_count, off_mdep_intr_nest, off_mdep_intr_pending (links are to index).

We have this counters available

<Off hardware interrupt counters. >= (U->)
extern volatile u_long off_mdep_intr_nest;
extern volatile u_long off_mdep_intr_pending;
//extern volatile u_long off_mdep_intr_count;

\subsubsection{Trap delivering}

The OSKit delivers traps to the base_trap_handler routine, if defined. We provide such routine just to notify the trap table of the current processor that a trap happened.

Such handler is installed by the machine dependent trap table at boot time.

<Other private members of off_TrapTbl. >+= (<-U) [<-D]
off_mdepTrapTbl mdep;           // Mach dep. trap table.

<Initialize other aggregate members of off_TrapTbl. >+= (<-U) [<-D]
mdep()

<Off machine dependent trap table. >= (U->)
// An ix86 trap table
//
class off_mdepTrapTbl {
public:
  // Creates an x86 trap table. 
  off_mdepTrapTbl(void);

  // Returns the id for the page fault trap. 
  static inline int get_pgflt(void);
};

extern inline int off_mdepTrapTbl::get_pgflt(void){ return T_PAGE_FAULT;}

Defines off_mdepTrapTbl (links are to index).

<Off machine dependent event table dependencies. >+= (U->) [<-D]
#include <flux/machine/trap.h>  // for T_PAGE_FAULT

<off_mdepTrapTbl::off_mdepTrapTbl implementation. >= (U->)
extern "C" {
  extern int off_mdep_trap_handler(struct trap_state *state);
};
off_mdepTrapTbl::off_mdepTrapTbl(void)
{
  base_trap_handler = off_mdep_trap_handler;
}

<Off machine dependent event table implementation dependencies. >+= (U->) [<-D]
#include <flux/machine/base_trap.h> // for  base_trap_handler

The machine dependent trap handler simply obtains a reference to the current processor and delivers the trap on its trap table. In those cases where extra information must be gathered from hardware to determine the trap reason (e.g. on page faults)

<off_mdep_trap_handler implementation. >= (U->)
// 
//
int off_mdep_trap_handler(struct trap_state *state)
{
  err_t err;
  // < <Nest user [[state]] (w/ ts) here. > > XXX

  err=off_Processor::self()->get_trap(state->trapno)->raise(state->err);

  // < <Unwind user [[state]]. > >
 
  return err;
}

Defines off_mdep_trap_handler (links are to index).

\subsubsection{Machine dependent Event \cpp{} source files}

We maintain machine dependent event table stuff in mdep/mEventTbl.h and mdep/mEventTbl.C.

<mdep mEventTbl.h*>=
<Read the literate code instead warning. >
#ifndef __OFF_MDEP_EV_TBL_H
#define __OFF_MDEP_EV_TBL_H 1

<Off machine dependent event table dependencies. >

<Off machine dependent interrupt table size. >
<Off machine dependent trap table size. >
<Off machine dependent interrupt numbers. >

#ifdef __KERNEL__
<Off machine dependent interrupt mask. >
<Off machine dependent interrupt table. >
<Off machine dependent trap table. >
#endif // __KERNEL__

#ifdef __KERNEL__
<off_mdepIntTbl::is_masked implementation. >
#endif // __KERNEL__

#endif // __OFF_MDEP_EV_TBL_H

<mdep mEventTbl.C*>=
<Read the literate code instead warning. >

#include <hw/mdep/mEventTbl.h>  // Exported interface
<Off machine dependent event table implementation dependencies. >
<Off machine dependent interrupt inittab. >

<off_mdepIntTbl::off_mdepIntTbl implementation. >
<off_mdepTrapTbl::off_mdepTrapTbl implementation. >
<off_mdepIntTbl::mask implementation. >
<off_mdepIntTbl::unmask implementation. >

extern "C" {
  <off_mdep_intr_handler implementation. >
  <off_mdep_trap_handler implementation. >
};

Finally, assembly interrupt glue code is kept in mdep/intr.h and mdep/intr.S.

<mdep intr.h*>=
#ifndef __MDEP_INTR_H
#define __MDEP_INTR_H 1

#ifndef ASSEMBLER
extern "C" {
<Off machine dependent interrupt inittab. >
#endif
#include <flux/machine/gate_init.h>
#ifndef ASSEMBLER

<Off hardware interrupt counters. >
#endif
<Off hardware IRQBASE. >

#ifndef ASSEMBLER
} // "C" linkage
#endif

#endif // __MDEP_INTR_H
Defines IDT_IRQ_BASE (links are to index).

The interrupt entry point code is adapted from the \oskit{} libfdev utility library. The layout of this file is as follows

<mdep intr.S*>=
<Oskit copyright notice. >
#include <hw/mdep/intr.h>       // Exported interface
<Off hardware interrupts implementation dependencies. >

        .text
<Off hardware interrupts definitions. >
<Off hardware interrupt entry points. >

        P2ALIGN(TEXT_ALIGN)
allints:
<Off hardware interrupt common handling routine. >

        P2ALIGN(TEXT_ALIGN)

As the code is taken almost literally from the \oskit{} their copyright notice is included:

<Oskit copyright notice. >= (<-U)
/*
 * Copyright (c) 1996 The University of Utah and
 * the Computer Systems Laboratory at the University of Utah (CSL).
 * All rights reserved.
 *
 * Permission to use, copy, modify and distribute this software is hereby
 * granted provided that (1) source code retains these copyright, permission,
 * and disclaimer notices, and (2) redistributions including binaries
 * reproduce the notices in supporting documentation, and (3) all advertising
 * materials mentioning features or use of this software display the following
 * acknowledgement: ``This product includes software developed by the
 * Computer Systems Laboratory at the University of Utah.''
 *
 * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS
 * IS" CONDITION.  THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF
 * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 *
 * CSL requests users of this software to return to csl-dist@cs.utah.edu any
 * improvements that they make and grant CSL redistribution rights.
 */

%% --------------------------------------------------------------


next up previous contents
Next: 4.2 Timers Up: 4.1.9 Events for ix86 Previous: 4.1.9 Events for ix86
Francisco J. Ballesteros
1998-05-25