xref: /aosp_15_r20/external/crosvm/rutabaga_gfx/src/rutabaga_gralloc/minigbm.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2018 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 //! minigbm: implements swapchain allocation using ChromeOS's minigbm library.
6 //!
7 //! External code found at <https://chromium.googlesource.com/chromiumos/platform/minigbm>.
8 
9 #![cfg(feature = "minigbm")]
10 
11 use std::fs::File;
12 use std::io::Error;
13 use std::io::Seek;
14 use std::io::SeekFrom;
15 use std::os::fd::FromRawFd;
16 use std::sync::Arc;
17 
18 use crate::rutabaga_gralloc::formats::DrmFormat;
19 use crate::rutabaga_gralloc::gralloc::Gralloc;
20 use crate::rutabaga_gralloc::gralloc::ImageAllocationInfo;
21 use crate::rutabaga_gralloc::gralloc::ImageMemoryRequirements;
22 use crate::rutabaga_gralloc::minigbm_bindings::*;
23 use crate::rutabaga_os::FromRawDescriptor;
24 use crate::rutabaga_utils::*;
25 
26 struct MinigbmDeviceInner {
27     _fd: File,
28     gbm: *mut gbm_device,
29 }
30 
31 // SAFETY:
32 // Safe because minigbm handles synchronization internally.
33 unsafe impl Send for MinigbmDeviceInner {}
34 // SAFETY:
35 // Safe because minigbm handles synchronization internally.
36 unsafe impl Sync for MinigbmDeviceInner {}
37 
38 impl Drop for MinigbmDeviceInner {
drop(&mut self)39     fn drop(&mut self) {
40         // SAFETY:
41         // Safe because MinigbmDeviceInner is only constructed with a valid minigbm_device.
42         unsafe {
43             gbm_device_destroy(self.gbm);
44         }
45     }
46 }
47 
48 /// A device capable of allocating `MinigbmBuffer`.
49 #[derive(Clone)]
50 pub struct MinigbmDevice {
51     minigbm_device: Arc<MinigbmDeviceInner>,
52     last_buffer: Option<Arc<MinigbmBuffer>>,
53 }
54 
55 impl MinigbmDevice {
56     /// Returns a new `MinigbmDevice` if there is a rendernode in `/dev/dri/` that is accepted by
57     /// the minigbm library.
init() -> RutabagaResult<Box<dyn Gralloc>>58     pub fn init() -> RutabagaResult<Box<dyn Gralloc>> {
59         let descriptor: File;
60         let gbm: *mut gbm_device;
61         // SAFETY:
62         // Safe because minigbm_create_default_device is safe to call with an unused fd,
63         // and fd is guaranteed to be overwritten with a valid descriptor when a non-null
64         // pointer is returned.
65         unsafe {
66             let mut fd = -1;
67 
68             gbm = minigbm_create_default_device(&mut fd);
69             if gbm.is_null() {
70                 return Err(RutabagaError::IoError(Error::last_os_error()));
71             }
72             descriptor = File::from_raw_fd(fd);
73         }
74 
75         Ok(Box::new(MinigbmDevice {
76             minigbm_device: Arc::new(MinigbmDeviceInner {
77                 _fd: descriptor,
78                 gbm,
79             }),
80             last_buffer: None,
81         }))
82     }
83 }
84 
85 impl Gralloc for MinigbmDevice {
supports_external_gpu_memory(&self) -> bool86     fn supports_external_gpu_memory(&self) -> bool {
87         true
88     }
89 
supports_dmabuf(&self) -> bool90     fn supports_dmabuf(&self) -> bool {
91         true
92     }
93 
get_image_memory_requirements( &mut self, info: ImageAllocationInfo, ) -> RutabagaResult<ImageMemoryRequirements>94     fn get_image_memory_requirements(
95         &mut self,
96         info: ImageAllocationInfo,
97     ) -> RutabagaResult<ImageMemoryRequirements> {
98         // TODO(b/315870313): Add safety comment
99         #[allow(clippy::undocumented_unsafe_blocks)]
100         let bo = unsafe {
101             gbm_bo_create(
102                 self.minigbm_device.gbm,
103                 info.width,
104                 info.height,
105                 info.drm_format.0,
106                 info.flags.0,
107             )
108         };
109         if bo.is_null() {
110             return Err(RutabagaError::IoError(Error::last_os_error()));
111         }
112 
113         let mut reqs: ImageMemoryRequirements = Default::default();
114         let gbm_buffer = MinigbmBuffer {
115             bo,
116             _device: self.clone(),
117         };
118 
119         if gbm_buffer.cached() {
120             reqs.map_info = RUTABAGA_MAP_CACHE_CACHED;
121         } else {
122             reqs.map_info = RUTABAGA_MAP_CACHE_WC;
123         }
124 
125         reqs.modifier = gbm_buffer.format_modifier();
126         for plane in 0..gbm_buffer.num_planes() {
127             reqs.strides[plane] = gbm_buffer.plane_stride(plane);
128             reqs.offsets[plane] = gbm_buffer.plane_offset(plane);
129         }
130 
131         let mut fd = gbm_buffer.export()?;
132         let size = fd.seek(SeekFrom::End(0))?;
133 
134         // minigbm does have the ability to query image requirements without allocating memory
135         // via the TEST_ALLOC flag.  However, support has only been added in i915.  Until this
136         // flag is supported everywhere, do the actual allocation here and stash it away.
137         if self.last_buffer.is_some() {
138             return Err(RutabagaError::AlreadyInUse);
139         }
140 
141         self.last_buffer = Some(Arc::new(gbm_buffer));
142         reqs.info = info;
143         reqs.size = size;
144         Ok(reqs)
145     }
146 
allocate_memory(&mut self, reqs: ImageMemoryRequirements) -> RutabagaResult<RutabagaHandle>147     fn allocate_memory(&mut self, reqs: ImageMemoryRequirements) -> RutabagaResult<RutabagaHandle> {
148         let last_buffer = self.last_buffer.take();
149         if let Some(gbm_buffer) = last_buffer {
150             if gbm_buffer.width() != reqs.info.width
151                 || gbm_buffer.height() != reqs.info.height
152                 || gbm_buffer.format() != reqs.info.drm_format
153             {
154                 return Err(RutabagaError::InvalidGrallocDimensions);
155             }
156 
157             let dmabuf = gbm_buffer.export()?.into();
158             return Ok(RutabagaHandle {
159                 os_handle: dmabuf,
160                 handle_type: RUTABAGA_MEM_HANDLE_TYPE_DMABUF,
161             });
162         }
163 
164         // TODO(b/315870313): Add safety comment
165         #[allow(clippy::undocumented_unsafe_blocks)]
166         let bo = unsafe {
167             gbm_bo_create(
168                 self.minigbm_device.gbm,
169                 reqs.info.width,
170                 reqs.info.height,
171                 reqs.info.drm_format.0,
172                 reqs.info.flags.0,
173             )
174         };
175 
176         if bo.is_null() {
177             return Err(RutabagaError::IoError(Error::last_os_error()));
178         }
179 
180         let gbm_buffer = MinigbmBuffer {
181             bo,
182             _device: self.clone(),
183         };
184         let dmabuf = gbm_buffer.export()?.into();
185         Ok(RutabagaHandle {
186             os_handle: dmabuf,
187             handle_type: RUTABAGA_MEM_HANDLE_TYPE_DMABUF,
188         })
189     }
190 }
191 
192 /// An allocation from a `MinigbmDevice`.
193 pub struct MinigbmBuffer {
194     bo: *mut gbm_bo,
195     _device: MinigbmDevice,
196 }
197 
198 // SAFETY:
199 // Safe because minigbm handles synchronization internally.
200 unsafe impl Send for MinigbmBuffer {}
201 // SAFETY:
202 // Safe because minigbm handles synchronization internally.
203 unsafe impl Sync for MinigbmBuffer {}
204 
205 impl MinigbmBuffer {
206     /// Width in pixels.
width(&self) -> u32207     pub fn width(&self) -> u32 {
208         // SAFETY:
209         // This is always safe to call with a valid gbm_bo pointer.
210         unsafe { gbm_bo_get_width(self.bo) }
211     }
212 
213     /// Height in pixels.
height(&self) -> u32214     pub fn height(&self) -> u32 {
215         // SAFETY:
216         // This is always safe to call with a valid gbm_bo pointer.
217         unsafe { gbm_bo_get_height(self.bo) }
218     }
219 
220     /// `DrmFormat` of the buffer.
format(&self) -> DrmFormat221     pub fn format(&self) -> DrmFormat {
222         // SAFETY:
223         // This is always safe to call with a valid gbm_bo pointer.
224         unsafe { DrmFormat(gbm_bo_get_format(self.bo)) }
225     }
226 
227     /// DrmFormat modifier flags for the buffer.
format_modifier(&self) -> u64228     pub fn format_modifier(&self) -> u64 {
229         // SAFETY:
230         // This is always safe to call with a valid gbm_bo pointer.
231         unsafe { gbm_bo_get_modifier(self.bo) }
232     }
233 
234     /// Number of planes present in this buffer.
num_planes(&self) -> usize235     pub fn num_planes(&self) -> usize {
236         // SAFETY:
237         // This is always safe to call with a valid gbm_bo pointer.
238         unsafe { gbm_bo_get_plane_count(self.bo) as usize }
239     }
240 
241     /// Offset in bytes for the given plane.
plane_offset(&self, plane: usize) -> u32242     pub fn plane_offset(&self, plane: usize) -> u32 {
243         // SAFETY:
244         // This is always safe to call with a valid gbm_bo pointer.
245         unsafe { gbm_bo_get_offset(self.bo, plane) }
246     }
247 
248     /// Length in bytes of one row for the given plane.
plane_stride(&self, plane: usize) -> u32249     pub fn plane_stride(&self, plane: usize) -> u32 {
250         // SAFETY:
251         // This is always safe to call with a valid gbm_bo pointer.
252         unsafe { gbm_bo_get_stride_for_plane(self.bo, plane) }
253     }
254 
255     /// Should buffer use cached mapping to guest
cached(&self) -> bool256     pub fn cached(&self) -> bool {
257         // SAFETY:
258         // This is always safe to call with a valid gbm_bo pointer.
259         let mode = unsafe { gbm_bo_get_map_info(self.bo) };
260         mode == gbm_bo_map_cache_mode::GBM_BO_MAP_CACHE_CACHED
261     }
262 
263     /// Exports a new dmabuf/prime file descriptor.
export(&self) -> RutabagaResult<File>264     pub fn export(&self) -> RutabagaResult<File> {
265         // SAFETY:
266         // This is always safe to call with a valid gbm_bo pointer.
267         match unsafe { gbm_bo_get_fd(self.bo) } {
268             fd if fd >= 0 => {
269                 // SAFETY: fd is expected to be valid.
270                 let dmabuf = unsafe { File::from_raw_descriptor(fd) };
271                 Ok(dmabuf)
272             }
273             ret => Err(RutabagaError::ComponentError(ret)),
274         }
275     }
276 }
277 
278 impl Drop for MinigbmBuffer {
drop(&mut self)279     fn drop(&mut self) {
280         // SAFETY:
281         // This is always safe to call with a valid gbm_bo pointer.
282         unsafe { gbm_bo_destroy(self.bo) }
283     }
284 }
285