1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
//! The Interrupt Table and Isr (Interrupt Service Routine) classes.

use core::mem::transmute;
use core::ptr::RawPtr;

use cpu::DtReg;
use cpu::exception::Fault;
use cpu::idt::{IdtEntry, IdtReg, INTR_GATE, PRESENT};
use platform::drivers::pic;
use kernel::heap;

// TODO for Rust: nested C-like enums
// #[repr(u8)]
pub enum Int {
    Fault(Fault)
}

pub struct Table {
    reg: &'static IdtReg,
    table: *mut IdtEntry,
    mask: u16,
}

impl Table {
    pub fn new() -> Table {
        unsafe {
            let table = heap::zero_alloc::<IdtEntry>(256);
            let reg = heap::alloc::<IdtReg>(1);
            *(reg as *mut IdtReg) = DtReg::new(table, 256);
            Table {
                reg: transmute(reg),
                table: table,
                mask: 0xffff
            }
        }
    }

    pub unsafe fn enable_maskable(&mut self, irq: uint, isr: unsafe extern "C" fn()) {
        *self.table.offset(irq as int) = IdtEntry::new(
            isr,                // interrupt service routine
            1 << 3,             // segment selector
            INTR_GATE | PRESENT // flags
        );

        self.mask &= !(1u16 << (irq & 0b1111));
        pic::mask(self.mask);
    }

    #[allow(visible_private_types)]
    pub unsafe fn set_isr(&mut self, val: Fault, code: bool, handler: unsafe extern "C" fn()) {
        *self.table.offset(val as int) = Isr::new(Fault(val), code).idt_entry(handler);
    }

    pub fn load(&self) {
        self.reg.load();
        pic::remap();
        pic::mask(self.mask);
        enable();
    }
}

fn enable() {
    unsafe {
        asm!("sti" :::: "volatile", "intel");
    }
}

/// An exception is generated by the CPU to indicate incorrect code behavior[[1]].
/// To distinguish exceptions, every exception needs a different entry point[[2]].
/// For example:
/// ```asm
/// isr8_double_fault:
///   push eax ; (smaller than push 0) or replaced by nop
///   push byte 8
///   jmp isr_common
/// ```
/// Since inline assembly has limitations, we build these entries dynamically.
/// The CPU doesn't push error code on the stack when calling some exceptions.
/// In these cases, we push a dummy value to align the stack[[3]].
///
/// 1. [Exceptions - OSDev Wiki][[1]]
/// 2. [piipkernel][[2]]
/// 3. Dru Nelson. [Single Byte or Small x86 Opcodes][[3]]
/// [1]: http://wiki.osdev.org/Exceptions "Exceptions - OSDev Wiki"
/// [2]: http://www.srcf.ucam.org/piipkernel/git_repository/kernel/src/isr.asm
/// [3]: http://www.xxeo.com/single-byte-or-small-x86-opcodes
#[packed]
pub struct Isr {
    push_dummy: u8, // push eax  // (only for exceptions without error codes)
    push: u8,       // push byte <imm>  // save int. number
    value: Int,
    jmp: u8,        // jmp rel  // jump to the common handler
    rel: i32
}

impl Isr {
    // TODO: drop for Isr
    pub fn new<'a>(val: Int, code: bool) -> &'a mut Isr {
        let this: &mut Isr = unsafe { transmute(heap::alloc::<Isr>(1)) };
        *this = Isr {
            push_dummy: if code { 0x90 } else { 0x50 },   // [3]
            push: 0x6a, value: val,
            jmp: 0xe9, rel: -5
        };
        this
    }

    pub unsafe fn idt_entry(&mut self, handler: unsafe extern "C" fn()) -> IdtEntry {
        self.rel = handler as i32 - (self as *mut Isr).offset(1) as i32;
        IdtEntry::new(transmute(self), 1 << 3, INTR_GATE | PRESENT)
    }
}