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)]
31pub struct MapInfo {
33 pub(crate) id: ObjID,
34 pub(crate) flags: MapFlags,
35}
36
37#[derive(Default)]
38pub 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 pub fn stat(&self) -> SpaceStats {
72 SpaceStats {
73 mapped: self.maps.len(),
74 }
75 }
76
77 pub fn map<'a>(this: &Mutex<Self>, info: MapInfo) -> Result<MapHandle, TwzError> {
79 let mut guard = this.lock().unwrap();
81 let item = match guard.maps.get_mut(&info) {
82 Some(item) => item,
83 None => {
84 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 guard.maps.get_mut(&info).unwrap()
113 }
114 };
115
116 item.handle_count += 1;
118 Ok(Arc::new(MapHandleInner::new(info, item.addrs)))
119 }
120
121 pub fn map_pair(
123 &mut self,
124 info: MapInfo,
125 info2: MapInfo,
126 ) -> Result<(MapHandle, MapHandle), TwzError> {
127 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 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 pub fn handle_drop(&mut self, info: MapInfo) -> Option<UnmapOnDrop> {
190 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 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 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
262pub(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 }
278 }
279 }
280}
281
282pub 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}