gpu.rs 14 KB

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