Goto sanos source index

//
// trap.c
//
// Interrupt and trap handling
//
// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 
// 1. Redistributions of source code must retain the above copyright 
//    notice, this list of conditions and the following disclaimer.  
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.  
// 3. Neither the name of the project nor the names of its contributors
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission. 
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
// SUCH DAMAGE.
// 

#include <os/krnl.h>

#define INTRS MAXIDT

struct syspage *syspage = (struct syspage *) SYSPAGE_ADDRESS;
struct interrupt *intrhndlr[INTRS];
int intrcount[INTRS];

static struct interrupt divintr;
static struct interrupt brkptintr;
static struct interrupt overflowintr;
static struct interrupt boundsintr;
static struct interrupt illopintr;
static struct interrupt segintr;
static struct interrupt stackintr;
static struct interrupt genprointr;
static struct interrupt pgfintr;
static struct interrupt alignintr;

static struct interrupt sigexitintr;

char *trapnames[INTRS] =  {
  "Divide error",
  "Debug exception",
  "Non-maskable interrupt",
  "Breakpoint",
  "Overflow",
  "Bounds check",
  "Invalid opcode",
  "FPU not available",
  "Double fault",
  "FPU segment overrun",
  "Invalid task state segment",
  "Segment not present",
  "Stack fault",
  "General protection fault",
  "Page fault",
  "(reserved)",
  "FPU error",
  "Alignment check",
  "Machine check",
  "SIMD FP exception",
  "(reserved)",
  "(reserved)",
  "(reserved)",
  "(reserved)",
  "(reserved)",
  "(reserved)",
  "(reserved)",
  "(reserved)",
  "(reserved)",
  "(reserved)",
  "(reserved)",
  "(reserved)",

  "IRQ0 (timer)",
  "IRQ1 (keyboard)",
  "IRQ2",
  "IRQ3 (com2)",
  "IRQ4 (com1)",
  "IRQ5",
  "IRQ6 (fdc)",
  "IRQ7 (par)",
  "IRQ8 (rtc)",
  "IRQ9",
  "IRQ10",
  "IRQ11",
  "IRQ12",
  "IRQ13",
  "IRQ14 (hdc1)",
  "IRQ15 (hdc2)",

  "System call",
  "Signal exit",
  "(unused)",
  "(unused)",
  "(unused)",
  "(unused)",
  "(unused)",
  "(unused)",
  "(unused)",
  "(unused)",
  "(unused)",
  "(unused)",
  "(unused)",
  "(unused)",
  "(unused)",
  "(unused)"
};

//
// trap
//
// Common entry point for all interrupt service routines
//

static void trap(unsigned long args);

static void __declspec(naked) isr() {
  __asm {
    cld
    push    eax                     // Save registers
    push    ecx
    push    edx
    push    ebx
    push    ebp
    push    esi
    push    edi
    push    ds
    push    es
    mov     ax, SEL_KDATA           // Setup kernel data segment
#ifdef VMACH
    or      eax, cs:mach.kring
#endif
    mov     ds, ax
    mov     es, ax
    call    trap                    // Call trap handler
    pop     es                      // Restore registers
    pop     ds
    pop     edi
    pop     esi
    pop     ebp
    pop     ebx
    pop     edx
    pop     ecx
    pop     eax
    add     esp, 8
    IRETD                           // Return
  }
}

//
// syscall
//
// Kernel entry point for int 48 syscall
//

int syscall(int syscallno, char *params, struct context *ctxt);

static void __declspec(naked) systrap(void) {
  __asm {
    push    0                       // Push dummy errcode
    push    INTR_SYSCALL            // Push traptype

    push    eax                     // Save registers
    push    ecx
    push    edx
    push    ebx
    push    ebp
    push    esi
    push    edi
    push    ds
    push    es

    mov     bx, SEL_KDATA           // Setup kernel data segment
#ifdef VMACH
    or      ebx, cs:mach.kring
#endif
    mov     ds, bx
    mov     es, bx

    mov     ebx, esp                // ebx = context
    push    ebx                     // Push context
    push    edx                     // Push params
    push    eax                     // Push syscallno

    call    syscall                 // Call syscall
    add     esp, 12

    pop     es                      // Restore registers
    pop     ds
    pop     edi
    pop     esi
    pop     ebp
    pop     ebx
    pop     edx
    pop     ecx

    add     esp, 12                 // Skip eax, errcode, and traptype

    IRETD                           // Return
  }
}

