驱动接口
OpenOS 的驱动运行在用户空间,通过 IPC 与内核通信。内核提供中断处理和端口 I/O 接口。
┌─────────────────────────────────────────────────────────────┐│ 用户空间 (Ring 3) │├─────────────────────────────────────────────────────────────┤│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ││ │ 键盘驱动 │ │ 磁盘驱动 │ │ 网卡驱动 │ │ 显示驱动 │ ││ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ ││ │ │ │ │ ││ └──────────────┴──────────────┴──────────────┘ ││ │ IPC │├──────────────────────────┼──────────────────────────────────┤│ 内核空间 (Ring 0) │├─────────────────────────────────────────────────────────────┤│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ││ │ IDT │ │ PIC │ │ 端口 I/O │ │ 中断分发 │ ││ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │├─────────────────────────────────────────────────────────────┤│ 硬件 │└─────────────────────────────────────────────────────────────┘IDT 初始化
Section titled “IDT 初始化”use x86_64::structures::idt::InterruptDescriptorTable;
static IDT: InterruptDescriptorTable = { let mut idt = InterruptDescriptorTable::new();
// 异常处理函数 idt.breakpoint.set_handler_fn(breakpoint_handler); idt.double_fault.set_handler_fn(double_fault_handler); idt.page_fault.set_handler_fn(page_fault_handler); idt.general_protection_fault.set_handler_fn(general_protection_fault_handler);
// IRQ 处理函数 idt[InterruptIndex::Timer.as_usize()].set_handler_fn(timer_interrupt_handler); idt[InterruptIndex::Keyboard.as_usize()].set_handler_fn(keyboard_interrupt_handler); idt[InterruptIndex::Serial1.as_usize()].set_handler_fn(serial_interrupt_handler);
idt};中断处理函数规范
Section titled “中断处理函数规范”extern "x86-interrupt" fn handler(stack_frame: InterruptStackFrame) { // 1. 读取硬件数据(端口 I/O 或 MMIO) let data = unsafe { port.read() };
// 2. 处理数据 process_interrupt_data(data);
// 3. 发送 EOI 到 PIC(必须!) unsafe { PICS.lock().notify_end_of_interrupt(InterruptIndex::NewIrq.as_u8()); }}中断处理约束
Section titled “中断处理约束”| 约束 | 说明 |
|---|---|
| 不得阻塞 | 不得睡眠,不得等待用户输入 |
| 必须发送 EOI | 否则 PIC 将屏蔽 IRQ 线 |
| 避免死锁 | 不得获取被中断上下文已持有的自旋锁 |
| 快速执行 | 尽量减少中断处理时间 |
端口 I/O
Section titled “端口 I/O”use x86_64::instructions::port::Port;
/// 读取端口pub unsafe fn port_read(port: u16) -> u8 { let mut port = Port::new(port); port.read()}
/// 写入端口pub unsafe fn port_write(port: u16, value: u8) { let mut port = Port::new(port); port.write(value);}常用端口地址
Section titled “常用端口地址”| 端口 | 设备 | 说明 |
|---|---|---|
0x20 / 0xA0 | PIC | 主/从 PIC 命令端口 |
0x21 / 0xA1 | PIC | 主/从 PIC 数据端口 |
0x60 | 键盘 | 键盘数据端口 |
0x64 | 键盘 | 键盘状态/命令端口 |
0x3F8 | 串口 | COM1 数据端口 |
0x3F9 | 串口 | COM1 中断使能 |
0x3FA | 串口 | COM1 FIFO 控制 |
0x3FB | 串口 | COM1 线路控制 |
0x3FC | 串口 | COM1 调制解调器控制 |
0x3FD | 串口 | COM1 线路状态 |
0x3FE | 串口 | COM1 调制解调器状态 |
MMIO 访问
Section titled “MMIO 访问”Volatile 读写
Section titled “Volatile 读写”use volatile::Volatile;
/// MMIO 读取pub fn mmio_read<T: Copy>(addr: *const T) -> T { unsafe { Volatile::new(addr).read() }}
/// MMIO 写入pub fn mmio_write<T: Copy>(addr: *mut T, value: T) { unsafe { Volatile::new(addr).write(value) }}VGA 缓冲区示例
Section titled “VGA 缓冲区示例”const VGA_BUFFER_ADDR: *mut u16 = 0xB8000 as *mut u16;
/// 写入 VGA 字符pub fn vga_write_char(x: usize, y: usize, ch: u8, color: u8) { let offset = y * 80 + x; let value = (color as u16) << 8 | ch as u16;
unsafe { mmio_write(VGA_BUFFER_ADDR.add(offset), value); }}PIC 8259
Section titled “PIC 8259”use pic8259::ChainedPics;use spin::Mutex;
/// PIC 端口地址pub const PIC_1_OFFSET: u8 = 32;pub const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8;
/// 全局 PIC 实例pub static PICS: Mutex<ChainedPics> = Mutex::new(unsafe { ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET) });
/// 初始化 PICpub fn init() { unsafe { PICS.lock().initialize(); }}IRQ 重映射
Section titled “IRQ 重映射”原始 IRQ 重映射后 INTIRQ 0 → INT 32 (定时器)IRQ 1 → INT 33 (键盘)IRQ 2 → INT 34 (级联)IRQ 3 → INT 35 (COM2)IRQ 4 → INT 36 (COM1)IRQ 5 → INT 37 (LPT2)IRQ 6 → INT 38 (软盘)IRQ 7 → INT 39 (LPT1)IRQ 8 → INT 40 (CMOS)IRQ 9 → INT 41 (自由)IRQ 10 → INT 42 (自由)IRQ 11 → INT 43 (自由)IRQ 12 → INT 44 (PS/2 鼠标)IRQ 13 → INT 45 (FPU)IRQ 14 → INT 46 (主 ATA)IRQ 15 → INT 47 (从 ATA)发送 EOI
Section titled “发送 EOI”/// 发送中断结束信号pub fn end_of_interrupt(interrupt_id: u8) { unsafe { PICS.lock().notify_end_of_interrupt(interrupt_id); }}注册中断处理函数
Section titled “注册中断处理函数”/// 注册 IRQ 处理函数pub fn register_irq_handler(irq: u8, handler: extern "x86-interrupt" fn(InterruptStackFrame)) { let mut idt = IDT.lock(); idt[(PIC_1_OFFSET + irq) as usize].set_handler_fn(handler);}驱动初始化流程
Section titled “驱动初始化流程”/// 驱动初始化pub fn init() { // 1. 检测硬件 let hardware = detect_hardware();
// 2. 配置硬件 configure_hardware(&hardware);
// 3. 注册中断处理函数 register_irq_handler(hardware.irq, interrupt_handler);
// 4. 启用中断 enable_irq(hardware.irq);
// 5. 创建 IPC 端口 let port_id = sys_port_create().unwrap();
// 6. 注册服务 register_service("driver.name", port_id);}use uart_16550::SerialPort;use spin::Mutex;
/// COM1 串口static SERIAL1: Mutex<SerialPort> = Mutex::new(unsafe { SerialPort::new(0x3F8) });
/// 初始化串口pub fn init() { SERIAL1.lock().init();}
/// 读取字节pub fn read_byte() -> u8 { SERIAL1.lock().receive()}
/// 写入字节pub fn write_byte(byte: u8) { SERIAL1.lock().send(byte);}
/// 写入字符串pub fn write_string(s: &str) { for byte in s.bytes() { write_byte(byte); }}VGA 驱动
Section titled “VGA 驱动”use volatile::Volatile;use spin::Mutex;
/// VGA 颜色#[derive(Debug, Clone, Copy, PartialEq, Eq)]#[repr(u8)]pub enum Color { Black = 0, Blue = 1, Green = 2, // ... 其他颜色 White = 15,}
/// VGA 缓冲区const BUFFER_HEIGHT: usize = 25;const BUFFER_WIDTH: usize = 80;const VGA_BUFFER: *mut Volatile<u16> = 0xB8000 as *mut _;
/// VGA 驱动pub struct VgaDriver { column: usize, color_code: u8, buffer: &'static mut [[Volatile<u16>; BUFFER_WIDTH]; BUFFER_HEIGHT],}
impl VgaDriver { pub fn new() -> Self { VgaDriver { column: 0, color_code: Color::White as u8 | (Color::Black as u8) << 4, buffer: unsafe { &mut *(VGA_BUFFER as *mut _) }, } }
pub fn write_byte(&mut self, byte: u8) { match byte { b'\n' => self.new_line(), byte => { if self.column >= BUFFER_WIDTH { self.new_line(); }
let row = BUFFER_HEIGHT - 1; let col = self.column; let color_code = self.color_code; let char = (color_code as u16) << 8 | byte as u16;
self.buffer[row][col].write(char); self.column += 1; } } }
fn new_line(&mut self) { // 滚动屏幕 for row in 1..BUFFER_HEIGHT { for col in 0..BUFFER_WIDTH { let char = self.buffer[row][col].read(); self.buffer[row - 1][col].write(char); } } self.clear_row(BUFFER_HEIGHT - 1); self.column = 0; }}驱动开发指南
Section titled “驱动开发指南”步骤 1:创建驱动文件
Section titled “步骤 1:创建驱动文件”mkdir kernel/src/drivers/my_drivertouch kernel/src/drivers/my_driver/mod.rs步骤 2:实现驱动
Section titled “步骤 2:实现驱动”use x86_64::instructions::port::Port;
/// 驱动状态pub struct MyDriver { port: Port<u8>, initialized: bool,}
impl MyDriver { pub fn new(port_base: u16) -> Self { MyDriver { port: Port::new(port_base), initialized: false, } }
pub fn init(&mut self) { // 初始化硬件 unsafe { self.port.write(0x01); // 发送初始化命令 } self.initialized = true; }
pub fn read_data(&mut self) -> u8 { if !self.initialized { return 0; } unsafe { self.port.read() } }
pub fn write_data(&mut self, data: u8) { if !self.initialized { return; } unsafe { self.port.write(data); } }}步骤 3:注册驱动
Section titled “步骤 3:注册驱动”pub mod my_driver;
/// 初始化所有驱动pub fn init_all() { my_driver::MY_DRIVER.lock().init();}步骤 4:添加中断处理
Section titled “步骤 4:添加中断处理”extern "x86-interrupt" fn my_driver_handler(_stack_frame: InterruptStackFrame) { // 处理中断 let data = drivers::my_driver::MY_DRIVER.lock().read_data(); process_data(data);
// 发送 EOI unsafe { PICS.lock().notify_end_of_interrupt(InterruptIndex::MyDriver.as_u8()); }}启用调试输出
Section titled “启用调试输出”// 使用串口输出调试信息serial_println!("[DRIVER] Initializing...");serial_println!("[DRIVER] Port: 0x{:X}", port_base);serial_println!("[DRIVER] IRQ: {}", irq);问题:中断不触发
- 检查 PIC 初始化
- 检查 IRQ 使能
- 检查 IDT 注册
问题:数据丢失
- 检查 FIFO 缓冲区
- 增加中断处理速度
- 使用 DMA
问题:死锁
- 避免在中断处理中获取锁
- 使用
without_interrupts()保护共享数据