|
|
//! 中断服务例程
|
|
|
//!
|
|
|
//! # 中断处理总流程
|
|
|
//!
|
|
|
//! * 中断发生时,CPU首先完成以下工作:
|
|
|
//! * 设置rsp的值(Ring3->0 或 DoubleFault,在TSS中设置)
|
|
|
//! * 向栈中push一些值(Ring0时3项,Ring3时5项,某些中断还有错误码)
|
|
|
//! * 跳转到`vector{i}`符号(位于`vector.asm`)
|
|
|
//! * 进入中断服务例程后,完成以下工作:
|
|
|
//! * `vector{i}`向栈中push空错误码(如CPU没有做)和中断号
|
|
|
//! * 跳转到`__alltraps`(位于`trap.asm`)
|
|
|
//! * `__alltraps`向栈中push全部寄存器的值
|
|
|
//! * 调用`rust_trap`,并把此时的rsp传过来,也就是`TrapFrame`的指针
|
|
|
//! * `rust_trap`完成以下工作:
|
|
|
//! * 根据tf中的中断号,再次分发到具体的中断处理函数。
|
|
|
//! 一些函数可能会调用switch(),切换到其它线程执行,在某一时刻再切换回来。
|
|
|
//! * 如果需要,执行进程调度
|
|
|
//! * `__alltraps`完成以下工作:
|
|
|
//! * 检查tf中的cs段,如果是用户态,向TSS中设置下一次中断时重置rsp的值,使其指向该用户线程的内核栈
|
|
|
//! * 从栈中pop全部寄存器的值,pop中断号和错误码
|
|
|
//! * 执行iret,CPU从栈中pop一些值(若其中的CS是Ring0,3项,否则5项),重置rip和rsp(Ring3时)
|
|
|
//!
|
|
|
//! ``` mermaid
|
|
|
//! sequenceDiagram
|
|
|
//! activate CPU
|
|
|
//! Note right of CPU: set rsp (user)
|
|
|
//! Note right of CPU: push something
|
|
|
//! deactivate CPU
|
|
|
//!
|
|
|
//! CPU ->> +ASM: jmp vector_i
|
|
|
//! Note right of ASM: push error_code & trap_num
|
|
|
//!
|
|
|
//! ASM ->> ASM: jmp alltraps
|
|
|
//! Note right of ASM: push registers
|
|
|
//!
|
|
|
//! ASM ->> +Rust: call rust_trap(rsp)
|
|
|
//! Note right of Rust: handle the trap
|
|
|
//!
|
|
|
//! opt schedule
|
|
|
//! Rust ->> +NewThread: switch
|
|
|
//! Note left of NewThread: forkret:
|
|
|
//! NewThread -->> -ASM: ret
|
|
|
//! Note left of ASM: trapret:
|
|
|
//! ASM -->> +NewThread: from another trap
|
|
|
//! NewThread ->> -Rust: switch
|
|
|
//! end
|
|
|
//!
|
|
|
//! Rust -->> -ASM: ret
|
|
|
//! Note left of ASM: trapret:
|
|
|
//!
|
|
|
//! opt CS is user mode
|
|
|
//! ASM ->> +Rust: call set_return_rsp
|
|
|
//! Note right of Rust: set in TSS
|
|
|
//! Rust -->> -ASM: ret
|
|
|
//! end
|
|
|
//!
|
|
|
//! Note right of ASM: pop registers
|
|
|
//! Note right of ASM: pop error_code & trap_num
|
|
|
//! ASM -->> -CPU: iret
|
|
|
//!
|
|
|
//! activate CPU
|
|
|
//! Note right of CPU: pop something
|
|
|
//! Note right of CPU: set rsp (user)
|
|
|
//! deactivate CPU
|
|
|
//! ```
|
|
|
|
|
|
use super::consts::*;
|
|
|
use super::TrapFrame;
|
|
|
use crate::drivers::DRIVERS;
|
|
|
use bitflags::*;
|
|
|
use log::*;
|
|
|
|
|
|
global_asm!(include_str!("trap.asm"));
|
|
|
global_asm!(include_str!("vector.asm"));
|
|
|
|
|
|
#[allow(non_upper_case_globals)]
|
|
|
#[no_mangle]
|
|
|
pub extern "C" fn rust_trap(tf: &mut TrapFrame) {
|
|
|
trace!(
|
|
|
"Interrupt: {:#x} @ CPU{}",
|
|
|
tf.trap_num,
|
|
|
super::super::cpu::id()
|
|
|
);
|
|
|
// Dispatch
|
|
|
match tf.trap_num as u8 {
|
|
|
Breakpoint => breakpoint(),
|
|
|
DoubleFault => double_fault(tf),
|
|
|
PageFault => page_fault(tf),
|
|
|
IRQ0...63 => {
|
|
|
let irq = tf.trap_num as u8 - IRQ0;
|
|
|
super::ack(irq); // must ack before switching
|
|
|
match irq {
|
|
|
Timer => crate::trap::timer(),
|
|
|
Keyboard => keyboard(),
|
|
|
COM1 => com1(),
|
|
|
COM2 => com2(),
|
|
|
IDE => ide(),
|
|
|
_ => {
|
|
|
for driver in DRIVERS.read().iter() {
|
|
|
if driver.try_handle_interrupt(Some(irq.into())) == true {
|
|
|
debug!("driver processed interrupt");
|
|
|
return;
|
|
|
}
|
|
|
}
|
|
|
warn!("unhandled external IRQ number: {}", irq);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
Syscall32 => syscall32(tf),
|
|
|
InvalidOpcode => invalid_opcode(tf),
|
|
|
DivideError | GeneralProtectionFault => error(tf),
|
|
|
_ => panic!("Unhandled interrupt {:x}", tf.trap_num),
|
|
|
}
|
|
|
}
|
|
|
|
|
|
fn breakpoint() {
|
|
|
error!("\nEXCEPTION: Breakpoint");
|
|
|
}
|
|
|
|
|
|
fn double_fault(tf: &TrapFrame) {
|
|
|
error!("\nEXCEPTION: Double Fault\n{:#x?}", tf);
|
|
|
loop {}
|
|
|
}
|
|
|
|
|
|
fn page_fault(tf: &mut TrapFrame) {
|
|
|
let addr: usize;
|
|
|
unsafe {
|
|
|
asm!("mov %cr2, $0" : "=r" (addr));
|
|
|
}
|
|
|
|
|
|
bitflags! {
|
|
|
struct PageError: u8 {
|
|
|
const PRESENT = 1 << 0;
|
|
|
const WRITE = 1 << 1;
|
|
|
const USER = 1 << 2;
|
|
|
const RESERVED_WRITE = 1 << 3;
|
|
|
const INST = 1 << 4;
|
|
|
}
|
|
|
}
|
|
|
let code = PageError::from_bits(tf.error_code as u8).unwrap();
|
|
|
|
|
|
if crate::memory::handle_page_fault(addr) {
|
|
|
return;
|
|
|
}
|
|
|
error!("\nEXCEPTION: Page Fault @ {:#x}, code: {:?}", addr, code);
|
|
|
error(tf);
|
|
|
}
|
|
|
|
|
|
fn keyboard() {
|
|
|
use crate::arch::driver::keyboard;
|
|
|
use pc_keyboard::{DecodedKey, KeyCode};
|
|
|
trace!("\nInterupt: Keyboard");
|
|
|
if let Some(key) = keyboard::receive() {
|
|
|
match key {
|
|
|
DecodedKey::Unicode(c) => crate::trap::serial(c),
|
|
|
DecodedKey::RawKey(code) => {
|
|
|
let s = match code {
|
|
|
KeyCode::ArrowUp => "\u{1b}[A",
|
|
|
KeyCode::ArrowDown => "\u{1b}[B",
|
|
|
KeyCode::ArrowRight => "\u{1b}[C",
|
|
|
KeyCode::ArrowLeft => "\u{1b}[D",
|
|
|
_ => "",
|
|
|
};
|
|
|
for c in s.chars() {
|
|
|
crate::trap::serial(c);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
fn com1() {
|
|
|
use crate::arch::driver::serial::*;
|
|
|
trace!("\nInterupt: COM1");
|
|
|
crate::trap::serial(COM1.lock().receive() as char);
|
|
|
}
|
|
|
|
|
|
fn com2() {
|
|
|
use crate::arch::driver::serial::*;
|
|
|
trace!("\nInterupt: COM2");
|
|
|
COM2.lock().receive();
|
|
|
}
|
|
|
|
|
|
fn ide() {
|
|
|
trace!("\nInterupt: IDE");
|
|
|
}
|
|
|
|
|
|
#[no_mangle]
|
|
|
pub extern "C" fn syscall(tf: &mut TrapFrame) {
|
|
|
trace!("\nInterupt: Syscall {:#x?}", tf.rax);
|
|
|
let ret = crate::syscall::syscall(tf.rax, [tf.rdi, tf.rsi, tf.rdx, tf.r10, tf.r8, tf.r9], tf);
|
|
|
tf.rax = ret as usize;
|
|
|
}
|
|
|
|
|
|
fn syscall32(tf: &mut TrapFrame) {
|
|
|
trace!("\nInterupt: Syscall {:#x?}", tf.rax);
|
|
|
let ret = crate::syscall::syscall(tf.rax, [tf.rdx, tf.rcx, tf.rbx, tf.rdi, tf.rsi, 0], tf);
|
|
|
tf.rax = ret as usize;
|
|
|
}
|
|
|
|
|
|
/// Support `syscall` instruction
|
|
|
fn invalid_opcode(tf: &mut TrapFrame) {
|
|
|
let opcode = unsafe { (tf.rip as *mut u16).read() };
|
|
|
const SYSCALL_OPCODE: u16 = 0x05_0f;
|
|
|
if opcode == SYSCALL_OPCODE {
|
|
|
tf.rip += 2; // must before syscall
|
|
|
syscall(tf);
|
|
|
} else {
|
|
|
crate::trap::error(tf);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
fn error(tf: &TrapFrame) {
|
|
|
crate::trap::error(tf);
|
|
|
}
|
|
|
|
|
|
#[no_mangle]
|
|
|
pub unsafe extern "C" fn set_return_rsp(tf: *const TrapFrame) {
|
|
|
use crate::arch::gdt::Cpu;
|
|
|
Cpu::current().set_ring0_rsp(tf.add(1) as usize);
|
|
|
}
|