gpu.rs 15 KB

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