//
// sysentry
//
// Kernel entry point for sysenter syscall
//

static void __declspec(naked) sysentry(void) {
  __asm {
    mov     esp, ss:[esp]           // Get kernel stack pointer from TSS
    STI                             // Sysenter disables interrupts, re-enable now

    push    SEL_UDATA + SEL_RPL3    // Push ss (fixed)
    push    ebp                     // Push esp (ebp set to esp by caller)
    pushfd                          // Push eflg
    push    SEL_UTEXT + SEL_RPL3    // Push cs (fixed)
    push    ecx                     // Push eip (return address set by caller)
    push    0                       // Push errcode
    push    INTR_SYSENTER           // Push traptype (always sysenter)

    push    eax                     // Push registers
    push    ecx
    push    edx
    push    ebx
    push    ebp
    push    esi
    push    edi

    push    SEL_UDATA + SEL_RPL3    // Push ds (fixed)
    push    SEL_UDATA + SEL_RPL3    // Push es (fixed)

    mov     bx, SEL_KDATA           // Setup kernel data segment
#ifdef VMACH
    or      ebx, cs:mach.kring
#endif
    mov     ds, bx
    mov     es, bx

    mov     ebx, esp                // ebx = context
    push    ebx                     // Push context
    push    edx                     // Push params
    push    eax                     // Push syscallno

    call    syscall                 // Call syscall
    add     esp, 12

    pop     es                      // Restore registers
    pop     ds
    pop     edi
    pop     esi
    pop     ebp
    pop     ebx

    add     esp, 20                 // Skip edx, ecx, eax, errcode, traptype
    pop     edx                     // Put eip into edx for sysexit
    add     esp, 4                  // Skip cs
    popfd                           // Restore flags
    pop     ecx                     // Put esp into ecx for sysexit
    add     esp, 4                  // Skip ss

    SYSEXIT                         // Return
  }
}

//
// Generate interrupt service routines
//

#define ISR(n)                                \
static void __declspec(naked) isr##n(void) {  \
  __asm { push 0 }                            \
  __asm { push n }                            \
  __asm { jmp isr }                           \
}

#define ISRE(n)                               \
static void __declspec(naked) isr##n(void) {  \
  __asm { push n }                            \
  __asm { jmp isr }                           \
}

ISR(0)  ISR(1)  ISR(2)   ISR(3)   ISR(4)   ISR(5)   ISR(6)   ISR(7)
ISRE(8) ISR(9)  ISRE(10) ISRE(11) ISRE(12) ISRE(13) ISRE(14) ISR(15)
ISR(16) ISR(17) ISR(18)  ISR(19)  ISR(20)  ISR(21)  ISR(22)  ISR(23)
ISR(24) ISR(25) ISR(26)  ISR(27)  ISR(28)  ISR(29)  ISR(30)  ISR(31)
ISR(32) ISR(33) ISR(34)  ISR(35)  ISR(36)  ISR(37)  ISR(38)  ISR(39)
ISR(40) ISR(41) ISR(42)  ISR(43)  ISR(44)  ISR(45)  ISR(46)  ISR(47)
        ISR(49) ISR(50)  ISR(51)  ISR(52)  ISR(53)  ISR(54)  ISR(55)
ISR(56) ISR(57) ISR(58)  ISR(59)  ISR(60)  ISR(61)  ISR(62)  ISR(63)

//
// usermode
//
// Tests if context is a user mode context
//

__inline static int usermode(struct context *ctxt) {
  return USERSPACE(ctxt->eip);
}

//
// setup_signal_frame
//
// Setup a call frame for invoking the global signal handler
//

static struct siginfo *setup_signal_frame(unsigned long *esp) {
  struct context *ctxt;
  struct siginfo *info;

  // Push context onto stack
  *esp -= sizeof(struct context);
  ctxt = (struct context *) *esp;

  // Push siginfo onto stack
  *esp -= sizeof(struct siginfo);
  info = (struct siginfo *) *esp;
  info->si_ctxt = ctxt;

  // Push pointer to signal info to stack
  *esp -= sizeof(struct siginfo *);
  *((struct siginfo **) *esp) = info;

  // Push dummy return address to stack
  *esp -= sizeof(void *);
  *((void **) *esp) = NULL;

  return info;
}

//
// send_signal
//
// Setup user stack to execute global signal handler
//

