monitor/mon/
space.rs

1use std::{
2    collections::HashMap,
3    sync::{Arc, Mutex},
4};
5
6use miette::IntoDiagnostic;
7use monitor_api::MappedObjectAddrs;
8use twizzler_abi::{
9    object::Protections,
10    syscall::{
11        sys_object_create, sys_object_ctrl, sys_object_map, sys_object_unmap, BackingType,
12        CreateTieFlags, CreateTieSpec, DeleteFlags, LifetimeType, ObjectControlCmd, ObjectCreate,
13        ObjectCreateFlags, ObjectSource, UnmapFlags,
14    },
15};
16use twizzler_rt_abi::{
17    error::{ResourceError, TwzError},
18    object::{MapFlags, ObjID},
19};
20
21use self::handle::MapHandleInner;
22use crate::gates::SpaceStats;
23
24mod handle;
25mod unmapper;
26
27pub use handle::MapHandle;
28pub use unmapper::Unmapper;
29
30#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
31/// A mapping of an object and flags.
32pub struct MapInfo {
33    pub(crate) id: ObjID,
34    pub(crate) flags: MapFlags,
35}
36
37#[derive(Default)]
38/// An address space we can map objects into.
39pub struct Space {
40    maps: HashMap<MapInfo, MappedObject>,
41}
42
43struct MappedObject {
44    addrs: MappedObjectAddrs,
45    handle_count: usize,
46}
47
48fn mapflags_into_prot(flags: MapFlags) -> Protections {
49    let mut prot = Protections::empty();
50    if flags.contains(MapFlags::READ) {
51        prot.insert(Protections::READ);
52    }
53    if flags.contains(MapFlags::WRITE) {
54        prot.insert(Protections::WRITE);
55    }
56    if flags.contains(MapFlags::EXEC) {
57        prot.insert(Protections::EXEC);
58    }
59    prot
60}
61
62extern "C-unwind" {
63    fn __monitor_get_slot() -> isize;
64    fn __monitor_get_slot_pair(one: *mut usize, two: *mut usize) -> bool;
65    fn __monitor_release_pair(one: usize, two: usize);
66    fn __monitor_release_slot(slot: usize);
67}
68
69impl Space {
70    /// Get the stats.
71    pub fn stat(&self) -> SpaceStats {
72        SpaceStats {
73            mapped: self.maps.len(),
74        }
75    }
76
77    /// Map an object into the space.
78    pub fn map<'a>(this: &Mutex<Self>, info: MapInfo) -> Result<MapHandle, TwzError> {
79        // Can't use the entry API here because the closure may fail.
80        let mut guard = this.lock().unwrap();
81        let item = match guard.maps.get_mut(&info) {
82            Some(item) => item,
83            None => {
84                // Not yet mapped, so allocate a slot and map it.
85                let slot = unsafe { __monitor_get_slot() }
86                    .try_into()
87                    .ok()
88                    .ok_or(ResourceError::OutOfResources)?;
89
90                drop(guard);
91                let res = sys_object_map(
92                    None,
93                    info.id,
94                    slot,
95                    mapflags_into_prot(info.flags),
96                    info.flags.into(),
97                );
98                guard = this.lock().unwrap();
99                let Ok(_) = res else {
100                    unsafe {
101                        __monitor_release_slot(slot);
102                    }
103                    return Err(res.unwrap_err());
104                };
105
106                let map = MappedObject {
107                    addrs: MappedObjectAddrs::new(slot),
108                    handle_count: 0,
109                };
110                guard.maps.insert(info, map);
111                // Unwrap-Ok: just inserted.
112                guard.maps.get_mut(&info).unwrap()
113            }
114        };
115
116        // New maps will be set to zero, so this is unconditional.
117        item.handle_count += 1;
118        Ok(Arc::new(MapHandleInner::new(info, item.addrs)))
119    }
120
121    /// Map a pair of objects into the space.
122    pub fn map_pair(
123        &mut self,
124        info: MapInfo,
125        info2: MapInfo,
126    ) -> Result<(MapHandle, MapHandle), TwzError> {
127        // Not yet mapped, so allocate a slot and map it.
128        let mut one = 0;
129        let mut two = 0;
130        if !unsafe { __monitor_get_slot_pair(&mut one, &mut two) } {
131            return Err(ResourceError::OutOfResources.into());
132        }
133
134        let res = sys_object_map(
135            None,
136            info.id,
137            one,
138            mapflags_into_prot(info.flags),
139            twizzler_abi::syscall::MapFlags::empty(),
140        );
141        if res.is_err() {
142            unsafe {
143                __monitor_release_pair(one, two);
144            }
145            return Err(res.unwrap_err());
146        };
147
148        let res = sys_object_map(
149            None,
150            info2.id,
151            two,
152            mapflags_into_prot(info2.flags),
153            twizzler_abi::syscall::MapFlags::empty(),
154        );
155        if res.is_err() {
156            let _ = sys_object_unmap(None, one, UnmapFlags::empty())
157                .inspect_err(|e| tracing::warn!("failed to unmap first in pair on error: {}", e));
158            unsafe {
159                __monitor_release_pair(one, two);
160            }
161            return Err(res.unwrap_err());
162        };
163
164        let map = MappedObject {
165            addrs: MappedObjectAddrs::new(one),
166            handle_count: 0,
167        };
168        let map2 = MappedObject {
169            addrs: MappedObjectAddrs::new(two),
170            handle_count: 0,
171        };
172        self.maps.insert(info, map);
173        self.maps.insert(info2, map2);
174        // Unwrap-Ok: just inserted.
175        let item = self.maps.get_mut(&info).unwrap();
176        item.handle_count += 1;
177        let addrs = item.addrs;
178        let item2 = self.maps.get_mut(&info2).unwrap();
179        item2.handle_count += 1;
180        let addrs2 = item2.addrs;
181        Ok((
182            Arc::new(MapHandleInner::new(info, addrs)),
183            Arc::new(MapHandleInner::new(info2, addrs2)),
184        ))
185    }
186
187    /// Remove an object from the space. The actual unmapping syscall only happens once the returned
188    /// value from this function is dropped.
189    pub fn handle_drop(&mut self, info: MapInfo) -> Option<UnmapOnDrop> {
190        // Missing maps in unmap should be ignored.
191        let Some(item) = self.maps.get_mut(&info) else {
192            tracing::warn!("unmap called for missing object {:?}", info);
193            return None;
194        };
195        if item.handle_count == 0 {
196            tracing::error!("unmap called for unmapped object {:?}", info);
197            return None;
198        }
199
200        // Decrement and maybe actually unmap.
201        tracing::debug!("drop: {:?}: handle count: {}", info, item.handle_count);
202        item.handle_count -= 1;
203        if item.handle_count == 0 {
204            let slot = item.addrs.slot;
205            self.maps.remove(&info);
206            Some(UnmapOnDrop { slot })
207        } else {
208            None
209        }
210    }
211
212    /// Utility function for creating an object and mapping it, deleting it if the mapping fails.
213    pub(crate) fn safe_create_and_map_object(
214        this: &Mutex<Self>,
215        spec: ObjectCreate,
216        sources: &[ObjectSource],
217        ties: &[CreateTieSpec],
218        map_flags: MapFlags,
219    ) -> miette::Result<MapHandle> {
220        let id = sys_object_create(spec, sources, ties).into_diagnostic()?;
221
222        match Space::map(
223            this,
224            MapInfo {
225                id,
226                flags: map_flags,
227            },
228        ) {
229            Ok(mh) => Ok(mh),
230            Err(me) => {
231                if let Err(e) = sys_object_ctrl(id, ObjectControlCmd::Delete(DeleteFlags::empty()))
232                {
233                    tracing::warn!("failed to delete object {} after map failure {}", e, me);
234                }
235                Err(me)
236            }
237        }
238        .into_diagnostic()
239    }
240
241    pub(crate) fn safe_create_and_map_runtime_object(
242        this: &Mutex<Self>,
243        instance: ObjID,
244        map_flags: MapFlags,
245    ) -> miette::Result<MapHandle> {
246        Space::safe_create_and_map_object(
247            this,
248            ObjectCreate::new(
249                BackingType::Normal,
250                LifetimeType::Volatile,
251                Some(instance),
252                ObjectCreateFlags::DELETE,
253                Protections::all(),
254            ),
255            &[],
256            &[CreateTieSpec::new(instance, CreateTieFlags::empty())],
257            map_flags,
258        )
259    }
260}
261
262/// Allows us to call handle_drop and do all the hard work in the caller, since
263/// the caller probably had to hold a lock to call these functions.
264pub(crate) struct UnmapOnDrop {
265    slot: usize,
266}
267
268impl Drop for UnmapOnDrop {
269    fn drop(&mut self) {
270        match sys_object_unmap(None, self.slot, UnmapFlags::empty()) {
271            Ok(_) => unsafe {
272                __monitor_release_slot(self.slot);
273            },
274            Err(_e) => {
275                // TODO: once the kernel-side works properly, uncomment this.
276                //tracing::warn!("failed to unmap slot {}: {}", self.slot, e);
277            }
278        }
279    }
280}
281
282/// Map an object into the address space, without tracking it. This leaks the mapping, but is useful
283/// for bootstrapping. See the object mapping gate comments for more details.
284pub fn early_object_map(info: MapInfo) -> MappedObjectAddrs {
285    let slot = unsafe { __monitor_get_slot() }.try_into().unwrap();
286
287    sys_object_map(
288        None,
289        info.id,
290        slot,
291        mapflags_into_prot(info.flags),
292        twizzler_abi::syscall::MapFlags::empty(),
293    )
294    .unwrap();
295
296    MappedObjectAddrs::new(slot)
297}