mirror of
https://github.com/panpaul/tiny_os
synced 2024-09-20 01:35:19 +08:00
feat: kernel: drivers: add plic
This commit is contained in:
parent
392d5ce59e
commit
272fdf303c
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -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"
|
||||
|
2
Makefile
2
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
|
||||
|
8
kernel/src/drivers/irq/mod.rs
Normal file
8
kernel/src/drivers/irq/mod.rs
Normal 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;
|
175
kernel/src/drivers/irq/plic.rs
Normal file
175
kernel/src/drivers/irq/plic.rs
Normal 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 {}
|
@ -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};
|
||||
|
@ -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
25
kernel/src/plat/irq.rs
Normal 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],
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
pub mod backtrace;
|
||||
pub mod console;
|
||||
pub mod irq;
|
||||
pub mod lowlevel;
|
||||
pub mod timer;
|
||||
pub mod trap;
|
||||
|
Loading…
Reference in New Issue
Block a user