twizzler_abi/runtime/
object.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//! Implementation of the object runtime.

use core::{ptr::NonNull, sync::atomic::AtomicU64};

use twizzler_rt_abi::object::{MapError, MapFlags, ObjectHandle};

use super::MinimalRuntime;
use crate::{
    object::{ObjID, Protections, MAX_SIZE, NULLPAGE_SIZE},
    runtime::object::slot::global_allocate,
    rustc_alloc::boxed::Box,
    syscall::{sys_object_map, ObjectMapError, UnmapFlags},
};

mod handle;

#[allow(unused_imports)]
pub use handle::*;

pub(crate) mod slot;

impl From<MapFlags> for Protections {
    fn from(value: MapFlags) -> Self {
        let mut f = Self::empty();
        if value.contains(MapFlags::READ) {
            f.insert(Protections::READ);
        }
        if value.contains(MapFlags::WRITE) {
            f.insert(Protections::WRITE);
        }
        if value.contains(MapFlags::EXEC) {
            f.insert(Protections::EXEC);
        }
        f
    }
}

impl From<MapFlags> for crate::syscall::MapFlags {
    fn from(_value: MapFlags) -> Self {
        Self::empty()
    }
}

impl Into<MapError> for ObjectMapError {
    fn into(self) -> MapError {
        match self {
            ObjectMapError::Unknown => MapError::Other,
            ObjectMapError::ObjectNotFound => MapError::NoSuchObject,
            ObjectMapError::InvalidSlot => MapError::Other,
            ObjectMapError::InvalidProtections => MapError::PermissionDenied,
            ObjectMapError::InvalidArgument => MapError::InvalidArgument,
        }
    }
}

#[repr(C)]
struct RuntimeHandleInfo {
    refs: AtomicU64,
}

pub(crate) fn new_runtime_info() -> *mut RuntimeHandleInfo {
    let rhi = Box::new(RuntimeHandleInfo {
        refs: AtomicU64::new(1),
    });
    Box::into_raw(rhi)
}

impl MinimalRuntime {
    pub fn map_object(&self, id: ObjID, flags: MapFlags) -> Result<ObjectHandle, MapError> {
        let slot = global_allocate().ok_or(MapError::OutOfResources)?;
        let _ = sys_object_map(None, id, slot, flags.into(), flags.into()).map_err(|e| e.into())?;
        let start = (slot * MAX_SIZE) as *mut _;
        let meta = (((slot + 1) * MAX_SIZE) - NULLPAGE_SIZE) as *mut _;
        Ok(unsafe {
            ObjectHandle::new(
                id,
                new_runtime_info().cast(),
                start,
                meta,
                flags,
                MAX_SIZE as u32,
            )
        })
    }

    pub fn release_handle(&self, handle: *mut twizzler_rt_abi::bindings::object_handle) {
        let slot = (unsafe { (*handle).start } as usize) / MAX_SIZE;

        if crate::syscall::sys_object_unmap(None, slot, UnmapFlags::empty()).is_ok() {
            slot::global_release(slot);
        }
    }

    /// Map two objects in sequence, useful for executable loading. The default implementation makes
    /// no guarantees about ordering.
    pub fn map_two_objects(
        &self,
        in_id_a: ObjID,
        in_flags_a: MapFlags,
        in_id_b: ObjID,
        in_flags_b: MapFlags,
    ) -> Result<(ObjectHandle, ObjectHandle), MapError> {
        let map_and_check = |rev: bool| {
            let (id_a, flags_a) = if rev {
                (in_id_b, in_flags_b)
            } else {
                (in_id_a, in_flags_a)
            };

            let (id_b, flags_b) = if !rev {
                (in_id_b, in_flags_b)
            } else {
                (in_id_a, in_flags_a)
            };

            let a = self.map_object(id_a, flags_a)?;
            let b = self.map_object(id_b, flags_b)?;
            let a_addr = a.start() as usize;
            let b_addr = b.start() as usize;

            if rev && a_addr > b_addr {
                Ok((b, a))
            } else if !rev && b_addr > a_addr {
                Ok((a, b))
            } else {
                Err(MapError::Other)
            }
        };

        map_and_check(false).or_else(|_| map_and_check(true))
    }
}