You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

222 lines
6.6 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

//! 中断服务例程
//!
//! # 中断处理总流程
//!
//! * 中断发生时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中断号和错误码
//! * 执行iretCPU从栈中pop一些值若其中的CS是Ring03项否则5项重置rip和rspRing3时
//!
//! ``` 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);
}