DisplayConnector.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. /*
  2. * Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/CharacterDevicesDirectory.h>
  7. #include <Kernel/FileSystem/SysFS/Subsystems/Devices/Graphics/DisplayConnector/DeviceDirectory.h>
  8. #include <Kernel/FileSystem/SysFS/Subsystems/Devices/Graphics/DisplayConnector/Directory.h>
  9. #include <Kernel/Graphics/DisplayConnector.h>
  10. #include <Kernel/Graphics/GraphicsManagement.h>
  11. #include <Kernel/Memory/MemoryManager.h>
  12. #include <LibC/sys/ioctl_numbers.h>
  13. namespace Kernel {
  14. DisplayConnector::DisplayConnector(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, bool enable_write_combine_optimization)
  15. : CharacterDevice(226, GraphicsManagement::the().allocate_minor_device_number())
  16. , m_enable_write_combine_optimization(enable_write_combine_optimization)
  17. , m_framebuffer_at_arbitrary_physical_range(false)
  18. , m_framebuffer_address(framebuffer_address)
  19. , m_framebuffer_resource_size(framebuffer_resource_size)
  20. {
  21. }
  22. DisplayConnector::DisplayConnector(size_t framebuffer_resource_size, bool enable_write_combine_optimization)
  23. : CharacterDevice(226, GraphicsManagement::the().allocate_minor_device_number())
  24. , m_enable_write_combine_optimization(enable_write_combine_optimization)
  25. , m_framebuffer_at_arbitrary_physical_range(true)
  26. , m_framebuffer_address({})
  27. , m_framebuffer_resource_size(framebuffer_resource_size)
  28. {
  29. }
  30. ErrorOr<NonnullLockRefPtr<Memory::VMObject>> DisplayConnector::vmobject_for_mmap(Process&, Memory::VirtualRange const&, u64& offset, bool)
  31. {
  32. VERIFY(m_shared_framebuffer_vmobject);
  33. if (offset != 0)
  34. return Error::from_errno(ENOTSUP);
  35. return *m_shared_framebuffer_vmobject;
  36. }
  37. ErrorOr<size_t> DisplayConnector::read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t)
  38. {
  39. return Error::from_errno(ENOTIMPL);
  40. }
  41. ErrorOr<size_t> DisplayConnector::write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t)
  42. {
  43. return Error::from_errno(ENOTIMPL);
  44. }
  45. void DisplayConnector::will_be_destroyed()
  46. {
  47. GraphicsManagement::the().detach_display_connector({}, *this);
  48. VERIFY(m_symlink_sysfs_component);
  49. before_will_be_destroyed_remove_symlink_from_device_identifier_directory();
  50. m_symlink_sysfs_component.clear();
  51. SysFSDisplayConnectorsDirectory::the().unplug({}, *m_sysfs_device_directory);
  52. before_will_be_destroyed_remove_from_device_management();
  53. }
  54. void DisplayConnector::after_inserting()
  55. {
  56. after_inserting_add_to_device_management();
  57. auto sysfs_display_connector_device_directory = DisplayConnectorSysFSDirectory::create(SysFSDisplayConnectorsDirectory::the(), *this);
  58. m_sysfs_device_directory = sysfs_display_connector_device_directory;
  59. SysFSDisplayConnectorsDirectory::the().plug({}, *sysfs_display_connector_device_directory);
  60. VERIFY(!m_symlink_sysfs_component);
  61. auto sys_fs_component = MUST(SysFSSymbolicLinkDeviceComponent::try_create(SysFSCharacterDevicesDirectory::the(), *this, *m_sysfs_device_directory));
  62. m_symlink_sysfs_component = sys_fs_component;
  63. after_inserting_add_symlink_to_device_identifier_directory();
  64. auto rounded_size = MUST(Memory::page_round_up(m_framebuffer_resource_size));
  65. if (!m_framebuffer_at_arbitrary_physical_range) {
  66. VERIFY(m_framebuffer_address.value().page_base() == m_framebuffer_address.value());
  67. m_shared_framebuffer_vmobject = MUST(Memory::SharedFramebufferVMObject::try_create_for_physical_range(m_framebuffer_address.value(), rounded_size));
  68. m_framebuffer_region = MUST(MM.allocate_kernel_region(m_framebuffer_address.value().page_base(), rounded_size, "Framebuffer"sv, Memory::Region::Access::ReadWrite));
  69. } else {
  70. m_shared_framebuffer_vmobject = MUST(Memory::SharedFramebufferVMObject::try_create_at_arbitrary_physical_range(rounded_size));
  71. m_framebuffer_region = MUST(MM.allocate_kernel_region_with_vmobject(m_shared_framebuffer_vmobject->real_writes_framebuffer_vmobject(), rounded_size, "Framebuffer"sv, Memory::Region::Access::ReadWrite));
  72. }
  73. m_framebuffer_data = m_framebuffer_region->vaddr().as_ptr();
  74. m_fake_writes_framebuffer_region = MUST(MM.allocate_kernel_region_with_vmobject(m_shared_framebuffer_vmobject->fake_writes_framebuffer_vmobject(), rounded_size, "Fake Writes Framebuffer"sv, Memory::Region::Access::ReadWrite));
  75. GraphicsManagement::the().attach_new_display_connector({}, *this);
  76. if (m_enable_write_combine_optimization) {
  77. [[maybe_unused]] auto result = m_framebuffer_region->set_write_combine(true);
  78. }
  79. }
  80. bool DisplayConnector::console_mode() const
  81. {
  82. VERIFY(m_control_lock.is_locked());
  83. return m_console_mode;
  84. }
  85. void DisplayConnector::set_display_mode(Badge<GraphicsManagement>, DisplayMode mode)
  86. {
  87. SpinlockLocker locker(m_control_lock);
  88. {
  89. SpinlockLocker locker(m_modeset_lock);
  90. [[maybe_unused]] auto result = set_y_offset(0);
  91. }
  92. m_console_mode = mode == DisplayMode::Console ? true : false;
  93. if (m_console_mode) {
  94. VERIFY(m_framebuffer_region->size() == m_fake_writes_framebuffer_region->size());
  95. memcpy(m_fake_writes_framebuffer_region->vaddr().as_ptr(), m_framebuffer_region->vaddr().as_ptr(), m_framebuffer_region->size());
  96. m_shared_framebuffer_vmobject->switch_to_fake_sink_framebuffer_writes({});
  97. enable_console();
  98. } else {
  99. disable_console();
  100. m_shared_framebuffer_vmobject->switch_to_real_framebuffer_writes({});
  101. VERIFY(m_framebuffer_region->size() == m_fake_writes_framebuffer_region->size());
  102. memcpy(m_framebuffer_region->vaddr().as_ptr(), m_fake_writes_framebuffer_region->vaddr().as_ptr(), m_framebuffer_region->size());
  103. }
  104. }
  105. ErrorOr<void> DisplayConnector::initialize_edid_for_generic_monitor(Optional<Array<u8, 3>> possible_manufacturer_id_string)
  106. {
  107. u8 raw_manufacturer_id[2] = { 0x0, 0x0 };
  108. if (possible_manufacturer_id_string.has_value()) {
  109. Array<u8, 3> manufacturer_id_string = possible_manufacturer_id_string.release_value();
  110. u8 byte1 = (((static_cast<u8>(manufacturer_id_string[0]) - '@') & 0x1f) << 2) | (((static_cast<u8>(manufacturer_id_string[1]) - '@') >> 3) & 3);
  111. u8 byte2 = ((static_cast<u8>(manufacturer_id_string[2]) - '@') & 0x1f) | (((static_cast<u8>(manufacturer_id_string[1]) - '@') << 5) & 0xe0);
  112. Array<u8, 2> manufacturer_id_string_packed_bytes = { byte1, byte2 };
  113. raw_manufacturer_id[0] = manufacturer_id_string_packed_bytes[1];
  114. raw_manufacturer_id[1] = manufacturer_id_string_packed_bytes[0];
  115. }
  116. Array<u8, 128> virtual_monitor_edid = {
  117. 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, /* header */
  118. raw_manufacturer_id[1], raw_manufacturer_id[0], /* manufacturer */
  119. 0x00, 0x00, /* product code */
  120. 0x00, 0x00, 0x00, 0x00, /* serial number goes here */
  121. 0x01, /* week of manufacture */
  122. 0x00, /* year of manufacture */
  123. 0x01, 0x03, /* EDID version */
  124. 0x80, /* capabilities - digital */
  125. 0x00, /* horiz. res in cm, zero for projectors */
  126. 0x00, /* vert. res in cm */
  127. 0x78, /* display gamma (120 == 2.2). */
  128. 0xEE, /* features (standby, suspend, off, RGB, std */
  129. /* colour space, preferred timing mode) */
  130. 0xEE, 0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0x0F, 0x50, 0x54,
  131. /* chromaticity for standard colour space. */
  132. 0x00, 0x00, 0x00, /* no default timings */
  133. 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
  134. 0x01, 0x01,
  135. 0x01, 0x01, 0x01, 0x01, /* no standard timings */
  136. 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x02, 0x02,
  137. 0x02, 0x02,
  138. /* descriptor block 1 goes below */
  139. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  140. /* descriptor block 2, monitor ranges */
  141. 0x00, 0x00, 0x00, 0xFD, 0x00,
  142. 0x00, 0xC8, 0x00, 0xC8, 0x64, 0x00, 0x0A, 0x20, 0x20, 0x20,
  143. 0x20, 0x20,
  144. /* 0-200Hz vertical, 0-200KHz horizontal, 1000MHz pixel clock */
  145. 0x20,
  146. /* descriptor block 3, monitor name */
  147. 0x00, 0x00, 0x00, 0xFC, 0x00,
  148. 'G', 'e', 'n', 'e', 'r', 'i', 'c', 'S', 'c', 'r', 'e', 'e', 'n',
  149. /* descriptor block 4: dummy data */
  150. 0x00, 0x00, 0x00, 0x10, 0x00,
  151. 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20,
  152. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
  153. 0x20,
  154. 0x00, /* number of extensions */
  155. 0x00 /* checksum goes here */
  156. };
  157. // Note: Fix checksum to avoid warnings about checksum mismatch.
  158. size_t checksum = 0;
  159. // Note: Read all 127 bytes to add them to the checksum. Byte 128 is zeroed so
  160. // we could technically add it to the sum result, but it could lead to an error if it contained
  161. // a non-zero value, so we are not using it.
  162. for (size_t index = 0; index < sizeof(virtual_monitor_edid) - 1; index++)
  163. checksum += virtual_monitor_edid[index];
  164. virtual_monitor_edid[127] = 0x100 - checksum;
  165. set_edid_bytes(virtual_monitor_edid);
  166. return {};
  167. }
  168. void DisplayConnector::set_edid_bytes(Array<u8, 128> const& edid_bytes)
  169. {
  170. memcpy((u8*)m_edid_bytes, edid_bytes.data(), sizeof(m_edid_bytes));
  171. if (auto parsed_edid = EDID::Parser::from_bytes({ m_edid_bytes, sizeof(m_edid_bytes) }); !parsed_edid.is_error()) {
  172. m_edid_parser = parsed_edid.release_value();
  173. m_edid_valid = true;
  174. } else {
  175. dmesgln("DisplayConnector: Print offending EDID");
  176. for (size_t x = 0; x < 128; x = x + 16) {
  177. dmesgln("{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}",
  178. m_edid_bytes[x], m_edid_bytes[x + 1], m_edid_bytes[x + 2], m_edid_bytes[x + 3],
  179. m_edid_bytes[x + 4], m_edid_bytes[x + 5], m_edid_bytes[x + 6], m_edid_bytes[x + 7],
  180. m_edid_bytes[x + 8], m_edid_bytes[x + 9], m_edid_bytes[x + 10], m_edid_bytes[x + 11],
  181. m_edid_bytes[x + 12], m_edid_bytes[x + 13], m_edid_bytes[x + 14], m_edid_bytes[x + 15]);
  182. }
  183. dmesgln("DisplayConnector: Parsing EDID failed: {}", parsed_edid.error());
  184. }
  185. }
  186. ErrorOr<void> DisplayConnector::flush_rectangle(size_t, FBRect const&)
  187. {
  188. return Error::from_errno(ENOTSUP);
  189. }
  190. DisplayConnector::ModeSetting DisplayConnector::current_mode_setting() const
  191. {
  192. SpinlockLocker locker(m_modeset_lock);
  193. return m_current_mode_setting;
  194. }
  195. ErrorOr<ByteBuffer> DisplayConnector::get_edid() const
  196. {
  197. if (!m_edid_valid)
  198. return Error::from_errno(ENOTIMPL);
  199. return ByteBuffer::copy(m_edid_bytes, sizeof(m_edid_bytes));
  200. }
  201. struct GraphicsIOCtlChecker {
  202. unsigned ioctl_number;
  203. StringView name;
  204. bool requires_ownership { false };
  205. };
  206. static constexpr GraphicsIOCtlChecker s_checkers[] = {
  207. { GRAPHICS_IOCTL_GET_PROPERTIES, "GRAPHICS_IOCTL_GET_PROPERTIES"sv, false },
  208. { GRAPHICS_IOCTL_SET_HEAD_VERTICAL_OFFSET_BUFFER, "GRAPHICS_IOCTL_SET_HEAD_VERTICAL_OFFSET_BUFFER"sv, true },
  209. { GRAPHICS_IOCTL_GET_HEAD_VERTICAL_OFFSET_BUFFER, "GRAPHICS_IOCTL_GET_HEAD_VERTICAL_OFFSET_BUFFER"sv, false },
  210. { GRAPHICS_IOCTL_FLUSH_HEAD_BUFFERS, "GRAPHICS_IOCTL_FLUSH_HEAD_BUFFERS"sv, true },
  211. { GRAPHICS_IOCTL_FLUSH_HEAD, "GRAPHICS_IOCTL_FLUSH_HEAD"sv, true },
  212. { GRAPHICS_IOCTL_SET_HEAD_MODE_SETTING, "GRAPHICS_IOCTL_SET_HEAD_MODE_SETTING"sv, true },
  213. { GRAPHICS_IOCTL_GET_HEAD_MODE_SETTING, "GRAPHICS_IOCTL_GET_HEAD_MODE_SETTING"sv, false },
  214. { GRAPHICS_IOCTL_SET_SAFE_HEAD_MODE_SETTING, "GRAPHICS_IOCTL_SET_SAFE_HEAD_MODE_SETTING"sv, true },
  215. { GRAPHICS_IOCTL_SET_RESPONSIBLE, "GRAPHICS_IOCTL_SET_RESPONSIBLE"sv, false },
  216. { GRAPHICS_IOCTL_UNSET_RESPONSIBLE, "GRAPHICS_IOCTL_UNSET_RESPONSIBLE"sv, true },
  217. };
  218. static StringView ioctl_to_stringview(unsigned request)
  219. {
  220. for (auto& checker : s_checkers) {
  221. if (checker.ioctl_number == request)
  222. return checker.name;
  223. }
  224. VERIFY_NOT_REACHED();
  225. }
  226. bool DisplayConnector::ioctl_requires_ownership(unsigned request) const
  227. {
  228. for (auto& checker : s_checkers) {
  229. if (checker.ioctl_number == request)
  230. return checker.requires_ownership;
  231. }
  232. VERIFY_NOT_REACHED();
  233. }
  234. ErrorOr<void> DisplayConnector::ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg)
  235. {
  236. TRY(Process::current().require_promise(Pledge::video));
  237. // Note: We only allow to set responsibility on a DisplayConnector,
  238. // get the current ModeSetting or the hardware framebuffer properties without the
  239. // need of having an established responsibility on a DisplayConnector.
  240. if (ioctl_requires_ownership(request)) {
  241. auto process = m_responsible_process.strong_ref();
  242. if (!process || process.ptr() != &Process::current()) {
  243. dbgln("DisplayConnector::ioctl: {} requires ownership over the device", ioctl_to_stringview(request));
  244. return Error::from_errno(EPERM);
  245. }
  246. }
  247. switch (request) {
  248. case GRAPHICS_IOCTL_SET_RESPONSIBLE: {
  249. SpinlockLocker locker(m_responsible_process_lock);
  250. auto process = m_responsible_process.strong_ref();
  251. // Note: If there's already a process being responsible, just return an error.
  252. // We could technically return 0 if the the requesting process is already
  253. // was set to be responsible for this DisplayConnector, but it servicing no
  254. // no good purpose and should be considered a bug if this happens anyway.
  255. if (process)
  256. return Error::from_errno(EPERM);
  257. m_responsible_process = Process::current();
  258. return {};
  259. }
  260. case GRAPHICS_IOCTL_UNSET_RESPONSIBLE: {
  261. SpinlockLocker locker(m_responsible_process_lock);
  262. auto process = m_responsible_process.strong_ref();
  263. if (!process)
  264. return Error::from_errno(ESRCH);
  265. if (process.ptr() != &Process::current())
  266. return Error::from_errno(EPERM);
  267. m_responsible_process.clear();
  268. return {};
  269. }
  270. case GRAPHICS_IOCTL_GET_PROPERTIES: {
  271. VERIFY(m_shared_framebuffer_vmobject);
  272. auto user_properties = static_ptr_cast<GraphicsConnectorProperties*>(arg);
  273. GraphicsConnectorProperties properties {};
  274. properties.flushing_support = flush_support();
  275. properties.doublebuffer_support = double_framebuffering_capable();
  276. properties.partial_flushing_support = partial_flush_support();
  277. properties.refresh_rate_support = refresh_rate_support();
  278. properties.max_buffer_bytes = m_shared_framebuffer_vmobject->size();
  279. return copy_to_user(user_properties, &properties);
  280. }
  281. case GRAPHICS_IOCTL_GET_HEAD_MODE_SETTING: {
  282. auto user_head_mode_setting = static_ptr_cast<GraphicsHeadModeSetting*>(arg);
  283. GraphicsHeadModeSetting head_mode_setting {};
  284. TRY(copy_from_user(&head_mode_setting, user_head_mode_setting));
  285. {
  286. SpinlockLocker control_locker(m_control_lock);
  287. head_mode_setting.horizontal_stride = m_current_mode_setting.horizontal_stride;
  288. head_mode_setting.pixel_clock_in_khz = m_current_mode_setting.pixel_clock_in_khz;
  289. head_mode_setting.horizontal_active = m_current_mode_setting.horizontal_active;
  290. head_mode_setting.horizontal_front_porch_pixels = m_current_mode_setting.horizontal_front_porch_pixels;
  291. head_mode_setting.horizontal_sync_time_pixels = m_current_mode_setting.horizontal_sync_time_pixels;
  292. head_mode_setting.horizontal_blank_pixels = m_current_mode_setting.horizontal_blank_pixels;
  293. head_mode_setting.vertical_active = m_current_mode_setting.vertical_active;
  294. head_mode_setting.vertical_front_porch_lines = m_current_mode_setting.vertical_front_porch_lines;
  295. head_mode_setting.vertical_sync_time_lines = m_current_mode_setting.vertical_sync_time_lines;
  296. head_mode_setting.vertical_blank_lines = m_current_mode_setting.vertical_blank_lines;
  297. head_mode_setting.horizontal_offset = m_current_mode_setting.horizontal_offset;
  298. head_mode_setting.vertical_offset = m_current_mode_setting.vertical_offset;
  299. }
  300. return copy_to_user(user_head_mode_setting, &head_mode_setting);
  301. }
  302. case GRAPHICS_IOCTL_SET_HEAD_MODE_SETTING: {
  303. auto user_mode_setting = static_ptr_cast<GraphicsHeadModeSetting const*>(arg);
  304. auto head_mode_setting = TRY(copy_typed_from_user(user_mode_setting));
  305. if (head_mode_setting.horizontal_stride < 0)
  306. return Error::from_errno(EINVAL);
  307. if (head_mode_setting.pixel_clock_in_khz < 0)
  308. return Error::from_errno(EINVAL);
  309. if (head_mode_setting.horizontal_active < 0)
  310. return Error::from_errno(EINVAL);
  311. if (head_mode_setting.horizontal_front_porch_pixels < 0)
  312. return Error::from_errno(EINVAL);
  313. if (head_mode_setting.horizontal_sync_time_pixels < 0)
  314. return Error::from_errno(EINVAL);
  315. if (head_mode_setting.horizontal_blank_pixels < 0)
  316. return Error::from_errno(EINVAL);
  317. if (head_mode_setting.vertical_active < 0)
  318. return Error::from_errno(EINVAL);
  319. if (head_mode_setting.vertical_front_porch_lines < 0)
  320. return Error::from_errno(EINVAL);
  321. if (head_mode_setting.vertical_sync_time_lines < 0)
  322. return Error::from_errno(EINVAL);
  323. if (head_mode_setting.vertical_blank_lines < 0)
  324. return Error::from_errno(EINVAL);
  325. if (head_mode_setting.horizontal_offset < 0)
  326. return Error::from_errno(EINVAL);
  327. if (head_mode_setting.vertical_offset < 0)
  328. return Error::from_errno(EINVAL);
  329. {
  330. SpinlockLocker control_locker(m_control_lock);
  331. ModeSetting requested_mode_setting;
  332. requested_mode_setting.horizontal_stride = 0;
  333. requested_mode_setting.pixel_clock_in_khz = head_mode_setting.pixel_clock_in_khz;
  334. requested_mode_setting.horizontal_active = head_mode_setting.horizontal_active;
  335. requested_mode_setting.horizontal_front_porch_pixels = head_mode_setting.horizontal_front_porch_pixels;
  336. requested_mode_setting.horizontal_sync_time_pixels = head_mode_setting.horizontal_sync_time_pixels;
  337. requested_mode_setting.horizontal_blank_pixels = head_mode_setting.horizontal_blank_pixels;
  338. requested_mode_setting.vertical_active = head_mode_setting.vertical_active;
  339. requested_mode_setting.vertical_front_porch_lines = head_mode_setting.vertical_front_porch_lines;
  340. requested_mode_setting.vertical_sync_time_lines = head_mode_setting.vertical_sync_time_lines;
  341. requested_mode_setting.vertical_blank_lines = head_mode_setting.vertical_blank_lines;
  342. requested_mode_setting.horizontal_offset = head_mode_setting.horizontal_offset;
  343. requested_mode_setting.vertical_offset = head_mode_setting.vertical_offset;
  344. TRY(set_mode_setting(requested_mode_setting));
  345. }
  346. return {};
  347. }
  348. case GRAPHICS_IOCTL_SET_SAFE_HEAD_MODE_SETTING: {
  349. SpinlockLocker control_locker(m_control_lock);
  350. TRY(set_safe_mode_setting());
  351. return {};
  352. }
  353. case GRAPHICS_IOCTL_SET_HEAD_VERTICAL_OFFSET_BUFFER: {
  354. // FIXME: We silently ignore the request if we are in console mode.
  355. // WindowServer is not ready yet to handle errors such as EBUSY currently.
  356. SpinlockLocker control_locker(m_control_lock);
  357. if (console_mode()) {
  358. return {};
  359. }
  360. auto user_head_vertical_buffer_offset = static_ptr_cast<GraphicsHeadVerticalOffset const*>(arg);
  361. auto head_vertical_buffer_offset = TRY(copy_typed_from_user(user_head_vertical_buffer_offset));
  362. SpinlockLocker locker(m_modeset_lock);
  363. if (head_vertical_buffer_offset.offsetted < 0 || head_vertical_buffer_offset.offsetted > 1)
  364. return Error::from_errno(EINVAL);
  365. TRY(set_y_offset(head_vertical_buffer_offset.offsetted == 0 ? 0 : m_current_mode_setting.vertical_active));
  366. if (head_vertical_buffer_offset.offsetted == 0)
  367. m_vertical_offsetted = false;
  368. else
  369. m_vertical_offsetted = true;
  370. return {};
  371. }
  372. case GRAPHICS_IOCTL_GET_HEAD_VERTICAL_OFFSET_BUFFER: {
  373. auto user_head_vertical_buffer_offset = static_ptr_cast<GraphicsHeadVerticalOffset*>(arg);
  374. GraphicsHeadVerticalOffset head_vertical_buffer_offset {};
  375. TRY(copy_from_user(&head_vertical_buffer_offset, user_head_vertical_buffer_offset));
  376. head_vertical_buffer_offset.offsetted = m_vertical_offsetted;
  377. return copy_to_user(user_head_vertical_buffer_offset, &head_vertical_buffer_offset);
  378. }
  379. case GRAPHICS_IOCTL_FLUSH_HEAD_BUFFERS: {
  380. if (console_mode())
  381. return {};
  382. if (!partial_flush_support())
  383. return Error::from_errno(ENOTSUP);
  384. MutexLocker locker(m_flushing_lock);
  385. auto user_flush_rects = static_ptr_cast<FBFlushRects const*>(arg);
  386. auto flush_rects = TRY(copy_typed_from_user(user_flush_rects));
  387. if (Checked<unsigned>::multiplication_would_overflow(flush_rects.count, sizeof(FBRect)))
  388. return Error::from_errno(EFAULT);
  389. if (flush_rects.count > 0) {
  390. for (unsigned i = 0; i < flush_rects.count; i++) {
  391. FBRect user_dirty_rect;
  392. TRY(copy_from_user(&user_dirty_rect, &flush_rects.rects[i]));
  393. {
  394. SpinlockLocker control_locker(m_control_lock);
  395. if (console_mode()) {
  396. return {};
  397. }
  398. TRY(flush_rectangle(flush_rects.buffer_index, user_dirty_rect));
  399. }
  400. }
  401. }
  402. return {};
  403. };
  404. case GRAPHICS_IOCTL_FLUSH_HEAD: {
  405. // FIXME: We silently ignore the request if we are in console mode.
  406. // WindowServer is not ready yet to handle errors such as EBUSY currently.
  407. MutexLocker locker(m_flushing_lock);
  408. SpinlockLocker control_locker(m_control_lock);
  409. if (console_mode()) {
  410. return {};
  411. }
  412. if (!flush_support())
  413. return Error::from_errno(ENOTSUP);
  414. TRY(flush_first_surface());
  415. return {};
  416. }
  417. }
  418. return EINVAL;
  419. }
  420. }