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