Skip to content

Commit

Permalink
Merge pull request #765 from stlankes/aarch64
Browse files Browse the repository at this point in the history
AArch64: use a software-generated interrupt (SGI)to reschedule the system
  • Loading branch information
mkroening authored Jun 20, 2023
2 parents 0bd4b47 + c7c5083 commit bfb4550
Show file tree
Hide file tree
Showing 13 changed files with 520 additions and 312 deletions.
88 changes: 33 additions & 55 deletions src/arch/aarch64/kernel/core_local.rs
Original file line number Diff line number Diff line change
@@ -1,94 +1,72 @@
use alloc::boxed::Box;
use core::arch::asm;
use core::cell::Cell;
use core::ptr;
use core::sync::atomic::Ordering;

use super::interrupts::{IrqStatistics, IRQ_COUNTERS};
use super::CPU_ONLINE;
use crate::scheduler::{CoreId, PerCoreScheduler};

#[no_mangle]
pub static mut CORE_LOCAL: CoreLocal = CoreLocal::new(0);

pub struct CoreLocal {
this: *const Self,
/// ID of the current Core.
core_id: CoreLocalVariable<CoreId>,
core_id: CoreId,
/// Scheduler of the current Core.
scheduler: CoreLocalVariable<*mut PerCoreScheduler>,
scheduler: Cell<*mut PerCoreScheduler>,
/// Interface to the interrupt counters
irq_statistics: &'static IrqStatistics,
}

impl CoreLocal {
pub const fn new(core_id: CoreId) -> Self {
Self {
core_id: CoreLocalVariable::new(core_id),
scheduler: CoreLocalVariable::new(0 as *mut PerCoreScheduler),
}
}

pub fn install() {
let core_id = CPU_ONLINE.load(Ordering::Relaxed);

let irq_statistics = &*Box::leak(Box::new(IrqStatistics::new()));
IRQ_COUNTERS.lock().insert(core_id, irq_statistics);
}
}

#[repr(C)]
pub struct CoreLocalVariable<T> {
data: T,
}

pub trait CoreLocalVariableMethods<T: Clone> {
unsafe fn get(&self) -> T;
unsafe fn set(&mut self, value: T);
}

impl<T> CoreLocalVariable<T> {
const fn new(value: T) -> Self {
Self { data: value }
}
}
let this = Self {
this: ptr::null_mut(),
core_id,
scheduler: Cell::new(ptr::null_mut()),
irq_statistics,
};
let mut this = Box::leak(Box::new(this));
this.this = &*this;

// Treat all per-core variables as 64-bit variables by default. This is true for u64, usize, pointers.
// Implement the CoreLocalVariableMethods trait functions using 64-bit memory moves.
// The functions are implemented as default functions, which can be overridden in specialized implementations of the trait.
impl<T> CoreLocalVariableMethods<T> for CoreLocalVariable<T>
where
T: Clone,
{
#[inline]
default unsafe fn get(&self) -> T {
self.data.clone()
unsafe {
asm!("msr tpidr_el1, {}", in(reg) this, options(nomem, nostack, preserves_flags));
}
}

#[inline]
default unsafe fn set(&mut self, value: T) {
self.data = value;
pub fn get() -> &'static Self {
unsafe {
let raw: *const Self;
asm!("mrs {}, tpidr_el1", out(reg) raw, options(nomem, nostack, preserves_flags));
&*raw
}
}
}

#[inline]
pub fn core_id() -> CoreId {
unsafe { CORE_LOCAL.core_id.get() }
if cfg!(target_os = "none") {
CoreLocal::get().core_id
} else {
0
}
}

#[inline]
pub fn core_scheduler() -> &'static mut PerCoreScheduler {
unsafe { &mut *CORE_LOCAL.scheduler.get() }
unsafe { &mut *CoreLocal::get().scheduler.get() }
}

#[inline]
pub fn set_core_scheduler(scheduler: *mut PerCoreScheduler) {
unsafe {
CORE_LOCAL.scheduler.set(scheduler);
}
CoreLocal::get().scheduler.set(scheduler);
}

