diff options
Diffstat (limited to 'Kernel')
19 files changed, 309 insertions, 341 deletions
diff --git a/Kernel/Graphics/Bochs/DisplayConnector.cpp b/Kernel/Graphics/Bochs/DisplayConnector.cpp index c6bc1b8c3c..5e9c74711c 100644 --- a/Kernel/Graphics/Bochs/DisplayConnector.cpp +++ b/Kernel/Graphics/Bochs/DisplayConnector.cpp @@ -14,9 +14,9 @@ namespace Kernel { -NonnullRefPtr<BochsDisplayConnector> BochsDisplayConnector::must_create(PhysicalAddress framebuffer_address) +NonnullRefPtr<BochsDisplayConnector> BochsDisplayConnector::must_create(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size) { - auto device_or_error = DeviceManagement::try_create_device<BochsDisplayConnector>(framebuffer_address); + auto device_or_error = DeviceManagement::try_create_device<BochsDisplayConnector>(framebuffer_address, framebuffer_resource_size); VERIFY(!device_or_error.is_error()); auto connector = device_or_error.release_value(); MUST(connector->create_attached_framebuffer_console()); @@ -24,20 +24,15 @@ NonnullRefPtr<BochsDisplayConnector> BochsDisplayConnector::must_create(Physical return connector; } -BochsDisplayConnector::BochsDisplayConnector(PhysicalAddress framebuffer_address) - : DisplayConnector() - , m_framebuffer_address(framebuffer_address) +BochsDisplayConnector::BochsDisplayConnector(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size) + : DisplayConnector(framebuffer_address, framebuffer_resource_size, false) { } 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)); + m_framebuffer_console = Graphics::ContiguousFramebufferConsole::initialize(m_framebuffer_address.value(), 1024, 768, 1024 * sizeof(u32)); GraphicsManagement::the().set_console(*m_framebuffer_console); return {}; } @@ -59,15 +54,6 @@ 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()); diff --git a/Kernel/Graphics/Bochs/DisplayConnector.h b/Kernel/Graphics/Bochs/DisplayConnector.h index 48d31e22bd..445e5dcc7c 100644 --- a/Kernel/Graphics/Bochs/DisplayConnector.h +++ b/Kernel/Graphics/Bochs/DisplayConnector.h @@ -24,14 +24,14 @@ class BochsDisplayConnector public: TYPEDEF_DISTINCT_ORDERED_ID(u16, IndexID); - static NonnullRefPtr<BochsDisplayConnector> must_create(PhysicalAddress framebuffer_address); + static NonnullRefPtr<BochsDisplayConnector> must_create(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size); virtual IndexID index_id() const; protected: ErrorOr<void> create_attached_framebuffer_console(); - explicit BochsDisplayConnector(PhysicalAddress framebuffer_address); + BochsDisplayConnector(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size); virtual bool mutable_mode_setting_capable() const override final { return true; } virtual bool double_framebuffering_capable() const override { return false; } @@ -45,15 +45,11 @@ protected: // 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 {}; }; } diff --git a/Kernel/Graphics/Bochs/GraphicsAdapter.cpp b/Kernel/Graphics/Bochs/GraphicsAdapter.cpp index ed64295587..82e63686ee 100644 --- a/Kernel/Graphics/Bochs/GraphicsAdapter.cpp +++ b/Kernel/Graphics/Bochs/GraphicsAdapter.cpp @@ -43,13 +43,14 @@ UNMAP_AFTER_INIT ErrorOr<void> BochsGraphicsAdapter::initialize_adapter(PCI::Dev // 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. + auto bar0_space_size = PCI::get_BAR_space_size(pci_device_identifier.address(), 0); 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)); + m_display_connector = BochsDisplayConnector::must_create(PhysicalAddress(PCI::get_BAR0(pci_device_identifier.address()) & 0xfffffff0), bar0_space_size); } 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)); + m_display_connector = QEMUDisplayConnector::must_create(PhysicalAddress(PCI::get_BAR0(pci_device_identifier.address()) & 0xfffffff0), bar0_space_size, move(registers_mapping)); } // Note: According to Gerd Hoffmann - "The linux driver simply does diff --git a/Kernel/Graphics/Bochs/QEMUDisplayConnector.cpp b/Kernel/Graphics/Bochs/QEMUDisplayConnector.cpp index cf1f681b60..1f76222a56 100644 --- a/Kernel/Graphics/Bochs/QEMUDisplayConnector.cpp +++ b/Kernel/Graphics/Bochs/QEMUDisplayConnector.cpp @@ -12,9 +12,9 @@ namespace Kernel { -NonnullRefPtr<QEMUDisplayConnector> QEMUDisplayConnector::must_create(PhysicalAddress framebuffer_address, Memory::TypedMapping<BochsDisplayMMIORegisters volatile> registers_mapping) +NonnullRefPtr<QEMUDisplayConnector> QEMUDisplayConnector::must_create(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, Memory::TypedMapping<BochsDisplayMMIORegisters volatile> registers_mapping) { - auto device_or_error = DeviceManagement::try_create_device<QEMUDisplayConnector>(framebuffer_address, move(registers_mapping)); + auto device_or_error = DeviceManagement::try_create_device<QEMUDisplayConnector>(framebuffer_address, framebuffer_resource_size, move(registers_mapping)); VERIFY(!device_or_error.is_error()); auto connector = device_or_error.release_value(); MUST(connector->create_attached_framebuffer_console()); @@ -31,8 +31,8 @@ ErrorOr<void> QEMUDisplayConnector::fetch_and_initialize_edid() return {}; } -QEMUDisplayConnector::QEMUDisplayConnector(PhysicalAddress framebuffer_address, Memory::TypedMapping<BochsDisplayMMIORegisters volatile> registers_mapping) - : BochsDisplayConnector(framebuffer_address) +QEMUDisplayConnector::QEMUDisplayConnector(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, Memory::TypedMapping<BochsDisplayMMIORegisters volatile> registers_mapping) + : BochsDisplayConnector(framebuffer_address, framebuffer_resource_size) , m_registers(move(registers_mapping)) { } @@ -111,10 +111,7 @@ ErrorOr<void> QEMUDisplayConnector::set_mode_setting(ModeSetting const& mode_set 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 { diff --git a/Kernel/Graphics/Bochs/QEMUDisplayConnector.h b/Kernel/Graphics/Bochs/QEMUDisplayConnector.h index 003be2af7f..9486d1e663 100644 --- a/Kernel/Graphics/Bochs/QEMUDisplayConnector.h +++ b/Kernel/Graphics/Bochs/QEMUDisplayConnector.h @@ -22,13 +22,13 @@ class QEMUDisplayConnector final friend class DeviceManagement; public: - static NonnullRefPtr<QEMUDisplayConnector> must_create(PhysicalAddress framebuffer_address, Memory::TypedMapping<BochsDisplayMMIORegisters volatile>); + static NonnullRefPtr<QEMUDisplayConnector> must_create(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, 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>); + QEMUDisplayConnector(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, Memory::TypedMapping<BochsDisplayMMIORegisters volatile>); virtual bool double_framebuffering_capable() const override { return true; } virtual ErrorOr<void> set_mode_setting(ModeSetting const&) override; diff --git a/Kernel/Graphics/DisplayConnector.cpp b/Kernel/Graphics/DisplayConnector.cpp index 77ae47e395..31ae25e0b7 100644 --- a/Kernel/Graphics/DisplayConnector.cpp +++ b/Kernel/Graphics/DisplayConnector.cpp @@ -6,18 +6,42 @@ #include <Kernel/Graphics/DisplayConnector.h> #include <Kernel/Graphics/GraphicsManagement.h> +#include <Kernel/Memory/MemoryManager.h> #include <LibC/sys/ioctl_numbers.h> namespace Kernel { -DisplayConnector::DisplayConnector() +DisplayConnector::DisplayConnector(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, bool enable_write_combine_optimization) : CharacterDevice(226, GraphicsManagement::the().allocate_minor_device_number()) + , m_enable_write_combine_optimization(enable_write_combine_optimization) + , m_framebuffer_at_arbitrary_physical_range(false) + , m_framebuffer_address(framebuffer_address) + , m_framebuffer_resource_size(framebuffer_resource_size) { } -ErrorOr<Memory::Region*> DisplayConnector::mmap(Process&, OpenFileDescription&, Memory::VirtualRange const&, u64, int, bool) +DisplayConnector::DisplayConnector(size_t framebuffer_resource_size, bool enable_write_combine_optimization) + : CharacterDevice(226, GraphicsManagement::the().allocate_minor_device_number()) + , m_enable_write_combine_optimization(enable_write_combine_optimization) + , m_framebuffer_at_arbitrary_physical_range(true) + , m_framebuffer_address({}) + , m_framebuffer_resource_size(framebuffer_resource_size) { - return Error::from_errno(ENOTSUP); +} + +ErrorOr<Memory::Region*> DisplayConnector::mmap(Process& process, OpenFileDescription&, Memory::VirtualRange const& range, u64 offset, int prot, bool shared) +{ + VERIFY(m_shared_framebuffer_vmobject); + if (offset != 0) + return Error::from_errno(ENOTSUP); + + return process.address_space().allocate_region_with_vmobject( + range, + *m_shared_framebuffer_vmobject, + 0, + "Mapped Framebuffer", + prot, + shared); } ErrorOr<size_t> DisplayConnector::read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) @@ -32,7 +56,10 @@ ErrorOr<size_t> DisplayConnector::write(OpenFileDescription&, u64 offset, UserOr if (console_mode()) { return length; } - return write_to_first_surface(offset, framebuffer_data, length); + if (offset + length > m_framebuffer_region->size()) + return Error::from_errno(EOVERFLOW); + TRY(framebuffer_data.read(m_framebuffer_data + offset, 0, length)); + return length; } void DisplayConnector::will_be_destroyed() @@ -44,7 +71,24 @@ void DisplayConnector::will_be_destroyed() void DisplayConnector::after_inserting() { Device::after_inserting(); + auto rounded_size = MUST(Memory::page_round_up(m_framebuffer_resource_size)); + + if (!m_framebuffer_at_arbitrary_physical_range) { + VERIFY(m_framebuffer_address.value().page_base() == m_framebuffer_address.value()); + m_shared_framebuffer_vmobject = MUST(Memory::SharedFramebufferVMObject::try_create_for_physical_range(m_framebuffer_address.value(), rounded_size)); + m_framebuffer_region = MUST(MM.allocate_kernel_region(m_framebuffer_address.value().page_base(), rounded_size, "Framebuffer"sv, Memory::Region::Access::ReadWrite)); + } else { + m_shared_framebuffer_vmobject = MUST(Memory::SharedFramebufferVMObject::try_create_at_arbitrary_physical_range(rounded_size)); + m_framebuffer_region = MUST(MM.allocate_kernel_region_with_vmobject(m_shared_framebuffer_vmobject->real_writes_framebuffer_vmobject(), rounded_size, "Framebuffer"sv, Memory::Region::Access::ReadWrite)); + } + + m_framebuffer_data = m_framebuffer_region->vaddr().as_ptr(); + m_fake_writes_framebuffer_region = MUST(MM.allocate_kernel_region_with_vmobject(m_shared_framebuffer_vmobject->fake_writes_framebuffer_vmobject(), rounded_size, "Fake Writes Framebuffer"sv, Memory::Region::Access::ReadWrite)); + GraphicsManagement::the().attach_new_display_connector({}, *this); + if (m_enable_write_combine_optimization) { + [[maybe_unused]] auto result = m_framebuffer_region->set_write_combine(true); + } } bool DisplayConnector::console_mode() const @@ -56,15 +100,24 @@ bool DisplayConnector::console_mode() const void DisplayConnector::set_display_mode(Badge<GraphicsManagement>, DisplayMode mode) { SpinlockLocker locker(m_control_lock); + { SpinlockLocker locker(m_modeset_lock); [[maybe_unused]] auto result = set_y_offset(0); } + m_console_mode = mode == DisplayMode::Console ? true : false; - if (m_console_mode) + if (m_console_mode) { + VERIFY(m_framebuffer_region->size() == m_fake_writes_framebuffer_region->size()); + memcpy(m_fake_writes_framebuffer_region->vaddr().as_ptr(), m_framebuffer_region->vaddr().as_ptr(), m_framebuffer_region->size()); + m_shared_framebuffer_vmobject->switch_to_fake_sink_framebuffer_writes({}); enable_console(); - else + } else { disable_console(); + m_shared_framebuffer_vmobject->switch_to_real_framebuffer_writes({}); + VERIFY(m_framebuffer_region->size() == m_fake_writes_framebuffer_region->size()); + memcpy(m_framebuffer_region->vaddr().as_ptr(), m_fake_writes_framebuffer_region->vaddr().as_ptr(), m_framebuffer_region->size()); + } } ErrorOr<void> DisplayConnector::initialize_edid_for_generic_monitor() diff --git a/Kernel/Graphics/DisplayConnector.h b/Kernel/Graphics/DisplayConnector.h index ee01d7cdf4..f817a69686 100644 --- a/Kernel/Graphics/DisplayConnector.h +++ b/Kernel/Graphics/DisplayConnector.h @@ -8,6 +8,7 @@ #include <AK/Types.h> #include <Kernel/Devices/CharacterDevice.h> +#include <Kernel/Memory/SharedFramebufferVMObject.h> #include <LibC/sys/ioctl_numbers.h> #include <LibEDID/EDID.h> @@ -99,6 +100,8 @@ public: void set_display_mode(Badge<GraphicsManagement>, DisplayMode); + Memory::Region const& framebuffer_region() const { return *m_framebuffer_region; } + protected: void set_edid_bytes(Array<u8, 128> const& edid_bytes); @@ -112,8 +115,8 @@ protected: virtual ErrorOr<void> ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg) override final; virtual StringView class_name() const override final { return "DisplayConnector"sv; } - DisplayConnector(); - virtual ErrorOr<size_t> write_to_first_surface(u64 offset, UserOrKernelBuffer const&, size_t length) = 0; + DisplayConnector(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, bool enable_write_combine_optimization); + DisplayConnector(size_t framebuffer_resource_size, bool enable_write_combine_optimization); virtual void enable_console() = 0; virtual void disable_console() = 0; virtual ErrorOr<void> flush_first_surface() = 0; @@ -135,10 +138,30 @@ protected: EDID::Parser::RawBytes m_edid_bytes {}; bool m_edid_valid { false }; + u8* framebuffer_data() { return m_framebuffer_data; } + private: + DisplayConnector& operator=(DisplayConnector const&) = delete; + DisplayConnector& operator=(DisplayConnector&&) = delete; + DisplayConnector(DisplayConnector&&) = delete; + virtual void will_be_destroyed() override; virtual void after_inserting() override; + OwnPtr<Memory::Region> m_framebuffer_region; + OwnPtr<Memory::Region> m_fake_writes_framebuffer_region; + u8* m_framebuffer_data {}; + + bool const m_enable_write_combine_optimization { false }; + bool const m_framebuffer_at_arbitrary_physical_range { false }; + +protected: + Optional<PhysicalAddress> const m_framebuffer_address; + size_t const m_framebuffer_resource_size; + +private: + RefPtr<Memory::SharedFramebufferVMObject> m_shared_framebuffer_vmobject; + IntrusiveListNode<DisplayConnector, RefPtr<DisplayConnector>> m_list_node; }; } diff --git a/Kernel/Graphics/Intel/NativeDisplayConnector.cpp b/Kernel/Graphics/Intel/NativeDisplayConnector.cpp index 724f4c4b1a..4f4470e116 100644 --- a/Kernel/Graphics/Intel/NativeDisplayConnector.cpp +++ b/Kernel/Graphics/Intel/NativeDisplayConnector.cpp @@ -175,10 +175,10 @@ Optional<IntelGraphics::PLLSettings> IntelNativeDisplayConnector::create_pll_set return {}; } -NonnullRefPtr<IntelNativeDisplayConnector> IntelNativeDisplayConnector::must_create(PhysicalAddress framebuffer_address, PhysicalAddress registers_region_address, size_t registers_region_length) +NonnullRefPtr<IntelNativeDisplayConnector> IntelNativeDisplayConnector::must_create(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, PhysicalAddress registers_region_address, size_t registers_region_length) { auto registers_region = MUST(MM.allocate_kernel_region(PhysicalAddress(registers_region_address), registers_region_length, "Intel Native Graphics Registers", Memory::Region::Access::ReadWrite)); - auto device_or_error = DeviceManagement::try_create_device<IntelNativeDisplayConnector>(framebuffer_address, move(registers_region)); + auto device_or_error = DeviceManagement::try_create_device<IntelNativeDisplayConnector>(framebuffer_address, framebuffer_resource_size, move(registers_region)); VERIFY(!device_or_error.is_error()); auto connector = device_or_error.release_value(); MUST(connector->initialize_gmbus_settings_and_read_edid()); @@ -208,15 +208,6 @@ ErrorOr<void> IntelNativeDisplayConnector::unblank() return Error::from_errno(ENOTIMPL); } -ErrorOr<size_t> IntelNativeDisplayConnector::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 IntelNativeDisplayConnector::enable_console() { VERIFY(m_control_lock.is_locked()); @@ -238,19 +229,13 @@ ErrorOr<void> IntelNativeDisplayConnector::flush_first_surface() ErrorOr<void> IntelNativeDisplayConnector::create_attached_framebuffer_console() { - auto rounded_size = TRY(Memory::page_round_up(m_current_mode_setting.vertical_active * m_current_mode_setting.horizontal_stride)); - 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 = Graphics::ContiguousFramebufferConsole::initialize(m_framebuffer_address, m_current_mode_setting.horizontal_active, m_current_mode_setting.vertical_active, m_current_mode_setting.horizontal_stride); + m_framebuffer_console = Graphics::ContiguousFramebufferConsole::initialize(m_framebuffer_address.value(), m_current_mode_setting.horizontal_active, m_current_mode_setting.vertical_active, m_current_mode_setting.horizontal_stride); GraphicsManagement::the().set_console(*m_framebuffer_console); return {}; } -IntelNativeDisplayConnector::IntelNativeDisplayConnector(PhysicalAddress framebuffer_address, NonnullOwnPtr<Memory::Region> registers_region) - : DisplayConnector() - , m_framebuffer_address(framebuffer_address) +IntelNativeDisplayConnector::IntelNativeDisplayConnector(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, NonnullOwnPtr<Memory::Region> registers_region) + : DisplayConnector(framebuffer_address, framebuffer_resource_size, true) , m_registers_region(move(registers_region)) { { @@ -506,7 +491,7 @@ bool IntelNativeDisplayConnector::set_safe_crt_resolution() dbgln_if(INTEL_GRAPHICS_DEBUG, "PLL settings for {} {} {} {} {}", settings.n, settings.m1, settings.m2, settings.p1, settings.p2); enable_dpll_without_vga(pll_settings.value(), dac_multiplier); set_display_timings(modesetting); - enable_output(m_framebuffer_address, modesetting.horizontal.blanking_start()); + enable_output(m_framebuffer_address.value(), modesetting.horizontal.blanking_start()); DisplayConnector::ModeSetting mode_set { .horizontal_stride = modesetting.horizontal.blanking_start() * sizeof(u32), @@ -525,10 +510,6 @@ bool IntelNativeDisplayConnector::set_safe_crt_resolution() m_current_mode_setting = mode_set; - auto rounded_size = MUST(Memory::page_round_up(m_current_mode_setting.vertical_active * m_current_mode_setting.horizontal_stride)); - m_framebuffer_region = MUST(MM.allocate_kernel_region(m_framebuffer_address, rounded_size, "Intel Native Graphics Framebuffer", Memory::Region::Access::ReadWrite)); - m_framebuffer_data = m_framebuffer_region->vaddr().offset(m_framebuffer_address.offset_in_page()).as_ptr(); - if (m_framebuffer_console) m_framebuffer_console->set_resolution(m_current_mode_setting.horizontal_active, m_current_mode_setting.vertical_active, m_current_mode_setting.horizontal_stride); diff --git a/Kernel/Graphics/Intel/NativeDisplayConnector.h b/Kernel/Graphics/Intel/NativeDisplayConnector.h index 53598c24f1..84e2ef0dba 100644 --- a/Kernel/Graphics/Intel/NativeDisplayConnector.h +++ b/Kernel/Graphics/Intel/NativeDisplayConnector.h @@ -81,7 +81,7 @@ class IntelNativeDisplayConnector final friend class DeviceManagement; public: - static NonnullRefPtr<IntelNativeDisplayConnector> must_create(PhysicalAddress framebuffer_address, PhysicalAddress registers_region_address, size_t registers_region_length); + static NonnullRefPtr<IntelNativeDisplayConnector> must_create(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, PhysicalAddress registers_region_address, size_t registers_region_length); private: // ^DisplayConnector @@ -93,7 +93,6 @@ private: virtual ErrorOr<void> set_safe_mode_setting() override; virtual ErrorOr<void> set_y_offset(size_t y) override; virtual ErrorOr<void> unblank() override; - 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; virtual void disable_console() override; @@ -104,7 +103,7 @@ private: ErrorOr<void> initialize_gmbus_settings_and_read_edid(); - IntelNativeDisplayConnector(PhysicalAddress framebuffer_address, NonnullOwnPtr<Memory::Region> registers_region); + IntelNativeDisplayConnector(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, NonnullOwnPtr<Memory::Region> registers_region); ErrorOr<void> create_attached_framebuffer_console(); @@ -156,10 +155,7 @@ private: Optional<IntelGraphics::PLLSettings> create_pll_settings(u64 target_frequency, u64 reference_clock, IntelGraphics::PLLMaxSettings const&); mutable Spinlock m_registers_lock; - const PhysicalAddress m_framebuffer_address; RefPtr<Graphics::GenericFramebufferConsole> m_framebuffer_console; - OwnPtr<Memory::Region> m_framebuffer_region; - u8* m_framebuffer_data {}; const PhysicalAddress m_registers; NonnullOwnPtr<Memory::Region> m_registers_region; diff --git a/Kernel/Graphics/Intel/NativeGraphicsAdapter.cpp b/Kernel/Graphics/Intel/NativeGraphicsAdapter.cpp index d9bbd29bf4..f5e14989f4 100644 --- a/Kernel/Graphics/Intel/NativeGraphicsAdapter.cpp +++ b/Kernel/Graphics/Intel/NativeGraphicsAdapter.cpp @@ -43,11 +43,12 @@ ErrorOr<void> IntelNativeGraphicsAdapter::initialize_adapter() dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel Native Graphics Adapter @ {}", address); auto bar0_space_size = PCI::get_BAR_space_size(address, 0); VERIFY(bar0_space_size == 0x80000); + auto bar2_space_size = PCI::get_BAR_space_size(address, 2); dmesgln("Intel Native Graphics Adapter @ {}, MMIO @ {}, space size is {:x} bytes", address, PhysicalAddress(PCI::get_BAR0(address)), bar0_space_size); dmesgln("Intel Native Graphics Adapter @ {}, framebuffer @ {}", address, PhysicalAddress(PCI::get_BAR2(address))); PCI::enable_bus_mastering(address); - m_display_connector = IntelNativeDisplayConnector::must_create(PhysicalAddress(PCI::get_BAR2(address) & 0xfffffff0), PhysicalAddress(PCI::get_BAR0(address) & 0xfffffff0), bar0_space_size); + m_display_connector = IntelNativeDisplayConnector::must_create(PhysicalAddress(PCI::get_BAR2(address) & 0xfffffff0), bar2_space_size, PhysicalAddress(PCI::get_BAR0(address) & 0xfffffff0), bar0_space_size); return {}; } diff --git a/Kernel/Graphics/VGA/DisplayConnector.cpp b/Kernel/Graphics/VGA/DisplayConnector.cpp index c83f3fbc4c..da3ce993b2 100644 --- a/Kernel/Graphics/VGA/DisplayConnector.cpp +++ b/Kernel/Graphics/VGA/DisplayConnector.cpp @@ -24,8 +24,7 @@ NonnullRefPtr<GenericDisplayConnector> GenericDisplayConnector::must_create_with } GenericDisplayConnector::GenericDisplayConnector(PhysicalAddress framebuffer_address, size_t width, size_t height, size_t pitch) - : DisplayConnector() - , m_framebuffer_address(framebuffer_address) + : DisplayConnector(framebuffer_address, height * pitch, true) { m_current_mode_setting.horizontal_active = width; m_current_mode_setting.vertical_active = height; @@ -38,24 +37,11 @@ ErrorOr<void> GenericDisplayConnector::create_attached_framebuffer_console() auto height = m_current_mode_setting.vertical_active; auto pitch = m_current_mode_setting.horizontal_stride; - auto rounded_size = TRY(Memory::page_round_up(pitch * height)); - 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 = Graphics::ContiguousFramebufferConsole::initialize(m_framebuffer_address, width, height, pitch); + m_framebuffer_console = Graphics::ContiguousFramebufferConsole::initialize(m_framebuffer_address.value(), width, height, pitch); GraphicsManagement::the().set_console(*m_framebuffer_console); return {}; } -ErrorOr<size_t> GenericDisplayConnector::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 GenericDisplayConnector::enable_console() { VERIFY(m_control_lock.is_locked()); diff --git a/Kernel/Graphics/VGA/DisplayConnector.h b/Kernel/Graphics/VGA/DisplayConnector.h index e2c7a7e3f6..ac48386f2c 100644 --- a/Kernel/Graphics/VGA/DisplayConnector.h +++ b/Kernel/Graphics/VGA/DisplayConnector.h @@ -39,15 +39,11 @@ protected: // Note: This is possibly a paravirtualized hardware, but since we don't know, we assume there's no refresh rate... 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 {}; }; } diff --git a/Kernel/Graphics/VMWare/DisplayConnector.cpp b/Kernel/Graphics/VMWare/DisplayConnector.cpp index a5ee870de4..a50d22ebcf 100644 --- a/Kernel/Graphics/VMWare/DisplayConnector.cpp +++ b/Kernel/Graphics/VMWare/DisplayConnector.cpp @@ -12,9 +12,9 @@ namespace Kernel { -NonnullRefPtr<VMWareDisplayConnector> VMWareDisplayConnector::must_create(VMWareGraphicsAdapter const& parent_adapter, PhysicalAddress framebuffer_address) +NonnullRefPtr<VMWareDisplayConnector> VMWareDisplayConnector::must_create(VMWareGraphicsAdapter const& parent_adapter, PhysicalAddress framebuffer_address, size_t framebuffer_resource_size) { - auto connector = MUST(DeviceManagement::try_create_device<VMWareDisplayConnector>(parent_adapter, framebuffer_address)); + auto connector = MUST(DeviceManagement::try_create_device<VMWareDisplayConnector>(parent_adapter, framebuffer_address, framebuffer_resource_size)); MUST(connector->create_attached_framebuffer_console()); MUST(connector->initialize_edid_for_generic_monitor()); return connector; @@ -22,18 +22,13 @@ NonnullRefPtr<VMWareDisplayConnector> VMWareDisplayConnector::must_create(VMWare ErrorOr<void> VMWareDisplayConnector::create_attached_framebuffer_console() { - auto rounded_size = TRY(Memory::page_round_up(1024 * sizeof(u32) * 768)); - 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 = VMWareFramebufferConsole::initialize(*this); GraphicsManagement::the().set_console(*m_framebuffer_console); return {}; } -VMWareDisplayConnector::VMWareDisplayConnector(VMWareGraphicsAdapter const& parent_adapter, PhysicalAddress framebuffer_address) - : DisplayConnector() - , m_framebuffer_address(framebuffer_address) +VMWareDisplayConnector::VMWareDisplayConnector(VMWareGraphicsAdapter const& parent_adapter, PhysicalAddress framebuffer_address, size_t framebuffer_resource_size) + : DisplayConnector(framebuffer_address, framebuffer_resource_size, false) , m_parent_adapter(parent_adapter) { } @@ -63,15 +58,6 @@ ErrorOr<void> VMWareDisplayConnector::unblank() return Error::from_errno(ENOTIMPL); } -ErrorOr<size_t> VMWareDisplayConnector::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 VMWareDisplayConnector::enable_console() { VERIFY(m_control_lock.is_locked()); @@ -124,10 +110,6 @@ ErrorOr<void> VMWareDisplayConnector::set_mode_setting(ModeSetting const& mode_s TRY(m_parent_adapter->modeset_primary_screen_resolution({}, width, height)); - auto rounded_size = TRY(Memory::page_round_up(width * sizeof(u32) * height)); - 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)); auto pitch = m_parent_adapter->primary_screen_pitch({}); diff --git a/Kernel/Graphics/VMWare/DisplayConnector.h b/Kernel/Graphics/VMWare/DisplayConnector.h index d17e51b8e3..6f60653fd1 100644 --- a/Kernel/Graphics/VMWare/DisplayConnector.h +++ b/Kernel/Graphics/VMWare/DisplayConnector.h @@ -23,10 +23,10 @@ class VMWareDisplayConnector : public DisplayConnector { friend class DeviceManagement; public: - static NonnullRefPtr<VMWareDisplayConnector> must_create(VMWareGraphicsAdapter const& parent_adapter, PhysicalAddress framebuffer_address); + static NonnullRefPtr<VMWareDisplayConnector> must_create(VMWareGraphicsAdapter const& parent_adapter, PhysicalAddress framebuffer_address, size_t framebuffer_resource_size); private: - VMWareDisplayConnector(VMWareGraphicsAdapter const& parent_adapter, PhysicalAddress framebuffer_address); + VMWareDisplayConnector(VMWareGraphicsAdapter const& parent_adapter, PhysicalAddress framebuffer_address, size_t framebuffer_resource_size); ErrorOr<void> create_attached_framebuffer_console(); virtual bool mutable_mode_setting_capable() const override { return true; } @@ -41,7 +41,6 @@ private: // Note: Paravirtualized hardware doesn't require a defined refresh rate for modesetting. virtual bool refresh_rate_support() const override { return false; } - virtual ErrorOr<size_t> write_to_first_surface(u64 offset, UserOrKernelBuffer const&, size_t length) override; virtual ErrorOr<void> flush_first_surface() override; virtual ErrorOr<void> flush_rectangle(size_t buffer_index, FBRect const& rect) override; @@ -49,12 +48,7 @@ private: virtual void disable_console() override; private: - u8* framebuffer_data() { return m_framebuffer_data; } - - const PhysicalAddress m_framebuffer_address; NonnullRefPtr<VMWareGraphicsAdapter> m_parent_adapter; RefPtr<VMWareFramebufferConsole> m_framebuffer_console; - OwnPtr<Memory::Region> m_framebuffer_region; - u8* m_framebuffer_data {}; }; } diff --git a/Kernel/Graphics/VMWare/GraphicsAdapter.cpp b/Kernel/Graphics/VMWare/GraphicsAdapter.cpp index debea6043e..29ad1c1adb 100644 --- a/Kernel/Graphics/VMWare/GraphicsAdapter.cpp +++ b/Kernel/Graphics/VMWare/GraphicsAdapter.cpp @@ -181,7 +181,9 @@ UNMAP_AFTER_INIT ErrorOr<void> VMWareGraphicsAdapter::initialize_adapter() // Note: enable the device by modesetting the primary screen resolution modeset_primary_screen_resolution(640, 480); - m_display_connector = VMWareDisplayConnector::must_create(*this, PhysicalAddress(PCI::get_BAR1(pci_address()) & 0xfffffff0)); + auto bar1_space_size = PCI::get_BAR_space_size(pci_address(), 1); + + m_display_connector = VMWareDisplayConnector::must_create(*this, PhysicalAddress(PCI::get_BAR1(pci_address()) & 0xfffffff0), bar1_space_size); TRY(m_display_connector->set_safe_mode_setting()); return {}; } diff --git a/Kernel/Graphics/VirtIOGPU/DisplayConnector.cpp b/Kernel/Graphics/VirtIOGPU/DisplayConnector.cpp index c9086f92ec..e7f8fe2671 100644 --- a/Kernel/Graphics/VirtIOGPU/DisplayConnector.cpp +++ b/Kernel/Graphics/VirtIOGPU/DisplayConnector.cpp @@ -24,8 +24,10 @@ NonnullRefPtr<VirtIODisplayConnector> VirtIODisplayConnector::must_create(VirtIO return connector; } +static_assert((MAX_VIRTIOGPU_RESOLUTION_WIDTH * MAX_VIRTIOGPU_RESOLUTION_HEIGHT * sizeof(u32) * 2) % PAGE_SIZE == 0); + VirtIODisplayConnector::VirtIODisplayConnector(VirtIOGraphicsAdapter& graphics_adapter, Graphics::VirtIOGPU::ScanoutID scanout_id) - : DisplayConnector() + : DisplayConnector((MAX_VIRTIOGPU_RESOLUTION_WIDTH * MAX_VIRTIOGPU_RESOLUTION_HEIGHT * sizeof(u32) * 2), false) , m_graphics_adapter(graphics_adapter) , m_scanout_id(scanout_id) { @@ -48,14 +50,14 @@ ErrorOr<void> VirtIODisplayConnector::set_mode_setting(ModeSetting const& mode_s return Error::from_errno(ENOTSUP); auto& info = m_display_info; - info.rect = { .x = 0, .y = 0, .width = (u32)mode_setting.horizontal_active, .height = (u32)mode_setting.vertical_active, }; - TRY(create_framebuffer()); + + TRY(m_graphics_adapter->mode_set_resolution({}, *this, mode_setting.horizontal_active, mode_setting.vertical_active)); DisplayConnector::ModeSetting mode_set { .horizontal_stride = info.rect.width * sizeof(u32), .pixel_clock_in_khz = 0, // Note: There's no pixel clock in paravirtualized hardware @@ -71,6 +73,8 @@ ErrorOr<void> VirtIODisplayConnector::set_mode_setting(ModeSetting const& mode_s .vertical_offset = 0, }; m_current_mode_setting = mode_set; + + m_display_info.enabled = 1; return {}; } ErrorOr<void> VirtIODisplayConnector::set_safe_mode_setting() @@ -96,9 +100,9 @@ ErrorOr<void> VirtIODisplayConnector::set_y_offset(size_t y) { VERIFY(m_control_lock.is_locked()); if (y == 0) - m_current_buffer = &m_main_buffer; + m_last_set_buffer_index.store(0); else if (y == m_display_info.rect.height) - m_current_buffer = &m_back_buffer; + m_last_set_buffer_index.store(1); else return Error::from_errno(EINVAL); return {}; @@ -108,25 +112,11 @@ ErrorOr<void> VirtIODisplayConnector::unblank() return Error::from_errno(ENOTIMPL); } -ErrorOr<size_t> VirtIODisplayConnector::write_to_first_surface(u64 offset, UserOrKernelBuffer const& buffer, size_t length) -{ - VERIFY(m_control_lock.is_locked()); - if (offset + length > (m_buffer_size * 2)) - return Error::from_errno(EOVERFLOW); - if (offset < m_buffer_size && (offset + length) > (m_buffer_size)) - return Error::from_errno(EOVERFLOW); - if (offset < m_buffer_size) { - TRY(buffer.read(m_main_buffer.framebuffer_data + offset, 0, length)); - } else { - TRY(buffer.read(m_back_buffer.framebuffer_data + offset - m_buffer_size, 0, length)); - } - - return length; -} - ErrorOr<void> VirtIODisplayConnector::flush_rectangle(size_t buffer_index, FBRect const& rect) { VERIFY(m_flushing_lock.is_locked()); + if (!is_valid_buffer_index(buffer_index)) + return Error::from_errno(EINVAL); SpinlockLocker locker(m_graphics_adapter->operation_lock()); Graphics::VirtIOGPU::Protocol::Rect dirty_rect { .x = rect.x, @@ -135,23 +125,13 @@ ErrorOr<void> VirtIODisplayConnector::flush_rectangle(size_t buffer_index, FBRec .height = rect.height }; - auto& buffer = buffer_from_index(buffer_index); - transfer_framebuffer_data_to_host(dirty_rect, buffer); - if (&buffer == m_current_buffer) { + bool main_buffer = (buffer_index == 0); + m_graphics_adapter->transfer_framebuffer_data_to_host({}, *this, dirty_rect, main_buffer); + if (m_last_set_buffer_index.load() == buffer_index) { // Flushing directly to screen - flush_displayed_image(dirty_rect, buffer); - buffer.dirty_rect = {}; + flush_displayed_image(dirty_rect, main_buffer); } else { - if (buffer.dirty_rect.width == 0 || buffer.dirty_rect.height == 0) { - buffer.dirty_rect = dirty_rect; - } else { - auto current_dirty_right = buffer.dirty_rect.x + buffer.dirty_rect.width; - auto current_dirty_bottom = buffer.dirty_rect.y + buffer.dirty_rect.height; - buffer.dirty_rect.x = min(buffer.dirty_rect.x, dirty_rect.x); - buffer.dirty_rect.y = min(buffer.dirty_rect.y, dirty_rect.y); - buffer.dirty_rect.width = max(current_dirty_right, dirty_rect.x + dirty_rect.width) - buffer.dirty_rect.x; - buffer.dirty_rect.height = max(current_dirty_bottom, dirty_rect.y + dirty_rect.height) - buffer.dirty_rect.y; - } + set_dirty_displayed_rect(dirty_rect, main_buffer); } return {}; } @@ -166,24 +146,14 @@ ErrorOr<void> VirtIODisplayConnector::flush_first_surface() .width = m_display_info.rect.width, .height = m_display_info.rect.height }; - auto& buffer = buffer_from_index(0); - transfer_framebuffer_data_to_host(dirty_rect, buffer); - if (&buffer == m_current_buffer) { - // Flushing directly to screen - flush_displayed_image(dirty_rect, buffer); - buffer.dirty_rect = {}; - } else { - if (buffer.dirty_rect.width == 0 || buffer.dirty_rect.height == 0) { - buffer.dirty_rect = dirty_rect; - } else { - auto current_dirty_right = buffer.dirty_rect.x + buffer.dirty_rect.width; - auto current_dirty_bottom = buffer.dirty_rect.y + buffer.dirty_rect.height; - buffer.dirty_rect.x = min(buffer.dirty_rect.x, dirty_rect.x); - buffer.dirty_rect.y = min(buffer.dirty_rect.y, dirty_rect.y); - buffer.dirty_rect.width = max(current_dirty_right, dirty_rect.x + dirty_rect.width) - buffer.dirty_rect.x; - buffer.dirty_rect.height = max(current_dirty_bottom, dirty_rect.y + dirty_rect.height) - buffer.dirty_rect.y; - } - } + + auto current_buffer_index = m_last_set_buffer_index.load(); + VERIFY(is_valid_buffer_index(current_buffer_index)); + + bool main_buffer = (current_buffer_index == 0); + m_graphics_adapter->transfer_framebuffer_data_to_host({}, *this, dirty_rect, main_buffer); + // Flushing directly to screen + flush_displayed_image(dirty_rect, main_buffer); return {}; } @@ -201,11 +171,21 @@ void VirtIODisplayConnector::disable_console() m_console->disable(); } -void VirtIODisplayConnector::clear_to_black(Buffer& buffer) +void VirtIODisplayConnector::set_edid_bytes(Badge<VirtIOGraphicsAdapter>, Array<u8, 128> const& edid_bytes) +{ + DisplayConnector::set_edid_bytes(edid_bytes); +} + +Graphics::VirtIOGPU::Protocol::DisplayInfoResponse::Display VirtIODisplayConnector::display_information(Badge<VirtIOGraphicsAdapter>) const +{ + return m_display_info; +} + +void VirtIODisplayConnector::clear_to_black() { size_t width = m_display_info.rect.width; size_t height = m_display_info.rect.height; - u8* data = buffer.framebuffer_data; + u8* data = framebuffer_data(); for (size_t i = 0; i < width * height; ++i) { data[4 * i + 0] = 0x00; data[4 * i + 1] = 0x00; @@ -214,7 +194,7 @@ void VirtIODisplayConnector::clear_to_black(Buffer& buffer) } } -void VirtIODisplayConnector::draw_ntsc_test_pattern(Buffer& buffer) +void VirtIODisplayConnector::draw_ntsc_test_pattern(Badge<VirtIOGraphicsAdapter>) { constexpr u8 colors[12][4] = { { 0xff, 0xff, 0xff, 0xff }, // White @@ -232,134 +212,57 @@ void VirtIODisplayConnector::draw_ntsc_test_pattern(Buffer& buffer) }; size_t width = m_display_info.rect.width; size_t height = m_display_info.rect.height; - u8* data = buffer.framebuffer_data; + u8* data = framebuffer_data(); // Draw NTSC test card - for (size_t y = 0; y < height; ++y) { - for (size_t x = 0; x < width; ++x) { - size_t color = 0; - if (3 * y < 2 * height) { - // Top 2/3 of image is 7 vertical stripes of color spectrum - color = (7 * x) / width; - } else if (4 * y < 3 * height) { - // 2/3 mark to 3/4 mark is backwards color spectrum alternating with black - auto segment = (7 * x) / width; - color = segment % 2 ? 10 : 6 - segment; - } else { - if (28 * x < 5 * width) { - color = 8; - } else if (28 * x < 10 * width) { - color = 0; - } else if (28 * x < 15 * width) { - color = 7; - } else if (28 * x < 20 * width) { - color = 10; - } else if (7 * x < 6 * width) { - // Grayscale gradient - color = 26 - ((21 * x) / width); + for (size_t i = 0; i < 2; ++i) { + for (size_t y = 0; y < height; ++y) { + for (size_t x = 0; x < width; ++x) { + size_t color = 0; + if (3 * y < 2 * height) { + // Top 2/3 of image is 7 vertical stripes of color spectrum + color = (7 * x) / width; + } else if (4 * y < 3 * height) { + // 2/3 mark to 3/4 mark is backwards color spectrum alternating with black + auto segment = (7 * x) / width; + color = segment % 2 ? 10 : 6 - segment; } else { - // Solid black - color = 10; + if (28 * x < 5 * width) { + color = 8; + } else if (28 * x < 10 * width) { + color = 0; + } else if (28 * x < 15 * width) { + color = 7; + } else if (28 * x < 20 * width) { + color = 10; + } else if (7 * x < 6 * width) { + // Grayscale gradient + color = 26 - ((21 * x) / width); + } else { + // Solid black + color = 10; + } + } + u8* pixel = &data[4 * (y * width + x)]; + for (int i = 0; i < 4; ++i) { + pixel[i] = colors[color][i]; } - } - u8* pixel = &data[4 * (y * width + x)]; - for (int i = 0; i < 4; ++i) { - pixel[i] = colors[color][i]; } } + data = data + (width * height * sizeof(u32)); } dbgln_if(VIRTIO_DEBUG, "Finish drawing the pattern"); } -u8* VirtIODisplayConnector::framebuffer_data() -{ - return m_current_buffer->framebuffer_data; -} - -ErrorOr<void> VirtIODisplayConnector::create_framebuffer() -{ - SpinlockLocker locker(m_graphics_adapter->operation_lock()); - // First delete any existing framebuffers to free the memory first - m_framebuffer = nullptr; - m_framebuffer_sink_vmobject = nullptr; - - // Allocate frame buffer for both front and back - m_buffer_size = calculate_framebuffer_size(m_display_info.rect.width, m_display_info.rect.height); - auto region_name = TRY(KString::formatted("VirtGPU FrameBuffer #{}", m_scanout_id.value())); - m_framebuffer = TRY(MM.allocate_kernel_region(m_buffer_size * 2, region_name->view(), Memory::Region::Access::ReadWrite, AllocationStrategy::AllocateNow)); - auto write_sink_page = TRY(MM.allocate_user_physical_page(Memory::MemoryManager::ShouldZeroFill::No)); - auto num_needed_pages = m_framebuffer->vmobject().page_count(); - - NonnullRefPtrVector<Memory::PhysicalPage> pages; - for (auto i = 0u; i < num_needed_pages; ++i) { - TRY(pages.try_append(write_sink_page)); - } - m_framebuffer_sink_vmobject = TRY(Memory::AnonymousVMObject::try_create_with_physical_pages(pages.span())); - - m_current_buffer = &buffer_from_index(m_last_set_buffer_index.load()); - create_buffer(m_main_buffer, 0, m_buffer_size); - create_buffer(m_back_buffer, m_buffer_size, m_buffer_size); - - return {}; -} - -void VirtIODisplayConnector::set_edid_bytes(Badge<VirtIOGraphicsAdapter>, Array<u8, 128> const& edid_bytes) -{ - DisplayConnector::set_edid_bytes(edid_bytes); -} - -Graphics::VirtIOGPU::Protocol::DisplayInfoResponse::Display VirtIODisplayConnector::display_information(Badge<VirtIOGraphicsAdapter>) -{ - return m_display_info; -} - -void VirtIODisplayConnector::create_buffer(Buffer& buffer, size_t framebuffer_offset, size_t framebuffer_size) +void VirtIODisplayConnector::flush_displayed_image(Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect, bool main_buffer) { VERIFY(m_graphics_adapter->operation_lock().is_locked()); - buffer.framebuffer_offset = framebuffer_offset; - buffer.framebuffer_data = m_framebuffer->vaddr().as_ptr() + framebuffer_offset; - - // 1. Create BUFFER using VIRTIO_GPU_CMD_RESOURCE_CREATE_2D - if (buffer.resource_id.value() != 0) - m_graphics_adapter->delete_resource(buffer.resource_id); - buffer.resource_id = m_graphics_adapter->create_2d_resource(m_display_info.rect); - - // 2. Attach backing storage using VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING - m_graphics_adapter->ensure_backing_storage(buffer.resource_id, *m_framebuffer, buffer.framebuffer_offset, framebuffer_size); - // 3. Use VIRTIO_GPU_CMD_SET_SCANOUT to link the framebuffer to a display scanout. - if (&buffer == m_current_buffer) - m_graphics_adapter->set_scanout_resource(m_scanout_id, buffer.resource_id, m_display_info.rect); - // 4. Render our test pattern - draw_ntsc_test_pattern(buffer); - // 5. Use VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D to update the host resource from guest memory. - transfer_framebuffer_data_to_host(m_display_info.rect, buffer); - // 6. Use VIRTIO_GPU_CMD_RESOURCE_FLUSH to flush the updated resource to the display. - if (&buffer == m_current_buffer) - flush_displayed_image(m_display_info.rect, buffer); - - // Make sure we constrain the existing dirty rect (if any) - if (buffer.dirty_rect.width != 0 || buffer.dirty_rect.height != 0) { - auto dirty_right = buffer.dirty_rect.x + buffer.dirty_rect.width; - auto dirty_bottom = buffer.dirty_rect.y + buffer.dirty_rect.height; - buffer.dirty_rect.width = min(dirty_right, m_display_info.rect.x + m_display_info.rect.width) - buffer.dirty_rect.x; - buffer.dirty_rect.height = min(dirty_bottom, m_display_info.rect.y + m_display_info.rect.height) - buffer.dirty_rect.y; - } - - m_display_info.enabled = 1; -} - -void VirtIODisplayConnector::transfer_framebuffer_data_to_host(Graphics::VirtIOGPU::Protocol::Rect const& rect, Buffer& buffer) -{ - m_graphics_adapter->transfer_framebuffer_data_to_host(m_scanout_id, buffer.resource_id, rect); + m_graphics_adapter->flush_displayed_image({}, *this, dirty_rect, main_buffer); } -void VirtIODisplayConnector::flush_dirty_window(Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect, Buffer& buffer) +void VirtIODisplayConnector::set_dirty_displayed_rect(Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect, bool main_buffer) { - m_graphics_adapter->flush_dirty_rectangle(m_scanout_id, buffer.resource_id, dirty_rect); -} - -void VirtIODisplayConnector::flush_displayed_image(Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect, Buffer& buffer) -{ - m_graphics_adapter->flush_displayed_image(buffer.resource_id, dirty_rect); + VERIFY(m_graphics_adapter->operation_lock().is_locked()); + m_graphics_adapter->set_dirty_displayed_rect({}, *this, dirty_rect, main_buffer); } } diff --git a/Kernel/Graphics/VirtIOGPU/DisplayConnector.h b/Kernel/Graphics/VirtIOGPU/DisplayConnector.h index 1a3b462b63..0584df94a7 100644 --- a/Kernel/Graphics/VirtIOGPU/DisplayConnector.h +++ b/Kernel/Graphics/VirtIOGPU/DisplayConnector.h @@ -29,20 +29,15 @@ class VirtIODisplayConnector final : public DisplayConnector { friend class Graphics::VirtIOGPU::Console; friend class DeviceManagement; -private: - struct Buffer { - size_t framebuffer_offset { 0 }; - u8* framebuffer_data { nullptr }; - Graphics::VirtIOGPU::Protocol::Rect dirty_rect {}; - Graphics::VirtIOGPU::ResourceID resource_id { 0 }; - }; - public: static NonnullRefPtr<VirtIODisplayConnector> must_create(VirtIOGraphicsAdapter& graphics_adapter, Graphics::VirtIOGPU::ScanoutID scanout_id); void set_edid_bytes(Badge<VirtIOGraphicsAdapter>, Array<u8, 128> const& edid_bytes); void set_safe_mode_setting_after_initialization(Badge<VirtIOGraphicsAdapter>); - Graphics::VirtIOGPU::Protocol::DisplayInfoResponse::Display display_information(Badge<VirtIOGraphicsAdapter>); + Graphics::VirtIOGPU::ScanoutID scanout_id() const { return m_scanout_id; } + Graphics::VirtIOGPU::Protocol::DisplayInfoResponse::Display display_information(Badge<VirtIOGraphicsAdapter>) const; + + void draw_ntsc_test_pattern(Badge<VirtIOGraphicsAdapter>); private: void initialize_console(); @@ -59,45 +54,28 @@ private: // Note: Paravirtualized hardware doesn't require a defined refresh rate for modesetting. virtual bool refresh_rate_support() const override { return false; } - virtual ErrorOr<size_t> write_to_first_surface(u64 offset, UserOrKernelBuffer const&, size_t length) override; virtual ErrorOr<void> flush_first_surface() override; virtual ErrorOr<void> flush_rectangle(size_t buffer_index, FBRect const& rect) override; virtual void enable_console() override; virtual void disable_console() override; + static bool is_valid_buffer_index(size_t buffer_index) + { + return buffer_index == 0 || buffer_index == 1; + } + private: VirtIODisplayConnector(VirtIOGraphicsAdapter& graphics_adapter, Graphics::VirtIOGPU::ScanoutID scanout_id); + void flush_displayed_image(Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect, bool main_buffer); + void set_dirty_displayed_rect(Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect, bool main_buffer); + void query_display_information(); ErrorOr<void> query_edid_from_virtio_adapter(); void query_display_edid(); - void flush_dirty_window(Graphics::VirtIOGPU::Protocol::Rect const&, Buffer&); - void transfer_framebuffer_data_to_host(Graphics::VirtIOGPU::Protocol::Rect const&, Buffer&); - void flush_displayed_image(Graphics::VirtIOGPU::Protocol::Rect const&, Buffer&); - - // Basic 2D framebuffer methods - static size_t calculate_framebuffer_size(size_t width, size_t height) - { - // VirtIO resources can only map on page boundaries! - return Memory::page_round_up(sizeof(u32) * width * height).value(); - } - u8* framebuffer_data(); - void draw_ntsc_test_pattern(Buffer&); - void clear_to_black(Buffer&); - ErrorOr<void> create_framebuffer(); - void create_buffer(Buffer&, size_t, size_t); - void set_buffer(int); - static bool is_valid_buffer_index(int buffer_index) - { - return buffer_index == 0 || buffer_index == 1; - } - Buffer& buffer_from_index(int buffer_index) - { - return buffer_index == 0 ? m_main_buffer : m_back_buffer; - } - Buffer& current_buffer() const { return *m_current_buffer; } + void clear_to_black(); // Member data // Context used for kernel operations (e.g. flushing resources to scanout) @@ -109,13 +87,7 @@ private: Graphics::VirtIOGPU::ScanoutID m_scanout_id; // 2D framebuffer Member data - size_t m_buffer_size { 0 }; - Buffer* m_current_buffer { nullptr }; - Atomic<int, AK::memory_order_relaxed> m_last_set_buffer_index { 0 }; - Buffer m_main_buffer; - Buffer m_back_buffer; - OwnPtr<Memory::Region> m_framebuffer; - RefPtr<Memory::VMObject> m_framebuffer_sink_vmobject; + Atomic<size_t, AK::memory_order_relaxed> m_last_set_buffer_index { 0 }; constexpr static size_t NUM_TRANSFER_REGION_PAGES = 256; }; diff --git a/Kernel/Graphics/VirtIOGPU/GraphicsAdapter.cpp b/Kernel/Graphics/VirtIOGPU/GraphicsAdapter.cpp index 1748304484..9fb1083164 100644 --- a/Kernel/Graphics/VirtIOGPU/GraphicsAdapter.cpp +++ b/Kernel/Graphics/VirtIOGPU/GraphicsAdapter.cpp @@ -48,6 +48,83 @@ ErrorOr<void> VirtIOGraphicsAdapter::initialize_adapter() return {}; } +ErrorOr<void> VirtIOGraphicsAdapter::mode_set_resolution(Badge<VirtIODisplayConnector>, VirtIODisplayConnector& connector, size_t width, size_t height) +{ + SpinlockLocker locker(m_operation_lock); + VERIFY(connector.scanout_id() < VIRTIO_GPU_MAX_SCANOUTS); + auto rounded_buffer_size = TRY(calculate_framebuffer_size(width, height)); + TRY(attach_physical_range_to_framebuffer(connector, true, 0, rounded_buffer_size)); + TRY(attach_physical_range_to_framebuffer(connector, false, rounded_buffer_size, rounded_buffer_size)); + return {}; +} + +void VirtIOGraphicsAdapter::set_dirty_displayed_rect(Badge<VirtIODisplayConnector>, VirtIODisplayConnector& connector, Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect, bool main_buffer) +{ + VERIFY(m_operation_lock.is_locked()); + VERIFY(connector.scanout_id() < VIRTIO_GPU_MAX_SCANOUTS); + Scanout::PhysicalBuffer& buffer = main_buffer ? m_scanouts[connector.scanout_id().value()].main_buffer : m_scanouts[connector.scanout_id().value()].back_buffer; + if (buffer.dirty_rect.width == 0 || buffer.dirty_rect.height == 0) { + buffer.dirty_rect = dirty_rect; + } else { + auto current_dirty_right = buffer.dirty_rect.x + buffer.dirty_rect.width; + auto current_dirty_bottom = buffer.dirty_rect.y + buffer.dirty_rect.height; + buffer.dirty_rect.x = min(buffer.dirty_rect.x, dirty_rect.x); + buffer.dirty_rect.y = min(buffer.dirty_rect.y, dirty_rect.y); + buffer.dirty_rect.width = max(current_dirty_right, dirty_rect.x + dirty_rect.width) - buffer.dirty_rect.x; + buffer.dirty_rect.height = max(current_dirty_bottom, dirty_rect.y + dirty_rect.height) - buffer.dirty_rect.y; + } +} + +void VirtIOGraphicsAdapter::flush_displayed_image(Badge<VirtIODisplayConnector>, VirtIODisplayConnector& connector, Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect, bool main_buffer) +{ + VERIFY(m_operation_lock.is_locked()); + VERIFY(connector.scanout_id() < VIRTIO_GPU_MAX_SCANOUTS); + Scanout::PhysicalBuffer& buffer = main_buffer ? m_scanouts[connector.scanout_id().value()].main_buffer : m_scanouts[connector.scanout_id().value()].back_buffer; + flush_displayed_image(buffer.resource_id, dirty_rect); + buffer.dirty_rect = {}; +} + +void VirtIOGraphicsAdapter::transfer_framebuffer_data_to_host(Badge<VirtIODisplayConnector>, VirtIODisplayConnector& connector, Graphics::VirtIOGPU::Protocol::Rect const& rect, bool main_buffer) +{ + VERIFY(m_operation_lock.is_locked()); + VERIFY(connector.scanout_id() < VIRTIO_GPU_MAX_SCANOUTS); + Scanout::PhysicalBuffer& buffer = main_buffer ? m_scanouts[connector.scanout_id().value()].main_buffer : m_scanouts[connector.scanout_id().value()].back_buffer; + transfer_framebuffer_data_to_host(connector.scanout_id(), buffer.resource_id, rect); +} + +ErrorOr<void> VirtIOGraphicsAdapter::attach_physical_range_to_framebuffer(VirtIODisplayConnector& connector, bool main_buffer, size_t framebuffer_offset, size_t framebuffer_size) +{ + VERIFY(m_operation_lock.is_locked()); + Scanout::PhysicalBuffer& buffer = main_buffer ? m_scanouts[connector.scanout_id().value()].main_buffer : m_scanouts[connector.scanout_id().value()].back_buffer; + buffer.framebuffer_offset = framebuffer_offset; + + // 1. Create BUFFER using VIRTIO_GPU_CMD_RESOURCE_CREATE_2D + if (buffer.resource_id.value() != 0) + delete_resource(buffer.resource_id); + auto display_info = connector.display_information({}); + buffer.resource_id = create_2d_resource(display_info.rect); + + // 2. Attach backing storage using VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING + ensure_backing_storage(buffer.resource_id, connector.framebuffer_region(), buffer.framebuffer_offset, framebuffer_size); + // 3. Use VIRTIO_GPU_CMD_SET_SCANOUT to link the framebuffer to a display scanout. + set_scanout_resource(connector.scanout_id(), buffer.resource_id, display_info.rect); + // 4. Render our test pattern + connector.draw_ntsc_test_pattern({}); + // 5. Use VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D to update the host resource from guest memory. + transfer_framebuffer_data_to_host(connector.scanout_id(), buffer.resource_id, display_info.rect); + // 6. Use VIRTIO_GPU_CMD_RESOURCE_FLUSH to flush the updated resource to the display. + flush_displayed_image(buffer.resource_id, display_info.rect); + + // Make sure we constrain the existing dirty rect (if any) + if (buffer.dirty_rect.width != 0 || buffer.dirty_rect.height != 0) { + auto dirty_right = buffer.dirty_rect.x + buffer.dirty_rect.width; + auto dirty_bottom = buffer.dirty_rect.y + buffer.dirty_rect.height; + buffer.dirty_rect.width = min(dirty_right, display_info.rect.x + display_info.rect.width) - buffer.dirty_rect.x; + buffer.dirty_rect.height = min(dirty_bottom, display_info.rect.y + display_info.rect.height) - buffer.dirty_rect.y; + } + return {}; +} + VirtIOGraphicsAdapter::VirtIOGraphicsAdapter(PCI::DeviceIdentifier const& device_identifier, NonnullOwnPtr<Memory::Region> scratch_space_region) : VirtIO::Device(device_identifier) , m_scratch_space(move(scratch_space_region)) diff --git a/Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h b/Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h index 2e90d84f63..e4eab527d7 100644 --- a/Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h +++ b/Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h @@ -50,10 +50,25 @@ public: Graphics::VirtIOGPU::ResourceID allocate_resource_id(Badge<VirtIODisplayConnector>); Graphics::VirtIOGPU::ContextID allocate_context_id(Badge<VirtIODisplayConnector>); + ErrorOr<void> mode_set_resolution(Badge<VirtIODisplayConnector>, VirtIODisplayConnector&, size_t width, size_t height); + void set_dirty_displayed_rect(Badge<VirtIODisplayConnector>, VirtIODisplayConnector&, Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect, bool main_buffer); + void flush_displayed_image(Badge<VirtIODisplayConnector>, VirtIODisplayConnector&, Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect, bool main_buffer); + void transfer_framebuffer_data_to_host(Badge<VirtIODisplayConnector>, VirtIODisplayConnector&, Graphics::VirtIOGPU::Protocol::Rect const& rect, bool main_buffer); + private: + ErrorOr<void> attach_physical_range_to_framebuffer(VirtIODisplayConnector& connector, bool main_buffer, size_t framebuffer_offset, size_t framebuffer_size); + void flush_dirty_rectangle(Graphics::VirtIOGPU::ScanoutID, Graphics::VirtIOGPU::ResourceID, Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect); struct Scanout { + struct PhysicalBuffer { + size_t framebuffer_offset { 0 }; + Graphics::VirtIOGPU::Protocol::Rect dirty_rect {}; + Graphics::VirtIOGPU::ResourceID resource_id { 0 }; + }; + RefPtr<VirtIODisplayConnector> display_connector; + PhysicalBuffer main_buffer; + PhysicalBuffer back_buffer; }; VirtIOGraphicsAdapter(PCI::DeviceIdentifier const&, NonnullOwnPtr<Memory::Region> scratch_space_region); @@ -65,6 +80,13 @@ private: u32 get_pending_events(); void clear_pending_events(u32 event_bitmask); + // 2D framebuffer stuff + static ErrorOr<FlatPtr> calculate_framebuffer_size(size_t width, size_t height) + { + // VirtIO resources can only map on page boundaries! + return Memory::page_round_up(sizeof(u32) * width * height); + } + // 3D Command stuff Graphics::VirtIOGPU::ContextID create_context(); void attach_resource_to_context(Graphics::VirtIOGPU::ResourceID resource_id, Graphics::VirtIOGPU::ContextID context_id); |