Skip to content
2 changes: 2 additions & 0 deletions .gdbinit
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
file build/elsos-i686.bin
target remote localhost:1234
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
build
target
grub/grub.cfg
.gdb_history
1 change: 1 addition & 0 deletions src/arch/i686/boot.asm
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ section .text
global _start:function (_start.end - _start)
bits 32
_start:
cli
mov esp, stack_top
xor ebp, ebp

Expand Down
14 changes: 14 additions & 0 deletions src/arch/i686/instructions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use core::arch::asm;
use super::interrupts::idt;

#[inline(always)]
pub unsafe fn lidt(idtr: &idt::descriptor)
{
asm!("lidt [{}]", in(reg) idtr, options(nostack));
}

#[inline(always)]
pub unsafe fn sti()
{
asm!("sti");
}
66 changes: 66 additions & 0 deletions src/arch/i686/interrupts/exceptions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use super::State;

pub enum ExceptionClass
{
Trap, Fault, Abort, NA
}

pub struct Exception
{
pub message: &'static str,
pub class: ExceptionClass,
pub has_error: bool
}

pub static EXCEPTIONS: [Exception; 32] =
[
Exception {message: "Divide Error Exception", class: ExceptionClass::Fault, has_error: false},
Exception {message: "Debug Exception", class: ExceptionClass::Fault, has_error: false},
Exception {message: "NMI Interrupt", class: ExceptionClass::NA, has_error: false},
Exception {message: "Breakpoint Exception", class: ExceptionClass::Trap, has_error: false},
Exception {message: "Overflow Exception", class: ExceptionClass::Trap, has_error: false},
Exception {message: "Bound Range Exceeded Exception", class: ExceptionClass::Fault, has_error: false},
Exception {message: "Invalid Opcode Exception", class: ExceptionClass::Fault, has_error: false},
Exception {message: "Device Not Available Exception", class: ExceptionClass::Fault, has_error: false},
Exception {message: "Double Fault Exception", class: ExceptionClass::Abort, has_error: true},
Exception {message: "Coprocessor Segment Overrun", class: ExceptionClass::Abort, has_error: false},
Exception {message: "Invalid TSS Exception", class: ExceptionClass::Fault, has_error: true},
Exception {message: "Segment Not Present", class: ExceptionClass::Fault, has_error: true},
Exception {message: "Stack Fault Exception", class: ExceptionClass::Fault, has_error: true},
Exception {message: "General Protection Exception", class: ExceptionClass::Fault, has_error: true},
Exception {message: "Page Fault Exception", class: ExceptionClass::Fault, has_error: true},
Exception {message: "Reserved", class: ExceptionClass::NA, has_error: false},

Exception {message: "x87 Floating-Point Exception", class: ExceptionClass::Fault, has_error: false},
Exception {message: "Alignment Check Exception", class: ExceptionClass::Fault, has_error: true},
Exception {message: "Machine Check Exception", class: ExceptionClass::Abort, has_error: false},
Exception {message: "SIMD Floating-Point Exception", class: ExceptionClass::Fault, has_error: false},
Exception {message: "Virtualization Exception", class: ExceptionClass::Fault, has_error: false},
Exception {message: "Control Protection Exception", class: ExceptionClass::Fault, has_error: true},
Exception {message: "Reserved", class: ExceptionClass::NA, has_error: false},
Exception {message: "Reserved", class: ExceptionClass::NA, has_error: false},
Exception {message: "Reserved", class: ExceptionClass::NA, has_error: false},
Exception {message: "Reserved", class: ExceptionClass::NA, has_error: false},
Exception {message: "Reserved", class: ExceptionClass::NA, has_error: false},
Exception {message: "Reserved", class: ExceptionClass::NA, has_error: false},
Exception {message: "Hypervisor Injection Exception", class: ExceptionClass::Fault, has_error: false},
Exception {message: "VMM Communication Exception", class: ExceptionClass::Fault, has_error: true},
Exception {message: "Security Exception", class: ExceptionClass::Fault, has_error: true},
Exception {message: "Reserved", class: ExceptionClass::NA, has_error: false},
];

pub fn handler(state: &State)
{
state.save();
let interrupt = state.interrupt;
let exception = &EXCEPTIONS[interrupt as usize];
let error = state.error;
if exception.has_error
{
panic!("{:02x} - {} - error {:08x}", interrupt, exception.message, error);
}
else
{
panic!("{:02x} - {}", interrupt, exception.message);
}
}
111 changes: 111 additions & 0 deletions src/arch/i686/interrupts/idt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
use core::mem::size_of;
use crate::arch::i686;

#[repr(C, packed)]
pub struct descriptor
{
limit: u16,
base: u32
}

enum GateType
{
Task = 0b0101,
Interrupt16 = 0b0110,
Trap16 = 0b0111,
Interrupt32 = 0b1110,
Trap32 = 0b1111,
}

#[derive(Copy, Clone)]
#[repr(C, packed)]
struct gate
{
isr_low: u16,
segment: u16,
reserved: u8,
flags: u8,
isr_high: u16
}

