Skip to main content

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::{Iommu, NullableHandle, ObjectQuery, Pmt, Status, Topic, Vmo, ok, sys};
12
13/// An object representing a Zircon Bus Transaction Initiator object.
14/// See [BTI Documentation](https://fuchsia.dev/fuchsia-src/reference/kernel_objects/bus_transaction_initiator) for details.
15///
16/// As essentially a subtype of `NullableHandle`, it can be freely interconverted.
17#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
18#[repr(transparent)]
19pub struct Bti(NullableHandle);
20impl_handle_based!(Bti);
21
22static_assert_align!(
23    #[doc="Ergonomic equivalent of [sys::zx_info_bti_t]. Must be ABI-compatible with it."]
24    #[repr(C)]
25    #[derive(Debug, Copy, Clone, Eq, PartialEq, FromBytes, Immutable)]
26    <sys::zx_info_bti_t> pub struct BtiInfo {
27        pub minimum_contiguity: u64,
28        pub aspace_size: u64,
29        pub pmo_count: u64,
30        pub quarantine_count: u64,
31    }
32);
33
34impl Default for BtiInfo {
35    fn default() -> BtiInfo {
36        Self::from(sys::zx_info_bti_t::default())
37    }
38}
39
40impl From<sys::zx_info_bti_t> for BtiInfo {
41    fn from(info: sys::zx_info_bti_t) -> BtiInfo {
42        zerocopy::transmute!(info)
43    }
44}
45
46struct BtiInfoQuery;
47unsafe impl ObjectQuery for BtiInfoQuery {
48    const TOPIC: Topic = Topic::BTI;
49    type InfoTy = sys::zx_info_bti_t;
50}
51
52bitflags! {
53    /// Options that may be used for [`zx::Bti::pin`].
54    #[repr(transparent)]
55    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
56    pub struct BtiOptions: u32 {
57        const PERM_READ = sys::ZX_BTI_PERM_READ;
58        const PERM_WRITE = sys::ZX_BTI_PERM_WRITE;
59        const PERM_EXECUTE = sys::ZX_BTI_PERM_EXECUTE;
60        const COMPRESS = sys::ZX_BTI_COMPRESS;
61        const CONTIGUOUS = sys::ZX_BTI_CONTIGUOUS;
62    }
63}
64
65impl Bti {
66    // Create a Bus Transaction Initiator object.
67    // Wraps the
68    // [`zx_bti_create`](https://fuchsia.dev/fuchsia-src/reference/syscalls/bti_create) system call to create a bti.
69    pub fn create(iommu: &Iommu, id: u64) -> Result<Bti, Status> {
70        let mut bti_handle = crate::sys::zx_handle_t::default();
71        let status = unsafe {
72            // SAFETY: regular system call with no unsafe parameters.
73            crate::sys::zx_bti_create(iommu.raw_handle(), 0, id, &mut bti_handle)
74        };
75        ok(status)?;
76        unsafe {
77            // SAFETY: The syscall docs claim that upon success, bti_handle will be a valid
78            // handle to bti object.
79            Ok(Bti::from(NullableHandle::from_raw(bti_handle)))
80        }
81    }
82
83    /// Wraps the [zx_object_get_info](https://fuchsia.dev/fuchsia-src/reference/syscalls/object_get_info.md)
84    /// syscall for the ZX_INFO_BTI topic.
85    pub fn info(&self) -> Result<BtiInfo, Status> {
86        Ok(BtiInfo::from(self.0.get_info_single::<BtiInfoQuery>()?))
87    }
88
89    // Pins pages in a VMO.
90    // Wraps the [`zx_bti_pin`](https://fuchsia.dev/fuchsia-src/reference/syscalls/bti_pin) system
91    // call.
92    pub fn pin(
93        &self,
94        options: BtiOptions,
95        vmo: &Vmo,
96        offset: u64,
97        size: u64,
98        out_paddrs: &mut [zx_paddr_t],
99    ) -> Result<Pmt, Status> {
100        let mut pmt = crate::sys::zx_handle_t::default();
101        let status = unsafe {
102            // SAFETY: zx_bti_pin requires that out_paddrs is valid to write to for its whole
103            // length, which is guaranteed by being a mutable slice reference.
104            crate::sys::zx_bti_pin(
105                self.raw_handle(),
106                options.bits(),
107                vmo.raw_handle(),
108                offset,
109                size,
110                out_paddrs.as_mut_ptr(),
111                out_paddrs.len(),
112                &mut pmt,
113            )
114        };
115        ok(status)?;
116        unsafe {
117            // SAFETY: The syscall docs claim that upon success, pmt will be a valid handle to a
118            // zx_pmt_t.
119            Ok(Pmt::from(NullableHandle::from_raw(pmt)))
120        }
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127    use crate::{IommuDescStub, ObjectType, Resource, Vmo};
128    use fidl_fuchsia_kernel as fkernel;
129    use fuchsia_component::client::connect_channel_to_protocol;
130
131    #[test]
132    fn create_bti_wrong_handle() {
133        let vmo = Vmo::create(0).unwrap();
134        let wrong_handle = unsafe { Iommu::from(NullableHandle::from_raw(vmo.into_raw())) };
135
136        let status = Bti::create(&wrong_handle, 0);
137        assert_eq!(status, Err(Status::WRONG_TYPE));
138    }
139
140    fn create_iommu() -> Iommu {
141        use zx::{Channel, MonotonicInstant};
142        let (client_end, server_end) = Channel::create();
143        connect_channel_to_protocol::<fkernel::IommuResourceMarker>(server_end).unwrap();
144        let service = fkernel::IommuResourceSynchronousProxy::new(client_end);
145        let resource =
146            service.get(MonotonicInstant::INFINITE).expect("couldn't get iommu resource");
147        // This test and fuchsia-zircon are different crates, so we need
148        // to use from_raw to convert between the zx handle and this test handle.
149        // See https://fxbug.dev/42173139 for details.
150        let resource = unsafe { Resource::from(NullableHandle::from_raw(resource.into_raw())) };
151        Iommu::create_stub(&resource, IommuDescStub::default()).unwrap()
152    }
153
154    #[test]
155    fn create_from_valid_iommu() {
156        let iommu = create_iommu();
157        let bti = Bti::create(&iommu, 0).unwrap();
158
159        let info = bti.as_handle_ref().basic_info().unwrap();
160        assert_eq!(info.object_type, ObjectType::BTI);
161        let bti_info = bti.info().unwrap();
162        // Sanity check one of the info fields.
163        assert!(bti_info.minimum_contiguity >= crate::system_get_page_size() as u64);
164        assert!(bti_info.minimum_contiguity % crate::system_get_page_size() as u64 == 0);
165    }
166
167    #[test]
168    fn create_and_pin_from_valid_iommu() {
169        let iommu = create_iommu();
170        let bti = Bti::create(&iommu, 0).unwrap();
171
172        let vmo = Vmo::create_contiguous(&bti, 4096, 0).unwrap();
173        let mut paddr = [0x1]; // Unaligned address, so we know it's invalid
174
175        let pmt = bti.pin(BtiOptions::PERM_READ, &vmo, 0, 4096, &mut paddr[..]).unwrap();
176        assert_ne!(paddr, [0x1]);
177
178        let info = pmt.as_handle_ref().basic_info().unwrap();
179        assert_eq!(info.object_type, ObjectType::PMT);
180
181        unsafe {
182            pmt.unpin().unwrap();
183        }
184    }
185}