use crate::{
bind::CfgBindRegistry,
kernel::{hook, interrupt, raw, raw_cfg},
utils::{refcell::RefCell, ComptimeVec, ConstAllocator, Frozen, Init, PhantomInvariant},
};
macro overview_ref() {
"See [`KernelStatic`][]'s documentation for an overview of the
configuration process and the traits involved."
}
pub struct Cfg<'c, C: raw_cfg::CfgBase> {
raw: &'c mut C,
st: CfgSt,
pub(crate) shared: &'c CfgShared,
pub(super) startup_hooks: ComptimeVec<hook::CfgStartupHook>,
pub(super) hunk_pool_len: usize,
pub(super) hunk_pool_align: usize,
pub(super) interrupt_lines: ComptimeVec<interrupt::CfgInterruptLineInfo>,
pub(super) interrupt_handlers: ComptimeVec<interrupt::CfgInterruptHandler>,
}
#[doc(hidden)]
pub struct CfgShared {
pub(crate) bind_registry: RefCell<CfgBindRegistry>,
}
#[derive(PartialEq, Eq)]
enum CfgSt {
Phase1,
Phase2,
Phase3 { interrupts: bool },
}
impl CfgShared {
pub const fn __internal_new(allocator: &ConstAllocator) -> Self {
Self {
bind_registry: RefCell::new(CfgBindRegistry::new_in(allocator.clone())),
}
}
}
impl<'c, C: raw_cfg::CfgBase> Cfg<'c, C> {
const fn new(
raw: &'c mut C,
shared: &'c CfgShared,
allocator: &'c ConstAllocator,
st: CfgSt,
) -> Self {
Self {
raw,
st,
shared,
startup_hooks: ComptimeVec::new_in(allocator.clone()),
hunk_pool_len: 0,
hunk_pool_align: 1,
interrupt_lines: ComptimeVec::new_in(allocator.clone()),
interrupt_handlers: ComptimeVec::new_in(allocator.clone()),
}
}
#[doc(hidden)]
pub const fn __internal_new_phase1(
raw: &'c mut C,
shared: &'c CfgShared,
allocator: &'c ConstAllocator,
) -> Self {
Self::new(raw, shared, allocator, CfgSt::Phase1)
}
#[doc(hidden)]
pub const fn __internal_new_phase2(
raw: &'c mut C,
shared: &'c CfgShared,
allocator: &'c ConstAllocator,
) -> Self
where
C::System: CfgPhase1,
{
Self::new(raw, shared, allocator, CfgSt::Phase2)
}
#[doc(hidden)]
pub const fn __internal_new_phase3(
raw: &'c mut C,
shared: &'c CfgShared,
allocator: &'c ConstAllocator,
) -> Self
where
C::System: CfgPhase2,
{
Self::new(raw, shared, allocator, CfgSt::Phase3 { interrupts: false })
}
pub const fn raw(&mut self) -> &mut C {
self.raw
}
pub const fn num_task_priority_levels(&mut self, new_value: usize)
where
C: ~const raw_cfg::CfgBase,
{
self.raw.num_task_priority_levels(new_value);
}
pub const fn finish_phase1(&mut self) -> CfgPhase1Data<C::System>
where
C: ~const raw_cfg::CfgBase,
{
assert!(
matches!(self.st, CfgSt::Phase1),
"this `Cfg` wasn't made for phase 1 configuration"
);
self.shared.bind_registry.borrow_mut().finalize(self);
hook::sort_hooks(&mut self.startup_hooks);
interrupt::sort_handlers(&mut self.interrupt_handlers);
CfgPhase1Data {
_phantom: Init::INIT,
startup_hooks: Frozen::leak_slice(
&self.startup_hooks.map(hook::CfgStartupHook::to_attr),
),
hunk_pool_len: self.hunk_pool_len,
hunk_pool_align: self.hunk_pool_align,
interrupt_handlers: Frozen::leak_slice(&self.interrupt_handlers),
}
}
pub const fn finish_phase2(&mut self) -> CfgPhase2Data<C::System>
where
C: ~const raw_cfg::CfgBase,
{
assert!(
matches!(self.st, CfgSt::Phase2),
"this `Cfg` wasn't made for phase 2 configuration"
);
self.shared.bind_registry.borrow_mut().finalize(self);
hook::sort_hooks(&mut self.startup_hooks);
interrupt::sort_handlers(&mut self.interrupt_handlers);
CfgPhase2Data {
_phantom: Init::INIT,
}
}
pub const fn finish_phase3_interrupt(&mut self)
where
C: ~const raw_cfg::CfgInterruptLine,
C::System: CfgPhase2 + raw::KernelInterruptLine,
{
match &mut self.st {
CfgSt::Phase3 { interrupts } => {
assert!(
!*interrupts,
"interrupt line finalization (`Cfg::finish_phase3_interrupt`)
has already been done on this `Cfg`"
);
*interrupts = true;
}
_ => {
panic!("this `Cfg` wasn't made for phase 3 configuration");
}
}
interrupt::panic_if_unmanaged_safety_is_violated::<C::System>(
&self.interrupt_lines,
&self.interrupt_handlers,
);
let mut i = 0;
while i < self.interrupt_lines.len() {
let interrupt_line = &self.interrupt_lines[i];
let start = C::System::CFG_INTERRUPT_HANDLERS
.get(interrupt_line.num)
.copied()
.flatten();
self.raw.interrupt_line_define(
raw_cfg::InterruptLineDescriptor {
phantom: Init::INIT,
line: interrupt_line.num,
priority: interrupt_line.priority,
start,
enabled: interrupt_line.enabled,
},
(),
);
i += 1;
}
self.interrupt_lines.clear();
self.interrupt_handlers.clear();
}
pub const fn finish_phase3(self) -> CfgPhase3Data<C::System>
where
C: ~const raw_cfg::CfgBase,
C::System: CfgPhase2,
{
assert!(
matches!(self.st, CfgSt::Phase3 { .. }),
"this `Cfg` wasn't made for phase 3 configuration"
);
assert!(
self.interrupt_lines.is_empty() && self.interrupt_handlers.is_empty(),
"missing call to `Cfg::finish_phase3_interrupt`"
);
self.raw.startup_hook_define(startup_hook::<C::System>);
#[inline(always)]
fn startup_hook<System: CfgPhase2>() {
for startup_hook in System::CFG_STARTUP_HOOKS.iter() {
startup_hook.start.call();
}
}
CfgPhase3Data {
_phantom: Init::INIT,
}
}
}
#[doc = overview_ref!()]
pub struct CfgPhase1Data<System> {
_phantom: PhantomInvariant<System>,
pub startup_hooks: &'static [Frozen<hook::StartupHookAttr>],
pub hunk_pool_len: usize,
pub hunk_pool_align: usize,
pub interrupt_handlers: &'static [Frozen<interrupt::CfgInterruptHandler>],
}
#[doc = overview_ref!()]
pub struct CfgPhase2Data<System> {
_phantom: PhantomInvariant<System>,
}
#[doc = overview_ref!()]
pub struct CfgPhase3Data<System> {
_phantom: PhantomInvariant<System>,
}
#[doc = svgbobdoc::transform!(
/// ```svgbob
/// Kernel-provided | Application-provided
/// configuration macro | configuration function
/// ---------------------------------------------------------------------------
///
/// C "Cfg<C>" "&mut Cfg<C>"
///
/// │ │ │
/// ┌┴┐C::new │ │
/// │ │ "cfg_phase1!" │ │
/// │ │ ------------------> ┌┴┐ $configure │
/// │ │ │ │ ------------------> ┌┴┐
/// │ │ finish_phase1 │ │ └┬┘
/// └┬┘ ,--------- └┬┘ │
/// │ | │ │
/// │ v │ │
/// │ .-----------------. │ │
/// │ | CfgPhase1Data | │ │
/// │ '-----------------' │ │
/// │ | │ │
/// │ v │ │
/// │ "attach_phase1!" │ │
/// │ | │ │
/// │ v │ │
/// │ .-------------------. │ │
/// │ | impl CfgPhase1 | │ │
/// │ | for C::System | │ │
/// │ '-------------------' │ │
/// │ | │ │
/// ┌┴┐C::new | │ │
/// │ │ v "cfg_phase2!"│ │
/// │ │ ------+-----------> ┌┴┐ $configure │
/// │ │ │ │ ------------------> ┌┴┐
/// │ │ finish_phase2 │ │ └┬┘
/// └┬┘ .--------- └┬┘ │
/// │ | │ │
/// │ v │ │
/// │ .-----------------. │ │
/// │ | CfgPhase2Data | │ │
/// │ '-----------------' │ │
/// │ | │ │
/// │ v │ │
/// │ "attach_phase2!" │ │
/// │ | │ │
/// │ v │ │
/// │ .-------------------. │ │
/// │ | impl CfgPhase2 | │ │
/// │ | for C::System | │ │
/// │ '-------------------' │ │
/// │ | │ │
/// ┌┴┐C::new | │ │
/// │ │ v "cfg_phase3!"│ │
/// │ │ ------+-----------> ┌┴┐ $configure │
/// │ │ │ │ ------------------> ┌┴┐
/// │ │ finish_phase3 │ │ └┬┘
/// │ │ <--------+--------- └┬┘ │
/// .------ └┬┘ | │ │
/// | │ v │ │
/// v │ .-----------------. │ │
/// Kernel-specific │ | CfgPhase3Data | │ │
/// configuration │ '-----------------' │ │
/// process │ | │ │
/// │ v │ │
/// │ "attach_phase3!" │ │
/// │ | │ │
/// │ v │ │
/// │ .-------------------. │ │
/// │ | impl KernelStatic | │ │
/// │ | for C::System | │ │
/// │ '-------------------' │ │
/// │ │ │
/// ```
)]
#[doc = include_str!("../common.md")]
pub trait KernelStatic<System = Self>: CfgPhase2<System> {}
#[doc = overview_ref!()]
pub trait CfgPhase2<System = Self>: CfgPhase1<System> {}
#[doc = overview_ref!()]
pub trait CfgPhase1<System = Self> {
const CFG_STARTUP_HOOKS: &'static [hook::StartupHookAttr];
const CFG_INTERRUPT_HANDLERS: &'static [Option<interrupt::InterruptHandlerFn>];
fn cfg_hunk_pool_ptr() -> *mut u8;
}
pub trait DelegateKernelStatic<System> {
type Target;
}
impl<T: DelegateKernelStatic<System>, System> KernelStatic<System> for T where
T::Target: KernelStatic<System>
{
}
impl<T: DelegateKernelStatic<System>, System> CfgPhase2<System> for T where
T::Target: CfgPhase2<System>
{
}
impl<T: DelegateKernelStatic<System>, System> CfgPhase1<System> for T
where
T::Target: CfgPhase1<System>,
{
const CFG_STARTUP_HOOKS: &'static [hook::StartupHookAttr] = T::Target::CFG_STARTUP_HOOKS;
const CFG_INTERRUPT_HANDLERS: &'static [Option<interrupt::InterruptHandlerFn>] =
T::Target::CFG_INTERRUPT_HANDLERS;
#[inline(always)]
fn cfg_hunk_pool_ptr() -> *mut u8 {
T::Target::cfg_hunk_pool_ptr()
}
}
pub macro cfg_phase3(
let mut $cfg:ident = Cfg::<$RawCfg:ty>::new($raw_cfg:expr, $allocator:expr)
) {
let allocator: &_ = $allocator;
let shared = CfgShared::__internal_new(allocator);
let mut $cfg = Cfg::<$RawCfg>::__internal_new_phase3(&mut *$raw_cfg, &shared, allocator);
}
pub macro cfg_phase2(
let mut $cfg:ident = Cfg::<$RawCfg:ty>::new($raw_cfg:expr, $allocator:expr)
) {
let allocator: &_ = $allocator;
let shared = CfgShared::__internal_new(allocator);
let mut $cfg = Cfg::<$RawCfg>::__internal_new_phase2(&mut *$raw_cfg, &shared, allocator);
}
pub macro cfg_phase1(
let mut $cfg:ident = Cfg::<$RawCfg:ty>::new($raw_cfg:expr, $allocator:expr)
) {
let allocator: &_ = $allocator;
let shared = CfgShared::__internal_new(allocator);
let mut $cfg = Cfg::<$RawCfg>::__internal_new_phase1(&mut *$raw_cfg, &shared, allocator);
}
#[doc = overview_ref!()]
pub macro attach_phase3($params:expr, impl KernelStatic<$System:ty> for $Ty:ty $(,)?) {
const _: () = {
const _: $crate::kernel::cfg::CfgPhase3Data<$System> = $params;
impl $crate::kernel::cfg::KernelStatic<$System> for $Ty {}
};
}
#[doc = overview_ref!()]
pub macro attach_phase2($params:expr, impl CfgPhase2<$System:ty> for $Ty:ty $(,)?) {
const _: () = {
const _: $crate::kernel::cfg::CfgPhase2Data<$System> = $params;
impl $crate::kernel::cfg::CfgPhase2<$System> for $Ty {}
};
}
#[doc = overview_ref!()]
pub macro attach_phase1($params:expr, impl CfgPhase1<$System:ty> for $Ty:ty $(,)?) {
const _: () = {
use $crate::{
kernel::{cfg, hook, interrupt},
utils::{for_times::U, AlignedStorage, Init, RawCell},
};
const STATIC_PARAMS: cfg::CfgPhase1Data<$System> = $params;
static HUNK_POOL: RawCell<
AlignedStorage<{ STATIC_PARAMS.hunk_pool_len }, { STATIC_PARAMS.hunk_pool_align }>,
> = Init::INIT;
array_item_from_fn! {
const STARTUP_HOOKS: [hook::StartupHookAttr; _] =
(0..STATIC_PARAMS.startup_hooks.len())
.map(|i| STATIC_PARAMS.startup_hooks[i].get());
}
array_item_from_fn! {
const INTERRUPT_HANDLERS: [interrupt::CfgInterruptHandler; _] =
(0..STATIC_PARAMS.interrupt_handlers.len())
.map(|i| STATIC_PARAMS.interrupt_handlers[i].get());
}
const NUM_INTERRUPT_HANDLERS: usize = INTERRUPT_HANDLERS.len();
const NUM_INTERRUPT_LINES: usize =
interrupt::num_required_interrupt_line_slots(&INTERRUPT_HANDLERS);
struct Handlers;
impl interrupt::CfgInterruptHandlerList for Handlers {
type NumHandlers = U<NUM_INTERRUPT_HANDLERS>;
const HANDLERS: &'static [interrupt::CfgInterruptHandler] = &INTERRUPT_HANDLERS;
}
const INTERRUPT_HANDLERS_COMBINED: [Option<interrupt::InterruptHandlerFn>;
NUM_INTERRUPT_LINES] = unsafe {
interrupt::new_interrupt_handler_table::<
$System,
U<NUM_INTERRUPT_LINES>,
Handlers,
NUM_INTERRUPT_LINES,
NUM_INTERRUPT_HANDLERS,
>()
};
impl $crate::kernel::cfg::CfgPhase1<$System> for $Ty {
const CFG_STARTUP_HOOKS: &'static [hook::StartupHookAttr] = &STARTUP_HOOKS;
const CFG_INTERRUPT_HANDLERS: &'static [Option<interrupt::InterruptHandlerFn>] =
&INTERRUPT_HANDLERS_COMBINED;
#[inline(always)]
fn cfg_hunk_pool_ptr() -> *mut u8 {
HUNK_POOL.get() as *mut u8
}
}
};
}
#[allow(unused_macros)]
macro array_item_from_fn($(
$static_or_const:tt $out:ident: [$ty:ty; _] = (0..$len:expr).map(|$var:ident| $map:expr);
)*) {$(
$static_or_const $out: [$ty; { $len }] = {
use $crate::{core::mem::MaybeUninit, utils::mem};
let mut values: [MaybeUninit<$ty>; { $len }] = mem::uninit_array();
let mut i = 0;
while i < $len {
values[i] = MaybeUninit::<$ty>::new({
let $var = i;
$map
});
i += 1;
}
unsafe { mem::transmute(values) }
};
)*}
#[const_trait]
pub trait CfgStatic: ~const raw_cfg::CfgBase<System: KernelStatic> {}
impl<C> const CfgStatic for C
where
C: ~const raw_cfg::CfgBase,
C::System: KernelStatic,
{
}