impl gate
{
fn init(&mut self)
{
self.segment = 0x08;
}

fn set_isr(&mut self, isr: u32)
{
self.isr_low = (isr & 0xffff) as u16;
self.isr_high = (isr >> 16) as u16;
}

fn set_present(&mut self)
{
self.set_flag(0b1000_0000);
}

fn unset_present(&mut self)
{
self.unset_flag(0b1000_0000);
}

fn set_type(&mut self, gate_type: GateType)
{
// unset the type represented by only 0s
self.unset_flag(GateType::Trap32 as u8);
self.set_flag(gate_type as u8);
}

#[inline(always)]
fn set_flag(&mut self, flag: u8)
{
self.flags |= flag;
}

#[inline(always)]
fn unset_flag(&mut self, flag: u8)
{
self.flags &= !flag;
}
}

static mut DESCRIPTOR: descriptor = descriptor
{
limit: 0,
base: 0
};

static mut IDT: [gate; 256] = [gate
{
isr_low: 0,
segment: 0,
reserved: 0,
flags: 0,
isr_high: 0
}; 256];

extern "C"
{
static mut _isr_table: [u32; 256];
}

pub unsafe fn init()
{
DESCRIPTOR.base = &IDT as *const _ as u32;
DESCRIPTOR.limit = (size_of::<gate>() * IDT.len() - 1) as u16;

for (vector, gate) in IDT.iter_mut().enumerate()
{
gate.init();
gate.set_isr(_isr_table[vector as usize]);
gate.set_type(GateType::Interrupt32);
gate.set_present();
}
}

pub unsafe fn load()
{
i686::instructions::lidt(&DESCRIPTOR);
}
50 changes: 50 additions & 0 deletions src/arch/i686/interrupts/irq.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use super::State;
use super::pic;

pub struct IRQ
{
pub message: &'static str,
pub handler: unsafe fn(&State)
}

pub static IRQS: [IRQ; 15] =
[
IRQ {message: "Programmable Interrupt Timer Interrupt", handler: pit_interrupt},
IRQ {message: "Keyboard Interrupt", handler: keyboard_interrupt},
IRQ {message: "", handler: unhandled_interrupt},
IRQ {message: "", handler: unhandled_interrupt},
IRQ {message: "", handler: unhandled_interrupt},
IRQ {message: "", handler: unhandled_interrupt},
IRQ {message: "", handler: unhandled_interrupt},
IRQ {message: "", handler: unhandled_interrupt},
IRQ {message: "", handler: unhandled_interrupt},
IRQ {message: "", handler: unhandled_interrupt},
IRQ {message: "", handler: unhandled_interrupt},
IRQ {message: "", handler: unhandled_interrupt},
IRQ {message: "", handler: unhandled_interrupt},
IRQ {message: "", handler: unhandled_interrupt},
IRQ {message: "", handler: unhandled_interrupt}
];

pub unsafe fn handler(state: &State)
{
let irq = state.interrupt - 0x20;
(IRQS[irq as usize].handler)(state);
pic::send_eoi(irq as u8);
}

unsafe fn pit_interrupt(_state: &State)
{
crate::time::JIFFIES += 1;
}

unsafe fn keyboard_interrupt(_state: &State)
{
crate::keyboard::get_scancode();
}

unsafe fn unhandled_interrupt(state: &State)
{
let interrupt = state.interrupt;
crate::serial_println!("Got unhandled irq {:02x}", interrupt);
}
94 changes: 94 additions & 0 deletions src/arch/i686/interrupts/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
use crate::arch::i686::instructions;

mod exceptions;
pub mod idt;
mod irq;
mod pic;

#[inline(always)]
pub unsafe fn init()
{
idt::init();
idt::load();
pic::init();
}

#[inline(always)]
pub unsafe fn enable()
{
instructions::sti();
}

#[derive(Copy, Clone, Debug)]
#[repr(C, packed)]
pub struct State
{
pub ds: u32,
// pusha
pub edi: u32,
pub esi: u32,
pub ebp: u32,
pub esp: u32,
pub ebx: u32,
pub edx: u32,
pub ecx: u32,
pub eax: u32,
// push interrupt
pub interrupt: u32,
// pushed automatically
pub error: u32,
// pushed by the CPU
pub eip: u32,
pub cs: u32,
pub eflags: u32,
}

impl State
{
fn save(self)
{
unsafe
{
let state = &mut crate::INTERRUPT_STATE;
state.eax = self.eax;
state.ebx = self.ebx;
state.ecx = self.ecx;
state.edx = self.edx;

state.esi = self.esi;
state.edi = self.edi;
state.esp = self.esp;
state.ebp = self.ebp;

state.cs = self.cs;
state.ds = self.ds;

state.interrupt = self.interrupt;
state.error = self.error;

state.eip = self.eip;
state.eflags = self.eflags;
}
}
}

#[no_mangle]
pub unsafe extern "C" fn interrupt_handler(state: &State)
{
let interrupt = state.interrupt;
match interrupt
{
0x00..=0x1f =>
{
exceptions::handler(state);
},
0x20..=0x2f =>
{
irq::handler(state);
}
_ =>
{
crate::serial_println!("Got unhandled interrupt {:02x}", interrupt);
}
};
}
Loading