zx/
bti.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Type-safe bindings for Zircon bti objects.
6
7use 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/// An object representing a Zircon Bus Transaction Initiator object.
17/// See [BTI Documentation](https://fuchsia.dev/fuchsia-src/reference/kernel_objects/bus_transaction_initiator) for details.
18///
19/// As essentially a subtype of `Handle`, it can be freely interconverted.
20#[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    /// Options that may be used for [`zx::Bti::pin`].
57    #[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    // Create a Bus Transaction Initiator object.
70    // Wraps the
71    // [`zx_bti_create`](https://fuchsia.dev/fuchsia-src/reference/syscalls/bti_create) system call to create a bti.
72    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            // SAFETY: regular system call with no unsafe parameters.
76            crate::sys::zx_bti_create(iommu.raw_handle(), 0, id, &mut bti_handle)
77        };
78        ok(status)?;
79        unsafe {
80            // SAFETY: The syscall docs claim that upon success, bti_handle will be a valid
81            // handle to bti object.
82            Ok(Bti::from(Handle::from_raw(bti_handle)))
83        }
84    }
85
86    /// Wraps the [zx_object_get_info](https://fuchsia.dev/fuchsia-src/reference/syscalls/object_get_info.md)
87    /// syscall for the ZX_INFO_BTI topic.
88    pub fn info(&self) -> Result<BtiInfo, Status> {
89        Ok(BtiInfo::from(object_get_info_single::<BtiInfoQuery>(self.as_handle_ref())?))
90    }
91
92    // Pins pages in a VMO.
93    // Wraps the [`zx_bti_pin`](https://fuchsia.dev/fuchsia-src/reference/syscalls/bti_pin) system
94    // call.
95    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            // SAFETY: zx_bti_pin requires that out_paddrs is valid to write to for its whole
106            // length, which is guaranteed by being a mutable slice reference.
107            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            // SAFETY: The syscall docs claim that upon success, pmt will be a valid handle to a
121            // zx_pmt_t.
122            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        // This test and fuchsia-zircon are different crates, so we need
157        // to use from_raw to convert between the zx handle and this test handle.
158        // See https://fxbug.dev/42173139 for details.
159        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        // Sanity check one of the info fields.
172        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]; // Unaligned address, so we know it's invalid
183
184        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}