1use 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#[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 #[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 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 crate::sys::zx_bti_create(iommu.raw_handle(), 0, id, &mut bti_handle)
74 };
75 ok(status)?;
76 unsafe {
77 Ok(Bti::from(NullableHandle::from_raw(bti_handle)))
80 }
81 }
82
83 pub fn info(&self) -> Result<BtiInfo, Status> {
86 Ok(BtiInfo::from(self.0.get_info_single::<BtiInfoQuery>()?))
87 }
88
89 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 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 Ok(Pmt::from(NullableHandle::from_raw(pmt)))
120 }
121 }
122
123 pub unsafe fn release_quarantine(&self) -> Result<(), Status> {
134 let status = unsafe {
135 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 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 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]; 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 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}