Skip to main content

sel4_microkit_base/
handler.rs

1//
2// Copyright 2023, Colias Group, LLC
3//
4// SPDX-License-Identifier: BSD-2-Clause
5//
6
7use core::fmt;
8
9#[cfg(feature = "alloc")]
10use alloc::boxed::Box;
11
12use crate::{
13    Channel, Child, MessageInfo,
14    defer::{DeferredAction, PreparedDeferredAction},
15    ipc::{self, ChannelSet, Event},
16    pd_is_passive, symbols,
17};
18
19pub use core::convert::Infallible;
20
21/// Trait for the application-specific part of a protection domain's main loop.
22pub trait Handler {
23    /// Error type returned by this protection domain's entrypoints.
24    type Error: fmt::Display;
25
26    /// This method has the same meaning and type as its analog in `libmicrokit`.
27    ///
28    /// The default implementation just panics.
29    fn notified(&mut self, channels: ChannelSet) -> Result<(), Self::Error> {
30        panic!(
31            "unexpected notification from channels {}",
32            channels.display()
33        )
34    }
35
36    /// This method has the same meaning and type as its analog in `libmicrokit`.
37    ///
38    /// The default implementation just panics.
39    fn protected(
40        &mut self,
41        channel: Channel,
42        msg_info: MessageInfo,
43    ) -> Result<MessageInfo, Self::Error> {
44        panic!(
45            "unexpected protected procedure call from channel {channel:?} with msg_info={msg_info:?}"
46        )
47    }
48
49    /// This method has the same meaning and type as its analog in `libmicrokit`.
50    ///
51    /// The default implementation just panics.
52    fn fault(
53        &mut self,
54        child: Child,
55        msg_info: MessageInfo,
56    ) -> Result<Option<MessageInfo>, Self::Error> {
57        panic!("unexpected fault from protection domain {child:?} with msg_info={msg_info:?}")
58    }
59
60    /// An advanced feature for use by protection domains which seek to coalesce syscalls when
61    /// possible.
62    ///
63    /// This method is used by the main loop to fuse a queued `seL4_Send` call with the next
64    /// `seL4_Recv` using `seL4_NBSendRecv`. Its default implementation just returns `None`.
65    fn take_deferred_action(&mut self) -> Option<DeferredAction> {
66        None
67    }
68
69    #[doc(hidden)]
70    fn run(&mut self) -> Result<Never, Self::Error> {
71        let mut reply_tag: Option<MessageInfo> = None;
72
73        let mut prepared_deferred_action: Option<PreparedDeferredAction> = if pd_is_passive() {
74            Some(ipc::forfeit_sc())
75        } else {
76            None
77        };
78
79        // Work around https://github.com/seL4/seL4/issues/1536
80        {
81            let mut bits = symbols::pd_irqs();
82            while bits != 0 {
83                let i = bits.trailing_zeros();
84                Channel::new(i.try_into().unwrap()).irq_ack().unwrap();
85                bits &= bits - 1; // clear lowest bit
86            }
87        }
88
89        loop {
90            let event = match (reply_tag.take(), prepared_deferred_action.take()) {
91                (Some(msg_info), action_opt) => {
92                    if let Some(action) = action_opt {
93                        ipc::send(action);
94                    }
95                    ipc::reply_recv(msg_info)
96                }
97                (None, Some(action)) => ipc::nb_send_recv(action),
98                (None, None) => ipc::recv(),
99            };
100
101            match event {
102                Event::Notified(channels) => {
103                    self.notified(channels)?;
104                }
105                Event::Protected(channel, msg_info) => {
106                    reply_tag = Some(self.protected(channel, msg_info)?);
107                }
108                Event::Fault(child, msg_info) => {
109                    reply_tag = self.fault(child, msg_info)?;
110                }
111            };
112
113            prepared_deferred_action = self
114                .take_deferred_action()
115                .as_ref()
116                .map(DeferredAction::prepare);
117        }
118    }
119}
120
121#[doc(hidden)]
122pub enum Never {}
123
124impl Handler for Never {
125    type Error = Infallible;
126}
127
128/// A [`Handler`] implementation which does not override any of the default method implementations.
129pub struct NullHandler(());
130
131impl NullHandler {
132    #[allow(clippy::new_without_default)]
133    pub fn new() -> Self {
134        Self(())
135    }
136}
137
138impl Handler for NullHandler {
139    type Error = Infallible;
140}
141
142#[cfg(feature = "alloc")]
143impl<T: Handler + ?Sized> Handler for Box<T> {
144    type Error = T::Error;
145
146    fn notified(&mut self, channels: ChannelSet) -> Result<(), Self::Error> {
147        (**self).notified(channels)
148    }
149
150    fn protected(
151        &mut self,
152        channel: Channel,
153        msg_info: MessageInfo,
154    ) -> Result<MessageInfo, Self::Error> {
155        (**self).protected(channel, msg_info)
156    }
157
158    fn fault(
159        &mut self,
160        child: Child,
161        msg_info: MessageInfo,
162    ) -> Result<Option<MessageInfo>, Self::Error> {
163        (**self).fault(child, msg_info)
164    }
165
166    fn take_deferred_action(&mut self) -> Option<DeferredAction> {
167        (**self).take_deferred_action()
168    }
169
170    #[doc(hidden)]
171    fn run(&mut self) -> Result<Never, Self::Error> {
172        (**self).run()
173    }
174}