mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 17:52:45 +00:00 
			
		
		
		
	Kernel/Graphics: Use DisplayConnector design for the Bochs driver
This commit is contained in:
		
							parent
							
								
									6d7e2596e0
								
							
						
					
					
						commit
						e9a74cbefb
					
				
					 8 changed files with 450 additions and 226 deletions
				
			
		|  | @ -2,8 +2,8 @@ | ||||||
| MainScreen=0 | MainScreen=0 | ||||||
| 
 | 
 | ||||||
| [Screen0] | [Screen0] | ||||||
| Mode=Device | Mode=DisplayConnectorDevice | ||||||
| Device=/dev/fb0 | Device=/dev/gpu/connector0 | ||||||
| Left=0 | Left=0 | ||||||
| Top=0 | Top=0 | ||||||
| Width=1024 | Width=1024 | ||||||
|  |  | ||||||
|  | @ -74,7 +74,9 @@ set(KERNEL_SOURCES | ||||||
|     Devices/HID/PS2MouseDevice.cpp |     Devices/HID/PS2MouseDevice.cpp | ||||||
|     Devices/HID/VMWareMouseDevice.cpp |     Devices/HID/VMWareMouseDevice.cpp | ||||||
|     GlobalProcessExposed.cpp |     GlobalProcessExposed.cpp | ||||||
|  |     Graphics/Bochs/DisplayConnector.cpp | ||||||
|     Graphics/Bochs/GraphicsAdapter.cpp |     Graphics/Bochs/GraphicsAdapter.cpp | ||||||
|  |     Graphics/Bochs/QEMUDisplayConnector.cpp | ||||||
|     Graphics/Console/BootFramebufferConsole.cpp |     Graphics/Console/BootFramebufferConsole.cpp | ||||||
|     Graphics/Console/GenericFramebufferConsole.cpp |     Graphics/Console/GenericFramebufferConsole.cpp | ||||||
|     Graphics/Console/ContiguousFramebufferConsole.cpp |     Graphics/Console/ContiguousFramebufferConsole.cpp | ||||||
|  |  | ||||||
							
								
								
									
										161
									
								
								Kernel/Graphics/Bochs/DisplayConnector.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								Kernel/Graphics/Bochs/DisplayConnector.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,161 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il> | ||||||
|  |  * | ||||||
|  |  * SPDX-License-Identifier: BSD-2-Clause | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <Kernel/Arch/x86/IO.h> | ||||||
|  | #include <Kernel/Debug.h> | ||||||
|  | #include <Kernel/Devices/DeviceManagement.h> | ||||||
|  | #include <Kernel/Graphics/Bochs/Definitions.h> | ||||||
|  | #include <Kernel/Graphics/Bochs/DisplayConnector.h> | ||||||
|  | #include <Kernel/Graphics/Console/ContiguousFramebufferConsole.h> | ||||||
|  | #include <Kernel/Graphics/GraphicsManagement.h> | ||||||
|  | 
 | ||||||
|  | namespace Kernel { | ||||||
|  | 
 | ||||||
|  | NonnullRefPtr<BochsDisplayConnector> BochsDisplayConnector::must_create(PhysicalAddress framebuffer_address) | ||||||
|  | { | ||||||
|  |     auto device_or_error = DeviceManagement::try_create_device<BochsDisplayConnector>(framebuffer_address); | ||||||
|  |     VERIFY(!device_or_error.is_error()); | ||||||
|  |     auto connector = device_or_error.release_value(); | ||||||
|  |     MUST(connector->create_attached_framebuffer_console()); | ||||||
|  |     MUST(connector->initialize_edid_for_generic_monitor()); | ||||||
|  |     return connector; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | BochsDisplayConnector::BochsDisplayConnector(PhysicalAddress framebuffer_address) | ||||||
|  |     : DisplayConnector() | ||||||
|  |     , m_framebuffer_address(framebuffer_address) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ErrorOr<void> BochsDisplayConnector::create_attached_framebuffer_console() | ||||||
|  | { | ||||||
|  |     auto rounded_size = TRY(Memory::page_round_up(1024 * sizeof(u32) * 768 * 2)); | ||||||
|  |     m_framebuffer_region = TRY(MM.allocate_kernel_region(m_framebuffer_address.page_base(), rounded_size, "Framebuffer"sv, Memory::Region::Access::ReadWrite)); | ||||||
|  |     [[maybe_unused]] auto result = m_framebuffer_region->set_write_combine(true); | ||||||
|  |     m_framebuffer_data = m_framebuffer_region->vaddr().offset(m_framebuffer_address.offset_in_page()).as_ptr(); | ||||||
|  |     // We assume safe resolution is 1024x768x32
 | ||||||
|  |     m_framebuffer_console = Graphics::ContiguousFramebufferConsole::initialize(m_framebuffer_address, 1024, 768, 1024 * sizeof(u32)); | ||||||
|  |     GraphicsManagement::the().set_console(*m_framebuffer_console); | ||||||
|  |     return {}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void set_register_with_io(u16 index, u16 data) | ||||||
|  | { | ||||||
|  |     IO::out16(VBE_DISPI_IOPORT_INDEX, index); | ||||||
|  |     IO::out16(VBE_DISPI_IOPORT_DATA, data); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static u16 get_register_with_io(u16 index) | ||||||
|  | { | ||||||
|  |     IO::out16(VBE_DISPI_IOPORT_INDEX, index); | ||||||
|  |     return IO::in16(VBE_DISPI_IOPORT_DATA); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | BochsDisplayConnector::IndexID BochsDisplayConnector::index_id() const | ||||||
|  | { | ||||||
|  |     return get_register_with_io(0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ErrorOr<size_t> BochsDisplayConnector::write_to_first_surface(u64 offset, UserOrKernelBuffer const& buffer, size_t length) | ||||||
|  | { | ||||||
|  |     VERIFY(m_control_lock.is_locked()); | ||||||
|  |     if (offset + length > m_framebuffer_region->size()) | ||||||
|  |         return Error::from_errno(EOVERFLOW); | ||||||
|  |     TRY(buffer.read(m_framebuffer_data + offset, 0, length)); | ||||||
|  |     return length; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void BochsDisplayConnector::enable_console() | ||||||
|  | { | ||||||
|  |     VERIFY(m_control_lock.is_locked()); | ||||||
|  |     VERIFY(m_framebuffer_console); | ||||||
|  |     m_framebuffer_console->enable(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void BochsDisplayConnector::disable_console() | ||||||
|  | { | ||||||
|  |     VERIFY(m_control_lock.is_locked()); | ||||||
|  |     VERIFY(m_framebuffer_console); | ||||||
|  |     m_framebuffer_console->disable(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ErrorOr<void> BochsDisplayConnector::flush_first_surface() | ||||||
|  | { | ||||||
|  |     return Error::from_errno(ENOTSUP); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ErrorOr<void> BochsDisplayConnector::set_safe_mode_setting() | ||||||
|  | { | ||||||
|  |     DisplayConnector::ModeSetting safe_mode_set { | ||||||
|  |         .horizontal_stride = 1024 * sizeof(u32), | ||||||
|  |         .pixel_clock_in_khz = 0, // Note: There's no pixel clock in paravirtualized hardware
 | ||||||
|  |         .horizontal_active = 1024, | ||||||
|  |         .horizontal_front_porch_pixels = 0, // Note: There's no horizontal_front_porch_pixels in paravirtualized hardware
 | ||||||
|  |         .horizontal_sync_time_pixels = 0,   // Note: There's no horizontal_sync_time_pixels in paravirtualized hardware
 | ||||||
|  |         .horizontal_blank_pixels = 0,       // Note: There's no horizontal_blank_pixels in paravirtualized hardware
 | ||||||
|  |         .vertical_active = 768, | ||||||
|  |         .vertical_front_porch_lines = 0, // Note: There's no vertical_front_porch_lines in paravirtualized hardware
 | ||||||
|  |         .vertical_sync_time_lines = 0,   // Note: There's no vertical_sync_time_lines in paravirtualized hardware
 | ||||||
|  |         .vertical_blank_lines = 0,       // Note: There's no vertical_blank_lines in paravirtualized hardware
 | ||||||
|  |         .horizontal_offset = 0, | ||||||
|  |         .vertical_offset = 0, | ||||||
|  |     }; | ||||||
|  |     return set_mode_setting(safe_mode_set); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ErrorOr<void> BochsDisplayConnector::set_mode_setting(ModeSetting const& mode_setting) | ||||||
|  | { | ||||||
|  |     SpinlockLocker locker(m_modeset_lock); | ||||||
|  |     size_t width = mode_setting.horizontal_active; | ||||||
|  |     size_t height = mode_setting.vertical_active; | ||||||
|  | 
 | ||||||
|  |     dbgln_if(BXVGA_DEBUG, "BochsDisplayConnector resolution registers set to - {}x{}", width, height); | ||||||
|  | 
 | ||||||
|  |     set_register_with_io(to_underlying(BochsDISPIRegisters::ENABLE), 0); | ||||||
|  |     set_register_with_io(to_underlying(BochsDISPIRegisters::XRES), (u16)width); | ||||||
|  |     set_register_with_io(to_underlying(BochsDISPIRegisters::YRES), (u16)height); | ||||||
|  |     set_register_with_io(to_underlying(BochsDISPIRegisters::VIRT_WIDTH), (u16)width); | ||||||
|  |     set_register_with_io(to_underlying(BochsDISPIRegisters::VIRT_HEIGHT), (u16)height * 2); | ||||||
|  |     set_register_with_io(to_underlying(BochsDISPIRegisters::BPP), 32); | ||||||
|  |     set_register_with_io(to_underlying(BochsDISPIRegisters::ENABLE), to_underlying(BochsFramebufferSettings::Enabled) | to_underlying(BochsFramebufferSettings::LinearFramebuffer)); | ||||||
|  |     set_register_with_io(to_underlying(BochsDISPIRegisters::BANK), 0); | ||||||
|  |     if ((u16)width != get_register_with_io(to_underlying(BochsDISPIRegisters::XRES)) || (u16)height != get_register_with_io(to_underlying(BochsDISPIRegisters::YRES))) { | ||||||
|  |         return Error::from_errno(ENOTIMPL); | ||||||
|  |     } | ||||||
|  |     auto current_horizontal_active = get_register_with_io(to_underlying(BochsDISPIRegisters::XRES)); | ||||||
|  |     auto current_vertical_active = get_register_with_io(to_underlying(BochsDISPIRegisters::YRES)); | ||||||
|  |     DisplayConnector::ModeSetting mode_set { | ||||||
|  |         .horizontal_stride = current_horizontal_active * sizeof(u32), | ||||||
|  |         .pixel_clock_in_khz = 0, // Note: There's no pixel clock in paravirtualized hardware
 | ||||||
|  |         .horizontal_active = current_horizontal_active, | ||||||
|  |         .horizontal_front_porch_pixels = 0, // Note: There's no horizontal_front_porch_pixels in paravirtualized hardware
 | ||||||
|  |         .horizontal_sync_time_pixels = 0,   // Note: There's no horizontal_sync_time_pixels in paravirtualized hardware
 | ||||||
|  |         .horizontal_blank_pixels = 0,       // Note: There's no horizontal_blank_pixels in paravirtualized hardware
 | ||||||
|  |         .vertical_active = current_vertical_active, | ||||||
|  |         .vertical_front_porch_lines = 0, // Note: There's no vertical_front_porch_lines in paravirtualized hardware
 | ||||||
|  |         .vertical_sync_time_lines = 0,   // Note: There's no vertical_sync_time_lines in paravirtualized hardware
 | ||||||
|  |         .vertical_blank_lines = 0,       // Note: There's no vertical_blank_lines in paravirtualized hardware
 | ||||||
|  |         .horizontal_offset = 0, | ||||||
|  |         .vertical_offset = 0, | ||||||
|  |     }; | ||||||
|  |     m_current_mode_setting = mode_set; | ||||||
|  |     return {}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ErrorOr<void> BochsDisplayConnector::set_y_offset(size_t) | ||||||
|  | { | ||||||
|  |     // Note: Although when using this device on QEMU we can actually set the horizontal and vertical offsets
 | ||||||
|  |     // with IO ports, this class is meant to be used for plain old Bochs graphics which might not support
 | ||||||
|  |     // this feature at all.
 | ||||||
|  |     return Error::from_errno(ENOTIMPL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ErrorOr<void> BochsDisplayConnector::unblank() | ||||||
|  | { | ||||||
|  |     return Error::from_errno(ENOTIMPL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										59
									
								
								Kernel/Graphics/Bochs/DisplayConnector.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								Kernel/Graphics/Bochs/DisplayConnector.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,59 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il> | ||||||
|  |  * | ||||||
|  |  * SPDX-License-Identifier: BSD-2-Clause | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <AK/RefPtr.h> | ||||||
|  | #include <AK/Try.h> | ||||||
|  | #include <Kernel/Graphics/Bochs/Definitions.h> | ||||||
|  | #include <Kernel/Graphics/Console/GenericFramebufferConsole.h> | ||||||
|  | #include <Kernel/Graphics/DisplayConnector.h> | ||||||
|  | #include <Kernel/Locking/Spinlock.h> | ||||||
|  | #include <Kernel/Memory/TypedMapping.h> | ||||||
|  | 
 | ||||||
|  | namespace Kernel { | ||||||
|  | 
 | ||||||
|  | class BochsDisplayConnector | ||||||
|  |     : public DisplayConnector { | ||||||
|  |     friend class BochsGraphicsAdapter; | ||||||
|  |     friend class DeviceManagement; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     TYPEDEF_DISTINCT_ORDERED_ID(u16, IndexID); | ||||||
|  | 
 | ||||||
|  |     static NonnullRefPtr<BochsDisplayConnector> must_create(PhysicalAddress framebuffer_address); | ||||||
|  | 
 | ||||||
|  |     virtual IndexID index_id() const; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  |     ErrorOr<void> create_attached_framebuffer_console(); | ||||||
|  | 
 | ||||||
|  |     explicit BochsDisplayConnector(PhysicalAddress framebuffer_address); | ||||||
|  | 
 | ||||||
|  |     virtual bool mutable_mode_setting_capable() const override final { return true; } | ||||||
|  |     virtual bool double_framebuffering_capable() const override { return false; } | ||||||
|  |     virtual ErrorOr<void> set_mode_setting(ModeSetting const&) override; | ||||||
|  |     virtual ErrorOr<void> set_safe_mode_setting() override final; | ||||||
|  |     virtual ErrorOr<void> set_y_offset(size_t y) override; | ||||||
|  |     virtual ErrorOr<void> unblank() override; | ||||||
|  | 
 | ||||||
|  |     virtual bool partial_flush_support() const override final { return false; } | ||||||
|  |     virtual bool flush_support() const override final { return false; } | ||||||
|  |     // Note: Paravirtualized hardware doesn't require a defined refresh rate for modesetting.
 | ||||||
|  |     virtual bool refresh_rate_support() const override final { return false; } | ||||||
|  | 
 | ||||||
|  |     virtual ErrorOr<size_t> write_to_first_surface(u64 offset, UserOrKernelBuffer const&, size_t length) override final; | ||||||
|  |     virtual ErrorOr<void> flush_first_surface() override final; | ||||||
|  | 
 | ||||||
|  |     virtual void enable_console() override final; | ||||||
|  |     virtual void disable_console() override final; | ||||||
|  | 
 | ||||||
|  |     const PhysicalAddress m_framebuffer_address; | ||||||
|  |     RefPtr<Graphics::GenericFramebufferConsole> m_framebuffer_console; | ||||||
|  |     OwnPtr<Memory::Region> m_framebuffer_region; | ||||||
|  |     u8* m_framebuffer_data {}; | ||||||
|  | }; | ||||||
|  | } | ||||||
|  | @ -6,11 +6,15 @@ | ||||||
| 
 | 
 | ||||||
| #include <AK/Atomic.h> | #include <AK/Atomic.h> | ||||||
| #include <AK/Checked.h> | #include <AK/Checked.h> | ||||||
|  | #include <AK/Try.h> | ||||||
| #include <Kernel/Arch/x86/IO.h> | #include <Kernel/Arch/x86/IO.h> | ||||||
| #include <Kernel/Bus/PCI/API.h> | #include <Kernel/Bus/PCI/API.h> | ||||||
| #include <Kernel/Bus/PCI/IDs.h> | #include <Kernel/Bus/PCI/IDs.h> | ||||||
| #include <Kernel/Debug.h> | #include <Kernel/Debug.h> | ||||||
|  | #include <Kernel/Graphics/Bochs/Definitions.h> | ||||||
|  | #include <Kernel/Graphics/Bochs/DisplayConnector.h> | ||||||
| #include <Kernel/Graphics/Bochs/GraphicsAdapter.h> | #include <Kernel/Graphics/Bochs/GraphicsAdapter.h> | ||||||
|  | #include <Kernel/Graphics/Bochs/QEMUDisplayConnector.h> | ||||||
| #include <Kernel/Graphics/Console/ContiguousFramebufferConsole.h> | #include <Kernel/Graphics/Console/ContiguousFramebufferConsole.h> | ||||||
| #include <Kernel/Graphics/GraphicsManagement.h> | #include <Kernel/Graphics/GraphicsManagement.h> | ||||||
| #include <Kernel/Memory/TypedMapping.h> | #include <Kernel/Memory/TypedMapping.h> | ||||||
|  | @ -22,68 +26,44 @@ UNMAP_AFTER_INIT NonnullRefPtr<BochsGraphicsAdapter> BochsGraphicsAdapter::initi | ||||||
| { | { | ||||||
|     PCI::HardwareID id = pci_device_identifier.hardware_id(); |     PCI::HardwareID id = pci_device_identifier.hardware_id(); | ||||||
|     VERIFY((id.vendor_id == PCI::VendorID::QEMUOld && id.device_id == 0x1111) || (id.vendor_id == PCI::VendorID::VirtualBox && id.device_id == 0xbeef)); |     VERIFY((id.vendor_id == PCI::VendorID::QEMUOld && id.device_id == 0x1111) || (id.vendor_id == PCI::VendorID::VirtualBox && id.device_id == 0xbeef)); | ||||||
|     return adopt_ref(*new BochsGraphicsAdapter(pci_device_identifier)); |     auto adapter = adopt_ref(*new BochsGraphicsAdapter(pci_device_identifier)); | ||||||
| } |     MUST(adapter->initialize_adapter(pci_device_identifier)); | ||||||
| 
 |     return adapter; | ||||||
| void BochsGraphicsAdapter::set_framebuffer_to_big_endian_format() |  | ||||||
| { |  | ||||||
|     dbgln_if(BXVGA_DEBUG, "BochsGraphicsAdapter set_framebuffer_to_big_endian_format"); |  | ||||||
|     full_memory_barrier(); |  | ||||||
|     if (m_registers->extension_regs.region_size == 0xFFFFFFFF || m_registers->extension_regs.region_size == 0) |  | ||||||
|         return; |  | ||||||
|     full_memory_barrier(); |  | ||||||
|     m_registers->extension_regs.framebuffer_byteorder = BOCHS_DISPLAY_BIG_ENDIAN; |  | ||||||
|     full_memory_barrier(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void BochsGraphicsAdapter::set_framebuffer_to_little_endian_format() |  | ||||||
| { |  | ||||||
|     dbgln_if(BXVGA_DEBUG, "BochsGraphicsAdapter set_framebuffer_to_little_endian_format"); |  | ||||||
|     full_memory_barrier(); |  | ||||||
|     if (m_registers->extension_regs.region_size == 0xFFFFFFFF || m_registers->extension_regs.region_size == 0) |  | ||||||
|         return; |  | ||||||
|     full_memory_barrier(); |  | ||||||
|     m_registers->extension_regs.framebuffer_byteorder = BOCHS_DISPLAY_LITTLE_ENDIAN; |  | ||||||
|     full_memory_barrier(); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| UNMAP_AFTER_INIT BochsGraphicsAdapter::BochsGraphicsAdapter(PCI::DeviceIdentifier const& pci_device_identifier) | UNMAP_AFTER_INIT BochsGraphicsAdapter::BochsGraphicsAdapter(PCI::DeviceIdentifier const& pci_device_identifier) | ||||||
|     : PCI::Device(pci_device_identifier.address()) |     : PCI::Device(pci_device_identifier.address()) | ||||||
|     , m_mmio_registers(PCI::get_BAR2(pci_device_identifier.address()) & 0xfffffff0) |  | ||||||
|     , m_registers(Memory::map_typed_writable<BochsDisplayMMIORegisters volatile>(m_mmio_registers).release_value_but_fixme_should_propagate_errors()) |  | ||||||
| { | { | ||||||
|     // We assume safe resolution is 1024x768x32
 |  | ||||||
|     m_framebuffer_console = Graphics::ContiguousFramebufferConsole::initialize(PhysicalAddress(PCI::get_BAR0(pci_device_identifier.address()) & 0xfffffff0), 1024, 768, 1024 * sizeof(u32)); |  | ||||||
|     GraphicsManagement::the().set_console(*m_framebuffer_console); |  | ||||||
| 
 |  | ||||||
|     auto vendor_id = pci_device_identifier.hardware_id().vendor_id; |  | ||||||
|     auto device_id = pci_device_identifier.hardware_id().device_id; |  | ||||||
|     auto revision_id = pci_device_identifier.revision_id(); |  | ||||||
| 
 |  | ||||||
|     auto is_bochs = vendor_id == PCI::VendorID::QEMUOld && device_id == 0x1111 && revision_id == 0; |  | ||||||
|     auto is_virtualbox = vendor_id == PCI::VendorID::VirtualBox && device_id == 0xbeef; |  | ||||||
| 
 |  | ||||||
|     if (is_bochs || is_virtualbox) |  | ||||||
|         m_io_required = true; |  | ||||||
| 
 |  | ||||||
|     if (pci_device_identifier.class_code().value() == 0x3 && pci_device_identifier.subclass_code().value() == 0x0) |     if (pci_device_identifier.class_code().value() == 0x3 && pci_device_identifier.subclass_code().value() == 0x0) | ||||||
|         m_is_vga_capable = true; |         m_is_vga_capable = true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | UNMAP_AFTER_INIT ErrorOr<void> BochsGraphicsAdapter::initialize_adapter(PCI::DeviceIdentifier const& pci_device_identifier) | ||||||
|  | { | ||||||
|  |     // Note: If we use VirtualBox graphics adapter (which is based on Bochs one), we need to use IO ports
 | ||||||
|  |     // Note: Bochs (the real bochs graphics adapter in the Bochs emulator) uses revision ID of 0x0
 | ||||||
|  |     // and doesn't support memory-mapped IO registers.
 | ||||||
|  |     if (pci_device_identifier.revision_id().value() == 0x0 | ||||||
|  |         || (pci_device_identifier.hardware_id().vendor_id == 0x80ee && pci_device_identifier.hardware_id().device_id == 0xbeef)) { | ||||||
|  |         m_display_connector = BochsDisplayConnector::must_create(PhysicalAddress(PCI::get_BAR0(pci_device_identifier.address()) & 0xfffffff0)); | ||||||
|  |     } else { | ||||||
|  |         auto registers_mapping = TRY(Memory::map_typed_writable<BochsDisplayMMIORegisters volatile>(PhysicalAddress(PCI::get_BAR2(pci_device_identifier.address()) & 0xfffffff0))); | ||||||
|  |         VERIFY(registers_mapping.region); | ||||||
|  |         m_display_connector = QEMUDisplayConnector::must_create(PhysicalAddress(PCI::get_BAR0(pci_device_identifier.address()) & 0xfffffff0), move(registers_mapping)); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     // Note: According to Gerd Hoffmann - "The linux driver simply does
 |     // Note: According to Gerd Hoffmann - "The linux driver simply does
 | ||||||
|     // the unblank unconditionally. With bochs-display this is not needed but
 |     // the unblank unconditionally. With bochs-display this is not needed but
 | ||||||
|     // it also has no bad side effect".
 |     // it also has no bad side effect".
 | ||||||
|     unblank(); |     // FIXME: If the error is ENOTIMPL, ignore it for now until we implement
 | ||||||
|     set_safe_resolution(); |     // unblank support for VBoxDisplayConnector class too.
 | ||||||
| } |     auto result = m_display_connector->unblank(); | ||||||
|  |     if (result.is_error() && result.error().code() != ENOTIMPL) | ||||||
|  |         return result; | ||||||
| 
 | 
 | ||||||
| UNMAP_AFTER_INIT void BochsGraphicsAdapter::initialize_framebuffer_devices() |     TRY(m_display_connector->set_safe_mode_setting()); | ||||||
| { | 
 | ||||||
|     // FIXME: Find a better way to determine default resolution...
 |     return {}; | ||||||
|     m_framebuffer_device = FramebufferDevice::create(*this, PhysicalAddress(PCI::get_BAR0(pci_address()) & 0xfffffff0), 1024, 768, 1024 * sizeof(u32)); |  | ||||||
|     // While write-combine helps greatly on actual hardware, it greatly reduces performance in QEMU
 |  | ||||||
|     m_framebuffer_device->enable_write_combine(false); |  | ||||||
|     // FIXME: Would be nice to be able to return a ErrorOr<void> here.
 |  | ||||||
|     VERIFY(!m_framebuffer_device->try_to_initialize().is_error()); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool BochsGraphicsAdapter::vga_compatible() const | bool BochsGraphicsAdapter::vga_compatible() const | ||||||
|  | @ -91,151 +71,11 @@ bool BochsGraphicsAdapter::vga_compatible() const | ||||||
|     return m_is_vga_capable; |     return m_is_vga_capable; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void BochsGraphicsAdapter::unblank() |  | ||||||
| { |  | ||||||
|     full_memory_barrier(); |  | ||||||
|     m_registers->vga_ioports[0] = 0x20; |  | ||||||
|     full_memory_barrier(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void BochsGraphicsAdapter::set_safe_resolution() |  | ||||||
| { |  | ||||||
|     VERIFY(m_framebuffer_console); |  | ||||||
|     auto result = try_to_set_resolution(0, 1024, 768); |  | ||||||
|     VERIFY(result); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void set_register_with_io(u16 index, u16 data) |  | ||||||
| { |  | ||||||
|     IO::out16(VBE_DISPI_IOPORT_INDEX, index); |  | ||||||
|     IO::out16(VBE_DISPI_IOPORT_DATA, data); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static u16 get_register_with_io(u16 index) |  | ||||||
| { |  | ||||||
|     IO::out16(VBE_DISPI_IOPORT_INDEX, index); |  | ||||||
|     return IO::in16(VBE_DISPI_IOPORT_DATA); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| BochsGraphicsAdapter::IndexID BochsGraphicsAdapter::index_id() const |  | ||||||
| { |  | ||||||
|     if (m_io_required) { |  | ||||||
|         return get_register_with_io(0); |  | ||||||
|     } |  | ||||||
|     return m_registers->bochs_regs.index_id; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void BochsGraphicsAdapter::set_resolution_registers_via_io(size_t width, size_t height) |  | ||||||
| { |  | ||||||
|     dbgln_if(BXVGA_DEBUG, "BochsGraphicsAdapter resolution registers set to - {}x{}", width, height); |  | ||||||
| 
 |  | ||||||
|     set_register_with_io(to_underlying(BochsDISPIRegisters::ENABLE), 0); |  | ||||||
|     set_register_with_io(to_underlying(BochsDISPIRegisters::XRES), (u16)width); |  | ||||||
|     set_register_with_io(to_underlying(BochsDISPIRegisters::YRES), (u16)height); |  | ||||||
|     set_register_with_io(to_underlying(BochsDISPIRegisters::VIRT_WIDTH), (u16)width); |  | ||||||
|     set_register_with_io(to_underlying(BochsDISPIRegisters::VIRT_HEIGHT), (u16)height * 2); |  | ||||||
|     set_register_with_io(to_underlying(BochsDISPIRegisters::BPP), 32); |  | ||||||
|     set_register_with_io(to_underlying(BochsDISPIRegisters::ENABLE), to_underlying(BochsFramebufferSettings::Enabled) | to_underlying(BochsFramebufferSettings::LinearFramebuffer)); |  | ||||||
|     set_register_with_io(to_underlying(BochsDISPIRegisters::BANK), 0); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void BochsGraphicsAdapter::set_resolution_registers(size_t width, size_t height) |  | ||||||
| { |  | ||||||
|     dbgln_if(BXVGA_DEBUG, "BochsGraphicsAdapter resolution registers set to - {}x{}", width, height); |  | ||||||
|     m_registers->bochs_regs.enable = 0; |  | ||||||
|     full_memory_barrier(); |  | ||||||
|     m_registers->bochs_regs.xres = width; |  | ||||||
|     m_registers->bochs_regs.yres = height; |  | ||||||
|     m_registers->bochs_regs.virt_width = width; |  | ||||||
|     m_registers->bochs_regs.virt_height = height * 2; |  | ||||||
|     m_registers->bochs_regs.bpp = 32; |  | ||||||
|     full_memory_barrier(); |  | ||||||
|     m_registers->bochs_regs.enable = to_underlying(BochsFramebufferSettings::Enabled) | to_underlying(BochsFramebufferSettings::LinearFramebuffer); |  | ||||||
|     full_memory_barrier(); |  | ||||||
|     m_registers->bochs_regs.bank = 0; |  | ||||||
|     if (index_id().value() == VBE_DISPI_ID5) { |  | ||||||
|         set_framebuffer_to_little_endian_format(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool BochsGraphicsAdapter::try_to_set_resolution(size_t output_port_index, size_t width, size_t height) |  | ||||||
| { |  | ||||||
|     // Note: There's only one output port for this adapter
 |  | ||||||
|     VERIFY(output_port_index == 0); |  | ||||||
|     VERIFY(m_framebuffer_console); |  | ||||||
|     if (Checked<size_t>::multiplication_would_overflow(width, height, sizeof(u32))) |  | ||||||
|         return false; |  | ||||||
| 
 |  | ||||||
|     if (m_io_required) |  | ||||||
|         set_resolution_registers_via_io(width, height); |  | ||||||
|     else |  | ||||||
|         set_resolution_registers(width, height); |  | ||||||
|     dbgln_if(BXVGA_DEBUG, "BochsGraphicsAdapter resolution test - {}x{}", width, height); |  | ||||||
|     if (m_io_required) { |  | ||||||
|         if (!validate_setup_resolution_with_io(width, height)) |  | ||||||
|             return false; |  | ||||||
|     } else { |  | ||||||
|         if (!validate_setup_resolution(width, height)) |  | ||||||
|             return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     dbgln("BochsGraphicsAdapter: resolution set to {}x{}", width, height); |  | ||||||
|     m_framebuffer_console->set_resolution(width, height, width * sizeof(u32)); |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool BochsGraphicsAdapter::validate_setup_resolution_with_io(size_t width, size_t height) |  | ||||||
| { |  | ||||||
|     if ((u16)width != get_register_with_io(to_underlying(BochsDISPIRegisters::XRES)) || (u16)height != get_register_with_io(to_underlying(BochsDISPIRegisters::YRES))) { |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool BochsGraphicsAdapter::validate_setup_resolution(size_t width, size_t height) |  | ||||||
| { |  | ||||||
|     if ((u16)width != m_registers->bochs_regs.xres || (u16)height != m_registers->bochs_regs.yres) { |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool BochsGraphicsAdapter::set_y_offset(size_t output_port_index, size_t y_offset) |  | ||||||
| { |  | ||||||
|     VERIFY(output_port_index == 0); |  | ||||||
|     if (m_console_enabled) |  | ||||||
|         return false; |  | ||||||
|     m_registers->bochs_regs.y_offset = y_offset; |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void BochsGraphicsAdapter::enable_consoles() |  | ||||||
| { |  | ||||||
|     SpinlockLocker lock(m_console_mode_switch_lock); |  | ||||||
|     VERIFY(m_framebuffer_console); |  | ||||||
|     m_console_enabled = true; |  | ||||||
|     m_registers->bochs_regs.y_offset = 0; |  | ||||||
|     if (m_framebuffer_device) |  | ||||||
|         m_framebuffer_device->deactivate_writes(); |  | ||||||
|     m_framebuffer_console->enable(); |  | ||||||
| } |  | ||||||
| void BochsGraphicsAdapter::disable_consoles() |  | ||||||
| { |  | ||||||
|     SpinlockLocker lock(m_console_mode_switch_lock); |  | ||||||
|     VERIFY(m_framebuffer_console); |  | ||||||
|     VERIFY(m_framebuffer_device); |  | ||||||
|     m_console_enabled = false; |  | ||||||
|     m_registers->bochs_regs.y_offset = 0; |  | ||||||
|     m_framebuffer_console->disable(); |  | ||||||
|     m_framebuffer_device->activate_writes(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| ErrorOr<ByteBuffer> BochsGraphicsAdapter::get_edid(size_t output_port_index) const | ErrorOr<ByteBuffer> BochsGraphicsAdapter::get_edid(size_t output_port_index) const | ||||||
| { | { | ||||||
|     if (output_port_index != 0) |     if (output_port_index != 0) | ||||||
|         return Error::from_errno(ENODEV); |         return Error::from_errno(ENODEV); | ||||||
| 
 |     return m_display_connector->get_edid(); | ||||||
|     return ByteBuffer::copy(const_cast<u8 const*>(m_registers->edid_data), sizeof(m_registers->edid_data)); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -20,18 +20,17 @@ namespace Kernel { | ||||||
| class GraphicsManagement; | class GraphicsManagement; | ||||||
| struct BochsDisplayMMIORegisters; | struct BochsDisplayMMIORegisters; | ||||||
| 
 | 
 | ||||||
|  | class BochsDisplayConnector; | ||||||
| class BochsGraphicsAdapter final : public GenericGraphicsAdapter | class BochsGraphicsAdapter final : public GenericGraphicsAdapter | ||||||
|     , public PCI::Device { |     , public PCI::Device { | ||||||
|     friend class GraphicsManagement; |     friend class GraphicsManagement; | ||||||
| 
 | 
 | ||||||
| private: |  | ||||||
|     TYPEDEF_DISTINCT_ORDERED_ID(u16, IndexID); |  | ||||||
| 
 |  | ||||||
| public: | public: | ||||||
|     static NonnullRefPtr<BochsGraphicsAdapter> initialize(PCI::DeviceIdentifier const&); |     static NonnullRefPtr<BochsGraphicsAdapter> initialize(PCI::DeviceIdentifier const&); | ||||||
|     virtual ~BochsGraphicsAdapter() = default; |     virtual ~BochsGraphicsAdapter() = default; | ||||||
|     virtual bool framebuffer_devices_initialized() const override { return !m_framebuffer_device.is_null(); } |  | ||||||
| 
 | 
 | ||||||
|  |     // FIXME: Remove all of these methods when we get rid of the FramebufferDevice class.
 | ||||||
|  |     virtual bool framebuffer_devices_initialized() const override { return false; } | ||||||
|     virtual bool modesetting_capable() const override { return true; } |     virtual bool modesetting_capable() const override { return true; } | ||||||
|     virtual bool double_framebuffering_capable() const override { return true; } |     virtual bool double_framebuffering_capable() const override { return true; } | ||||||
| 
 | 
 | ||||||
|  | @ -40,39 +39,19 @@ public: | ||||||
| private: | private: | ||||||
|     ErrorOr<ByteBuffer> get_edid(size_t output_port_index) const override; |     ErrorOr<ByteBuffer> get_edid(size_t output_port_index) const override; | ||||||
| 
 | 
 | ||||||
|  |     ErrorOr<void> initialize_adapter(PCI::DeviceIdentifier const&); | ||||||
|  | 
 | ||||||
|     // ^GenericGraphicsAdapter
 |     // ^GenericGraphicsAdapter
 | ||||||
|     virtual bool try_to_set_resolution(size_t output_port_index, size_t width, size_t height) override; |     // FIXME: Remove all of these methods when we get rid of the FramebufferDevice class.
 | ||||||
|     virtual bool set_y_offset(size_t output_port_index, size_t y) override; |     virtual bool try_to_set_resolution(size_t, size_t, size_t) override { VERIFY_NOT_REACHED(); } | ||||||
| 
 |     virtual bool set_y_offset(size_t, size_t) override { VERIFY_NOT_REACHED(); } | ||||||
|     virtual void initialize_framebuffer_devices() override; |     virtual void initialize_framebuffer_devices() override { } | ||||||
| 
 |     virtual void enable_consoles() override { } | ||||||
|     virtual void enable_consoles() override; |     virtual void disable_consoles() override { } | ||||||
|     virtual void disable_consoles() override; |  | ||||||
| 
 | 
 | ||||||
|     explicit BochsGraphicsAdapter(PCI::DeviceIdentifier const&); |     explicit BochsGraphicsAdapter(PCI::DeviceIdentifier const&); | ||||||
| 
 | 
 | ||||||
|     IndexID index_id() const; |     RefPtr<BochsDisplayConnector> m_display_connector; | ||||||
| 
 |  | ||||||
|     void set_safe_resolution(); |  | ||||||
|     void unblank(); |  | ||||||
| 
 |  | ||||||
|     bool validate_setup_resolution(size_t width, size_t height); |  | ||||||
|     u32 find_framebuffer_address(); |  | ||||||
|     void set_resolution_registers(size_t width, size_t height); |  | ||||||
|     void set_resolution_registers_via_io(size_t width, size_t height); |  | ||||||
|     bool validate_setup_resolution_with_io(size_t width, size_t height); |  | ||||||
|     void set_y_offset(size_t); |  | ||||||
| 
 |  | ||||||
|     void set_framebuffer_to_big_endian_format(); |  | ||||||
|     void set_framebuffer_to_little_endian_format(); |  | ||||||
| 
 |  | ||||||
|     PhysicalAddress m_mmio_registers; |  | ||||||
|     Memory::TypedMapping<BochsDisplayMMIORegisters volatile> m_registers; |  | ||||||
|     RefPtr<FramebufferDevice> m_framebuffer_device; |  | ||||||
|     RefPtr<Graphics::GenericFramebufferConsole> m_framebuffer_console; |  | ||||||
|     Spinlock m_console_mode_switch_lock; |  | ||||||
|     bool m_console_enabled { false }; |  | ||||||
|     bool m_io_required { false }; |  | ||||||
|     bool m_is_vga_capable { false }; |     bool m_is_vga_capable { false }; | ||||||
| }; | }; | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										139
									
								
								Kernel/Graphics/Bochs/QEMUDisplayConnector.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								Kernel/Graphics/Bochs/QEMUDisplayConnector.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,139 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il> | ||||||
|  |  * | ||||||
|  |  * SPDX-License-Identifier: BSD-2-Clause | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <Kernel/Debug.h> | ||||||
|  | #include <Kernel/Devices/DeviceManagement.h> | ||||||
|  | #include <Kernel/Graphics/Bochs/QEMUDisplayConnector.h> | ||||||
|  | #include <Kernel/Graphics/GraphicsManagement.h> | ||||||
|  | #include <LibEDID/Definitions.h> | ||||||
|  | 
 | ||||||
|  | namespace Kernel { | ||||||
|  | 
 | ||||||
|  | NonnullRefPtr<QEMUDisplayConnector> QEMUDisplayConnector::must_create(PhysicalAddress framebuffer_address, Memory::TypedMapping<BochsDisplayMMIORegisters volatile> registers_mapping) | ||||||
|  | { | ||||||
|  |     auto device_or_error = DeviceManagement::try_create_device<QEMUDisplayConnector>(framebuffer_address, move(registers_mapping)); | ||||||
|  |     VERIFY(!device_or_error.is_error()); | ||||||
|  |     auto connector = device_or_error.release_value(); | ||||||
|  |     MUST(connector->create_attached_framebuffer_console()); | ||||||
|  |     MUST(connector->fetch_and_initialize_edid()); | ||||||
|  |     return connector; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ErrorOr<void> QEMUDisplayConnector::fetch_and_initialize_edid() | ||||||
|  | { | ||||||
|  |     Array<u8, 128> bochs_edid; | ||||||
|  |     static_assert(sizeof(BochsDisplayMMIORegisters::edid_data) >= sizeof(EDID::Definitions::EDID)); | ||||||
|  |     memcpy(bochs_edid.data(), (u8 const*)(m_registers.base_address().offset(__builtin_offsetof(BochsDisplayMMIORegisters, edid_data)).as_ptr()), sizeof(bochs_edid)); | ||||||
|  |     set_edid_bytes(bochs_edid); | ||||||
|  |     return {}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | QEMUDisplayConnector::QEMUDisplayConnector(PhysicalAddress framebuffer_address, Memory::TypedMapping<BochsDisplayMMIORegisters volatile> registers_mapping) | ||||||
|  |     : BochsDisplayConnector(framebuffer_address) | ||||||
|  |     , m_registers(move(registers_mapping)) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | QEMUDisplayConnector::IndexID QEMUDisplayConnector::index_id() const | ||||||
|  | { | ||||||
|  |     return m_registers->bochs_regs.index_id; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void QEMUDisplayConnector::set_framebuffer_to_big_endian_format() | ||||||
|  | { | ||||||
|  |     VERIFY(m_modeset_lock.is_locked()); | ||||||
|  |     dbgln_if(BXVGA_DEBUG, "QEMUDisplayConnector set_framebuffer_to_big_endian_format"); | ||||||
|  |     full_memory_barrier(); | ||||||
|  |     if (m_registers->extension_regs.region_size == 0xFFFFFFFF || m_registers->extension_regs.region_size == 0) | ||||||
|  |         return; | ||||||
|  |     full_memory_barrier(); | ||||||
|  |     m_registers->extension_regs.framebuffer_byteorder = BOCHS_DISPLAY_BIG_ENDIAN; | ||||||
|  |     full_memory_barrier(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void QEMUDisplayConnector::set_framebuffer_to_little_endian_format() | ||||||
|  | { | ||||||
|  |     VERIFY(m_modeset_lock.is_locked()); | ||||||
|  |     dbgln_if(BXVGA_DEBUG, "QEMUDisplayConnector set_framebuffer_to_little_endian_format"); | ||||||
|  |     full_memory_barrier(); | ||||||
|  |     if (m_registers->extension_regs.region_size == 0xFFFFFFFF || m_registers->extension_regs.region_size == 0) | ||||||
|  |         return; | ||||||
|  |     full_memory_barrier(); | ||||||
|  |     m_registers->extension_regs.framebuffer_byteorder = BOCHS_DISPLAY_LITTLE_ENDIAN; | ||||||
|  |     full_memory_barrier(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ErrorOr<void> QEMUDisplayConnector::unblank() | ||||||
|  | { | ||||||
|  |     SpinlockLocker locker(m_modeset_lock); | ||||||
|  |     full_memory_barrier(); | ||||||
|  |     m_registers->vga_ioports[0] = 0x20; | ||||||
|  |     full_memory_barrier(); | ||||||
|  |     return {}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ErrorOr<void> QEMUDisplayConnector::set_y_offset(size_t y_offset) | ||||||
|  | { | ||||||
|  |     VERIFY(m_modeset_lock.is_locked()); | ||||||
|  |     m_registers->bochs_regs.y_offset = y_offset; | ||||||
|  |     return {}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ErrorOr<void> QEMUDisplayConnector::set_mode_setting(ModeSetting const& mode_setting) | ||||||
|  | { | ||||||
|  |     SpinlockLocker locker(m_modeset_lock); | ||||||
|  |     VERIFY(m_framebuffer_console); | ||||||
|  |     size_t width = mode_setting.horizontal_active; | ||||||
|  |     size_t height = mode_setting.vertical_active; | ||||||
|  | 
 | ||||||
|  |     if (Checked<size_t>::multiplication_would_overflow(width, height, sizeof(u32))) | ||||||
|  |         return EOVERFLOW; | ||||||
|  | 
 | ||||||
|  |     dbgln_if(BXVGA_DEBUG, "QEMUDisplayConnector resolution registers set to - {}x{}", width, height); | ||||||
|  |     m_registers->bochs_regs.enable = 0; | ||||||
|  |     full_memory_barrier(); | ||||||
|  |     m_registers->bochs_regs.xres = width; | ||||||
|  |     m_registers->bochs_regs.yres = height; | ||||||
|  |     m_registers->bochs_regs.virt_width = width; | ||||||
|  |     m_registers->bochs_regs.virt_height = height * 2; | ||||||
|  |     m_registers->bochs_regs.bpp = 32; | ||||||
|  |     full_memory_barrier(); | ||||||
|  |     m_registers->bochs_regs.enable = to_underlying(BochsFramebufferSettings::Enabled) | to_underlying(BochsFramebufferSettings::LinearFramebuffer); | ||||||
|  |     full_memory_barrier(); | ||||||
|  |     m_registers->bochs_regs.bank = 0; | ||||||
|  |     if (index_id().value() == VBE_DISPI_ID5) { | ||||||
|  |         set_framebuffer_to_little_endian_format(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if ((u16)width != m_registers->bochs_regs.xres || (u16)height != m_registers->bochs_regs.yres) { | ||||||
|  |         return Error::from_errno(ENOTIMPL); | ||||||
|  |     } | ||||||
|  |     auto rounded_size = TRY(Memory::page_round_up(width * sizeof(u32) * height * 2)); | ||||||
|  |     m_framebuffer_region = TRY(MM.allocate_kernel_region(m_framebuffer_address.page_base(), rounded_size, "Framebuffer"sv, Memory::Region::Access::ReadWrite)); | ||||||
|  |     [[maybe_unused]] auto result = m_framebuffer_region->set_write_combine(true); | ||||||
|  |     m_framebuffer_data = m_framebuffer_region->vaddr().offset(m_framebuffer_address.offset_in_page()).as_ptr(); | ||||||
|  |     m_framebuffer_console->set_resolution(width, height, width * sizeof(u32)); | ||||||
|  | 
 | ||||||
|  |     DisplayConnector::ModeSetting mode_set { | ||||||
|  |         .horizontal_stride = m_registers->bochs_regs.xres * sizeof(u32), | ||||||
|  |         .pixel_clock_in_khz = 0, // Note: There's no pixel clock in paravirtualized hardware
 | ||||||
|  |         .horizontal_active = m_registers->bochs_regs.xres, | ||||||
|  |         .horizontal_front_porch_pixels = 0, // Note: There's no horizontal_front_porch_pixels in paravirtualized hardware
 | ||||||
|  |         .horizontal_sync_time_pixels = 0,   // Note: There's no horizontal_sync_time_pixels in paravirtualized hardware
 | ||||||
|  |         .horizontal_blank_pixels = 0,       // Note: There's no horizontal_blank_pixels in paravirtualized hardware
 | ||||||
|  |         .vertical_active = m_registers->bochs_regs.yres, | ||||||
|  |         .vertical_front_porch_lines = 0, // Note: There's no vertical_front_porch_lines in paravirtualized hardware
 | ||||||
|  |         .vertical_sync_time_lines = 0,   // Note: There's no vertical_sync_time_lines in paravirtualized hardware
 | ||||||
|  |         .vertical_blank_lines = 0,       // Note: There's no vertical_blank_lines in paravirtualized hardware
 | ||||||
|  |         .horizontal_offset = 0, | ||||||
|  |         .vertical_offset = 0, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     m_current_mode_setting = mode_set; | ||||||
|  |     return {}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										44
									
								
								Kernel/Graphics/Bochs/QEMUDisplayConnector.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								Kernel/Graphics/Bochs/QEMUDisplayConnector.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,44 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il> | ||||||
|  |  * | ||||||
|  |  * SPDX-License-Identifier: BSD-2-Clause | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <AK/RefPtr.h> | ||||||
|  | #include <AK/Try.h> | ||||||
|  | #include <Kernel/Graphics/Bochs/DisplayConnector.h> | ||||||
|  | #include <Kernel/Graphics/Console/GenericFramebufferConsole.h> | ||||||
|  | #include <Kernel/Locking/Spinlock.h> | ||||||
|  | #include <Kernel/Memory/TypedMapping.h> | ||||||
|  | 
 | ||||||
|  | namespace Kernel { | ||||||
|  | 
 | ||||||
|  | struct BochsDisplayMMIORegisters; | ||||||
|  | class QEMUDisplayConnector final | ||||||
|  |     : public BochsDisplayConnector { | ||||||
|  |     friend class BochsGraphicsAdapter; | ||||||
|  |     friend class DeviceManagement; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     static NonnullRefPtr<QEMUDisplayConnector> must_create(PhysicalAddress framebuffer_address, Memory::TypedMapping<BochsDisplayMMIORegisters volatile>); | ||||||
|  | 
 | ||||||
|  |     virtual IndexID index_id() const override; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     ErrorOr<void> fetch_and_initialize_edid(); | ||||||
|  |     QEMUDisplayConnector(PhysicalAddress framebuffer_address, Memory::TypedMapping<BochsDisplayMMIORegisters volatile>); | ||||||
|  | 
 | ||||||
|  |     virtual bool double_framebuffering_capable() const override { return true; } | ||||||
|  |     virtual ErrorOr<void> set_mode_setting(ModeSetting const&) override; | ||||||
|  |     virtual ErrorOr<void> set_y_offset(size_t y) override; | ||||||
|  |     virtual ErrorOr<void> unblank() override; | ||||||
|  | 
 | ||||||
|  |     void set_framebuffer_to_big_endian_format(); | ||||||
|  |     void set_framebuffer_to_little_endian_format(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     Memory::TypedMapping<BochsDisplayMMIORegisters volatile> m_registers; | ||||||
|  | }; | ||||||
|  | } | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Liav A
						Liav A