void send_signal(struct context *ctxt, int signum, void *addr) {
  unsigned long esp;
  struct siginfo *info;

  // Check for global handler
  if (!peb || !peb->globalhandler) {
    //panic("no global signal handler");
    dbg_enter(ctxt, addr);
  }

  // Get copy of user stack pointer 
  esp = ctxt->esp;

  // Create a signal call frame
  info = setup_signal_frame(&esp);

  // Initialize signal info
  info->si_signo = signum;
  info->si_code = ctxt->traptype;
  memcpy(info->si_ctxt, ctxt, sizeof(struct context));
  info->si_addr = addr;

  // Set new stack pointer in user context
  ctxt->esp = esp;

  // Set instruction pointer to global signal handler routine
  ctxt->eip = (unsigned long) peb->globalhandler;
}

//
// send_user_signal
//
// Queue a signal for execution on thread. The signal will be delivered
// by different mechanisms based on the thread state:
//
// THREAD_STATE_INITIALIZED:
//   No signals can be delivered while the thread is initializing.
//
// THREAD_STATE_READY:
//   The pending signals are delivered when the thread is scheduled
//   to run again.
//
// THREAD_STATE_RUNNING:
//   The thread (self) is running. Pending signals will be handled before
//   kernel exit.
//
// THREAD_STATE_WAITING:
//   Interrupt thread if it is in an alertable wait.
//
// THREAD_STATE_TERMINATED:
//   Cannot deliver signals to a terminated thread.
//
// THREAD_STATE_SUSPENDED:
//   Signals cannot awaken suspended threads. They remain pending until the 
//   thread resumes execution.
//
// THREAD_STATE_TRANSITION:
//   Signals are never sent in this intermediate state.
//

int send_user_signal(struct thread *t, int signum) {
  // Signal can only be sent to user threads
  if (!t->tib) return -EPERM;

  // Add signal to the pending signal mask for thread
  t->pending_signals |= (1 << signum);

  // Resume thread if signal is SIGCONT
  if (signum == SIGCONT) resume_thread(t);

  // If not signals are ready for delivery just return
  if (!signals_ready(t)) return 0;

  // Interrupt thread if it is in an alertable wait
  if (t->state == THREAD_STATE_WAITING) interrupt_thread(t);

  return 0;
}

//
// set_signal_mask
//
// Examine and change blocked signals
//

int set_signal_mask(int how, sigset_t *set, sigset_t *oldset) {
  struct thread *t = self();

  if (oldset) *oldset = t->blocked_signals;

  if (set) {
    switch (how) {
      case SIG_BLOCK:
        t->blocked_signals |= *set;
        break;

      case SIG_UNBLOCK:
        t->blocked_signals &= ~*set;
        break;

      case SIG_SETMASK:
        t->blocked_signals = *set;
        break;

      default:
        return -EINVAL;
    }

    if (signals_ready(t) && t->state == THREAD_STATE_WAITING) interrupt_thread(t);
  }

  return 0;
}

//
// get_pending_signals
//
// Examine pending signals
//

int get_pending_signals(sigset_t *set) {
  struct thread *t = self();

  if (!set) return -EINVAL;
  *set = t->pending_signals;

  return 0;
}

//
// deliver_pending_signals
//
// Deliver unblocked pending signals for current thread
//

int deliver_pending_signals(int retcode) {
  struct thread *t = self();
  struct context *ctxt;
  int sigmask;
  int signum;
  struct siginfo *info;
  unsigned long esp;

  // Find highest priority unblocked pending signal 
  sigmask = signals_ready(t);
  if (sigmask == 0) return 0;
  signum = find_lowest_bit(sigmask);
  t->pending_signals &= ~(1 << signum);

  //
  // Now we must deliver the signal to the global signal handler. 
  //
  // We must build a context that can be used by the sigexit handler for resuming 
  // normal operation after the signal has been handled. This context is allocated
  // on the user stack for the thread together with the siginfo structure. A
  // stack frame for calling the global signal handler with the siginfo structure
  // as argument is put on the stack. The instruction pointer (eip) and stack
  // pointer (esp) are adjusted such that when the thread returns from the kernel
  // the global signal handler is called with the siginfo structure.
  //
  // When the global handler has handled the signal it calls sigexit() with the
  // siginfo structure. The sigexit handler uses the context in the siginfo
  // structure to resume normal operation.
  //

  // Get call context
  ctxt = t->ctxt;

  // Setup signal frame
  esp = ctxt->esp;
  info = setup_signal_frame(&esp);

  // Setup signal info
  info->si_signo = signum;
  info->si_code = 0;
  memcpy(info->si_ctxt, ctxt, sizeof(struct context));
  info->si_addr = NULL;

  // Setup return code in eax of context
  if (ctxt->traptype == INTR_SYSENTER || ctxt->traptype == INTR_SYSCALL) {
    if (retcode < 0) {
      info->si_ctxt->errcode = retcode;
      info->si_ctxt->eax = -1;
    } else{
      info->si_ctxt->eax = retcode;
    }
  }

  // Set new stack pointer in user context
  ctxt->esp = esp;

  // Set instruction pointer to global signal handler routine
  ctxt->eip = (unsigned long) peb->globalhandler;

  return 0;
}

