1use bitflags::bitflags;
8use zerocopy::{FromBytes, Immutable};
9use zx_sys::zx_paddr_t;
10
11use crate::{
12    AsHandleRef, Handle, HandleBased, HandleRef, Iommu, ObjectQuery, Pmt, Status, Topic, Vmo,
13    object_get_info_single, ok, sys,
14};
15
16#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
21#[repr(transparent)]
22pub struct Bti(Handle);
23impl_handle_based!(Bti);
24
25static_assert_align!(
26    #[doc="Ergonomic equivalent of [sys::zx_info_bti_t]. Must be ABI-compatible with it."]
27    #[repr(C)]
28    #[derive(Debug, Copy, Clone, Eq, PartialEq, FromBytes, Immutable)]
29    <sys::zx_info_bti_t> pub struct BtiInfo {
30        pub minimum_contiguity: u64,
31        pub aspace_size: u64,
32        pub pmo_count: u64,
33        pub quarantine_count: u64,
34    }
35);
36
37impl Default for BtiInfo {
38    fn default() -> BtiInfo {
39        Self::from(sys::zx_info_bti_t::default())
40    }
41}
42
43impl From<sys::zx_info_bti_t> for BtiInfo {
44    fn from(info: sys::zx_info_bti_t) -> BtiInfo {
45        zerocopy::transmute!(info)
46    }
47}
48
49struct BtiInfoQuery;
50unsafe impl ObjectQuery for BtiInfoQuery {
51    const TOPIC: Topic = Topic::BTI;
52    type InfoTy = sys::zx_info_bti_t;
53}
54
55bitflags! {
56    #[repr(transparent)]
58    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
59    pub struct BtiOptions: u32 {
60        const PERM_READ = sys::ZX_BTI_PERM_READ;
61        const PERM_WRITE = sys::ZX_BTI_PERM_WRITE;
62        const PERM_EXECUTE = sys::ZX_BTI_PERM_EXECUTE;
63        const COMPRESS = sys::ZX_BTI_COMPRESS;
64        const CONTIGUOUS = sys::ZX_BTI_CONTIGUOUS;
65    }
66}
67
68impl Bti {
69    pub fn create(iommu: &Iommu, id: u64) -> Result<Bti, Status> {
73        let mut bti_handle = crate::sys::zx_handle_t::default();
74        let status = unsafe {
75            crate::sys::zx_bti_create(iommu.raw_handle(), 0, id, &mut bti_handle)
77        };
78        ok(status)?;
79        unsafe {
80            Ok(Bti::from(Handle::from_raw(bti_handle)))
83        }
84    }
85
86    pub fn info(&self) -> Result<BtiInfo, Status> {
89        Ok(BtiInfo::from(object_get_info_single::<BtiInfoQuery>(self.as_handle_ref())?))
90    }
91
92    pub fn pin(
96        &self,
97        options: BtiOptions,
98        vmo: &Vmo,
99        offset: u64,
100        size: u64,
101        out_paddrs: &mut [zx_paddr_t],
102    ) -> Result<Pmt, Status> {
103        let mut pmt = crate::sys::zx_handle_t::default();
104        let status = unsafe {
105            crate::sys::zx_bti_pin(
108                self.raw_handle(),
109                options.bits(),
110                vmo.raw_handle(),
111                offset,
112                size,
113                out_paddrs.as_mut_ptr(),
114                out_paddrs.len(),
115                &mut pmt,
116            )
117        };
118        ok(status)?;
119        unsafe {
120            Ok(Pmt::from(Handle::from_raw(pmt)))
123        }
124    }
125}
126
127#[cfg(test)]
128mod tests {
129    use super::*;
130    use crate::{IommuDescDummy, ObjectType, Resource, Vmo};
131    use fidl_fuchsia_kernel as fkernel;
132    use fuchsia_component::client::connect_channel_to_protocol;
133
134    #[test]
135    fn create_bti_invalid_handle() {
136        let status = Bti::create(&Iommu::from(Handle::invalid()), 0);
137        assert_eq!(status, Err(Status::BAD_HANDLE));
138    }
139
140    #[test]
141    fn create_bti_wrong_handle() {
142        let vmo = Vmo::create(0).unwrap();
143        let wrong_handle = unsafe { Iommu::from(Handle::from_raw(vmo.into_raw())) };
144
145        let status = Bti::create(&wrong_handle, 0);
146        assert_eq!(status, Err(Status::WRONG_TYPE));
147    }
148
149    fn create_iommu() -> Iommu {
150        use zx::{Channel, HandleBased, MonotonicInstant};
151        let (client_end, server_end) = Channel::create();
152        connect_channel_to_protocol::<fkernel::IommuResourceMarker>(server_end).unwrap();
153        let service = fkernel::IommuResourceSynchronousProxy::new(client_end);
154        let resource =
155            service.get(MonotonicInstant::INFINITE).expect("couldn't get iommu resource");
156        let resource = unsafe { Resource::from(Handle::from_raw(resource.into_raw())) };
160        Iommu::create_dummy(&resource, IommuDescDummy::default()).unwrap()
161    }
162
163    #[test]
164    fn create_from_valid_iommu() {
165        let iommu = create_iommu();
166        let bti = Bti::create(&iommu, 0).unwrap();
167
168        let info = bti.as_handle_ref().basic_info().unwrap();
169        assert_eq!(info.object_type, ObjectType::BTI);
170        let bti_info = bti.info().unwrap();
171        assert!(bti_info.minimum_contiguity >= crate::system_get_page_size() as u64);
173        assert!(bti_info.minimum_contiguity % crate::system_get_page_size() as u64 == 0);
174    }
175
176    #[test]
177    fn create_and_pin_from_valid_iommu() {
178        let iommu = create_iommu();
179        let bti = Bti::create(&iommu, 0).unwrap();
180
181        let vmo = Vmo::create_contiguous(&bti, 4096, 0).unwrap();
182        let mut paddr = [0x1]; let pmt = bti.pin(BtiOptions::PERM_READ, &vmo, 0, 4096, &mut paddr[..]).unwrap();
185        assert_ne!(paddr, [0x1]);
186
187        let info = pmt.as_handle_ref().basic_info().unwrap();
188        assert_eq!(info.object_type, ObjectType::PMT);
189
190        pmt.unpin().unwrap();
191    }
192}