|
| 1 | +use crate::syscall_scope::ShareList; |
| 2 | +use crate::Syscalls; |
| 3 | + |
| 4 | +// ----------------------------------------------------------------------------- |
| 5 | +// `Subscribe` struct |
| 6 | +// ----------------------------------------------------------------------------- |
| 7 | + |
| 8 | +/// A `Subscribe` instance allows safe code to call Tock's Subscribe system |
| 9 | +/// call, by guaranteeing the upcall will be cleaned up before 'scope ends. It |
| 10 | +/// is generally used with the `syscall_scope` function, which offers a safe |
| 11 | +/// interface for constructing `Subscribe` instances. |
| 12 | +pub struct Subscribe<'scope, S: Syscalls, const DRIVER_NUM: u32, const SUBSCRIBE_NUM: u32> { |
| 13 | + _syscalls: core::marker::PhantomData<S>, |
| 14 | + |
| 15 | + // Make this struct invariant with respect to the 'scope lifetime. |
| 16 | + // |
| 17 | + // Covariance would be unsound, as that would allow code with a |
| 18 | + // `Subscribe<'static, ...>` to register an upcall that lasts for a shorter |
| 19 | + // lifetime, resulting in use-after-free if the upcall in invoked. |
| 20 | + // Contravariance would be sound, but is not necessary and may be confusing. |
| 21 | + // |
| 22 | + // Additionally, we want to have at least one private member of this struct |
| 23 | + // so that code outside this module cannot construct a `Subscribe` without |
| 24 | + // calling `ShareList::new`. |
| 25 | + _scope: core::marker::PhantomData<core::cell::Cell<&'scope ()>>, |
| 26 | +} |
| 27 | + |
| 28 | +// TODO: Test Drop impl |
| 29 | +impl<'scope, S: Syscalls, const DRIVER_NUM: u32, const SUBSCRIBE_NUM: u32> Drop |
| 30 | + for Subscribe<'scope, S, DRIVER_NUM, SUBSCRIBE_NUM> |
| 31 | +{ |
| 32 | + fn drop(&mut self) { |
| 33 | + S::unsubscribe(DRIVER_NUM, SUBSCRIBE_NUM); |
| 34 | + } |
| 35 | +} |
| 36 | + |
| 37 | +impl<'scope, S: Syscalls, const DRIVER_NUM: u32, const SUBSCRIBE_NUM: u32> ShareList<'scope> |
| 38 | + for Subscribe<'scope, S, DRIVER_NUM, SUBSCRIBE_NUM> |
| 39 | +{ |
| 40 | + unsafe fn new() -> Subscribe<'scope, S, DRIVER_NUM, SUBSCRIBE_NUM> { |
| 41 | + Subscribe { |
| 42 | + _syscalls: core::marker::PhantomData, |
| 43 | + _scope: core::marker::PhantomData, |
| 44 | + } |
| 45 | + } |
| 46 | +} |
| 47 | + |
| 48 | +// ----------------------------------------------------------------------------- |
| 49 | +// `Upcall` trait |
| 50 | +// ----------------------------------------------------------------------------- |
| 51 | + |
| 52 | +/// A Tock kernel upcall. Upcalls are registered using the Subscribe system |
| 53 | +/// call, and are invoked during Yield calls. |
| 54 | +/// |
| 55 | +/// Each `Upcall` supports one or more subscribe IDs, which are indicated by the |
| 56 | +/// `SupportedIds` parameter. The types `AnySubscribeId` and `OneSubscribeId` |
| 57 | +/// are provided to use as `SupportedIds` parameters in `Upcall` |
| 58 | +/// implementations. |
| 59 | +pub trait Upcall<SupportedIds> { |
| 60 | + fn upcall(&self, arg0: u32, arg1: u32, arg2: u32); |
| 61 | +} |
| 62 | + |
| 63 | +pub trait SupportsId<const DRIVER_NUM: u32, const SUBSCRIBE_NUM: u32> {} |
| 64 | + |
| 65 | +pub struct AnyId; |
| 66 | +impl<const DRIVER_NUM: u32, const SUBSCRIBE_NUM: u32> SupportsId<DRIVER_NUM, SUBSCRIBE_NUM> |
| 67 | + for AnyId |
| 68 | +{ |
| 69 | +} |
| 70 | + |
| 71 | +pub struct OneId<const DRIVER_NUM: u32, const SUBSCRIBE_NUM: u32>; |
| 72 | +impl<const DRIVER_NUM: u32, const SUBSCRIBE_NUM: u32> SupportsId<DRIVER_NUM, SUBSCRIBE_NUM> |
| 73 | + for OneId<DRIVER_NUM, SUBSCRIBE_NUM> |
| 74 | +{ |
| 75 | +} |
| 76 | + |
| 77 | +// ----------------------------------------------------------------------------- |
| 78 | +// Upcall implementations that simply store their arguments |
| 79 | +// ----------------------------------------------------------------------------- |
| 80 | + |
| 81 | +/// An implementation of `Upcall` that sets the contained boolean value to |
| 82 | +/// `true` when the upcall is invoked. |
| 83 | +impl Upcall<AnyId> for core::cell::Cell<bool> { |
| 84 | + fn upcall(&self, _: u32, _: u32, _: u32) { |
| 85 | + self.set(true); |
| 86 | + } |
| 87 | +} |
| 88 | + |
| 89 | +/// Implemented for consistency with the other `Cell<Option<...>>` `Upcall` |
| 90 | +/// impls. Most users would prefer the `Cell<bool>` implementation over this |
| 91 | +/// impl, but this may be useful in a generic or macro context. |
| 92 | +impl Upcall<AnyId> for core::cell::Cell<Option<()>> { |
| 93 | + fn upcall(&self, _: u32, _: u32, _: u32) { |
| 94 | + self.set(Some(())); |
| 95 | + } |
| 96 | +} |
| 97 | + |
| 98 | +/// An `Upcall` implementation that stores its first argument when called. |
| 99 | +impl Upcall<AnyId> for core::cell::Cell<Option<(u32,)>> { |
| 100 | + fn upcall(&self, arg0: u32, _: u32, _: u32) { |
| 101 | + self.set(Some((arg0,))); |
| 102 | + } |
| 103 | +} |
| 104 | + |
| 105 | +/// An `Upcall` implementation that stores its first two arguments when called. |
| 106 | +impl Upcall<AnyId> for core::cell::Cell<Option<(u32, u32)>> { |
| 107 | + fn upcall(&self, arg0: u32, arg1: u32, _: u32) { |
| 108 | + self.set(Some((arg0, arg1))); |
| 109 | + } |
| 110 | +} |
| 111 | + |
| 112 | +/// An `Upcall` implementation that stores its arguments when called. |
| 113 | +impl Upcall<AnyId> for core::cell::Cell<Option<(u32, u32, u32)>> { |
| 114 | + fn upcall(&self, arg0: u32, arg1: u32, arg2: u32) { |
| 115 | + self.set(Some((arg0, arg1, arg2))); |
| 116 | + } |
| 117 | +} |
| 118 | + |
| 119 | +#[cfg(test)] |
| 120 | +#[test] |
| 121 | +fn upcall_impls() { |
| 122 | + let cell_bool = core::cell::Cell::new(false); |
| 123 | + cell_bool.upcall(1, 2, 3); |
| 124 | + assert!(cell_bool.get()); |
| 125 | + |
| 126 | + let cell_empty = core::cell::Cell::new(None); |
| 127 | + cell_empty.upcall(1, 2, 3); |
| 128 | + assert_eq!(cell_empty.get(), Some(())); |
| 129 | + |
| 130 | + let cell_one = core::cell::Cell::new(None); |
| 131 | + cell_one.upcall(1, 2, 3); |
| 132 | + assert_eq!(cell_one.get(), Some((1,))); |
| 133 | + |
| 134 | + let cell_two = core::cell::Cell::new(None); |
| 135 | + cell_two.upcall(1, 2, 3); |
| 136 | + assert_eq!(cell_two.get(), Some((1, 2))); |
| 137 | + |
| 138 | + let cell_three = core::cell::Cell::new(None); |
| 139 | + cell_three.upcall(1, 2, 3); |
| 140 | + assert_eq!(cell_three.get(), Some((1, 2, 3))); |
| 141 | +} |
| 142 | + |
| 143 | +// ----------------------------------------------------------------------------- |
| 144 | +// `Config` trait |
| 145 | +// ----------------------------------------------------------------------------- |
| 146 | + |
| 147 | +/// `Config` configures the behavior of the Subscribe system call. It should |
| 148 | +/// generally be passed through by drivers, to allow application code to |
| 149 | +/// configure error handling. |
| 150 | +pub trait Config { |
| 151 | + /// Called if a Subscribe call succeeds and returns a non-null upcall. In |
| 152 | + /// some applications, this may indicate unexpected reentrance. By default, |
| 153 | + /// the non-null upcall is ignored. |
| 154 | + fn returned_nonnull_upcall(_driver_num: u32, _subscribe_num: u32) {} |
| 155 | +} |
0 commit comments