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(bo, self.clone()); 115 116 if gbm_buffer.cached() { 117 reqs.map_info = RUTABAGA_MAP_CACHE_CACHED; 118 } else { 119 reqs.map_info = RUTABAGA_MAP_CACHE_WC; 120 } 121 122 reqs.modifier = gbm_buffer.format_modifier(); 123 for plane in 0..gbm_buffer.num_planes() { 124 reqs.strides[plane] = gbm_buffer.plane_stride(plane); 125 reqs.offsets[plane] = gbm_buffer.plane_offset(plane); 126 } 127 128 let mut fd = gbm_buffer.export()?; 129 let size = fd.seek(SeekFrom::End(0))?; 130 131 // minigbm does have the ability to query image requirements without allocating memory 132 // via the TEST_ALLOC flag. However, support has only been added in i915. Until this 133 // flag is supported everywhere, do the actual allocation here and stash it away. 134 if self.last_buffer.is_some() { 135 return Err(RutabagaError::AlreadyInUse); 136 } 137 138 self.last_buffer = Some(Arc::new(gbm_buffer)); 139 reqs.info = info; 140 reqs.size = size; 141 Ok(reqs) 142 } 143 allocate_memory(&mut self, reqs: ImageMemoryRequirements) -> RutabagaResult<RutabagaHandle>144 fn allocate_memory(&mut self, reqs: ImageMemoryRequirements) -> RutabagaResult<RutabagaHandle> { 145 let last_buffer = self.last_buffer.take(); 146 if let Some(gbm_buffer) = last_buffer { 147 if gbm_buffer.width() != reqs.info.width 148 || gbm_buffer.height() != reqs.info.height 149 || gbm_buffer.format() != reqs.info.drm_format 150 { 151 return Err(RutabagaError::InvalidGrallocDimensions); 152 } 153 154 let dmabuf = gbm_buffer.export()?.into(); 155 return Ok(RutabagaHandle { 156 os_handle: dmabuf, 157 handle_type: RUTABAGA_MEM_HANDLE_TYPE_DMABUF, 158 }); 159 } 160 161 // TODO(b/315870313): Add safety comment 162 #[allow(clippy::undocumented_unsafe_blocks)] 163 let bo = unsafe { 164 gbm_bo_create( 165 self.minigbm_device.gbm, 166 reqs.info.width, 167 reqs.info.height, 168 reqs.info.drm_format.0, 169 reqs.info.flags.0, 170 ) 171 }; 172 173 if bo.is_null() { 174 return Err(RutabagaError::IoError(Error::last_os_error())); 175 } 176 177 let gbm_buffer = MinigbmBuffer(bo, self.clone()); 178 let dmabuf = gbm_buffer.export()?.into(); 179 Ok(RutabagaHandle { 180 os_handle: dmabuf, 181 handle_type: RUTABAGA_MEM_HANDLE_TYPE_DMABUF, 182 }) 183 } 184 } 185 186 /// An allocation from a `MinigbmDevice`. 187 pub struct MinigbmBuffer(*mut gbm_bo, MinigbmDevice); 188 189 // SAFETY: 190 // Safe because minigbm handles synchronization internally. 191 unsafe impl Send for MinigbmBuffer {} 192 // SAFETY: 193 // Safe because minigbm handles synchronization internally. 194 unsafe impl Sync for MinigbmBuffer {} 195 196 impl MinigbmBuffer { 197 /// Width in pixels. width(&self) -> u32198 pub fn width(&self) -> u32 { 199 // SAFETY: 200 // This is always safe to call with a valid gbm_bo pointer. 201 unsafe { gbm_bo_get_width(self.0) } 202 } 203 204 /// Height in pixels. height(&self) -> u32205 pub fn height(&self) -> u32 { 206 // SAFETY: 207 // This is always safe to call with a valid gbm_bo pointer. 208 unsafe { gbm_bo_get_height(self.0) } 209 } 210 211 /// `DrmFormat` of the buffer. format(&self) -> DrmFormat212 pub fn format(&self) -> DrmFormat { 213 // SAFETY: 214 // This is always safe to call with a valid gbm_bo pointer. 215 unsafe { DrmFormat(gbm_bo_get_format(self.0)) } 216 } 217 218 /// DrmFormat modifier flags for the buffer. format_modifier(&self) -> u64219 pub fn format_modifier(&self) -> u64 { 220 // SAFETY: 221 // This is always safe to call with a valid gbm_bo pointer. 222 unsafe { gbm_bo_get_modifier(self.0) } 223 } 224 225 /// Number of planes present in this buffer. num_planes(&self) -> usize226 pub fn num_planes(&self) -> usize { 227 // SAFETY: 228 // This is always safe to call with a valid gbm_bo pointer. 229 unsafe { gbm_bo_get_plane_count(self.0) as usize } 230 } 231 232 /// Offset in bytes for the given plane. plane_offset(&self, plane: usize) -> u32233 pub fn plane_offset(&self, plane: usize) -> u32 { 234 // SAFETY: 235 // This is always safe to call with a valid gbm_bo pointer. 236 unsafe { gbm_bo_get_offset(self.0, plane) } 237 } 238 239 /// Length in bytes of one row for the given plane. plane_stride(&self, plane: usize) -> u32240 pub fn plane_stride(&self, plane: usize) -> u32 { 241 // SAFETY: 242 // This is always safe to call with a valid gbm_bo pointer. 243 unsafe { gbm_bo_get_stride_for_plane(self.0, plane) } 244 } 245 246 /// Should buffer use cached mapping to guest cached(&self) -> bool247 pub fn cached(&self) -> bool { 248 // SAFETY: 249 // This is always safe to call with a valid gbm_bo pointer. 250 let mode = unsafe { gbm_bo_get_map_info(self.0) }; 251 mode == gbm_bo_map_cache_mode::GBM_BO_MAP_CACHE_CACHED 252 } 253 254 /// Exports a new dmabuf/prime file descriptor. export(&self) -> RutabagaResult<File>255 pub fn export(&self) -> RutabagaResult<File> { 256 // SAFETY: 257 // This is always safe to call with a valid gbm_bo pointer. 258 match unsafe { gbm_bo_get_fd(self.0) } { 259 fd if fd >= 0 => { 260 // SAFETY: fd is expected to be valid. 261 let dmabuf = unsafe { File::from_raw_descriptor(fd) }; 262 Ok(dmabuf) 263 } 264 ret => Err(RutabagaError::ComponentError(ret)), 265 } 266 } 267 } 268 269 impl Drop for MinigbmBuffer { drop(&mut self)270 fn drop(&mut self) { 271 // SAFETY: 272 // This is always safe to call with a valid gbm_bo pointer. 273 unsafe { gbm_bo_destroy(self.0) } 274 } 275 } 276