gpu.rs 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. use super::*;
  2. use crate::queue::VirtQueue;
  3. use bitflags::*;
  4. use core::sync::atomic::spin_loop_hint;
  5. use log::*;
  6. use volatile::{ReadOnly, Volatile, WriteOnly};
  7. /// A virtio based graphics adapter.
  8. ///
  9. /// It can operate in 2D mode and in 3D (virgl) mode.
  10. /// 3D mode will offload rendering ops to the host gpu and therefore requires
  11. /// a gpu with 3D support on the host machine.
  12. /// In 2D mode the virtio-gpu device provides support for ARGB Hardware cursors
  13. /// and multiple scanouts (aka heads).
  14. pub struct VirtIOGpu<'a> {
  15. header: &'static mut VirtIOHeader,
  16. rect: Rect,
  17. /// DMA area of frame buffer.
  18. frame_buffer_dma: Option<DMA>,
  19. /// Queue for sending control commands.
  20. control_queue: VirtQueue<'a>,
  21. /// Queue for sending cursor commands.
  22. cursor_queue: VirtQueue<'a>,
  23. /// Queue buffer DMA
  24. queue_buf_dma: DMA,
  25. /// Send buffer for queue.
  26. queue_buf_send: &'a mut [u8],
  27. /// Recv buffer for queue.
  28. queue_buf_recv: &'a mut [u8],
  29. }
  30. impl VirtIOGpu<'_> {
  31. /// Create a new VirtIO-Gpu driver.
  32. pub fn new(header: &'static mut VirtIOHeader) -> Result<Self> {
  33. header.begin_init(|features| {
  34. let features = Features::from_bits_truncate(features);
  35. info!("Device features {:?}", features);
  36. let supported_features = Features::empty();
  37. (features & supported_features).bits()
  38. });
  39. // read configuration space
  40. let config = unsafe { &mut *(header.config_space() as *mut Config) };
  41. info!("Config: {:?}", config);
  42. let control_queue = VirtQueue::new(header, QUEUE_TRANSMIT, 2)?;
  43. let cursor_queue = VirtQueue::new(header, QUEUE_CURSOR, 2)?;
  44. let queue_buf_dma = DMA::new(2)?;
  45. let queue_buf_send = unsafe { &mut queue_buf_dma.as_buf()[..PAGE_SIZE] };
  46. let queue_buf_recv = unsafe { &mut queue_buf_dma.as_buf()[PAGE_SIZE..] };
  47. header.finish_init();
  48. Ok(VirtIOGpu {
  49. header,
  50. frame_buffer_dma: None,
  51. rect: Rect::default(),
  52. control_queue,
  53. cursor_queue,
  54. queue_buf_dma,
  55. queue_buf_send,
  56. queue_buf_recv,
  57. })
  58. }
  59. /// Acknowledge interrupt.
  60. pub fn ack_interrupt(&mut self) -> bool {
  61. self.header.ack_interrupt()
  62. }
  63. /// Get the resolution (width, height).
  64. pub fn resolution(&self) -> (u32, u32) {
  65. (self.rect.width, self.rect.height)
  66. }
  67. /// Setup framebuffer
  68. pub fn setup_framebuffer(&mut self) -> Result<&mut [u8]> {
  69. // get display info
  70. let display_info: RespDisplayInfo =
  71. self.request(CtrlHeader::with_type(Command::GetDisplayInfo))?;
  72. display_info.header.check_type(Command::OkDisplayInfo)?;
  73. info!("=> {:?}", display_info);
  74. self.rect = display_info.rect;
  75. // create resource 2d
  76. let rsp: CtrlHeader = self.request(ResourceCreate2D {
  77. header: CtrlHeader::with_type(Command::ResourceCreate2d),
  78. resource_id: RESOURCE_ID,
  79. format: Format::B8G8R8A8UNORM,
  80. width: display_info.rect.width,
  81. height: display_info.rect.height,
  82. })?;
  83. rsp.check_type(Command::OkNodata)?;
  84. // alloc continuous pages for the frame buffer
  85. let size = display_info.rect.width * display_info.rect.height * 4;
  86. let frame_buffer_dma = DMA::new(pages(size as usize))?;
  87. // resource_attach_backing
  88. let rsp: CtrlHeader = self.request(ResourceAttachBacking {
  89. header: CtrlHeader::with_type(Command::ResourceAttachBacking),
  90. resource_id: RESOURCE_ID,
  91. nr_entries: 1,
  92. addr: frame_buffer_dma.paddr() as u64,
  93. length: size,
  94. padding: 0,
  95. })?;
  96. rsp.check_type(Command::OkNodata)?;
  97. // map frame buffer to screen
  98. let rsp: CtrlHeader = self.request(SetScanout {
  99. header: CtrlHeader::with_type(Command::SetScanout),
  100. rect: display_info.rect,
  101. scanout_id: 0,
  102. resource_id: RESOURCE_ID,
  103. })?;
  104. rsp.check_type(Command::OkNodata)?;
  105. let buf = unsafe { frame_buffer_dma.as_buf() };
  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. // copy data from guest to host
  112. let rsp: CtrlHeader = self.request(TransferToHost2D {
  113. header: CtrlHeader::with_type(Command::TransferToHost2d),
  114. rect: self.rect,
  115. offset: 0,
  116. resource_id: RESOURCE_ID,
  117. padding: 0,
  118. })?;
  119. rsp.check_type(Command::OkNodata)?;
  120. // flush data to screen
  121. let rsp: CtrlHeader = self.request(ResourceFlush {
  122. header: CtrlHeader::with_type(Command::ResourceFlush),
  123. rect: self.rect,
  124. resource_id: RESOURCE_ID,
  125. padding: 0,
  126. })?;
  127. rsp.check_type(Command::OkNodata)?;
  128. Ok(())
  129. }
  130. /// Send a request to the device and block for a response.
  131. fn request<Req, Rsp>(&mut self, req: Req) -> Result<Rsp> {
  132. unsafe {
  133. (self.queue_buf_send.as_mut_ptr() as *mut Req).write(req);
  134. }
  135. self.control_queue
  136. .add(&[self.queue_buf_send], &[self.queue_buf_recv])?;
  137. self.header.notify(QUEUE_TRANSMIT as u32);
  138. while !self.control_queue.can_pop() {
  139. spin_loop_hint();
  140. }
  141. self.control_queue.pop_used()?;
  142. Ok(unsafe { (self.queue_buf_recv.as_ptr() as *const Rsp).read() })
  143. }
  144. }
  145. #[repr(C)]
  146. #[derive(Debug)]
  147. struct Config {
  148. /// Signals pending events to the driver。
  149. events_read: ReadOnly<u32>,
  150. /// Clears pending events in the device.
  151. events_clear: WriteOnly<u32>,
  152. /// Specifies the maximum number of scanouts supported by the device.
  153. ///
  154. /// Minimum value is 1, maximum value is 16.
  155. num_scanouts: Volatile<u32>,
  156. }
  157. /// Display configuration has changed.
  158. const EVENT_DISPLAY: u32 = 1 << 0;
  159. bitflags! {
  160. struct Features: u64 {
  161. /// virgl 3D mode is supported.
  162. const VIRGL = 1 << 0;
  163. /// EDID is supported.
  164. const EDID = 1 << 1;
  165. // device independent
  166. const NOTIFY_ON_EMPTY = 1 << 24; // legacy
  167. const ANY_LAYOUT = 1 << 27; // legacy
  168. const RING_INDIRECT_DESC = 1 << 28;
  169. const RING_EVENT_IDX = 1 << 29;
  170. const UNUSED = 1 << 30; // legacy
  171. const VERSION_1 = 1 << 32; // detect legacy
  172. // since virtio v1.1
  173. const ACCESS_PLATFORM = 1 << 33;
  174. const RING_PACKED = 1 << 34;
  175. const IN_ORDER = 1 << 35;
  176. const ORDER_PLATFORM = 1 << 36;
  177. const SR_IOV = 1 << 37;
  178. const NOTIFICATION_DATA = 1 << 38;
  179. }
  180. }
  181. #[repr(u32)]
  182. #[derive(Debug, PartialEq, Eq)]
  183. enum Command {
  184. GetDisplayInfo = 0x100,
  185. ResourceCreate2d = 0x101,
  186. ResourceUnref = 0x102,
  187. SetScanout = 0x103,
  188. ResourceFlush = 0x104,
  189. TransferToHost2d = 0x105,
  190. ResourceAttachBacking = 0x106,
  191. ResourceDetachBacking = 0x107,
  192. GetCapsetInfo = 0x108,
  193. GetCapset = 0x109,
  194. GetEdid = 0x10a,
  195. UpdateCursor = 0x300,
  196. MoveCursor = 0x301,
  197. OkNodata = 0x1100,
  198. OkDisplayInfo = 0x1101,
  199. OkCapsetInfo = 0x1102,
  200. OkCapset = 0x1103,
  201. OkEdid = 0x1104,
  202. ErrUnspec = 0x1200,
  203. ErrOutOfMemory = 0x1201,
  204. ErrInvalidScanoutId = 0x1202,
  205. }
  206. const GPU_FLAG_FENCE: u32 = 1 << 0;
  207. #[repr(C)]
  208. #[derive(Debug)]
  209. struct CtrlHeader {
  210. hdr_type: Command,
  211. flags: u32,
  212. fence_id: u64,
  213. ctx_id: u32,
  214. padding: u32,
  215. }
  216. impl CtrlHeader {
  217. fn with_type(hdr_type: Command) -> CtrlHeader {
  218. CtrlHeader {
  219. hdr_type,
  220. flags: 0,
  221. fence_id: 0,
  222. ctx_id: 0,
  223. padding: 0,
  224. }
  225. }
  226. /// Return error if the type is not same as expected.
  227. fn check_type(&self, expected: Command) -> Result {
  228. if self.hdr_type == expected {
  229. Ok(())
  230. } else {
  231. Err(Error::IoError)
  232. }
  233. }
  234. }
  235. #[repr(C)]
  236. #[derive(Debug, Copy, Clone, Default)]
  237. struct Rect {
  238. x: u32,
  239. y: u32,
  240. width: u32,
  241. height: u32,
  242. }
  243. #[repr(C)]
  244. #[derive(Debug)]
  245. struct RespDisplayInfo {
  246. header: CtrlHeader,
  247. rect: Rect,
  248. enabled: u32,
  249. flags: u32,
  250. }
  251. #[repr(C)]
  252. #[derive(Debug)]
  253. struct ResourceCreate2D {
  254. header: CtrlHeader,
  255. resource_id: u32,
  256. format: Format,
  257. width: u32,
  258. height: u32,
  259. }
  260. #[repr(u32)]
  261. #[derive(Debug)]
  262. enum Format {
  263. B8G8R8A8UNORM = 1,
  264. }
  265. #[repr(C)]
  266. #[derive(Debug)]
  267. struct ResourceAttachBacking {
  268. header: CtrlHeader,
  269. resource_id: u32,
  270. nr_entries: u32, // always 1
  271. addr: u64,
  272. length: u32,
  273. padding: u32,
  274. }
  275. #[repr(C)]
  276. #[derive(Debug)]
  277. struct SetScanout {
  278. header: CtrlHeader,
  279. rect: Rect,
  280. scanout_id: u32,
  281. resource_id: u32,
  282. }
  283. #[repr(C)]
  284. #[derive(Debug)]
  285. struct TransferToHost2D {
  286. header: CtrlHeader,
  287. rect: Rect,
  288. offset: u64,
  289. resource_id: u32,
  290. padding: u32,
  291. }
  292. #[repr(C)]
  293. #[derive(Debug)]
  294. struct ResourceFlush {
  295. header: CtrlHeader,
  296. rect: Rect,
  297. resource_id: u32,
  298. padding: u32,
  299. }
  300. const QUEUE_TRANSMIT: usize = 0;
  301. const QUEUE_CURSOR: usize = 1;
  302. const RESOURCE_ID: u32 = 0xbabe;