feat: kernel: drivers: add plic

This commit is contained in:
Paul Pan 2024-07-08 20:12:47 +08:00
parent 392d5ce59e
commit 272fdf303c
8 changed files with 213 additions and 2 deletions

2
Cargo.lock generated
View File

@ -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"

View File

@ -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

View File

@ -0,0 +1,8 @@
use crate::drivers::Driver;
use crate::plat::irq::IrqController;
pub trait IrqDriver: Driver + IrqController {}
mod plic;
pub use plic::IrqPlic;

View File

@ -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<usize> {
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 {}

View File

@ -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};

View File

@ -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)]

25
kernel/src/plat/irq.rs Normal file
View File

@ -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<usize>;
fn complete(&self, irq: usize);
}
pub struct IrqManager {
handler: [CapEntry; IRQ_NUM],
state: [IrqState; IRQ_NUM],
}

View File

@ -1,5 +1,6 @@
pub mod backtrace;
pub mod console;
pub mod irq;
pub mod lowlevel;
pub mod timer;
pub mod trap;