pub fn increment_irq_counter(irq_no: u8) {
unsafe {
let id = CORE_LOCAL.core_id.get();
if let Some(counter) = IRQ_COUNTERS.lock().get(&id) {
counter.inc(irq_no);
} else {
warn!("Unknown core {}, is core {} already registered?", id, id);
}
}
CoreLocal::get().irq_statistics.inc(irq_no);
}
64 changes: 46 additions & 18 deletions src/arch/aarch64/kernel/interrupts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,26 @@ use crate::arch::aarch64::kernel::scheduler::State;
use crate::arch::aarch64::mm::paging::{self, BasePageSize, PageSize, PageTableEntryFlags};
use crate::arch::aarch64::mm::{virtualmem, PhysAddr};
use crate::core_scheduler;
use crate::errno::EFAULT;
use crate::scheduler::{self, CoreId};

pub const IST_SIZE: usize = 8 * BasePageSize::SIZE as usize;

/// maximum number of interrupt handlers
const MAX_HANDLERS: usize = 256;
/// The ID of the first Private Peripheral Interrupt.
const PPI_START: u8 = 16;
/// The ID of the first Shared Peripheral Interrupt.
const SPI_START: u8 = 32;
/// Software-generated interrupt for rescheduling
pub(crate) const SGI_RESCHED: u8 = 1;

/// Number of the timer interrupt
static mut TIMER_INTERRUPT: u32 = 0;
/// Possible interrupt handlers
static mut INTERRUPT_HANDLERS: [Option<fn(state: &State)>; MAX_HANDLERS] = [None; MAX_HANDLERS];
static mut INTERRUPT_HANDLERS: [Option<fn(state: &State) -> bool>; MAX_HANDLERS] =
[None; MAX_HANDLERS];
/// Driver for the Arm Generic Interrupt Controller version 3 (or 4).
pub(crate) static mut GIC: OnceCell<GicV3> = OnceCell::new();

fn timer_handler(_state: &State) {
fn timer_handler(_state: &State) -> bool {
debug!("Handle timer interrupt");

// disable timer
Expand All @@ -47,6 +47,8 @@ fn timer_handler(_state: &State) {
options(nostack, nomem),
);
}

true
}

/// Enable all interrupts
Expand Down Expand Up @@ -88,16 +90,17 @@ pub fn disable() {
}
}