//
// sigexit_handler
//
// Signal exit handler
//

static int sigexit_handler(struct context *ctxt, void *arg) {
  struct thread *t = self();
  struct siginfo *info;
  int debug;

  // Get parameters from sysexit() call
  debug = ctxt->eax;
  info = (struct siginfo *) ctxt->ebx;

  // Restore processor context
  //memcpy(ctxt, info->si_ctxt, sizeof(struct context));
  set_context(t, info->si_ctxt);

  // Do not allow interrupts to be disabled
  t->ctxt->eflags |= EFLAG_IF;

  // Restore errno to errcode in context
  if (ctxt->traptype == INTR_SYSENTER || ctxt->traptype == INTR_SYSCALL) {
    struct tib *tib = t->tib;
    if (tib) tib->errnum = -((int) ctxt->errcode);
  }

  if (debug) dbg_enter(ctxt, info->si_addr);

  return 1;
}

//
// div_handler
//

static int div_handler(struct context *ctxt, void *arg) {
  if (usermode(ctxt)) {
    send_signal(ctxt, SIGFPE, (void *) ctxt->eip);
  } else {
    kprintf(KERN_CRIT "trap: division by zero in kernel mode\n");
    dbg_enter(ctxt, 0);
  }

  return 0;
}

//
// brkpt_handler
//

static int breakpoint_handler(struct context *ctxt, void *arg) {
  if (usermode(ctxt)) {
    send_signal(ctxt, SIGTRAP, (void *) ctxt->eip);
  } else {
    kprintf(KERN_CRIT "trap: breakpoint in kernel mode\n");
    dbg_enter(ctxt, 0);
  }

  return 0;
}

//
// overflow_handler
//

static int overflow_handler(struct context *ctxt, void *arg) {
  if (usermode(ctxt)) {
    send_signal(ctxt, SIGSEGV, NULL);
  } else {
    kprintf(KERN_CRIT "trap: overflow exception in kernel mode\n");
    dbg_enter(ctxt, 0);
  }

  return 0;
}

//
// bounds_handler
//

static int bounds_handler(struct context *ctxt, void *arg)
{
  if (usermode(ctxt)) {
    send_signal(ctxt, SIGSEGV, NULL);
  } else {
    kprintf(KERN_CRIT "trap: bounds exception in kernel mode\n");
    dbg_enter(ctxt, 0);
  }

  return 0;
}

//
// illop_handler
//

static int illop_handler(struct context *ctxt, void *arg) {
  if (usermode(ctxt)) {
    send_signal(ctxt, SIGILL, (void *) ctxt->eip);
  } else {
    kprintf(KERN_CRIT "trap: illegal instruction exception in kernel mode\n");
    dbg_enter(ctxt, 0);
  }

  return 0;
}

//
// seg_handler
//

static int seg_handler(struct context *ctxt, void *arg)
{
  if (usermode(ctxt)) {
    send_signal(ctxt, SIGBUS, NULL);
  } else {
    kprintf(KERN_CRIT "trap: sement not present exception in kernel mode\n");
    dbg_enter(ctxt, 0);
  }

  return 0;
}

//
// stack_handler
//

static int stack_handler(struct context *ctxt, void *arg) {
  if (usermode(ctxt)) {
    send_signal(ctxt, SIGBUS, NULL);
  } else {
    kprintf(KERN_CRIT "trap: stack segment exception in kernel mode\n");
    dbg_enter(ctxt, 0);
  }

  return 0;
}

//
// genpro_handler
//

static int genpro_handler(struct context *ctxt, void *arg) {
  if (usermode(ctxt)) {
    send_signal(ctxt, SIGSEGV, NULL);
  } else {
    kprintf(KERN_CRIT "trap: general protection exception in kernel mode\n");
    dbg_enter(ctxt, 0);
  }

  return 0;
}

//
// pagefault_handler
//

