diff --git a/Cargo.lock b/Cargo.lock index 5c5b079..78608f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -84,7 +84,7 @@ checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" [[package]] name = "fdt" version = "0.2.0-alpha1" -source = "git+https://github.com/panpaul/fdt.git#f913f139d02c936bbdea99371d9f1347e52b4953" +source = "git+https://github.com/panpaul/fdt.git#68e763be166bfbe150d7824387399cf0a4ac1ce7" [[package]] name = "flate2" diff --git a/Makefile b/Makefile index 7df1a0e..3d7eab1 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,7 @@ CARGO_BUILD_ARGS += $(CARGO_TARGET_ARGS) QEMU = qemu-system-$(ARCH) -QEMU_ARGS = -nographic -serial mon:stdio -smp 1 +QEMU_ARGS = -nographic -serial mon:stdio -smp 4 QEMU_ARGS += -machine virt # TODO: override by $(BOARD) QEMU_ARGS += -kernel target/$(BUILD_TARGET)/$(MODE)/kernel QEMU_ARGS += -initrd root/init.cpio diff --git a/kernel/src/drivers/irq/mod.rs b/kernel/src/drivers/irq/mod.rs new file mode 100644 index 0000000..7385bdd --- /dev/null +++ b/kernel/src/drivers/irq/mod.rs @@ -0,0 +1,8 @@ +use crate::drivers::Driver; +use crate::plat::irq::IrqController; + +pub trait IrqDriver: Driver + IrqController {} + +mod plic; + +pub use plic::IrqPlic; diff --git a/kernel/src/drivers/irq/plic.rs b/kernel/src/drivers/irq/plic.rs new file mode 100644 index 0000000..3139224 --- /dev/null +++ b/kernel/src/drivers/irq/plic.rs @@ -0,0 +1,175 @@ +use super::IrqDriver; +use crate::drivers::Driver; +use crate::entry::HART_ID; +use crate::plat::irq::IrqController; +use fdt::{node::FdtNode, Fdt}; +use log::trace; +use spin::Mutex; + +const IRQ_INVALID: usize = u32::MAX as usize; +const IRQ_S_EXT: usize = 9; +const IRQ_M_EXT: usize = 11; + +const PLIC_PRIORITY_OFFSET: usize = 0; +const PLIC_PENDING_OFFSET: usize = 0x1000; +const PLIC_ENABLE_OFFSET: usize = 0x2000; +const PLIC_ENABLE_STRIDE: usize = 0x80; +const PLIC_CONTROL_OFFSET: usize = 0x200000; +const PLIC_CONTROL_STRIDE: usize = 0x1000; + +#[repr(C, align(0x1000))] +#[derive(Debug)] +struct PlicCtrl { + threshold: *mut u32, + claim: *mut u32, +} + +#[derive(Debug)] +pub struct IrqPlic +where IrqPlic: IrqDriver +{ + priority: *mut u32, + pending: *mut u32, + enable: *mut u32, + control: *mut PlicCtrl, + + nr_irqs: usize, + lock: Mutex<()>, +} + +impl Driver for IrqPlic { + fn compatible() -> &'static [&'static str] { + &["riscv,plic0"] + } + + fn setup(node: FdtNode, fdt: &Fdt) -> Self { + let base_address = node + .reg() + .expect("no PLIC base address available") + .next() + .unwrap() + .starting_address as *mut u8; + + let nr_irqs = node + .property("riscv,ndev") + .expect("riscv,ndev property not available") + .as_usize(); + + let mut ctx_id = None; + for (i, ctx) in node.interrupts_extended().expect("no PLIC context available").enumerate() { + assert!(ctx.interrupts().count() == 1, "PLIC interrupts should have only one cell"); + + let phandle = ctx.phandle; // cpu intc + let irq = ctx.interrupts().next().unwrap(); // IRQ_M_EXT/IRQ_S_EXT + + if irq == IRQ_INVALID { + trace!("[IrqPlic] context {} taken by SBI", i); + continue; + } + + let cpu = fdt + .cpus() + .find(|cpu| { + cpu.as_fdt_node().children().any(|node| { + node.property("phandle") + .is_some_and(|prop| prop.as_usize().unwrap() == phandle as usize) + }) + }) + .expect("PLIC context should be connected to a CPU"); + let hart = cpu.ids().first(); // TODO: support SMT + + if irq != IRQ_S_EXT || hart != HART_ID.get() { + trace!("[IrqPlic] context {} is not for us, irq = {}", i, irq); + continue; + } + + trace!("[IrqPlic] context {} is for us", i); + ctx_id = Some(i); + break; + } + + let ctx_id = ctx_id.expect("No PLIC context found for us"); + + Self { + priority: unsafe { base_address.add(PLIC_PRIORITY_OFFSET) as *mut u32 }, + pending: unsafe { base_address.add(PLIC_PENDING_OFFSET) as *mut u32 }, + enable: unsafe { base_address.add(PLIC_ENABLE_OFFSET + ctx_id * PLIC_ENABLE_STRIDE) as *mut u32 }, + control: unsafe { (base_address.add(PLIC_CONTROL_OFFSET) as *mut PlicCtrl).add(ctx_id) }, + nr_irqs: nr_irqs.unwrap(), + lock: Mutex::new(()), + } + } +} + +impl IrqController for IrqPlic { + fn enable(&self, irq: usize) { + if irq == 0 || irq >= self.nr_irqs { + return; + } + + let word = irq / 32; + let bit = irq % 32; + + { + let guard = self.lock.lock(); + + let val = unsafe { self.enable.add(word).read_volatile() }; + let val = val | (1 << bit); + unsafe { self.enable.add(word).write_volatile(val) }; + + drop(guard); + } + } + + fn disable(&self, irq: usize) { + if irq == 0 || irq >= self.nr_irqs { + return; + } + + let word = irq / 32; + let bit = irq % 32; + + { + let guard = self.lock.lock(); + + let val = unsafe { self.enable.add(word).read_volatile() }; + let val = val & !(1 << bit); + unsafe { self.enable.add(word).write_volatile(val) }; + + drop(guard); + } + } + + fn is_pending(&self, irq: usize) -> bool { + if irq == 0 || irq >= self.nr_irqs { + return false; + } + + let word = irq / 32; + let bit = irq % 32; + + let val = unsafe { self.pending.add(word).read_volatile() }; + val & (1 << bit) != 0 + } + + fn claim(&self) -> Option { + let val = unsafe { self.control.as_ref().unwrap().claim.read_volatile() }; + + if val > 0 { + Some(val as usize) + } else { + None + } + } + + fn complete(&self, irq: usize) { + if irq == 0 || irq >= self.nr_irqs { + return; + } + + let val = irq as u32; + unsafe { self.control.as_ref().unwrap().claim.write_volatile(val) }; + } +} + +impl IrqDriver for IrqPlic {} diff --git a/kernel/src/drivers/mod.rs b/kernel/src/drivers/mod.rs index f57f41c..041809b 100644 --- a/kernel/src/drivers/mod.rs +++ b/kernel/src/drivers/mod.rs @@ -1,6 +1,7 @@ // This will only implement some basic drivers (PLIC, UART, ...) to boot the kernel. // For the rest of the drivers, we will implement them in root server. +pub mod irq; pub mod serial; use fdt::{node::FdtNode, Fdt}; diff --git a/kernel/src/main.rs b/kernel/src/main.rs index bda1af3..55fa7c6 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -5,6 +5,7 @@ #![feature(asm_const)] #![feature(cell_update)] #![feature(concat_idents)] +#![feature(iter_array_chunks)] #![feature(let_chains)] #![feature(naked_functions)] #![feature(thread_local)] diff --git a/kernel/src/plat/irq.rs b/kernel/src/plat/irq.rs new file mode 100644 index 0000000..39041ad --- /dev/null +++ b/kernel/src/plat/irq.rs @@ -0,0 +1,25 @@ +use crate::objects::*; + +const IRQ_NUM: usize = 32; + +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum IrqState { + Inactive, + Signal, + Ipi, + Reserved, +} + +pub trait IrqController { + fn enable(&self, irq: usize); + fn disable(&self, irq: usize); + fn is_pending(&self, irq: usize) -> bool; + fn claim(&self) -> Option; + fn complete(&self, irq: usize); +} + +pub struct IrqManager { + handler: [CapEntry; IRQ_NUM], + state: [IrqState; IRQ_NUM], +} + diff --git a/kernel/src/plat/mod.rs b/kernel/src/plat/mod.rs index bebe061..dbf842a 100644 --- a/kernel/src/plat/mod.rs +++ b/kernel/src/plat/mod.rs @@ -1,5 +1,6 @@ pub mod backtrace; pub mod console; +pub mod irq; pub mod lowlevel; pub mod timer; pub mod trap;