Serenity Operating System
at master 183 lines 7.6 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/Debug.h> 8#include <Kernel/Devices/DeviceManagement.h> 9#include <Kernel/Graphics/Bochs/QEMUDisplayConnector.h> 10#include <Kernel/Graphics/Console/ContiguousFramebufferConsole.h> 11#include <Kernel/Graphics/GraphicsManagement.h> 12#include <LibEDID/Definitions.h> 13 14namespace Kernel { 15 16NonnullLockRefPtr<QEMUDisplayConnector> QEMUDisplayConnector::must_create(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, Memory::TypedMapping<BochsDisplayMMIORegisters volatile> registers_mapping) 17{ 18 auto device_or_error = DeviceManagement::try_create_device<QEMUDisplayConnector>(framebuffer_address, framebuffer_resource_size, move(registers_mapping)); 19 VERIFY(!device_or_error.is_error()); 20 auto connector = device_or_error.release_value(); 21 MUST(connector->create_attached_framebuffer_console()); 22 MUST(connector->fetch_and_initialize_edid()); 23 return connector; 24} 25 26ErrorOr<void> QEMUDisplayConnector::fetch_and_initialize_edid() 27{ 28 Array<u8, 128> bochs_edid; 29 static_assert(sizeof(BochsDisplayMMIORegisters::edid_data) >= sizeof(EDID::Definitions::EDID)); 30 memcpy(bochs_edid.data(), (u8 const*)(m_registers.base_address().offset(__builtin_offsetof(BochsDisplayMMIORegisters, edid_data)).as_ptr()), sizeof(bochs_edid)); 31 set_edid_bytes(bochs_edid); 32 return {}; 33} 34 35ErrorOr<void> QEMUDisplayConnector::create_attached_framebuffer_console() 36{ 37 // We assume safe resolution is 1024x768x32 38 m_framebuffer_console = Graphics::ContiguousFramebufferConsole::initialize(m_framebuffer_address.value(), 1024, 768, 1024 * sizeof(u32)); 39 GraphicsManagement::the().set_console(*m_framebuffer_console); 40 return {}; 41} 42 43void QEMUDisplayConnector::enable_console() 44{ 45 VERIFY(m_control_lock.is_locked()); 46 VERIFY(m_framebuffer_console); 47 m_framebuffer_console->enable(); 48} 49 50void QEMUDisplayConnector::disable_console() 51{ 52 VERIFY(m_control_lock.is_locked()); 53 VERIFY(m_framebuffer_console); 54 m_framebuffer_console->disable(); 55} 56 57ErrorOr<void> QEMUDisplayConnector::flush_first_surface() 58{ 59 return Error::from_errno(ENOTSUP); 60} 61 62ErrorOr<void> QEMUDisplayConnector::set_safe_mode_setting() 63{ 64 DisplayConnector::ModeSetting safe_mode_set { 65 .horizontal_stride = 1024 * sizeof(u32), 66 .pixel_clock_in_khz = 0, // Note: There's no pixel clock in paravirtualized hardware 67 .horizontal_active = 1024, 68 .horizontal_front_porch_pixels = 0, // Note: There's no horizontal_front_porch_pixels in paravirtualized hardware 69 .horizontal_sync_time_pixels = 0, // Note: There's no horizontal_sync_time_pixels in paravirtualized hardware 70 .horizontal_blank_pixels = 0, // Note: There's no horizontal_blank_pixels in paravirtualized hardware 71 .vertical_active = 768, 72 .vertical_front_porch_lines = 0, // Note: There's no vertical_front_porch_lines in paravirtualized hardware 73 .vertical_sync_time_lines = 0, // Note: There's no vertical_sync_time_lines in paravirtualized hardware 74 .vertical_blank_lines = 0, // Note: There's no vertical_blank_lines in paravirtualized hardware 75 .horizontal_offset = 0, 76 .vertical_offset = 0, 77 }; 78 return set_mode_setting(safe_mode_set); 79} 80 81QEMUDisplayConnector::QEMUDisplayConnector(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, Memory::TypedMapping<BochsDisplayMMIORegisters volatile> registers_mapping) 82 : DisplayConnector(framebuffer_address, framebuffer_resource_size, false) 83 , m_registers(move(registers_mapping)) 84{ 85} 86 87QEMUDisplayConnector::IndexID QEMUDisplayConnector::index_id() const 88{ 89 return m_registers->bochs_regs.index_id; 90} 91 92void QEMUDisplayConnector::set_framebuffer_to_big_endian_format() 93{ 94 VERIFY(m_modeset_lock.is_locked()); 95 dbgln_if(BXVGA_DEBUG, "QEMUDisplayConnector set_framebuffer_to_big_endian_format"); 96 full_memory_barrier(); 97 if (m_registers->extension_regs.region_size == 0xFFFFFFFF || m_registers->extension_regs.region_size == 0) 98 return; 99 full_memory_barrier(); 100 m_registers->extension_regs.framebuffer_byteorder = BOCHS_DISPLAY_BIG_ENDIAN; 101 full_memory_barrier(); 102} 103 104void QEMUDisplayConnector::set_framebuffer_to_little_endian_format() 105{ 106 VERIFY(m_modeset_lock.is_locked()); 107 dbgln_if(BXVGA_DEBUG, "QEMUDisplayConnector set_framebuffer_to_little_endian_format"); 108 full_memory_barrier(); 109 if (m_registers->extension_regs.region_size == 0xFFFFFFFF || m_registers->extension_regs.region_size == 0) 110 return; 111 full_memory_barrier(); 112 m_registers->extension_regs.framebuffer_byteorder = BOCHS_DISPLAY_LITTLE_ENDIAN; 113 full_memory_barrier(); 114} 115 116ErrorOr<void> QEMUDisplayConnector::unblank() 117{ 118 SpinlockLocker locker(m_modeset_lock); 119 full_memory_barrier(); 120 m_registers->vga_ioports[0] = 0x20; 121 full_memory_barrier(); 122 return {}; 123} 124 125ErrorOr<void> QEMUDisplayConnector::set_y_offset(size_t y_offset) 126{ 127 VERIFY(m_modeset_lock.is_locked()); 128 m_registers->bochs_regs.y_offset = y_offset; 129 return {}; 130} 131 132ErrorOr<void> QEMUDisplayConnector::set_mode_setting(ModeSetting const& mode_setting) 133{ 134 SpinlockLocker locker(m_modeset_lock); 135 VERIFY(m_framebuffer_console); 136 size_t width = mode_setting.horizontal_active; 137 size_t height = mode_setting.vertical_active; 138 139 if (Checked<size_t>::multiplication_would_overflow(width, height, sizeof(u32))) 140 return EOVERFLOW; 141 142 dbgln_if(BXVGA_DEBUG, "QEMUDisplayConnector resolution registers set to - {}x{}", width, height); 143 m_registers->bochs_regs.enable = 0; 144 full_memory_barrier(); 145 m_registers->bochs_regs.xres = width; 146 m_registers->bochs_regs.yres = height; 147 m_registers->bochs_regs.virt_width = width; 148 m_registers->bochs_regs.virt_height = height * 2; 149 m_registers->bochs_regs.bpp = 32; 150 full_memory_barrier(); 151 m_registers->bochs_regs.enable = to_underlying(BochsFramebufferSettings::Enabled) | to_underlying(BochsFramebufferSettings::LinearFramebuffer); 152 full_memory_barrier(); 153 m_registers->bochs_regs.bank = 0; 154 if (index_id().value() == VBE_DISPI_ID5) { 155 set_framebuffer_to_little_endian_format(); 156 } 157 158 if ((u16)width != m_registers->bochs_regs.xres || (u16)height != m_registers->bochs_regs.yres) { 159 return Error::from_errno(ENOTIMPL); 160 } 161 162 m_framebuffer_console->set_resolution(width, height, width * sizeof(u32)); 163 164 DisplayConnector::ModeSetting mode_set { 165 .horizontal_stride = m_registers->bochs_regs.xres * sizeof(u32), 166 .pixel_clock_in_khz = 0, // Note: There's no pixel clock in paravirtualized hardware 167 .horizontal_active = m_registers->bochs_regs.xres, 168 .horizontal_front_porch_pixels = 0, // Note: There's no horizontal_front_porch_pixels in paravirtualized hardware 169 .horizontal_sync_time_pixels = 0, // Note: There's no horizontal_sync_time_pixels in paravirtualized hardware 170 .horizontal_blank_pixels = 0, // Note: There's no horizontal_blank_pixels in paravirtualized hardware 171 .vertical_active = m_registers->bochs_regs.yres, 172 .vertical_front_porch_lines = 0, // Note: There's no vertical_front_porch_lines in paravirtualized hardware 173 .vertical_sync_time_lines = 0, // Note: There's no vertical_sync_time_lines in paravirtualized hardware 174 .vertical_blank_lines = 0, // Note: There's no vertical_blank_lines in paravirtualized hardware 175 .horizontal_offset = 0, 176 .vertical_offset = 0, 177 }; 178 179 m_current_mode_setting = mode_set; 180 return {}; 181} 182 183}