static int pagefault_handler(struct context *ctxt, void *arg) {
  void *addr;
  void *pageaddr;

  addr = (void *) get_cr2();
  pageaddr = (void *) PAGEADDR(addr);

  if (usermode(ctxt)) {
    int signal = SIGSEGV;
    if (page_directory_mapped(pageaddr)) {
      pte_t flags = get_page_flags(pageaddr);
      if (flags & PT_GUARD) {
        unguard_page(pageaddr);
        signal = SIGSTKFLT;
        if (guard_page_handler(pageaddr) == 0) signal = 0;
      } else if (flags & PT_FILE) {
        if ((flags & PT_PRESENT) == 0) {
          sti();
          if (fetch_page(pageaddr) == 0) signal = 0;
        }
      }
    }
    if (signal != 0) send_signal(ctxt, signal, addr);
  } else {
    kprintf(KERN_CRIT "trap: page fault in kernel mode\n");
    dbg_enter(ctxt, addr);
  }

  return 0;
}

//
// alignment_handler
//

static int alignment_handler(struct context *ctxt, void *arg) {
  if (usermode(ctxt)) {
    send_signal(ctxt, SIGBUS, (void *) get_cr2());
  } else {
    kprintf(KERN_CRIT "trap: alignment exception in kernel mode\n");
    dbg_enter(ctxt, 0);
  }

  return 0;
}

//
// traps_proc
//
// Performance counters for traps
//

static int traps_proc(struct proc_file *pf, void *arg) {
  int i;

  pprintf(pf, "no trap                                  count\n");
  pprintf(pf, "-- -------------------------------- ----------\n");

  for (i = 0; i < INTRS; i++) {
    if (intrcount[i] != 0) {
      pprintf(pf,"%2d %-32s %10d\n", i, trapnames[i], intrcount[i]);
    }
  }

  return 0;
}

//
// init_trap
//
// Initialize traps and interrupts
//

void init_trap() {
  int i;

  // Initialize interrupt dispatch table
  for (i = 0; i < INTRS; i++) {
    intrhndlr[i] = NULL;
    intrcount[i] = 0;
  }

  // Setup idt
  set_idt_gate(0, isr0);
  set_idt_gate(1, isr1);
  set_idt_gate(2, isr2);
  set_idt_trap(3, isr3);
  set_idt_gate(4, isr4);
  set_idt_gate(5, isr5);
  set_idt_gate(6, isr6);
  set_idt_gate(7, isr7);
  set_idt_gate(8, isr8);
  set_idt_gate(9, isr9);
  set_idt_gate(10, isr10);
  set_idt_gate(11, isr11);
  set_idt_gate(12, isr12);
  set_idt_gate(13, isr13);
  set_idt_gate(14, isr14);
  set_idt_gate(15, isr15);
  set_idt_gate(16, isr16);
  set_idt_gate(17, isr17);
  set_idt_gate(18, isr18);
  set_idt_gate(19, isr19);
  set_idt_gate(20, isr20);
  set_idt_gate(21, isr21);
  set_idt_gate(22, isr22);
  set_idt_gate(23, isr23);
  set_idt_gate(24, isr24);
  set_idt_gate(25, isr25);
  set_idt_gate(26, isr26);
  set_idt_gate(27, isr27);
  set_idt_gate(28, isr28);
  set_idt_gate(29, isr29);
  set_idt_gate(30, isr30);
  set_idt_gate(31, isr31);
  set_idt_gate(32, isr32);
  set_idt_gate(33, isr33);
  set_idt_gate(34, isr34);
  set_idt_gate(35, isr35);
  set_idt_gate(36, isr36);
  set_idt_gate(37, isr37);
  set_idt_gate(38, isr38);
  set_idt_gate(39, isr39);
  set_idt_gate(40, isr40);
  set_idt_gate(41, isr41);
  set_idt_gate(42, isr42);
  set_idt_gate(43, isr43);
  set_idt_gate(44, isr44);
  set_idt_gate(45, isr45);
  set_idt_gate(46, isr46);
  set_idt_gate(47, isr47);
  set_idt_trap(48, systrap);
  set_idt_trap(49, isr49);
  set_idt_gate(50, isr50);
  set_idt_gate(51, isr51);
  set_idt_gate(52, isr52);
  set_idt_gate(53, isr53);
  set_idt_gate(54, isr54);
  set_idt_gate(55, isr55);
  set_idt_gate(56, isr56);
  set_idt_gate(57, isr57);
  set_idt_gate(58, isr58);
  set_idt_gate(59, isr59);
  set_idt_gate(60, isr60);
  set_idt_gate(61, isr61);
  set_idt_gate(62, isr62);
  set_idt_gate(63, isr63);

  // Set system trap handlers
  register_interrupt(&divintr, INTR_DIV, div_handler, NULL);
  register_interrupt(&brkptintr, INTR_BPT, breakpoint_handler, NULL);
  register_interrupt(&overflowintr, INTR_OVFL, overflow_handler, NULL);
  register_interrupt(&boundsintr, INTR_BOUND, bounds_handler, NULL);
  register_interrupt(&illopintr, INTR_INSTR, illop_handler, NULL);
  register_interrupt(&segintr, INTR_SEG, seg_handler, NULL);
  register_interrupt(&stackintr, INTR_STACK, stack_handler, NULL);
  register_interrupt(&genprointr, INTR_GENPRO, genpro_handler, NULL);
  register_interrupt(&pgfintr, INTR_PGFLT, pagefault_handler, NULL);
  register_interrupt(&alignintr, INTR_ALIGN, alignment_handler, NULL);
  register_interrupt(&sigexitintr, INTR_SIGEXIT, sigexit_handler, NULL);

  // Initialize fast syscall
  if (cpu.features & CPU_FEATURE_SEP) {
    wrmsr(MSR_SYSENTER_CS, SEL_KTEXT | mach.kring, 0);
    wrmsr(MSR_SYSENTER_ESP, TSS_ESP0, 0);
    wrmsr(MSR_SYSENTER_EIP, (unsigned long) sysentry, 0);
  }

  // Register /proc/traps
  register_proc_inode("traps", traps_proc, NULL);
}

