virtio-blk磁盘块设备
virtio_mmio@10008000 {
interrupts = <0x08>;
interrupt-parent = <0x03>;
reg = <0x00 0x10008000 0x00 0x1000>;
compatible = "virtio,mmio";
};
UART串口设备
uart@10000000 {
interrupts = <0x0a>;
interrupt-parent = <0x03>;
clock-frequency = <0x384000>;
reg = <0x00 0x10000000 0x00 0x100>;
compatible = "ns16550a";
};
virtio-input 键盘设备
virtio_mmio@10005000 {
interrupts = <0x05>;
interrupt-parent = <0x03>;
reg = <0x00 0x10005000 0x00 0x1000>;
compatible = "virtio,mmio";
};
virtio-input 鼠标设备
virtio_mmio@10006000 {
interrupts = <0x06>;
interrupt-parent = <0x03>;
reg = <0x00 0x10006000 0x00 0x1000>;
compatible = "virtio,mmio";
};
virtio-gpu 显示设备
virtio_mmio@10007000 {
interrupts = <0x07>;
interrupt-parent = <0x03>;
reg = <0x00 0x10007000 0x00 0x1000>;
compatible = "virtio,mmio";
};
PLIC中断源
PLIC支持多个中断源,每个中断源可以是不同触发类型,电平触发或者边沿触发、PLIC为每个中断源分配一个不同的编号。
PLIC中断处理流程
PLIC中断源
PLIC中断源
PLIC中断源
每个中断目标的中断源均分配了一个中断使能(IE)寄存器,IE寄存器是可读写寄存器,从而使得软件对其编程
git clone https://github.com/rcore-os/rCore-Tutorial-v3.git
cd rCore-Tutorial-v3
git checkout ch9
应用程序没有改变,但在串口输入输出、块设备读写的IO操作上是基于中断方式实现的。
内核的主要修改 ( os/src
)
├── boards
│ └── qemu.rs // UART、VIRTIO、PLIC的MMIO地址
├── console.rs //基于UART的STDIO
├── drivers
│ ├── block
│ │ └── virtio_blk.rs //基于中断/DMA方式的VIRTIO-BLK驱动
│ ├── chardev
│ │ └── ns16550a.rs //基于中断方式的串口驱动
│ └── plic.rs //PLIC驱动
├── main.rs //外设中断相关初始化
└── trap
├── mod.rs //支持处理外设中断
└── trap.S //支持内核态响应外设中断
设备直接相关(提供)
OS交互相关(需求)
const VIRT_PLIC: usize = 0xC00_0000; // PLIC
const VIRT_UART: usize = 0x1000_0000; // UART
const VIRTIO0: usize = 0x10008000; // VIRTIO_BLOCK
const VIRTIO5: usize = 0x10005000; // VIRTIO_KEYBOARD
const VIRTIO6: usize = 0x10006000; // VIRTIO_MOUSE
const VIRTIO7: usize = 0x10007000; // VIRTIO_GPU
// 在总中断处理例程中对不同外设的中断进行响应
match intr_src_id {
5 => KEYBOARD_DEVICE.handle_irq(),
6 => MOUSE_DEVICE.handle_irq(),
8 => BLOCK_DEVICE.handle_irq(),
10 => UART.handle_irq(),
系统设备管理初始化
sie
CSR寄存器,使能响应外部中断os/src/drivers/plic.rs
和 os/src/boards/qemu.rs::devices_init()
UART设备驱动的核心数据结构
pub struct NS16550a<const BASE_ADDR: usize> {
inner: UPIntrFreeCell<NS16550aInner>,
condvar: Condvar, //用于挂起/唤醒读字符的经常
}
struct NS16550aInner {
ns16550a: NS16550aRaw,
read_buffer: VecDeque<u8>, //用于缓存读取的字符
}
pub struct NS16550aRaw {
base_addr: usize, //控制寄存器基址
}
字符类设备需要实现的接口
pub trait CharDevice {
fn init(&self);
fn read(&self) -> u8;
fn write(&self, ch: u8);
fn handle_irq(&self);
}
impl<const BASE_ADDR: usize> CharDevice for NS16550a<BASE_ADDR> {
fn init(&self) {
let mut inner = self.inner.exclusive_access(); //独占访问
inner.ns16550a.init(); //调用ns16550a的UART初始化函数
drop(inner);
}
fn handle_irq(&self) {
let mut count = 0;
self.inner.exclusive_session(|inner| {
//调用ns16550a中读字符函数
while let Some(ch) = inner.ns16550a.read() {
count += 1;
inner.read_buffer.push_back(ch);
...
if count > 0 {
// 唤醒等待读取字符的进程
self.condvar.signal();
...
fn read(&self) -> u8 {
loop {
let mut inner = self.inner.exclusive_access();
if let Some(ch) = inner.read_buffer.pop_front() {
return ch;
} else {
let task_cx_ptr = self.condvar.wait_no_sched();
drop(inner);
schedule(task_cx_ptr);
...
fn write(&self, ch: u8) {
let mut inner = self.inner.exclusive_access();
inner.ns16550a.write(ch);
}
virtio_blk设备驱动的核心数据结构
pub struct VirtIOBlock {
virtio_blk: UPIntrFreeCell<VirtIOBlk<'static, VirtioHal>>,
condvars: BTreeMap<u16, Condvar>, //<虚拟队列号,条件变量>映射
}
存储类设备要实现的接口
pub trait BlockDevice: Send + Sync + Any {
fn read_block(&self, block_id: usize, buf: &mut [u8]);
fn write_block(&self, block_id: usize, buf: &[u8]);
fn handle_irq(&self);
}
pub fn new() -> Self {
let virtio_blk = unsafe {
UPIntrFreeCell::new(
// 初始化vritio_drivers中的VirtIOBlk块设备
VirtIOBlk::<VirtioHal>::new(&mut *(VIRTIO0 as *mut VirtIOHeader)).unwrap(),)
let mut condvars = BTreeMap::new();
let channels = virtio_blk.exclusive_access().virt_queue_size();
// 建立虚拟队列号与条件变量的映射
for i in 0..channels {
let condvar = Condvar::new();
condvars.insert(i, condvar);
}
...
fn handle_irq(&self) {
self.virtio_blk.exclusive_session(|blk| {
//获得块访问完成的虚拟队列号
while let Ok(token) = blk.pop_used() {
// 根据队列号对应的信号量,唤醒等待块访问结束的挂起进程
self.condvars.get(&token).unwrap().signal();
}
...
fn read_block(&self, block_id: usize, buf: &mut [u8]) {
...
let mut resp = BlkResp::default();// 生成一个块访问命令
let task_cx_ptr = self.virtio_blk.exclusive_session(|blk| {
// 调用virtio_drivers库中VirtIOBlk的read_block_nb函数,发出读块命令
let token = unsafe { blk.read_block_nb(block_id, buf, &mut resp).unwrap() };
// 通过条件变量挂起当前进程,等待块访问结束
self.condvars.get(&token).unwrap().wait_no_sched()
});
// 唤醒等待块访问结束的进程
schedule(task_cx_ptr);
...
}
fn write_block(&self, block_id: usize, buf: &[u8]) {
...
let mut resp = BlkResp::default(); // 生成一个块访问命令
let task_cx_ptr = self.virtio_blk.exclusive_session(|blk| {
// 调用virtio_drivers库中VirtIOBlk的read_block_nb函数,发出写块命令
let token = unsafe { blk.write_block_nb(block_id, buf, &mut resp).unwrap() };
// 通过条件变量挂起当前进程,等待块访问结束
self.condvars.get(&token).unwrap().wait_no_sched()
});
// 唤醒等待块访问结束的进程
schedule(task_cx_ptr);
...
侏罗猎龙的属名(Juravenator)来自拉丁语中的“Jura”(意为“侏罗纪”)及“Venator”(意为“猎人”),意思是“侏罗纪的猎人”。
这从某种角度说明了磁盘驱动程序促使了UNIX的诞生。
https://blog.csdn.net/weixin_40604731/article/details/109279426 2020.10.25 RISC-V --PLIC平台级中断控制器
https://blog.csdn.net/qq_42866646/article/details/120845750 PLIC简介&&cva6之PLIC模块阅读笔记
https://github.com/riscv/riscv-plic-spec/blob/master/images/PLICArch.jpg
https://github.com/riscv/riscv-plic-spec/blob/master/images/PLICInterruptFlow.jpg
https://github.com/riscv/riscv-plic-spec/blob/master/images/PLICArch.jpg
https://blog.csdn.net/weixin_40604731/article/details/109279426 2020.10.25 RISC-V --PLIC平台级中断控制器
https://blog.csdn.net/weixin_40604731/article/details/109279426 2020.10.25 RISC-V --PLIC平台级中断控制器
https://blog.csdn.net/weixin_40604731/article/details/109279426 2020.10.25 RISC-V --PLIC平台级中断控制器