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