aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTianhao Wang <shrik3@mailbox.org>2024-06-05 07:58:47 +0200
committerTianhao Wang <shrik3@mailbox.org>2024-06-11 15:17:13 +0200
commit2c4366a804ae6c389614ff8da9b0225ffb6ce14a (patch)
treefa2fd438ba16b6dfcbde2c0b728656f31bec30f6
parentd6815a5903716c058c4f8bee89bfa745ee752c9d (diff)
interrupt: better trampolines for interrupts
1. define trampolines for both exceptions with error code (automatically pushed to stack) and those without. 2. do not repeat vectors for unused IRQ numbers: we need 48, no need to fill in 256. 3. also pass the pointer to trap frame (on the stack) to the handler code Signed-off-by: Tianhao Wang <shrik3@mailbox.org>
-rw-r--r--src/arch/x86_64/asm/vectors.s79
-rw-r--r--src/arch/x86_64/interrupt/mod.rs23
2 files changed, 78 insertions, 24 deletions
diff --git a/src/arch/x86_64/asm/vectors.s b/src/arch/x86_64/asm/vectors.s
index 80ec612..cc7a7b7 100644
--- a/src/arch/x86_64/asm/vectors.s
+++ b/src/arch/x86_64/asm/vectors.s
@@ -4,7 +4,7 @@
[GLOBAL idt]
[GLOBAL idt_descr]
[GLOBAL vectors_start]
-[EXTERN interrupt_gate]
+[EXTERN trap_gate]
[SECTION .data.idt]
; Interrupt descriptor table with 256 entries
@@ -18,30 +18,66 @@ idt_descr:
dw 256*8 - 1 ; 256 entries
dq idt
-; NOTE: vectors MUST have fixed instruction length currently aligned to 16
-; bytes. DO NOT modify the wrapper, instead change the wrapper_body if needed.
-; if the vector has to be modified into more than 16 bytes,
-; arch::x86_64:: interrupt::_idt_init() must be modified accordingly
[SECTION .text.vectors]
-%macro vector 1
+%macro trap_without_err 1
align 16
vector_%1:
- push rbp
- mov rbp, rsp
+ push 0
push rax
- push rbx
mov al, %1
jmp vector_body
%endmacro
-; automatic generation of 256 interrupt-handling routines, based on above macro
+%macro trap_with_err 1
+align 16
+vector_e_%1:
+ push rax
+ mov al, %1
+ jmp vector_body
+%endmacro
vectors_start:
-%assign i 0
-%rep 256
- vector i
+; the first 32 are x86 exceptions / traps
+trap_without_err 0 ; Div By Zero
+trap_without_err 1 ; Debug
+trap_without_err 2 ; NMI
+trap_without_err 3 ; BP
+trap_without_err 4 ; OF
+trap_without_err 5 ; Bound Range
+trap_without_err 6 ; Invalid Opcode
+trap_without_err 7 ; Device N/A
+trap_with_err 8 ; Double Fault
+trap_without_err 9 ; Legacy (not used)
+trap_with_err 10 ; Invalid TSS
+trap_with_err 11 ; Segment Not Present
+trap_with_err 12 ; Stack Segment Fault
+trap_with_err 13 ; GPF
+trap_with_err 14 ; Page Fault
+trap_without_err 15 ; RESERVED
+trap_without_err 16 ; x87 FP exception
+trap_with_err 17 ; Alighment check
+trap_without_err 18 ; Machine Check
+trap_without_err 19 ; SIMD FP Exception
+trap_without_err 20 ; Virtualization Exception
+trap_with_err 21 ; Control Protection
+trap_without_err 22 ; RESERVED
+trap_without_err 23 ; RESERVED
+trap_without_err 24 ; RESERVED
+trap_without_err 25 ; RESERVED
+trap_without_err 26 ; RESERVED
+trap_without_err 27 ; RESERVED
+trap_without_err 28 ; Hypervisor Injection
+trap_with_err 29 ; VMM Communication
+trap_with_err 30 ; Security Exception
+trap_without_err 31 ; RESERVED
+; 16 PIC IRQs are remapped from 32 to 47
+%assign i 32
+%rep 16
+ trap_without_err i
%assign i i+1
%endrep
+; irqs from 48 are not valid, we define one extra vector for all of them
+trap_without_err 48 ; INVALID
; common handler body
vector_body:
@@ -59,11 +95,14 @@ vector_body:
; the generated wrapper only gives us 8 bits, mask the rest
and rax, 0xff
- ; call the interrupt handling code with interrupt number as parameter
+ ; the first parameter is the interrupt (exception) number
mov rdi, rax
- mov rbx, interrupt_gate
- ; TODO fix the long jump. I don't want to waste another register
- call rbx
+ ; the second parameter is a pointer to the trap frame
+ mov rsi, rsp
+ ; For a long jump, we need to put the (large) address in an register
+ ; here reusing one of the caller clobbered regs (pushed above)
+ mov r11, trap_gate
+ call r11
; restore volatile registers
pop r11
@@ -75,10 +114,8 @@ vector_body:
pop rdx
pop rcx
- ; ... also those from the vector wrapper
- pop rbx
pop rax
- pop rbp
-
+ ; "pop" the error code
+ add rsp, 8
; done
iretq
diff --git a/src/arch/x86_64/interrupt/mod.rs b/src/arch/x86_64/interrupt/mod.rs
index 9273ef7..6663b23 100644
--- a/src/arch/x86_64/interrupt/mod.rs
+++ b/src/arch/x86_64/interrupt/mod.rs
@@ -1,11 +1,14 @@
pub mod pic_8259;
pub mod pit;
+use crate::arch::x86_64::arch_regs::TrapFrame;
use crate::io::*;
use core::arch::asm;
use core::slice;
// TODO use P2V for extern symbol addresses
// number of entries in IDT
pub const IDT_CAPACITY: usize = 256;
+// 32 exceptions + 16 irqs from PIC = 48 valid interrupts
+pub const IDT_VALID: usize = 48;
// size of interrupt handler wrapper routine (vector)
pub const VECTOR_SIZE: u64 = 16;
extern "C" {
@@ -58,14 +61,24 @@ impl GateDescriptor64 {
#[no_mangle]
#[cfg(target_arch = "x86_64")]
-extern "C" fn interrupt_gate(_slot: u16) {
+extern "C" fn trap_gate(_nr: u16, fp: u64) {
interrupt_disable();
+ // let ctx_p = ptregs_addr as *mut PtRegs;
+ // let ctx_p = ctx_p.cast::<PtRegs>();
+ // let ctx = unsafe { &mut *ctx_p };
// NOTE: the interrupt handler should NEVER block on a lock; in this case
// the CGA screen is protected by a spinlock. The lock holder will never be
// able to release the lock if the interrupt handler blocks on it. Try
// spamming the keyboard with the following line of code uncommented: it
// will deadlock!
- // println!("interrupt received 0x{:x}", _slot);
+ println!("interrupt received 0x{:x}", _nr);
+ if _nr < 0x20 {
+ let _frame = unsafe { &mut *(fp as *mut TrapFrame) };
+ println!("trap: @{:#X} {:#X?}", fp, _frame);
+ unsafe {
+ asm!("hlt");
+ }
+ }
interrupt_enable();
}
@@ -91,10 +104,14 @@ fn _idt_init() {
unsafe { slice::from_raw_parts_mut(idt as *mut GateDescriptor64, 256) };
// write to idt
- for i in 0..IDT_CAPACITY {
+ for i in 0..IDT_VALID {
let offset: u64 = vectors_start as u64 + (i as u64 * VECTOR_SIZE);
gate_descriptors[i].set_default_interrupt(offset);
}
+ let offset_inv: u64 = vectors_start as u64 + (IDT_VALID as u64 * VECTOR_SIZE);
+ for i in IDT_VALID..IDT_CAPACITY {
+ gate_descriptors[i].set_default_interrupt(offset_inv);
+ }
// set idtr
unsafe { asm! ("lidt [{}]", in(reg) idt_descr) }
}