twizzler_abi/device/
mod.rs

1//! APIs for accessing the device tree and device representation objects.
2
3use core::{
4    fmt::Display,
5    sync::atomic::{AtomicU16, AtomicU64, Ordering},
6    time::Duration,
7};
8
9use twizzler_rt_abi::error::TwzError;
10
11use crate::{
12    kso::KsoHdr,
13    syscall::{
14        sys_thread_sync, ThreadSync, ThreadSyncFlags, ThreadSyncOp, ThreadSyncReference,
15        ThreadSyncSleep, ThreadSyncWake,
16    },
17};
18
19pub mod bus;
20
21pub const NUM_DEVICE_INTERRUPTS: usize = 32;
22
23/// Possible high-level device types.
24#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug)]
25#[repr(u32)]
26pub enum DeviceType {
27    /// An unknown device type. Should be ignored.
28    Unknown = 0,
29    /// A bus. This device has numerous children and should be enumerated.
30    Bus = 1,
31    /// A traditional "device". It may still have children, but their meaning is device-specific.
32    Device = 2,
33}
34
35/// All supported kernel-discovered bus types.
36#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug)]
37#[repr(u32)]
38pub enum BusType {
39    /// An unknown bus. Should be ignored.
40    Unknown = 0,
41    /// The "system" bus. Typically comprised of devices created by the kernel.
42    System = 1,
43    /// PCIe.
44    Pcie = 2,
45}
46
47/// A device will have a number of sub-objects to present enough information and access for a
48/// userspace driver to be implemented.
49#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug)]
50#[repr(u32)]
51pub enum SubObjectType {
52    /// An info sub-object, which is comprised of a device-specific (or bus-specific) information
53    /// structure.
54    Info = 0,
55    /// A mapping of the MMIO registers for this device into an object.
56    Mmio = 1,
57}
58
59impl From<SubObjectType> for u8 {
60    fn from(x: SubObjectType) -> Self {
61        match x {
62            SubObjectType::Info => 0,
63            SubObjectType::Mmio => 1,
64        }
65    }
66}
67
68impl TryFrom<u8> for SubObjectType {
69    type Error = ();
70    fn try_from(x: u8) -> Result<Self, ()> {
71        Ok(match x {
72            0 => SubObjectType::Info,
73            1 => SubObjectType::Mmio,
74            _ => return Err(()),
75        })
76    }
77}
78
79/// For MMIO registers, we may need to specify the caching type.
80#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
81#[repr(u32)]
82pub enum CacheType {
83    WriteBack = 0,
84    WriteCombining = 1,
85    WriteThrough = 2,
86    Uncacheable = 3,
87    MemoryMappedIO = 4,
88}
89
90/// Info struct at the base of an mmio sub-object.
91#[derive(Debug)]
92#[repr(C)]
93pub struct MmioInfo {
94    /// The length of this mapping.
95    pub length: u64,
96    /// The cache type.
97    pub cache_type: CacheType,
98    /// Device-specific info.
99    pub info: u64,
100}
101
102/// An mmio object has, at its base, a [MmioInfo] struct. At this offset, the mmio mapping actually
103/// starts.
104pub const MMIO_OFFSET: usize = 0x2000;
105
106bitflags::bitflags! {
107    /// Possible flags for device interrupts.
108    pub struct DeviceInterruptFlags: u16 {}
109}
110
111/// A vector number (used by the kernel).
112#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug)]
113#[repr(transparent)]
114pub struct InterruptVector(u32);
115
116impl TryFrom<u64> for InterruptVector {
117    type Error = TwzError;
118
119    fn try_from(value: u64) -> Result<Self, Self::Error> {
120        let u: u32 = value.try_into().map_err(|_| TwzError::INVALID_ARGUMENT)?;
121        Ok(InterruptVector(u))
122    }
123}
124
125impl From<InterruptVector> for u32 {
126    fn from(iv: InterruptVector) -> Self {
127        iv.0
128    }
129}
130
131/// A per-bus device ID.
132#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug)]
133#[repr(transparent)]
134pub struct DeviceId(u32);
135
136impl DeviceId {
137    pub fn new(v: u32) -> Self {
138        Self(v)
139    }
140}
141
142#[repr(C)]
143pub struct DeviceInterrupt {
144    pub sync: AtomicU64,
145    pub vec: InterruptVector,
146    pub flags: DeviceInterruptFlags,
147    pub taken: AtomicU16,
148}
149
150#[derive(Clone, Copy, Debug)]
151pub enum MailboxPriority {
152    Idle,
153    Low,
154    High,
155    Num,
156}
157
158impl TryFrom<usize> for MailboxPriority {
159    type Error = ();
160
161    fn try_from(value: usize) -> Result<Self, Self::Error> {
162        Ok(match value {
163            0 => MailboxPriority::Idle,
164            1 => MailboxPriority::Low,
165            2 => MailboxPriority::High,
166            3 => MailboxPriority::Num,
167            _ => return Err(()),
168        })
169    }
170}
171/// The base struct for a device object.
172#[repr(C)]
173pub struct DeviceRepr {
174    kso_hdr: KsoHdr,
175    pub device_type: DeviceType,
176    pub bus_type: BusType,
177    pub device_id: DeviceId,
178    pub interrupts: [DeviceInterrupt; NUM_DEVICE_INTERRUPTS],
179    pub mailboxes: [AtomicU64; MailboxPriority::Num as usize],
180}
181
182impl Display for DeviceRepr {
183    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
184        write!(
185            f,
186            "Device::{{{:?}, {:?}, {:?}}}({})",
187            self.device_type, self.bus_type, self.device_id, self.kso_hdr
188        )
189    }
190}
191
192impl DeviceRepr {
193    /// Construct a new device repr.
194    pub fn new(
195        kso_hdr: KsoHdr,
196        device_type: DeviceType,
197        bus_type: BusType,
198        device_id: DeviceId,
199    ) -> Self {
200        // Clippy complains about this, but I don't know another way to do it cleanly.
201        // There probably is one, but I don't know it. Anyways, this is fine because we're
202        // never writing to V.
203        #[allow(clippy::declare_interior_mutable_const)]
204        const V: DeviceInterrupt = DeviceInterrupt {
205            sync: AtomicU64::new(0),
206            vec: InterruptVector(0),
207            flags: DeviceInterruptFlags::empty(),
208            taken: AtomicU16::new(0),
209        };
210
211        #[allow(clippy::declare_interior_mutable_const)]
212        const M: AtomicU64 = AtomicU64::new(0);
213        Self {
214            kso_hdr,
215            device_type,
216            bus_type,
217            device_id,
218            interrupts: [V; NUM_DEVICE_INTERRUPTS],
219            mailboxes: [M; MailboxPriority::Num as usize],
220        }
221    }
222
223    /// Block until an interrupt fires.
224    pub fn wait_for_interrupt(&self, inum: usize, timeout: Option<Duration>) -> u64 {
225        loop {
226            let val = self.interrupts[inum].sync.swap(0, Ordering::SeqCst);
227            if val != 0 {
228                return val;
229            }
230            // Spin for a bit
231            for _ in 0..100 {
232                let val = self.interrupts[inum].sync.load(Ordering::SeqCst);
233                if val != 0 {
234                    return self.interrupts[inum].sync.swap(0, Ordering::SeqCst);
235                }
236            }
237            let op = ThreadSync::new_sleep(ThreadSyncSleep::new(
238                ThreadSyncReference::Virtual(&self.interrupts[inum].sync as *const AtomicU64),
239                0,
240                ThreadSyncOp::Equal,
241                ThreadSyncFlags::empty(),
242            ));
243            let res = crate::syscall::sys_thread_sync(&mut [op], timeout);
244            if res.is_err() {
245                return 0;
246            }
247        }
248    }
249
250    pub fn setup_interrupt_sleep(&self, inum: usize) -> ThreadSyncSleep {
251        ThreadSyncSleep {
252            reference: ThreadSyncReference::Virtual(&self.interrupts[inum].sync),
253            value: 0,
254            op: ThreadSyncOp::Equal,
255            flags: ThreadSyncFlags::empty(),
256        }
257    }
258
259    pub fn submit_mailbox_msg(&self, mb: MailboxPriority, msg: u64) {
260        while self.mailboxes[mb as usize]
261            .compare_exchange(0, msg, Ordering::SeqCst, Ordering::SeqCst)
262            .is_err()
263        {
264            core::hint::spin_loop()
265        }
266        let _ = sys_thread_sync(
267            &mut [ThreadSync::new_wake(ThreadSyncWake::new(
268                ThreadSyncReference::Virtual(&self.mailboxes[mb as usize]),
269                usize::MAX,
270            ))],
271            None,
272        );
273    }
274
275    /// Poll an interrupt vector to see if it has fired.
276    pub fn check_for_interrupt(&self, inum: usize) -> Option<u64> {
277        let val = self.interrupts[inum].sync.swap(0, Ordering::SeqCst);
278        if val == 0 {
279            None
280        } else {
281            Some(val)
282        }
283    }
284
285    /// Poll an interrupt vector to see if it has fired.
286    pub fn check_for_mailbox(&self, inum: usize) -> Option<u64> {
287        let val = self.mailboxes[inum].swap(0, Ordering::SeqCst);
288        if val == 0 {
289            None
290        } else {
291            Some(val)
292        }
293    }
294
295    /// Register an interrupt vector with this device.
296    pub fn register_interrupt(
297        &mut self,
298        inum: usize,
299        vec: InterruptVector,
300        flags: DeviceInterruptFlags,
301    ) {
302        self.interrupts[inum].vec = vec;
303        self.interrupts[inum].flags = flags;
304        self.interrupts[inum].sync = AtomicU64::new(0);
305    }
306}