Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions awkernel_lib/src/arch/rv_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub(super) mod interrupt;
pub mod interrupt_controller;
pub(super) mod page_table;
pub(super) mod paging;
pub mod plic;
pub(super) mod power;
pub(super) mod vm;

Expand Down
53 changes: 46 additions & 7 deletions awkernel_lib/src/arch/rv_common/clint.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::cpu::{cpu_id, num_cpu};
use core::ptr::{read_volatile, write_volatile};
use riscv::register::mie;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
Expand Down Expand Up @@ -33,6 +34,36 @@ impl Clint {
(self.base_addr + Self::MTIME_OFFSET) as *const u64
}

#[inline]
pub fn enable_soft_interrupt(&self) {
unsafe { mie::set_msoft() };
}

#[inline]
pub fn disable_soft_interrupt(&self) {
unsafe { mie::clear_msoft() };
}

#[inline]
pub fn enable_timer_interrupt(&self) {
unsafe { mie::set_mtimer() };
}

#[inline]
pub fn disable_timer_interrupt(&self) {
unsafe { mie::clear_mtimer() };
}

#[inline]
pub fn is_soft_interrupt_pending(&self) -> bool {
mie::read().msoft()
}

#[inline]
pub fn is_timer_interrupt_pending(&self) -> bool {
mie::read().mtimer()
}

/// Busy wait for a given number of microseconds.
#[inline]
pub fn wait_usec(&self, usec: u64) {
Expand Down Expand Up @@ -65,7 +96,7 @@ impl Clint {
///
/// The caller must ensure that the hart_id is valid.
#[inline]
unsafe fn soft_interrupt_unchecked(&self, hart_id: usize) {
unsafe fn set_soft_interrupt_unchecked(&self, hart_id: usize) {
// Safety: The caller must ensure that the hart_id is valid.
unsafe { write_volatile(self.msip(hart_id), 1) };
}
Expand All @@ -74,33 +105,41 @@ impl Clint {
///
/// If the hart_id is invalid, return an error.
#[inline]
pub fn soft_interrupt(&self, hart_id: usize) -> Result<(), Error> {
pub fn set_soft_interrupt(&self, hart_id: usize) -> Result<(), Error> {
if hart_id >= num_cpu() {
return Err(Error::InvalidHartId);
}
// Safety: hart_id is within the range of available harts
unsafe { self.soft_interrupt_unchecked(hart_id) };
unsafe { self.set_soft_interrupt_unchecked(hart_id) };
Ok(())
}

/// Trigger a software interrupt to all harts, including the current hart.
#[inline]
pub fn soft_interrupt_broadcast(&self) {
pub fn set_soft_interrupt_broadcast(&self) {
for hart_id in 0..num_cpu() {
// Safety: hart_id is within the range of available harts
unsafe { self.soft_interrupt_unchecked(hart_id) };
unsafe { self.set_soft_interrupt_unchecked(hart_id) };
}
}

/// Trigger a software interrupt to all harts, excluding the current hart.
#[inline]
pub fn soft_interrupt_broadcast_without_self(&self) {
pub fn set_soft_interrupt_broadcast_without_self(&self) {
let self_hart_id = cpu_id();
for hart_id in 0..num_cpu() {
if hart_id != self_hart_id {
// Safety: hart_id is within the range of available harts
unsafe { self.soft_interrupt_unchecked(hart_id) };
unsafe { self.set_soft_interrupt_unchecked(hart_id) };
}
}
}

/// Clear the software interrupt for the current hart.
#[inline]
pub fn clear_soft_interrupt(&self) {
let hart_id = cpu_id();
// Safety: hart_id is within the range of available harts
unsafe { write_volatile(self.msip(hart_id), 0) };
}
}
91 changes: 90 additions & 1 deletion awkernel_lib/src/arch/rv_common/interrupt_controller.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,107 @@
use super::clint::Clint;
use super::{clint::Clint, plic::Plic};
use crate::{cpu::cpu_id, interrupt};
use alloc::boxed::Box;

// TODO: get info from device tree
static INTERRUPT_CONTROLLER: InterruptController = InterruptController {
clint: Clint {
base_addr: 0x0200_0000,
mtime_freq: 10_000_000,
},
plic: Plic {
base_addr: 0x0c00_0000,
max_priority: 7,
num_sources: 53,
num_contexts: 16,
},
};

pub struct InterruptController {
clint: Clint,
plic: Plic,
}

#[inline]
pub(super) const fn clint() -> &'static Clint {
&INTERRUPT_CONTROLLER.clint
}

impl interrupt::InterruptController for InterruptController {
#[inline]
fn enable_irq(&mut self, irq: u16) {
match irq {
0 => self.clint.enable_soft_interrupt(),
_ => {
let context = cpu_id() as u16; // TODO: support S-mode contexts
let _ = self.plic.enable_interrupt(context, irq);
}
}
}

#[inline]
fn disable_irq(&mut self, irq: u16) {
match irq {
0 => self.clint.disable_soft_interrupt(),
_ => {
let context = cpu_id() as u16; // TODO: support S-mode contexts
let _ = self.plic.disable_interrupt(context, irq);
}
}
}

#[inline]
fn pending_irqs(&self) -> alloc::boxed::Box<dyn Iterator<Item = u16>> {
let context = cpu_id() as u16; // TODO: support S-mode contexts
let mut pending_irqs = alloc::vec::Vec::new();
if self.clint.is_soft_interrupt_pending() {
self.clint.clear_soft_interrupt();
pending_irqs.push(0);
}
// Safety: context is in range
while let Some(irq) = unsafe { self.plic.claim_interrupt_unchecked(context) } {
pending_irqs.push(irq);
}
Box::new(pending_irqs.into_iter())
}

#[inline]
fn send_ipi(&mut self, irq: u16, cpu_id: u32) {
if irq == 0 {
if let Err(e) = self.clint.set_soft_interrupt(cpu_id as _) {
log::warn!("Failed to send IPI to CPU {}: {:?}", cpu_id, e);
}
} else {
log::warn!("PLIC does not support sending IPIs, ignoring IRQ {}", irq);
}
}

#[inline]
fn send_ipi_broadcast(&mut self, irq: u16) {
if irq == 0 {
self.clint.set_soft_interrupt_broadcast();
} else {
log::warn!("PLIC does not support sending IPIs, ignoring IRQ {}", irq);
}
}

#[inline]
fn send_ipi_broadcast_without_self(&mut self, irq: u16) {
if irq == 0 {
self.clint.set_soft_interrupt_broadcast_without_self();
} else {
log::warn!("PLIC does not support sending IPIs, ignoring IRQ {}", irq);
}
}

#[inline]
fn irq_range(&self) -> (u16, u16) {
// 0 is CLINT, 1..=num_sources are PLIC
(0, self.plic.num_sources + 1)
}

#[inline]
fn irq_range_for_pnp(&self) -> (u16, u16) {
// PLIC cannot be used for IPIs, so the range is 1..=num_sources
(1, self.plic.num_sources + 1)
}
}
Loading
Loading