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    /// Releases all quarantined PMTs for the given BTI.
124    ///
125    /// Wraps the [`zx_bti_release_quarantine`](https://fuchsia.dev/fuchsia-src/reference/syscalls/bti_release_quarantine)
126    /// system call.
127    ///
128    /// # Safety
129    ///
130    /// The caller must ensure that the hardware is in a state where it is no longer attempting
131    /// to access the memory unpinned by this call. Otherwise, without an IOMMU, this can cause
132    /// arbitrary memory corruption.
133    pub unsafe fn release_quarantine(&self) -> Result<(), Status> {
134        let status = unsafe {
135            // SAFETY: The caller upholds that hardware is no longer accessing quarantined memory.
136            crate::sys::zx_bti_release_quarantine(self.raw_handle())
137        };
138        ok(status)
139    }
140}
141
142#[cfg(test)]
143mod tests {
144    use super::*;
145    use crate::{IommuDescStub, ObjectType, Resource, Vmo};
146    use fidl_fuchsia_kernel as fkernel;
147    use fuchsia_component::client::connect_channel_to_protocol;
148
149    #[test]
150    fn create_bti_wrong_handle() {
151        let vmo = Vmo::create(0).unwrap();
152        let wrong_handle = unsafe { Iommu::from(NullableHandle::from_raw(vmo.into_raw())) };
153
154        let status = Bti::create(&wrong_handle, 0);
155        assert_eq!(status, Err(Status::WRONG_TYPE));
156    }
157
158    fn create_iommu() -> Iommu {
159        use zx::{Channel, MonotonicInstant};
160        let (client_end, server_end) = Channel::create();
161        connect_channel_to_protocol::<fkernel::IommuResourceMarker>(server_end).unwrap();
162        let service = fkernel::IommuResourceSynchronousProxy::new(client_end);
163        let resource =
164            service.get(MonotonicInstant::INFINITE).expect("couldn't get iommu resource");
165        // This test and fuchsia-zircon are different crates, so we need
166        // to use from_raw to convert between the zx handle and this test handle.
167        // See https://fxbug.dev/42173139 for details.
168        let resource = unsafe { Resource::from(NullableHandle::from_raw(resource.into_raw())) };
169        Iommu::create_stub(&resource, IommuDescStub::default()).unwrap()
170    }
171
172    #[test]
173    fn create_from_valid_iommu() {
174        let iommu = create_iommu();
175        let bti = Bti::create(&iommu, 0).unwrap();
176
177        let info = bti.as_handle_ref().basic_info().unwrap();
178        assert_eq!(info.object_type, ObjectType::BTI);
179        let bti_info = bti.info().unwrap();
180        // Sanity check one of the info fields.
181        assert!(bti_info.minimum_contiguity >= crate::system_get_page_size() as u64);
182        assert!(bti_info.minimum_contiguity % crate::system_get_page_size() as u64 == 0);
183    }
184
185    #[test]
186    fn create_and_pin_from_valid_iommu() {
187        let iommu = create_iommu();
188        let bti = Bti::create(&iommu, 0).unwrap();
189
190        let vmo = Vmo::create_contiguous(&bti, 4096, 0).unwrap();
191        let mut paddr = [0x1]; // Unaligned address, so we know it's invalid
192
193        let pmt = bti.pin(BtiOptions::PERM_READ, &vmo, 0, 4096, &mut paddr[..]).unwrap();
194        assert_ne!(paddr, [0x1]);
195
196        let info = pmt.as_handle_ref().basic_info().unwrap();
197        assert_eq!(info.object_type, ObjectType::PMT);
198
199        unsafe {
200            pmt.unpin().unwrap();
201        }
202    }
203
204    #[test]
205    fn release_quarantine_wrong_handle() {
206        let vmo = Vmo::create(0).unwrap();
207        let wrong_handle = unsafe { Bti::from(NullableHandle::from_raw(vmo.into_raw())) };
208
209        let status = unsafe { wrong_handle.release_quarantine() };
210        assert_eq!(status, Err(Status::WRONG_TYPE));
211    }
212
213    #[test]
214    fn release_quarantine() {
215        let iommu = create_iommu();
216        let bti = Bti::create(&iommu, 0).unwrap();
217
218        let vmo = Vmo::create_contiguous(&bti, 4096, 0).unwrap();
219        let mut paddr = [0x1];
220        let pmt = bti.pin(BtiOptions::PERM_READ, &vmo, 0, 4096, &mut paddr[..]).unwrap();
221
222        // Dropping the PMT without calling `unpin` quarantines the pinned pages.
223        drop(pmt);
224        assert_eq!(bti.info().unwrap().quarantine_count, 1);
225
226        unsafe {
227            bti.release_quarantine().unwrap();
228        }
229        assert_eq!(bti.info().unwrap().quarantine_count, 0);
230    }
231}