1 //! Driver for VirtIO GPU devices. 2 3 use crate::hal::{BufferDirection, Dma, Hal}; 4 use crate::queue::VirtQueue; 5 use crate::transport::Transport; 6 use crate::volatile::{volread, ReadOnly, Volatile, WriteOnly}; 7 use crate::{pages, Error, Result, PAGE_SIZE}; 8 use alloc::boxed::Box; 9 use bitflags::bitflags; 10 use log::info; 11 use zerocopy::{AsBytes, FromBytes, FromZeroes}; 12 13 const QUEUE_SIZE: u16 = 2; 14 const SUPPORTED_FEATURES: Features = Features::RING_EVENT_IDX.union(Features::RING_INDIRECT_DESC); 15 16 /// A virtio based graphics adapter. 17 /// 18 /// It can operate in 2D mode and in 3D (virgl) mode. 19 /// 3D mode will offload rendering ops to the host gpu and therefore requires 20 /// a gpu with 3D support on the host machine. 21 /// In 2D mode the virtio-gpu device provides support for ARGB Hardware cursors 22 /// and multiple scanouts (aka heads). 23 pub struct VirtIOGpu<H: Hal, T: Transport> { 24 transport: T, 25 rect: Option<Rect>, 26 /// DMA area of frame buffer. 27 frame_buffer_dma: Option<Dma<H>>, 28 /// DMA area of cursor image buffer. 29 cursor_buffer_dma: Option<Dma<H>>, 30 /// Queue for sending control commands. 31 control_queue: VirtQueue<H, { QUEUE_SIZE as usize }>, 32 /// Queue for sending cursor commands. 33 cursor_queue: VirtQueue<H, { QUEUE_SIZE as usize }>, 34 /// Send buffer for queue. 35 queue_buf_send: Box<[u8]>, 36 /// Recv buffer for queue. 37 queue_buf_recv: Box<[u8]>, 38 } 39 40 impl<H: Hal, T: Transport> VirtIOGpu<H, T> { 41 /// Create a new VirtIO-Gpu driver. new(mut transport: T) -> Result<Self>42 pub fn new(mut transport: T) -> Result<Self> { 43 let negotiated_features = transport.begin_init(SUPPORTED_FEATURES); 44 45 // read configuration space 46 let config_space = transport.config_space::<Config>()?; 47 unsafe { 48 let events_read = volread!(config_space, events_read); 49 let num_scanouts = volread!(config_space, num_scanouts); 50 info!( 51 "events_read: {:#x}, num_scanouts: {:#x}", 52 events_read, num_scanouts 53 ); 54 } 55 56 let control_queue = VirtQueue::new( 57 &mut transport, 58 QUEUE_TRANSMIT, 59 negotiated_features.contains(Features::RING_INDIRECT_DESC), 60 negotiated_features.contains(Features::RING_EVENT_IDX), 61 )?; 62 let cursor_queue = VirtQueue::new( 63 &mut transport, 64 QUEUE_CURSOR, 65 negotiated_features.contains(Features::RING_INDIRECT_DESC), 66 negotiated_features.contains(Features::RING_EVENT_IDX), 67 )?; 68 69 let queue_buf_send = FromZeroes::new_box_slice_zeroed(PAGE_SIZE); 70 let queue_buf_recv = FromZeroes::new_box_slice_zeroed(PAGE_SIZE); 71 72 transport.finish_init(); 73 74 Ok(VirtIOGpu { 75 transport, 76 frame_buffer_dma: None, 77 cursor_buffer_dma: None, 78 rect: None, 79 control_queue, 80 cursor_queue, 81 queue_buf_send, 82 queue_buf_recv, 83 }) 84 } 85 86 /// Acknowledge interrupt. ack_interrupt(&mut self) -> bool87 pub fn ack_interrupt(&mut self) -> bool { 88 self.transport.ack_interrupt() 89 } 90 91 /// Get the resolution (width, height). resolution(&mut self) -> Result<(u32, u32)>92 pub fn resolution(&mut self) -> Result<(u32, u32)> { 93 let display_info = self.get_display_info()?; 94 Ok((display_info.rect.width, display_info.rect.height)) 95 } 96 97 /// Setup framebuffer setup_framebuffer(&mut self) -> Result<&mut [u8]>98 pub fn setup_framebuffer(&mut self) -> Result<&mut [u8]> { 99 // get display info 100 let display_info = self.get_display_info()?; 101 info!("=> {:?}", display_info); 102 self.rect = Some(display_info.rect); 103 104 // create resource 2d 105 self.resource_create_2d( 106 RESOURCE_ID_FB, 107 display_info.rect.width, 108 display_info.rect.height, 109 )?; 110 111 // alloc continuous pages for the frame buffer 112 let size = display_info.rect.width * display_info.rect.height * 4; 113 let frame_buffer_dma = Dma::new(pages(size as usize), BufferDirection::DriverToDevice)?; 114 115 // resource_attach_backing 116 self.resource_attach_backing(RESOURCE_ID_FB, frame_buffer_dma.paddr() as u64, size)?; 117 118 // map frame buffer to screen 119 self.set_scanout(display_info.rect, SCANOUT_ID, RESOURCE_ID_FB)?; 120 121 let buf = unsafe { frame_buffer_dma.raw_slice().as_mut() }; 122 self.frame_buffer_dma = Some(frame_buffer_dma); 123 Ok(buf) 124 } 125 126 /// Flush framebuffer to screen. flush(&mut self) -> Result127 pub fn flush(&mut self) -> Result { 128 let rect = self.rect.ok_or(Error::NotReady)?; 129 // copy data from guest to host 130 self.transfer_to_host_2d(rect, 0, RESOURCE_ID_FB)?; 131 // flush data to screen 132 self.resource_flush(rect, RESOURCE_ID_FB)?; 133 Ok(()) 134 } 135 136 /// Set the pointer shape and position. setup_cursor( &mut self, cursor_image: &[u8], pos_x: u32, pos_y: u32, hot_x: u32, hot_y: u32, ) -> Result137 pub fn setup_cursor( 138 &mut self, 139 cursor_image: &[u8], 140 pos_x: u32, 141 pos_y: u32, 142 hot_x: u32, 143 hot_y: u32, 144 ) -> Result { 145 let size = CURSOR_RECT.width * CURSOR_RECT.height * 4; 146 if cursor_image.len() != size as usize { 147 return Err(Error::InvalidParam); 148 } 149 let cursor_buffer_dma = Dma::new(pages(size as usize), BufferDirection::DriverToDevice)?; 150 let buf = unsafe { cursor_buffer_dma.raw_slice().as_mut() }; 151 buf.copy_from_slice(cursor_image); 152 153 self.resource_create_2d(RESOURCE_ID_CURSOR, CURSOR_RECT.width, CURSOR_RECT.height)?; 154 self.resource_attach_backing(RESOURCE_ID_CURSOR, cursor_buffer_dma.paddr() as u64, size)?; 155 self.transfer_to_host_2d(CURSOR_RECT, 0, RESOURCE_ID_CURSOR)?; 156 self.update_cursor( 157 RESOURCE_ID_CURSOR, 158 SCANOUT_ID, 159 pos_x, 160 pos_y, 161 hot_x, 162 hot_y, 163 false, 164 )?; 165 self.cursor_buffer_dma = Some(cursor_buffer_dma); 166 Ok(()) 167 } 168 169 /// Move the pointer without updating the shape. move_cursor(&mut self, pos_x: u32, pos_y: u32) -> Result170 pub fn move_cursor(&mut self, pos_x: u32, pos_y: u32) -> Result { 171 self.update_cursor(RESOURCE_ID_CURSOR, SCANOUT_ID, pos_x, pos_y, 0, 0, true)?; 172 Ok(()) 173 } 174 175 /// Send a request to the device and block for a response. request<Req: AsBytes, Rsp: FromBytes>(&mut self, req: Req) -> Result<Rsp>176 fn request<Req: AsBytes, Rsp: FromBytes>(&mut self, req: Req) -> Result<Rsp> { 177 req.write_to_prefix(&mut self.queue_buf_send).unwrap(); 178 self.control_queue.add_notify_wait_pop( 179 &[&self.queue_buf_send], 180 &mut [&mut self.queue_buf_recv], 181 &mut self.transport, 182 )?; 183 Ok(Rsp::read_from_prefix(&self.queue_buf_recv).unwrap()) 184 } 185 186 /// Send a mouse cursor operation request to the device and block for a response. cursor_request<Req: AsBytes>(&mut self, req: Req) -> Result187 fn cursor_request<Req: AsBytes>(&mut self, req: Req) -> Result { 188 req.write_to_prefix(&mut self.queue_buf_send).unwrap(); 189 self.cursor_queue.add_notify_wait_pop( 190 &[&self.queue_buf_send], 191 &mut [], 192 &mut self.transport, 193 )?; 194 Ok(()) 195 } 196 get_display_info(&mut self) -> Result<RespDisplayInfo>197 fn get_display_info(&mut self) -> Result<RespDisplayInfo> { 198 let info: RespDisplayInfo = 199 self.request(CtrlHeader::with_type(Command::GET_DISPLAY_INFO))?; 200 info.header.check_type(Command::OK_DISPLAY_INFO)?; 201 Ok(info) 202 } 203 resource_create_2d(&mut self, resource_id: u32, width: u32, height: u32) -> Result204 fn resource_create_2d(&mut self, resource_id: u32, width: u32, height: u32) -> Result { 205 let rsp: CtrlHeader = self.request(ResourceCreate2D { 206 header: CtrlHeader::with_type(Command::RESOURCE_CREATE_2D), 207 resource_id, 208 format: Format::B8G8R8A8UNORM, 209 width, 210 height, 211 })?; 212 rsp.check_type(Command::OK_NODATA) 213 } 214 set_scanout(&mut self, rect: Rect, scanout_id: u32, resource_id: u32) -> Result215 fn set_scanout(&mut self, rect: Rect, scanout_id: u32, resource_id: u32) -> Result { 216 let rsp: CtrlHeader = self.request(SetScanout { 217 header: CtrlHeader::with_type(Command::SET_SCANOUT), 218 rect, 219 scanout_id, 220 resource_id, 221 })?; 222 rsp.check_type(Command::OK_NODATA) 223 } 224 resource_flush(&mut self, rect: Rect, resource_id: u32) -> Result225 fn resource_flush(&mut self, rect: Rect, resource_id: u32) -> Result { 226 let rsp: CtrlHeader = self.request(ResourceFlush { 227 header: CtrlHeader::with_type(Command::RESOURCE_FLUSH), 228 rect, 229 resource_id, 230 _padding: 0, 231 })?; 232 rsp.check_type(Command::OK_NODATA) 233 } 234 transfer_to_host_2d(&mut self, rect: Rect, offset: u64, resource_id: u32) -> Result235 fn transfer_to_host_2d(&mut self, rect: Rect, offset: u64, resource_id: u32) -> Result { 236 let rsp: CtrlHeader = self.request(TransferToHost2D { 237 header: CtrlHeader::with_type(Command::TRANSFER_TO_HOST_2D), 238 rect, 239 offset, 240 resource_id, 241 _padding: 0, 242 })?; 243 rsp.check_type(Command::OK_NODATA) 244 } 245 resource_attach_backing(&mut self, resource_id: u32, paddr: u64, length: u32) -> Result246 fn resource_attach_backing(&mut self, resource_id: u32, paddr: u64, length: u32) -> Result { 247 let rsp: CtrlHeader = self.request(ResourceAttachBacking { 248 header: CtrlHeader::with_type(Command::RESOURCE_ATTACH_BACKING), 249 resource_id, 250 nr_entries: 1, 251 addr: paddr, 252 length, 253 _padding: 0, 254 })?; 255 rsp.check_type(Command::OK_NODATA) 256 } 257 update_cursor( &mut self, resource_id: u32, scanout_id: u32, pos_x: u32, pos_y: u32, hot_x: u32, hot_y: u32, is_move: bool, ) -> Result258 fn update_cursor( 259 &mut self, 260 resource_id: u32, 261 scanout_id: u32, 262 pos_x: u32, 263 pos_y: u32, 264 hot_x: u32, 265 hot_y: u32, 266 is_move: bool, 267 ) -> Result { 268 self.cursor_request(UpdateCursor { 269 header: if is_move { 270 CtrlHeader::with_type(Command::MOVE_CURSOR) 271 } else { 272 CtrlHeader::with_type(Command::UPDATE_CURSOR) 273 }, 274 pos: CursorPos { 275 scanout_id, 276 x: pos_x, 277 y: pos_y, 278 _padding: 0, 279 }, 280 resource_id, 281 hot_x, 282 hot_y, 283 _padding: 0, 284 }) 285 } 286 } 287 288 impl<H: Hal, T: Transport> Drop for VirtIOGpu<H, T> { drop(&mut self)289 fn drop(&mut self) { 290 // Clear any pointers pointing to DMA regions, so the device doesn't try to access them 291 // after they have been freed. 292 self.transport.queue_unset(QUEUE_TRANSMIT); 293 self.transport.queue_unset(QUEUE_CURSOR); 294 } 295 } 296 297 #[repr(C)] 298 struct Config { 299 /// Signals pending events to the driver。 300 events_read: ReadOnly<u32>, 301 302 /// Clears pending events in the device. 303 events_clear: WriteOnly<u32>, 304 305 /// Specifies the maximum number of scanouts supported by the device. 306 /// 307 /// Minimum value is 1, maximum value is 16. 308 num_scanouts: Volatile<u32>, 309 } 310 311 /// Display configuration has changed. 312 const EVENT_DISPLAY: u32 = 1 << 0; 313 314 bitflags! { 315 #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] 316 struct Features: u64 { 317 /// virgl 3D mode is supported. 318 const VIRGL = 1 << 0; 319 /// EDID is supported. 320 const EDID = 1 << 1; 321 322 // device independent 323 const NOTIFY_ON_EMPTY = 1 << 24; // legacy 324 const ANY_LAYOUT = 1 << 27; // legacy 325 const RING_INDIRECT_DESC = 1 << 28; 326 const RING_EVENT_IDX = 1 << 29; 327 const UNUSED = 1 << 30; // legacy 328 const VERSION_1 = 1 << 32; // detect legacy 329 330 // since virtio v1.1 331 const ACCESS_PLATFORM = 1 << 33; 332 const RING_PACKED = 1 << 34; 333 const IN_ORDER = 1 << 35; 334 const ORDER_PLATFORM = 1 << 36; 335 const SR_IOV = 1 << 37; 336 const NOTIFICATION_DATA = 1 << 38; 337 } 338 } 339 340 #[repr(transparent)] 341 #[derive(AsBytes, Clone, Copy, Debug, Eq, PartialEq, FromBytes, FromZeroes)] 342 struct Command(u32); 343 344 impl Command { 345 const GET_DISPLAY_INFO: Command = Command(0x100); 346 const RESOURCE_CREATE_2D: Command = Command(0x101); 347 const RESOURCE_UNREF: Command = Command(0x102); 348 const SET_SCANOUT: Command = Command(0x103); 349 const RESOURCE_FLUSH: Command = Command(0x104); 350 const TRANSFER_TO_HOST_2D: Command = Command(0x105); 351 const RESOURCE_ATTACH_BACKING: Command = Command(0x106); 352 const RESOURCE_DETACH_BACKING: Command = Command(0x107); 353 const GET_CAPSET_INFO: Command = Command(0x108); 354 const GET_CAPSET: Command = Command(0x109); 355 const GET_EDID: Command = Command(0x10a); 356 357 const UPDATE_CURSOR: Command = Command(0x300); 358 const MOVE_CURSOR: Command = Command(0x301); 359 360 const OK_NODATA: Command = Command(0x1100); 361 const OK_DISPLAY_INFO: Command = Command(0x1101); 362 const OK_CAPSET_INFO: Command = Command(0x1102); 363 const OK_CAPSET: Command = Command(0x1103); 364 const OK_EDID: Command = Command(0x1104); 365 366 const ERR_UNSPEC: Command = Command(0x1200); 367 const ERR_OUT_OF_MEMORY: Command = Command(0x1201); 368 const ERR_INVALID_SCANOUT_ID: Command = Command(0x1202); 369 } 370 371 const GPU_FLAG_FENCE: u32 = 1 << 0; 372 373 #[repr(C)] 374 #[derive(AsBytes, Debug, Clone, Copy, FromBytes, FromZeroes)] 375 struct CtrlHeader { 376 hdr_type: Command, 377 flags: u32, 378 fence_id: u64, 379 ctx_id: u32, 380 _padding: u32, 381 } 382 383 impl CtrlHeader { with_type(hdr_type: Command) -> CtrlHeader384 fn with_type(hdr_type: Command) -> CtrlHeader { 385 CtrlHeader { 386 hdr_type, 387 flags: 0, 388 fence_id: 0, 389 ctx_id: 0, 390 _padding: 0, 391 } 392 } 393 394 /// Return error if the type is not same as expected. check_type(&self, expected: Command) -> Result395 fn check_type(&self, expected: Command) -> Result { 396 if self.hdr_type == expected { 397 Ok(()) 398 } else { 399 Err(Error::IoError) 400 } 401 } 402 } 403 404 #[repr(C)] 405 #[derive(AsBytes, Debug, Copy, Clone, Default, FromBytes, FromZeroes)] 406 struct Rect { 407 x: u32, 408 y: u32, 409 width: u32, 410 height: u32, 411 } 412 413 #[repr(C)] 414 #[derive(Debug, FromBytes, FromZeroes)] 415 struct RespDisplayInfo { 416 header: CtrlHeader, 417 rect: Rect, 418 enabled: u32, 419 flags: u32, 420 } 421 422 #[repr(C)] 423 #[derive(AsBytes, Debug)] 424 struct ResourceCreate2D { 425 header: CtrlHeader, 426 resource_id: u32, 427 format: Format, 428 width: u32, 429 height: u32, 430 } 431 432 #[repr(u32)] 433 #[derive(AsBytes, Debug)] 434 enum Format { 435 B8G8R8A8UNORM = 1, 436 } 437 438 #[repr(C)] 439 #[derive(AsBytes, Debug)] 440 struct ResourceAttachBacking { 441 header: CtrlHeader, 442 resource_id: u32, 443 nr_entries: u32, // always 1 444 addr: u64, 445 length: u32, 446 _padding: u32, 447 } 448 449 #[repr(C)] 450 #[derive(AsBytes, Debug)] 451 struct SetScanout { 452 header: CtrlHeader, 453 rect: Rect, 454 scanout_id: u32, 455 resource_id: u32, 456 } 457 458 #[repr(C)] 459 #[derive(AsBytes, Debug)] 460 struct TransferToHost2D { 461 header: CtrlHeader, 462 rect: Rect, 463 offset: u64, 464 resource_id: u32, 465 _padding: u32, 466 } 467 468 #[repr(C)] 469 #[derive(AsBytes, Debug)] 470 struct ResourceFlush { 471 header: CtrlHeader, 472 rect: Rect, 473 resource_id: u32, 474 _padding: u32, 475 } 476 477 #[repr(C)] 478 #[derive(AsBytes, Debug, Clone, Copy)] 479 struct CursorPos { 480 scanout_id: u32, 481 x: u32, 482 y: u32, 483 _padding: u32, 484 } 485 486 #[repr(C)] 487 #[derive(AsBytes, Debug, Clone, Copy)] 488 struct UpdateCursor { 489 header: CtrlHeader, 490 pos: CursorPos, 491 resource_id: u32, 492 hot_x: u32, 493 hot_y: u32, 494 _padding: u32, 495 } 496 497 const QUEUE_TRANSMIT: u16 = 0; 498 const QUEUE_CURSOR: u16 = 1; 499 500 const SCANOUT_ID: u32 = 0; 501 const RESOURCE_ID_FB: u32 = 0xbabe; 502 const RESOURCE_ID_CURSOR: u32 = 0xdade; 503 504 const CURSOR_RECT: Rect = Rect { 505 x: 0, 506 y: 0, 507 width: 64, 508 height: 64, 509 }; 510