//! 中断服务例程 //! //! # 中断处理总流程 //! //! * 中断发生时,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::TrapFrame; #[no_mangle] pub extern fn rust_trap(tf: &mut TrapFrame) { trace!("Interrupt: {:#x}", tf.trap_num); // Dispatch match tf.trap_num as u8 { T_BRKPT => breakpoint(), T_DBLFLT => double_fault(tf), T_PGFLT => page_fault(tf), T_IRQ0...64 => { let irq = tf.trap_num as u8 - T_IRQ0; match irq { IRQ_TIMER => timer(), IRQ_KBD => keyboard(), IRQ_COM1 => com1(), IRQ_COM2 => com2(), _ => panic!("Invalid IRQ number."), } #[cfg(feature = "use_apic")] use arch::driver::apic::ack; #[cfg(not(feature = "use_apic"))] use arch::driver::pic::ack; ack(irq); } T_SWITCH_TOK => to_kernel(tf), T_SWITCH_TOU => to_user(tf), T_SYSCALL => syscall(tf), T_SYSCALL32 => syscall32(tf), T_DIVIDE | T_GPFLT | T_ILLOP => error(tf), _ => panic!("Unhandled interrupt {:x}", tf.trap_num), } use process::PROCESSOR; PROCESSOR.try().unwrap().lock().schedule(); } fn breakpoint() { error!("\nEXCEPTION: Breakpoint"); } fn double_fault(tf: &TrapFrame) { error!("\nEXCEPTION: Double Fault\n{:#x?}", tf); loop {} } fn page_fault(tf: &mut TrapFrame) { use x86_64::registers::control_regs::cr2; let addr = cr2().0; error!("\nEXCEPTION: Page Fault @ {:#x}, code: {:#x}", addr, tf.error_code); use memory::page_fault_handler; if page_fault_handler(addr) { return; } error(tf); } use super::consts::*; fn keyboard() { use arch::driver::keyboard; info!("\nInterupt: Keyboard"); let c = keyboard::get(); info!("Key = '{}' {}", c as u8 as char, c); } fn com1() { use arch::driver::serial::COM1; info!("\nInterupt: COM1"); COM1.lock().receive(); } fn com2() { use arch::driver::serial::COM2; info!("\nInterupt: COM2"); COM2.lock().receive(); } fn timer() { use process::PROCESSOR; let mut processor = PROCESSOR.try().unwrap().lock(); processor.tick(); } fn to_user(tf: &mut TrapFrame) { use arch::gdt; info!("\nInterupt: To User"); tf.cs = gdt::UCODE_SELECTOR.0 as usize; tf.ss = gdt::UDATA_SELECTOR.0 as usize; tf.rflags |= 3 << 12; // 设置EFLAG的I/O特权位,使得在用户态可使用in/out指令 } fn to_kernel(tf: &mut TrapFrame) { use arch::gdt; info!("\nInterupt: To Kernel"); tf.cs = gdt::KCODE_SELECTOR.0 as usize; tf.ss = gdt::KDATA_SELECTOR.0 as usize; } fn syscall(tf: &mut TrapFrame) { info!("\nInterupt: Syscall {:#x?}", tf.rax); use syscall::syscall; let ret = syscall(tf, false); tf.rax = ret as usize; } fn syscall32(tf: &mut TrapFrame) { // info!("\nInterupt: Syscall {:#x?}", tf.rax); use syscall::syscall; let ret = syscall(tf, true); tf.rax = ret as usize; } fn error(tf: &TrapFrame) { use process::PROCESSOR; if let Some(processor) = PROCESSOR.try() { let mut processor = processor.lock(); let pid = processor.current_pid(); error!("Process {} error:\n{:#x?}", pid, tf); processor.exit(pid, 0x100); // TODO: Exit code for error } else { error!("Exception {:#x} when processor not inited\n{:#x?}", tf.trap_num, tf); loop {} } } #[no_mangle] pub extern fn set_return_rsp(tf: &TrapFrame) { use arch::gdt::Cpu; use core::mem::size_of; if tf.cs & 0x3 == 3 { Cpu::current().set_ring0_rsp(tf as *const _ as usize + size_of::()); } }