//
// trap
//
// Trap dispatcher
//

static void trap(unsigned long args) {
  struct context *ctxt = (struct context *) &args;
  struct thread *t = self();
  struct context *prevctxt;
  struct interrupt *intr;
  int rc;

  // Save context
  prevctxt = t->ctxt;
  t->ctxt = ctxt;

  // Statistics
  intrcount[ctxt->traptype]++;

  // Call interrupt handlers
  intr = intrhndlr[ctxt->traptype];
  while (intr) {
    rc = intr->handler(ctxt, intr->arg);
    if (rc > 0) break;
    intr = intr->next;
  }

  // If we interrupted a user mode context, dispatch DPCs,
  // check for quantum expiry, and deliver signals.
  if (usermode(ctxt)) {
    check_dpc_queue();
    check_preempt();
    if (signals_ready(t)) deliver_pending_signals(0);
  }

  // Restore context
  t->ctxt = prevctxt;
}

//
// register_interrupt
//
// Register interrupt handler for interrupt
//

void register_interrupt(struct interrupt *intr, int intrno, intrproc_t f, void *arg) {
  intr->handler = f;
  intr->arg = arg;
  intr->flags = 0;
  intr->next = intrhndlr[intrno];
  intrhndlr[intrno] = intr;
}

//
// unregister_interrupt
//
// Remove interrupt handler for interrupt
//

void unregister_interrupt(struct interrupt *intr, int intrno) {
  struct interrupt *i;

  if (intrhndlr[intrno] == intr) {
    intrhndlr[intrno] = intr->next;
  } else {
    for (i = intrhndlr[intrno]; i != NULL; i = i->next) {
      if (i->next == intr) {
        i->next = intr->next;
        break;
      }
    }
  }
  intr->next = NULL;
}

//
// get_context
//
// Retrieves the processor context for a thread
//

int get_context(struct thread *t, struct context *ctxt) {
  if (!ctxt) return -EINVAL;
  memcpy(ctxt, t->ctxt, sizeof(struct context));
  return 0;
}

//
// set_context
//
// Sets the processor context for a thread
//

int set_context(struct thread *t, struct context *ctxt) {
  if (!ctxt) return -EINVAL;

  t->ctxt->esp = ctxt->esp;
  t->ctxt->eip = ctxt->eip;
  t->ctxt->eflags = ctxt->eflags;
  t->ctxt->ebp = ctxt->ebp;

  t->ctxt->eax = ctxt->eax;
  t->ctxt->ebx = ctxt->ebx;
  t->ctxt->ecx = ctxt->ecx;
  t->ctxt->edx = ctxt->edx;
  t->ctxt->esi = ctxt->esi;
  t->ctxt->edi = ctxt->edi;

  return 0;
}