FrameBufferDevice.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. /*
  2. * Copyright (c) 2021, Sahan Fernando <sahan.h.fernando@gmail.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <Kernel/Graphics/GraphicsManagement.h>
  7. #include <Kernel/Graphics/VirtIOGPU/FrameBufferDevice.h>
  8. #include <LibC/sys/ioctl_numbers.h>
  9. namespace Kernel::Graphics::VirtIOGPU {
  10. FrameBufferDevice::FrameBufferDevice(GPU& virtio_gpu, ScanoutID scanout)
  11. : BlockDevice(29, GraphicsManagement::the().allocate_minor_device_number())
  12. , m_gpu(virtio_gpu)
  13. , m_scanout(scanout)
  14. {
  15. if (display_info().enabled)
  16. create_framebuffer();
  17. }
  18. FrameBufferDevice::~FrameBufferDevice()
  19. {
  20. }
  21. void FrameBufferDevice::create_framebuffer()
  22. {
  23. // First delete any existing framebuffers to free the memory first
  24. m_framebuffer = nullptr;
  25. m_framebuffer_sink_vmobject = nullptr;
  26. // Allocate frame buffer for both front and back
  27. auto& info = display_info();
  28. m_buffer_size = calculate_framebuffer_size(info.rect.width, info.rect.height);
  29. m_framebuffer = MM.allocate_kernel_region(m_buffer_size * 2, String::formatted("VirtGPU FrameBuffer #{}", m_scanout.value()), Region::Access::Read | Region::Access::Write, AllocationStrategy::AllocateNow);
  30. auto write_sink_page = MM.allocate_user_physical_page(MemoryManager::ShouldZeroFill::No).release_nonnull();
  31. auto num_needed_pages = m_framebuffer->vmobject().page_count();
  32. NonnullRefPtrVector<PhysicalPage> pages;
  33. for (auto i = 0u; i < num_needed_pages; ++i) {
  34. pages.append(write_sink_page);
  35. }
  36. m_framebuffer_sink_vmobject = AnonymousVMObject::try_create_with_physical_pages(pages.span());
  37. MutexLocker locker(m_gpu.operation_lock());
  38. m_current_buffer = &buffer_from_index(m_last_set_buffer_index.load());
  39. create_buffer(m_main_buffer, 0, m_buffer_size);
  40. create_buffer(m_back_buffer, m_buffer_size, m_buffer_size);
  41. }
  42. void FrameBufferDevice::create_buffer(Buffer& buffer, size_t framebuffer_offset, size_t framebuffer_size)
  43. {
  44. buffer.framebuffer_offset = framebuffer_offset;
  45. buffer.framebuffer_data = m_framebuffer->vaddr().as_ptr() + framebuffer_offset;
  46. auto& info = display_info();
  47. // 1. Create BUFFER using VIRTIO_GPU_CMD_RESOURCE_CREATE_2D
  48. if (buffer.resource_id.value() != 0)
  49. m_gpu.delete_resource(buffer.resource_id);
  50. buffer.resource_id = m_gpu.create_2d_resource(info.rect);
  51. // 2. Attach backing storage using VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING
  52. m_gpu.ensure_backing_storage(*m_framebuffer, buffer.framebuffer_offset, framebuffer_size, buffer.resource_id);
  53. // 3. Use VIRTIO_GPU_CMD_SET_SCANOUT to link the framebuffer to a display scanout.
  54. if (&buffer == m_current_buffer)
  55. m_gpu.set_scanout_resource(m_scanout.value(), buffer.resource_id, info.rect);
  56. // 4. Render our test pattern
  57. draw_ntsc_test_pattern(buffer);
  58. // 5. Use VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D to update the host resource from guest memory.
  59. transfer_framebuffer_data_to_host(info.rect, buffer);
  60. // 6. Use VIRTIO_GPU_CMD_RESOURCE_FLUSH to flush the updated resource to the display.
  61. if (&buffer == m_current_buffer)
  62. flush_displayed_image(info.rect, buffer);
  63. // Make sure we constrain the existing dirty rect (if any)
  64. if (buffer.dirty_rect.width != 0 || buffer.dirty_rect.height != 0) {
  65. auto dirty_right = buffer.dirty_rect.x + buffer.dirty_rect.width;
  66. auto dirty_bottom = buffer.dirty_rect.y + buffer.dirty_rect.height;
  67. buffer.dirty_rect.width = min(dirty_right, info.rect.x + info.rect.width) - buffer.dirty_rect.x;
  68. buffer.dirty_rect.height = min(dirty_bottom, info.rect.y + info.rect.height) - buffer.dirty_rect.y;
  69. }
  70. info.enabled = 1;
  71. }
  72. Protocol::DisplayInfoResponse::Display const& FrameBufferDevice::display_info() const
  73. {
  74. return m_gpu.display_info(m_scanout);
  75. }
  76. Protocol::DisplayInfoResponse::Display& FrameBufferDevice::display_info()
  77. {
  78. return m_gpu.display_info(m_scanout);
  79. }
  80. void FrameBufferDevice::transfer_framebuffer_data_to_host(Protocol::Rect const& rect, Buffer& buffer)
  81. {
  82. m_gpu.transfer_framebuffer_data_to_host(m_scanout, rect, buffer.resource_id);
  83. }
  84. void FrameBufferDevice::flush_dirty_window(Protocol::Rect const& dirty_rect, Buffer& buffer)
  85. {
  86. m_gpu.flush_dirty_rectangle(m_scanout, dirty_rect, buffer.resource_id);
  87. }
  88. void FrameBufferDevice::flush_displayed_image(Protocol::Rect const& dirty_rect, Buffer& buffer)
  89. {
  90. m_gpu.flush_displayed_image(dirty_rect, buffer.resource_id);
  91. }
  92. bool FrameBufferDevice::try_to_set_resolution(size_t width, size_t height)
  93. {
  94. if (width > MAX_VIRTIOGPU_RESOLUTION_WIDTH || height > MAX_VIRTIOGPU_RESOLUTION_HEIGHT)
  95. return false;
  96. auto& info = display_info();
  97. MutexLocker locker(m_gpu.operation_lock());
  98. info.rect = {
  99. .x = 0,
  100. .y = 0,
  101. .width = (u32)width,
  102. .height = (u32)height,
  103. };
  104. create_framebuffer();
  105. return true;
  106. }
  107. void FrameBufferDevice::set_buffer(int buffer_index)
  108. {
  109. auto& buffer = buffer_index == 0 ? m_main_buffer : m_back_buffer;
  110. MutexLocker locker(m_gpu.operation_lock());
  111. if (&buffer == m_current_buffer)
  112. return;
  113. m_current_buffer = &buffer;
  114. m_gpu.set_scanout_resource(m_scanout.value(), buffer.resource_id, display_info().rect);
  115. m_gpu.flush_displayed_image(buffer.dirty_rect, buffer.resource_id); // QEMU SDL backend requires this (as per spec)
  116. buffer.dirty_rect = {};
  117. }
  118. int FrameBufferDevice::ioctl(FileDescription&, unsigned request, Userspace<void*> arg)
  119. {
  120. REQUIRE_PROMISE(video);
  121. switch (request) {
  122. case FB_IOCTL_GET_SIZE_IN_BYTES: {
  123. auto out = static_ptr_cast<size_t*>(arg);
  124. size_t value = m_buffer_size * 2;
  125. if (!copy_to_user(out, &value))
  126. return -EFAULT;
  127. return 0;
  128. }
  129. case FB_IOCTL_SET_RESOLUTION: {
  130. auto user_resolution = static_ptr_cast<FBResolution*>(arg);
  131. FBResolution resolution;
  132. if (!copy_from_user(&resolution, user_resolution))
  133. return -EFAULT;
  134. if (!try_to_set_resolution(resolution.width, resolution.height))
  135. return -EINVAL;
  136. resolution.pitch = pitch();
  137. if (!copy_to_user(user_resolution, &resolution))
  138. return -EFAULT;
  139. return 0;
  140. }
  141. case FB_IOCTL_GET_RESOLUTION: {
  142. auto user_resolution = static_ptr_cast<FBResolution*>(arg);
  143. FBResolution resolution {};
  144. resolution.pitch = pitch();
  145. resolution.width = width();
  146. resolution.height = height();
  147. if (!copy_to_user(user_resolution, &resolution))
  148. return -EFAULT;
  149. return 0;
  150. }
  151. case FB_IOCTL_SET_BUFFER: {
  152. auto buffer_index = static_cast<int>(arg.ptr());
  153. if (!is_valid_buffer_index(buffer_index))
  154. return -EINVAL;
  155. if (m_last_set_buffer_index.exchange(buffer_index) != buffer_index && m_are_writes_active)
  156. set_buffer(buffer_index);
  157. return 0;
  158. }
  159. case FB_IOCTL_FLUSH_BUFFERS: {
  160. auto user_flush_rects = static_ptr_cast<FBFlushRects*>(arg);
  161. FBFlushRects flush_rects;
  162. if (!copy_from_user(&flush_rects, user_flush_rects))
  163. return -EFAULT;
  164. if (!is_valid_buffer_index(flush_rects.buffer_index))
  165. return -EINVAL;
  166. if (Checked<unsigned>::multiplication_would_overflow(flush_rects.count, sizeof(FBRect)))
  167. return -EFAULT;
  168. if (m_are_writes_active && flush_rects.count > 0) {
  169. auto& buffer = buffer_from_index(flush_rects.buffer_index);
  170. MutexLocker locker(m_gpu.operation_lock());
  171. for (unsigned i = 0; i < flush_rects.count; i++) {
  172. FBRect user_dirty_rect;
  173. if (!copy_from_user(&user_dirty_rect, &flush_rects.rects[i]))
  174. return -EFAULT;
  175. Protocol::Rect dirty_rect {
  176. .x = user_dirty_rect.x,
  177. .y = user_dirty_rect.y,
  178. .width = user_dirty_rect.width,
  179. .height = user_dirty_rect.height
  180. };
  181. transfer_framebuffer_data_to_host(dirty_rect, buffer);
  182. if (&buffer == m_current_buffer) {
  183. // Flushing directly to screen
  184. flush_displayed_image(dirty_rect, buffer);
  185. buffer.dirty_rect = {};
  186. } else {
  187. if (buffer.dirty_rect.width == 0 || buffer.dirty_rect.height == 0) {
  188. buffer.dirty_rect = dirty_rect;
  189. } else {
  190. auto current_dirty_right = buffer.dirty_rect.x + buffer.dirty_rect.width;
  191. auto current_dirty_bottom = buffer.dirty_rect.y + buffer.dirty_rect.height;
  192. buffer.dirty_rect.x = min(buffer.dirty_rect.x, dirty_rect.x);
  193. buffer.dirty_rect.y = min(buffer.dirty_rect.y, dirty_rect.y);
  194. buffer.dirty_rect.width = max(current_dirty_right, dirty_rect.x + dirty_rect.width) - buffer.dirty_rect.x;
  195. buffer.dirty_rect.height = max(current_dirty_bottom, dirty_rect.y + dirty_rect.height) - buffer.dirty_rect.y;
  196. }
  197. }
  198. }
  199. }
  200. return 0;
  201. }
  202. case FB_IOCTL_GET_BUFFER_OFFSET: {
  203. auto user_buffer_offset = static_ptr_cast<FBBufferOffset*>(arg);
  204. FBBufferOffset buffer_offset;
  205. if (!copy_from_user(&buffer_offset, user_buffer_offset))
  206. return -EFAULT;
  207. if (!is_valid_buffer_index(buffer_offset.buffer_index))
  208. return -EINVAL;
  209. buffer_offset.offset = (size_t)buffer_offset.buffer_index * m_buffer_size;
  210. if (!copy_to_user(user_buffer_offset, &buffer_offset))
  211. return -EFAULT;
  212. return 0;
  213. }
  214. default:
  215. return -EINVAL;
  216. };
  217. }
  218. KResultOr<Region*> FrameBufferDevice::mmap(Process& process, FileDescription&, const Range& range, u64 offset, int prot, bool shared)
  219. {
  220. REQUIRE_PROMISE(video);
  221. if (!shared)
  222. return ENODEV;
  223. if (offset != 0 || !m_framebuffer)
  224. return ENXIO;
  225. if (range.size() > m_framebuffer->size())
  226. return EOVERFLOW;
  227. // We only allow one process to map the region
  228. if (m_userspace_mmap_region)
  229. return ENOMEM;
  230. auto vmobject = m_are_writes_active ? m_framebuffer->vmobject().try_clone() : m_framebuffer_sink_vmobject;
  231. if (vmobject.is_null())
  232. return ENOMEM;
  233. auto result = process.space().allocate_region_with_vmobject(
  234. range,
  235. vmobject.release_nonnull(),
  236. 0,
  237. "VirtIOGPU Framebuffer",
  238. prot,
  239. shared);
  240. if (result.is_error())
  241. return result;
  242. m_userspace_mmap_region = result.value();
  243. return result;
  244. }
  245. void FrameBufferDevice::deactivate_writes()
  246. {
  247. m_are_writes_active = false;
  248. if (m_userspace_mmap_region) {
  249. auto* region = m_userspace_mmap_region.unsafe_ptr();
  250. auto vm_object = m_framebuffer_sink_vmobject->try_clone();
  251. VERIFY(vm_object);
  252. region->set_vmobject(vm_object.release_nonnull());
  253. region->remap();
  254. }
  255. set_buffer(0);
  256. clear_to_black(buffer_from_index(0));
  257. }
  258. void FrameBufferDevice::activate_writes()
  259. {
  260. m_are_writes_active = true;
  261. auto last_set_buffer_index = m_last_set_buffer_index.load();
  262. if (m_userspace_mmap_region) {
  263. auto* region = m_userspace_mmap_region.unsafe_ptr();
  264. region->set_vmobject(m_framebuffer->vmobject());
  265. region->remap();
  266. }
  267. set_buffer(last_set_buffer_index);
  268. }
  269. void FrameBufferDevice::clear_to_black(Buffer& buffer)
  270. {
  271. auto& info = display_info();
  272. size_t width = info.rect.width;
  273. size_t height = info.rect.height;
  274. u8* data = buffer.framebuffer_data;
  275. for (size_t i = 0; i < width * height; ++i) {
  276. data[4 * i + 0] = 0x00;
  277. data[4 * i + 1] = 0x00;
  278. data[4 * i + 2] = 0x00;
  279. data[4 * i + 3] = 0xff;
  280. }
  281. }
  282. void FrameBufferDevice::draw_ntsc_test_pattern(Buffer& buffer)
  283. {
  284. static constexpr u8 colors[12][4] = {
  285. { 0xff, 0xff, 0xff, 0xff }, // White
  286. { 0x00, 0xff, 0xff, 0xff }, // Primary + Composite colors
  287. { 0xff, 0xff, 0x00, 0xff },
  288. { 0x00, 0xff, 0x00, 0xff },
  289. { 0xff, 0x00, 0xff, 0xff },
  290. { 0x00, 0x00, 0xff, 0xff },
  291. { 0xff, 0x00, 0x00, 0xff },
  292. { 0xba, 0x01, 0x5f, 0xff }, // Dark blue
  293. { 0x8d, 0x3d, 0x00, 0xff }, // Purple
  294. { 0x22, 0x22, 0x22, 0xff }, // Shades of gray
  295. { 0x10, 0x10, 0x10, 0xff },
  296. { 0x00, 0x00, 0x00, 0xff },
  297. };
  298. auto& info = display_info();
  299. size_t width = info.rect.width;
  300. size_t height = info.rect.height;
  301. u8* data = buffer.framebuffer_data;
  302. // Draw NTSC test card
  303. for (size_t y = 0; y < height; ++y) {
  304. for (size_t x = 0; x < width; ++x) {
  305. size_t color = 0;
  306. if (3 * y < 2 * height) {
  307. // Top 2/3 of image is 7 vertical stripes of color spectrum
  308. color = (7 * x) / width;
  309. } else if (4 * y < 3 * height) {
  310. // 2/3 mark to 3/4 mark is backwards color spectrum alternating with black
  311. auto segment = (7 * x) / width;
  312. color = segment % 2 ? 10 : 6 - segment;
  313. } else {
  314. if (28 * x < 5 * width) {
  315. color = 8;
  316. } else if (28 * x < 10 * width) {
  317. color = 0;
  318. } else if (28 * x < 15 * width) {
  319. color = 7;
  320. } else if (28 * x < 20 * width) {
  321. color = 10;
  322. } else if (7 * x < 6 * width) {
  323. // Grayscale gradient
  324. color = 26 - ((21 * x) / width);
  325. } else {
  326. // Solid black
  327. color = 10;
  328. }
  329. }
  330. u8* pixel = &data[4 * (y * width + x)];
  331. for (int i = 0; i < 4; ++i) {
  332. pixel[i] = colors[color][i];
  333. }
  334. }
  335. }
  336. dbgln_if(VIRTIO_DEBUG, "Finish drawing the pattern");
  337. }
  338. u8* FrameBufferDevice::framebuffer_data()
  339. {
  340. return m_current_buffer->framebuffer_data;
  341. }
  342. }