DisplayConnector.cpp 25 KB

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