mirror of
https://github.com/panpaul/tiny_os
synced 2024-09-20 09:45: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]]
|
[[package]]
|
||||||
name = "fdt"
|
name = "fdt"
|
||||||
version = "0.2.0-alpha1"
|
version = "0.2.0-alpha1"
|
||||||
source = "git+https://github.com/panpaul/fdt.git#f913f139d02c936bbdea99371d9f1347e52b4953"
|
source = "git+https://github.com/panpaul/fdt.git#68e763be166bfbe150d7824387399cf0a4ac1ce7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
|
2
Makefile
2
Makefile
@ -28,7 +28,7 @@ CARGO_BUILD_ARGS += $(CARGO_TARGET_ARGS)
|
|||||||
|
|
||||||
QEMU = qemu-system-$(ARCH)
|
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 += -machine virt # TODO: override by $(BOARD)
|
||||||
QEMU_ARGS += -kernel target/$(BUILD_TARGET)/$(MODE)/kernel
|
QEMU_ARGS += -kernel target/$(BUILD_TARGET)/$(MODE)/kernel
|
||||||
QEMU_ARGS += -initrd root/init.cpio
|
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.
|
// 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.
|
// For the rest of the drivers, we will implement them in root server.
|
||||||
|
|
||||||
|
pub mod irq;
|
||||||
pub mod serial;
|
pub mod serial;
|
||||||
|
|
||||||
use fdt::{node::FdtNode, Fdt};
|
use fdt::{node::FdtNode, Fdt};
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#![feature(asm_const)]
|
#![feature(asm_const)]
|
||||||
#![feature(cell_update)]
|
#![feature(cell_update)]
|
||||||
#![feature(concat_idents)]
|
#![feature(concat_idents)]
|
||||||
|
#![feature(iter_array_chunks)]
|
||||||
#![feature(let_chains)]
|
#![feature(let_chains)]
|
||||||
#![feature(naked_functions)]
|
#![feature(naked_functions)]
|
||||||
#![feature(thread_local)]
|
#![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 backtrace;
|
||||||
pub mod console;
|
pub mod console;
|
||||||
|
pub mod irq;
|
||||||
pub mod lowlevel;
|
pub mod lowlevel;
|
||||||
pub mod timer;
|
pub mod timer;
|
||||||
pub mod trap;
|
pub mod trap;
|
||||||
|
Loading…
Reference in New Issue
Block a user