pub fn irq_install_handler(irq_number: u8, handler: fn(state: &State)) {
pub(crate) fn irq_install_handler(irq_number: u8, handler: fn(state: &State) -> bool) {
debug!("Install handler for interrupt {}", irq_number);
unsafe {
INTERRUPT_HANDLERS[irq_number as usize + SPI_START as usize] = Some(handler);
}
}

#[no_mangle]
pub extern "C" fn do_fiq(state: &State) {
pub(crate) extern "C" fn do_fiq(state: &State) -> *mut usize {
if let Some(irqid) = GicV3::get_and_acknowledge_interrupt() {
let mut reschedule: bool = false;
let vector: usize = u32::from(irqid).try_into().unwrap();

debug!("Receive fiq {}", vector);
Expand All @@ -106,7 +109,7 @@ pub extern "C" fn do_fiq(state: &State) {
if vector < MAX_HANDLERS {
unsafe {
if let Some(handler) = INTERRUPT_HANDLERS[vector] {
handler(state);
reschedule = handler(state);
}
}
}
Expand All @@ -115,16 +118,25 @@ pub extern "C" fn do_fiq(state: &State) {

GicV3::end_interrupt(irqid);

if unsafe { vector == TIMER_INTERRUPT.try_into().unwrap() } {
if unsafe {
reschedule
|| vector == TIMER_INTERRUPT.try_into().unwrap()
|| vector == SGI_RESCHED.try_into().unwrap()
} {
// a timer interrupt may have caused unblocking of tasks
core_scheduler().scheduler();
return core_scheduler()
.scheduler()
.unwrap_or(core::ptr::null_mut());
}
}

core::ptr::null_mut()
}

#[no_mangle]
pub extern "C" fn do_irq(state: &State) {
pub(crate) extern "C" fn do_irq(state: &State) -> *mut usize {
if let Some(irqid) = GicV3::get_and_acknowledge_interrupt() {
let mut reschedule: bool = false;
let vector: usize = u32::from(irqid).try_into().unwrap();

debug!("Receive interrupt {}", vector);
Expand All @@ -133,7 +145,7 @@ pub extern "C" fn do_irq(state: &State) {
if vector < MAX_HANDLERS {
unsafe {
if let Some(handler) = INTERRUPT_HANDLERS[vector] {
handler(state);
reschedule = handler(state);
}
}
}
Expand All @@ -142,15 +154,23 @@ pub extern "C" fn do_irq(state: &State) {

GicV3::end_interrupt(irqid);

if unsafe { vector == TIMER_INTERRUPT.try_into().unwrap() } {
if unsafe {
reschedule
|| vector == TIMER_INTERRUPT.try_into().unwrap()
|| vector == SGI_RESCHED.try_into().unwrap()
} {
// a timer interrupt may have caused unblocking of tasks
core_scheduler().scheduler();
return core_scheduler()
.scheduler()
.unwrap_or(core::ptr::null_mut());
}
}

core::ptr::null_mut()
}

#[no_mangle]
pub extern "C" fn do_sync(_state: &State) {
pub(crate) extern "C" fn do_sync(state: &State) {
let irqid = GicV3::get_and_acknowledge_interrupt().unwrap();
let esr = ESR_EL1.get();
let ec = esr >> 26;
Expand All @@ -166,6 +186,7 @@ pub extern "C" fn do_sync(_state: &State) {

// add page fault handler

error!("Current stack pointer {:#x}", state as *const _ as u64);
error!("Unable to handle page fault at {:#x}", far);
error!("Exception return address {:#x}", ELR_EL1.get());
error!("Thread ID register {:#x}", TPIDR_EL0.get());
Expand All @@ -185,14 +206,14 @@ pub extern "C" fn do_sync(_state: &State) {
}

#[no_mangle]
pub extern "C" fn do_bad_mode(_state: &State, reason: u32) -> ! {
pub(crate) extern "C" fn do_bad_mode(_state: &State, reason: u32) -> ! {
error!("Receive unhandled exception: {}", reason);

scheduler::abort()
}

#[no_mangle]
pub extern "C" fn do_error(_state: &State) -> ! {
pub(crate) extern "C" fn do_error(_state: &State) -> ! {
error!("Receive error interrupt");

scheduler::abort()
Expand All @@ -202,7 +223,7 @@ pub fn wakeup_core(_core_to_wakeup: CoreId) {
todo!("wakeup_core stub");
}

pub fn init() {
pub(crate) fn init() {
info!("Intialize generic interrupt controller");

let dtb = unsafe {
Expand Down Expand Up @@ -311,6 +332,13 @@ pub fn init() {
}
}

let reschedid = IntId::sgi(SGI_RESCHED.into());
gic.set_interrupt_priority(reschedid, 0x00);
gic.enable_interrupt(reschedid, true);
IRQ_NAMES
.lock()
.insert(u8::try_from(SGI_RESCHED).unwrap(), "Reschedule");

unsafe {
GIC.set(gic).unwrap();
}
Expand Down
7 changes: 5 additions & 2 deletions src/arch/aarch64/kernel/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub mod switch;
pub mod systemtime;

use core::arch::global_asm;
use core::sync::atomic::{AtomicU32, Ordering};
use core::sync::atomic::{AtomicU32, AtomicU64, Ordering};

use hermit_entry::boot_info::{BootInfo, RawBootInfo};

Expand All @@ -29,7 +29,9 @@ static mut COM1: SerialPort = SerialPort::new(0x800);
/// `CPU_ONLINE` is the count of CPUs that finished initialization.
///
/// It also synchronizes initialization of CPU cores.
pub static CPU_ONLINE: AtomicU32 = AtomicU32::new(0);
pub(crate) static CPU_ONLINE: AtomicU32 = AtomicU32::new(0);

pub(crate) static CURRENT_STACK_ADDRESS: AtomicU64 = AtomicU64::new(0);

global_asm!(include_str!("start.s"));

Expand Down Expand Up @@ -209,6 +211,7 @@ pub fn boot_application_processors() {
}

/// Application Processor initialization
#[allow(dead_code)]
pub fn application_processor_init() {
CoreLocal::install();
finish_processor_init();
Expand Down
Loading

0 comments on commit bfb4550

Please sign in to comment.