diff --git a/Kernel/Arch/x86/Hypervisor/BochsDisplayConnector.cpp b/Kernel/Arch/x86/Hypervisor/BochsDisplayConnector.cpp index 4f52ed358d..74fb9b3338 100644 --- a/Kernel/Arch/x86/Hypervisor/BochsDisplayConnector.cpp +++ b/Kernel/Arch/x86/Hypervisor/BochsDisplayConnector.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -15,6 +16,45 @@ namespace Kernel { +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); +} + +LockRefPtr BochsDisplayConnector::try_create_for_vga_isa_connector() +{ + VERIFY(PCI::Access::is_hardware_disabled()); + BochsDisplayConnector::IndexID index_id = get_register_with_io(0); + if (index_id != VBE_DISPI_ID5) + return {}; + + auto video_ram_64k_chunks_count = get_register_with_io(to_underlying(BochsDISPIRegisters::VIDEO_RAM_64K_CHUNKS_COUNT)); + if (video_ram_64k_chunks_count == 0 || video_ram_64k_chunks_count == 0xffff) { + dmesgln("Graphics: Bochs ISA VGA compatible adapter does not indicate amount of VRAM, default to 8 MiB"); + video_ram_64k_chunks_count = (8 * MiB) / (64 * KiB); + } else { + dmesgln("Graphics: Bochs ISA VGA compatible adapter indicates {} bytes of VRAM", video_ram_64k_chunks_count * (64 * KiB)); + } + + // Note: The default physical address for isa-vga framebuffer in QEMU is 0xE0000000. + // Since this is probably hardcoded at other OSes in their guest drivers, + // we can assume this is going to stay the same framebuffer physical address for + // this device and will not be changed in the future. + auto device_or_error = DeviceManagement::try_create_device(PhysicalAddress(0xE0000000), video_ram_64k_chunks_count * (64 * KiB)); + 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; +} + NonnullLockRefPtr BochsDisplayConnector::must_create(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, bool virtual_box_hardware) { auto device_or_error = DeviceManagement::try_create_device(framebuffer_address, framebuffer_resource_size); @@ -41,18 +81,6 @@ ErrorOr BochsDisplayConnector::create_attached_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); diff --git a/Kernel/Arch/x86/Hypervisor/BochsDisplayConnector.h b/Kernel/Arch/x86/Hypervisor/BochsDisplayConnector.h index b13976d8b4..cf9f79f9a4 100644 --- a/Kernel/Arch/x86/Hypervisor/BochsDisplayConnector.h +++ b/Kernel/Arch/x86/Hypervisor/BochsDisplayConnector.h @@ -20,10 +20,13 @@ class BochsDisplayConnector : public DisplayConnector { friend class BochsGraphicsAdapter; friend class DeviceManagement; + friend class GraphicsManagement; public: AK_TYPEDEF_DISTINCT_ORDERED_ID(u16, IndexID); + static LockRefPtr try_create_for_vga_isa_connector(); + static NonnullLockRefPtr must_create(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, bool virtual_box_hardware); private: diff --git a/Kernel/Bus/PCI/Access.cpp b/Kernel/Bus/PCI/Access.cpp index 4c89d5c6b3..95d0162d73 100644 --- a/Kernel/Bus/PCI/Access.cpp +++ b/Kernel/Bus/PCI/Access.cpp @@ -40,6 +40,11 @@ bool Access::is_initialized() return (s_access != nullptr); } +bool Access::is_hardware_disabled() +{ + return g_pci_access_io_probe_failed; +} + bool Access::is_disabled() { return g_pci_access_is_disabled_from_commandline || g_pci_access_io_probe_failed; diff --git a/Kernel/Bus/PCI/Access.h b/Kernel/Bus/PCI/Access.h index 7f91d50e4e..51d7776b5e 100644 --- a/Kernel/Bus/PCI/Access.h +++ b/Kernel/Bus/PCI/Access.h @@ -30,6 +30,7 @@ public: static Access& the(); static bool is_initialized(); static bool is_disabled(); + static bool is_hardware_disabled(); void write8_field(Address address, u32 field, u8 value); void write16_field(Address address, u32 field, u16 value); diff --git a/Kernel/Graphics/Bochs/Definitions.h b/Kernel/Graphics/Bochs/Definitions.h index 12ba53650a..1891b9f756 100644 --- a/Kernel/Graphics/Bochs/Definitions.h +++ b/Kernel/Graphics/Bochs/Definitions.h @@ -34,6 +34,7 @@ enum class BochsDISPIRegisters { VIRT_HEIGHT = 0x7, X_OFFSET = 0x8, Y_OFFSET = 0x9, + VIDEO_RAM_64K_CHUNKS_COUNT = 0xA, }; struct [[gnu::packed]] DISPIInterface { @@ -47,6 +48,7 @@ struct [[gnu::packed]] DISPIInterface { u16 virt_height; u16 x_offset; u16 y_offset; + u16 vram_64k_chunks_count; }; struct [[gnu::packed]] ExtensionRegisters { diff --git a/Kernel/Graphics/GraphicsManagement.cpp b/Kernel/Graphics/GraphicsManagement.cpp index ef1f9ac195..6659b02b6f 100644 --- a/Kernel/Graphics/GraphicsManagement.cpp +++ b/Kernel/Graphics/GraphicsManagement.cpp @@ -7,6 +7,9 @@ #include #include #include +#if ARCH(I386) || ARCH(X86_64) +# include +#endif #include #include #include @@ -206,6 +209,25 @@ UNMAP_AFTER_INIT bool GraphicsManagement::initialize() return true; } + // Note: Don't try to initialize an ISA Bochs VGA adapter if PCI hardware is + // present but the user decided to disable its usage nevertheless. + // Otherwise we risk using the Bochs VBE driver on a wrong physical address + // for the framebuffer. + if (PCI::Access::is_hardware_disabled() && !(graphics_subsystem_mode == CommandLine::GraphicsSubsystemMode::Limited && !multiboot_framebuffer_addr.is_null() && multiboot_framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_RGB)) { +#if ARCH(I386) || ARCH(X86_64) + auto vga_isa_bochs_display_connector = BochsDisplayConnector::try_create_for_vga_isa_connector(); + if (vga_isa_bochs_display_connector) { + dmesgln("Graphics: Using a Bochs ISA VGA compatible adapter"); + MUST(vga_isa_bochs_display_connector->set_safe_mode_setting()); + m_platform_board_specific_display_connector = vga_isa_bochs_display_connector; + dmesgln("Graphics: Invoking manual blanking with VGA ISA ports"); + SpinlockLocker locker(m_main_vga_lock); + IO::out8(0x3c0, 0x20); + return true; + } +#endif + } + if (graphics_subsystem_mode == CommandLine::GraphicsSubsystemMode::Limited && !multiboot_framebuffer_addr.is_null() && multiboot_framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_RGB) { initialize_preset_resolution_generic_display_connector(); return true; diff --git a/Kernel/Graphics/GraphicsManagement.h b/Kernel/Graphics/GraphicsManagement.h index 28ffd32737..842c837d75 100644 --- a/Kernel/Graphics/GraphicsManagement.h +++ b/Kernel/Graphics/GraphicsManagement.h @@ -56,6 +56,8 @@ private: // Note: This is only used when booting with kernel commandline that includes "graphics_subsystem_mode=limited" LockRefPtr m_preset_resolution_generic_display_connector; + LockRefPtr m_platform_board_specific_display_connector; + unsigned m_current_minor_number { 0 }; SpinlockProtected> m_display_connector_nodes { LockRank::None };