diff options
author | Liav A <liavalb@gmail.com> | 2021-09-21 10:08:28 +0300 |
---|---|---|
committer | Idan Horowitz <idan.horowitz@gmail.com> | 2021-10-27 07:57:44 +0300 |
commit | f476b49fd831161184df7829b6a8d9d6cf67febd (patch) | |
tree | 3245e9b32a0174e78fc9f62b66fe04a1256208f1 /Kernel/Graphics/VirtIOGPU | |
parent | a9538b5879e910f26f3b2ac205ad8999d762192b (diff) | |
download | serenity-f476b49fd831161184df7829b6a8d9d6cf67febd.zip |
Kernel/Graphics: Merge VirtIO GraphicsAdapter and VirtIO GPU code
A VirtIO graphics adapter is really the VirtIO GPU, so the virtualized
hardware has no distinction between both components so there's no
need to put such distinction in software.
We might need to split things in the future, but when we do so, we must
take proper care to ensure that the interface between the components
is correct and use the usual codebase patterns.
Diffstat (limited to 'Kernel/Graphics/VirtIOGPU')
-rw-r--r-- | Kernel/Graphics/VirtIOGPU/Console.h | 2 | ||||
-rw-r--r-- | Kernel/Graphics/VirtIOGPU/FramebufferDevice.cpp | 44 | ||||
-rw-r--r-- | Kernel/Graphics/VirtIOGPU/FramebufferDevice.h | 9 | ||||
-rw-r--r-- | Kernel/Graphics/VirtIOGPU/GPU.cpp | 303 | ||||
-rw-r--r-- | Kernel/Graphics/VirtIOGPU/GPU.h | 131 | ||||
-rw-r--r-- | Kernel/Graphics/VirtIOGPU/GraphicsAdapter.cpp | 304 | ||||
-rw-r--r-- | Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h | 104 | ||||
-rw-r--r-- | Kernel/Graphics/VirtIOGPU/Protocol.h | 5 |
8 files changed, 432 insertions, 470 deletions
diff --git a/Kernel/Graphics/VirtIOGPU/Console.h b/Kernel/Graphics/VirtIOGPU/Console.h index 16dfb2bac7..d046663205 100644 --- a/Kernel/Graphics/VirtIOGPU/Console.h +++ b/Kernel/Graphics/VirtIOGPU/Console.h @@ -7,7 +7,7 @@ #pragma once #include <Kernel/Graphics/Console/GenericFramebufferConsole.h> -#include <Kernel/Graphics/VirtIOGPU/GPU.h> +#include <Kernel/Graphics/VirtIOGPU/FramebufferDevice.h> #include <Kernel/TimerQueue.h> namespace Kernel::Graphics::VirtIOGPU { diff --git a/Kernel/Graphics/VirtIOGPU/FramebufferDevice.cpp b/Kernel/Graphics/VirtIOGPU/FramebufferDevice.cpp index a61a1678b3..6f11e06799 100644 --- a/Kernel/Graphics/VirtIOGPU/FramebufferDevice.cpp +++ b/Kernel/Graphics/VirtIOGPU/FramebufferDevice.cpp @@ -6,13 +6,23 @@ #include <Kernel/Graphics/GraphicsManagement.h> #include <Kernel/Graphics/VirtIOGPU/FramebufferDevice.h> +#include <Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h> #include <LibC/sys/ioctl_numbers.h> namespace Kernel::Graphics::VirtIOGPU { -FramebufferDevice::FramebufferDevice(GraphicsDevice const& adapter, GPU& virtio_gpu, ScanoutID scanout) +GraphicsAdapter const& FramebufferDevice::adapter() const +{ + return static_cast<GraphicsAdapter const&>(*m_graphics_adapter); +} + +GraphicsAdapter& FramebufferDevice::adapter() +{ + return static_cast<GraphicsAdapter&>(*m_graphics_adapter); +} + +FramebufferDevice::FramebufferDevice(GraphicsAdapter const& adapter, ScanoutID scanout) : Kernel::FramebufferDevice(adapter, scanout.value()) - , m_gpu(virtio_gpu) , m_scanout(scanout) { if (display_info().enabled) { @@ -45,7 +55,7 @@ KResult FramebufferDevice::create_framebuffer() } m_framebuffer_sink_vmobject = TRY(Memory::AnonymousVMObject::try_create_with_physical_pages(pages.span())); - MutexLocker locker(m_gpu.operation_lock()); + MutexLocker locker(adapter().operation_lock()); 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); @@ -62,14 +72,14 @@ void FramebufferDevice::create_buffer(Buffer& buffer, size_t framebuffer_offset, // 1. Create BUFFER using VIRTIO_GPU_CMD_RESOURCE_CREATE_2D if (buffer.resource_id.value() != 0) - m_gpu.delete_resource(buffer.resource_id); - buffer.resource_id = m_gpu.create_2d_resource(info.rect); + adapter().delete_resource(buffer.resource_id); + buffer.resource_id = adapter().create_2d_resource(info.rect); // 2. Attach backing storage using VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING - m_gpu.ensure_backing_storage(*m_framebuffer, buffer.framebuffer_offset, framebuffer_size, buffer.resource_id); + adapter().ensure_backing_storage(*m_framebuffer, buffer.framebuffer_offset, framebuffer_size, buffer.resource_id); // 3. Use VIRTIO_GPU_CMD_SET_SCANOUT to link the framebuffer to a display scanout. if (&buffer == m_current_buffer) - m_gpu.set_scanout_resource(m_scanout.value(), buffer.resource_id, info.rect); + adapter().set_scanout_resource(m_scanout.value(), buffer.resource_id, 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. @@ -91,27 +101,27 @@ void FramebufferDevice::create_buffer(Buffer& buffer, size_t framebuffer_offset, Protocol::DisplayInfoResponse::Display const& FramebufferDevice::display_info() const { - return m_gpu.display_info(m_scanout); + return adapter().display_info(m_scanout); } Protocol::DisplayInfoResponse::Display& FramebufferDevice::display_info() { - return m_gpu.display_info(m_scanout); + return adapter().display_info(m_scanout); } void FramebufferDevice::transfer_framebuffer_data_to_host(Protocol::Rect const& rect, Buffer& buffer) { - m_gpu.transfer_framebuffer_data_to_host(m_scanout, rect, buffer.resource_id); + adapter().transfer_framebuffer_data_to_host(m_scanout, rect, buffer.resource_id); } void FramebufferDevice::flush_dirty_window(Protocol::Rect const& dirty_rect, Buffer& buffer) { - m_gpu.flush_dirty_rectangle(m_scanout, dirty_rect, buffer.resource_id); + adapter().flush_dirty_rectangle(m_scanout, dirty_rect, buffer.resource_id); } void FramebufferDevice::flush_displayed_image(Protocol::Rect const& dirty_rect, Buffer& buffer) { - m_gpu.flush_displayed_image(dirty_rect, buffer.resource_id); + adapter().flush_displayed_image(dirty_rect, buffer.resource_id); } KResult FramebufferDevice::try_to_set_resolution(size_t width, size_t height) @@ -121,7 +131,7 @@ KResult FramebufferDevice::try_to_set_resolution(size_t width, size_t height) auto& info = display_info(); - MutexLocker locker(m_gpu.operation_lock()); + MutexLocker locker(adapter().operation_lock()); info.rect = { .x = 0, @@ -136,12 +146,12 @@ KResult FramebufferDevice::try_to_set_resolution(size_t width, size_t height) void FramebufferDevice::set_buffer(int buffer_index) { auto& buffer = buffer_index == 0 ? m_main_buffer : m_back_buffer; - MutexLocker locker(m_gpu.operation_lock()); + MutexLocker locker(adapter().operation_lock()); if (&buffer == m_current_buffer) return; m_current_buffer = &buffer; - m_gpu.set_scanout_resource(m_scanout.value(), buffer.resource_id, display_info().rect); - m_gpu.flush_displayed_image(buffer.dirty_rect, buffer.resource_id); // QEMU SDL backend requires this (as per spec) + adapter().set_scanout_resource(m_scanout.value(), buffer.resource_id, display_info().rect); + adapter().flush_displayed_image(buffer.dirty_rect, buffer.resource_id); // QEMU SDL backend requires this (as per spec) buffer.dirty_rect = {}; } @@ -188,7 +198,7 @@ KResult FramebufferDevice::ioctl(OpenFileDescription&, unsigned request, Userspa return EFAULT; if (m_are_writes_active && flush_rects.count > 0) { auto& buffer = buffer_from_index(flush_rects.buffer_index); - MutexLocker locker(m_gpu.operation_lock()); + MutexLocker locker(adapter().operation_lock()); for (unsigned i = 0; i < flush_rects.count; i++) { FBRect user_dirty_rect; TRY(copy_from_user(&user_dirty_rect, &flush_rects.rects[i])); diff --git a/Kernel/Graphics/VirtIOGPU/FramebufferDevice.h b/Kernel/Graphics/VirtIOGPU/FramebufferDevice.h index b4bf08ce81..fa23fc3917 100644 --- a/Kernel/Graphics/VirtIOGPU/FramebufferDevice.h +++ b/Kernel/Graphics/VirtIOGPU/FramebufferDevice.h @@ -10,10 +10,11 @@ #include <Kernel/Bus/VirtIO/Queue.h> #include <Kernel/Graphics/FramebufferDevice.h> #include <Kernel/Graphics/GraphicsDevice.h> -#include <Kernel/Graphics/VirtIOGPU/GPU.h> +#include <Kernel/Graphics/VirtIOGPU/Protocol.h> namespace Kernel::Graphics::VirtIOGPU { +class GraphicsAdapter; class FramebufferDevice final : public Kernel::FramebufferDevice { friend class Console; struct Buffer { @@ -24,7 +25,7 @@ class FramebufferDevice final : public Kernel::FramebufferDevice { }; public: - FramebufferDevice(GraphicsDevice const&, VirtIOGPU::GPU& virtio_gpu, ScanoutID); + FramebufferDevice(GraphicsAdapter const&, ScanoutID); virtual ~FramebufferDevice() override; virtual void deactivate_writes(); @@ -75,7 +76,9 @@ private: } Buffer& current_buffer() const { return *m_current_buffer; } - GPU& m_gpu; + GraphicsAdapter const& adapter() const; + GraphicsAdapter& adapter(); + const ScanoutID m_scanout; Buffer* m_current_buffer { nullptr }; Atomic<int, AK::memory_order_relaxed> m_last_set_buffer_index { 0 }; diff --git a/Kernel/Graphics/VirtIOGPU/GPU.cpp b/Kernel/Graphics/VirtIOGPU/GPU.cpp deleted file mode 100644 index a961ac1dcf..0000000000 --- a/Kernel/Graphics/VirtIOGPU/GPU.cpp +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Copyright (c) 2021, Sahan Fernando <sahan.h.fernando@gmail.com> - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include <AK/BinaryBufferWriter.h> -#include <Kernel/Graphics/VirtIOGPU/Console.h> -#include <Kernel/Graphics/VirtIOGPU/FramebufferDevice.h> -#include <Kernel/Graphics/VirtIOGPU/GPU.h> - -#define DEVICE_EVENTS_READ 0x0 -#define DEVICE_EVENTS_CLEAR 0x4 -#define DEVICE_NUM_SCANOUTS 0x8 - -namespace Kernel::Graphics::VirtIOGPU { - -void GPU::initialize() -{ - Device::initialize(); - VERIFY(!!m_scratch_space); - if (auto cfg = get_config(VirtIO::ConfigurationType::Device)) { - m_device_configuration = cfg; - bool success = negotiate_features([&](u64 supported_features) { - u64 negotiated = 0; - if (is_feature_set(supported_features, VIRTIO_GPU_F_VIRGL)) - dbgln_if(VIRTIO_DEBUG, "GPU: VIRGL is not yet supported!"); - if (is_feature_set(supported_features, VIRTIO_GPU_F_EDID)) - dbgln_if(VIRTIO_DEBUG, "GPU: EDID is not yet supported!"); - return negotiated; - }); - if (success) { - read_config_atomic([&]() { - m_num_scanouts = config_read32(*cfg, DEVICE_NUM_SCANOUTS); - }); - dbgln_if(VIRTIO_DEBUG, "GPU: num_scanouts: {}", m_num_scanouts); - success = setup_queues(2); // CONTROLQ + CURSORQ - } - VERIFY(success); - finish_init(); - MutexLocker locker(m_operation_lock); - // Get display information using VIRTIO_GPU_CMD_GET_DISPLAY_INFO - query_display_information(); - } else { - VERIFY_NOT_REACHED(); - } -} - -GPU::GPU(GraphicsDevice const& adapter, PCI::DeviceIdentifier const& device_identifier) - : VirtIO::Device(device_identifier) - , m_adapter(adapter) -{ - auto region_or_error = MM.allocate_contiguous_kernel_region(32 * PAGE_SIZE, "VirtGPU Scratch Space", Memory::Region::Access::ReadWrite); - if (region_or_error.is_error()) - TODO(); - m_scratch_space = region_or_error.release_value(); -} - -GPU::~GPU() -{ -} - -void GPU::create_framebuffer_devices() -{ - for (size_t i = 0; i < min(m_num_scanouts, VIRTIO_GPU_MAX_SCANOUTS); i++) { - auto& scanout = m_scanouts[i]; - scanout.framebuffer = adopt_ref(*new VirtIOGPU::FramebufferDevice(*m_adapter, *this, i)); - scanout.framebuffer->after_inserting(); - scanout.console = Kernel::Graphics::VirtIOGPU::Console::initialize(scanout.framebuffer); - } -} - -bool GPU::handle_device_config_change() -{ - auto events = get_pending_events(); - if (events & VIRTIO_GPU_EVENT_DISPLAY) { - // The host window was resized, in SerenityOS we completely ignore this event - dbgln_if(VIRTIO_DEBUG, "VirtIO::GPU: Ignoring virtio gpu display resize event"); - clear_pending_events(VIRTIO_GPU_EVENT_DISPLAY); - } - if (events & ~VIRTIO_GPU_EVENT_DISPLAY) { - dbgln("GPU: Got unknown device config change event: {:#x}", events); - return false; - } - return true; -} - -void GPU::handle_queue_update(u16 queue_index) -{ - dbgln_if(VIRTIO_DEBUG, "GPU: Handle queue update"); - VERIFY(queue_index == CONTROLQ); - - auto& queue = get_queue(CONTROLQ); - SpinlockLocker queue_lock(queue.lock()); - queue.discard_used_buffers(); - m_outstanding_request.wake_all(); -} - -u32 GPU::get_pending_events() -{ - return config_read32(*m_device_configuration, DEVICE_EVENTS_READ); -} - -void GPU::clear_pending_events(u32 event_bitmask) -{ - config_write32(*m_device_configuration, DEVICE_EVENTS_CLEAR, event_bitmask); -} - -void GPU::query_display_information() -{ - VERIFY(m_operation_lock.is_locked()); - auto writer = create_scratchspace_writer(); - auto& request = writer.append_structure<Protocol::ControlHeader>(); - populate_virtio_gpu_request_header(request, Protocol::CommandType::VIRTIO_GPU_CMD_GET_DISPLAY_INFO, VIRTIO_GPU_FLAG_FENCE); - auto& response = writer.append_structure<Protocol::DisplayInfoResponse>(); - - synchronous_virtio_gpu_command(start_of_scratch_space(), sizeof(request), sizeof(response)); - - for (size_t i = 0; i < VIRTIO_GPU_MAX_SCANOUTS; ++i) { - auto& scanout = m_scanouts[i].display_info; - scanout = response.scanout_modes[i]; - dbgln_if(VIRTIO_DEBUG, "GPU: Scanout {}: enabled: {} x: {}, y: {}, width: {}, height: {}", i, !!scanout.enabled, scanout.rect.x, scanout.rect.y, scanout.rect.width, scanout.rect.height); - if (scanout.enabled && !m_default_scanout.has_value()) - m_default_scanout = i; - } - VERIFY(m_default_scanout.has_value()); -} - -ResourceID GPU::create_2d_resource(Protocol::Rect rect) -{ - VERIFY(m_operation_lock.is_locked()); - auto writer = create_scratchspace_writer(); - auto& request = writer.append_structure<Protocol::ResourceCreate2D>(); - auto& response = writer.append_structure<Protocol::ControlHeader>(); - - populate_virtio_gpu_request_header(request.header, Protocol::CommandType::VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, VIRTIO_GPU_FLAG_FENCE); - - auto resource_id = allocate_resource_id(); - request.resource_id = resource_id.value(); - request.width = rect.width; - request.height = rect.height; - request.format = static_cast<u32>(Protocol::TextureFormat::VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM); - - synchronous_virtio_gpu_command(start_of_scratch_space(), sizeof(request), sizeof(response)); - - VERIFY(response.type == static_cast<u32>(Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)); - dbgln_if(VIRTIO_DEBUG, "GPU: Allocated 2d resource with id {}", resource_id.value()); - return resource_id; -} - -void GPU::ensure_backing_storage(Memory::Region const& region, size_t buffer_offset, size_t buffer_length, ResourceID resource_id) -{ - VERIFY(m_operation_lock.is_locked()); - - VERIFY(buffer_offset % PAGE_SIZE == 0); - VERIFY(buffer_length % PAGE_SIZE == 0); - auto first_page_index = buffer_offset / PAGE_SIZE; - size_t num_mem_regions = buffer_length / PAGE_SIZE; - - // Send request - auto writer = create_scratchspace_writer(); - auto& request = writer.append_structure<Protocol::ResourceAttachBacking>(); - const size_t header_block_size = sizeof(request) + num_mem_regions * sizeof(Protocol::MemoryEntry); - - populate_virtio_gpu_request_header(request.header, Protocol::CommandType::VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, VIRTIO_GPU_FLAG_FENCE); - request.resource_id = resource_id.value(); - request.num_entries = num_mem_regions; - for (size_t i = 0; i < num_mem_regions; ++i) { - auto& memory_entry = writer.append_structure<Protocol::MemoryEntry>(); - memory_entry.address = region.physical_page(first_page_index + i)->paddr().get(); - memory_entry.length = PAGE_SIZE; - } - - auto& response = writer.append_structure<Protocol::ControlHeader>(); - - synchronous_virtio_gpu_command(start_of_scratch_space(), header_block_size, sizeof(response)); - - VERIFY(response.type == static_cast<u32>(Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)); - dbgln_if(VIRTIO_DEBUG, "GPU: Allocated backing storage"); -} - -void GPU::detach_backing_storage(ResourceID resource_id) -{ - VERIFY(m_operation_lock.is_locked()); - auto writer = create_scratchspace_writer(); - auto& request = writer.append_structure<Protocol::ResourceDetachBacking>(); - auto& response = writer.append_structure<Protocol::ControlHeader>(); - - populate_virtio_gpu_request_header(request.header, Protocol::CommandType::VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, VIRTIO_GPU_FLAG_FENCE); - request.resource_id = resource_id.value(); - - synchronous_virtio_gpu_command(start_of_scratch_space(), sizeof(request), sizeof(response)); - - VERIFY(response.type == static_cast<u32>(Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)); - dbgln_if(VIRTIO_DEBUG, "GPU: Detached backing storage"); -} - -void GPU::set_scanout_resource(ScanoutID scanout, ResourceID resource_id, Protocol::Rect rect) -{ - VERIFY(m_operation_lock.is_locked()); - auto writer = create_scratchspace_writer(); - auto& request = writer.append_structure<Protocol::SetScanOut>(); - auto& response = writer.append_structure<Protocol::ControlHeader>(); - - populate_virtio_gpu_request_header(request.header, Protocol::CommandType::VIRTIO_GPU_CMD_SET_SCANOUT, VIRTIO_GPU_FLAG_FENCE); - request.resource_id = resource_id.value(); - request.scanout_id = scanout.value(); - request.rect = rect; - - synchronous_virtio_gpu_command(start_of_scratch_space(), sizeof(request), sizeof(response)); - - VERIFY(response.type == static_cast<u32>(Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)); - dbgln_if(VIRTIO_DEBUG, "GPU: Set backing scanout"); -} - -void GPU::transfer_framebuffer_data_to_host(ScanoutID scanout, Protocol::Rect const& dirty_rect, ResourceID resource_id) -{ - VERIFY(m_operation_lock.is_locked()); - auto writer = create_scratchspace_writer(); - auto& request = writer.append_structure<Protocol::TransferToHost2D>(); - auto& response = writer.append_structure<Protocol::ControlHeader>(); - - populate_virtio_gpu_request_header(request.header, Protocol::CommandType::VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, VIRTIO_GPU_FLAG_FENCE); - request.offset = (dirty_rect.x + (dirty_rect.y * m_scanouts[scanout.value()].display_info.rect.width)) * sizeof(u32); - request.resource_id = resource_id.value(); - request.rect = dirty_rect; - - synchronous_virtio_gpu_command(start_of_scratch_space(), sizeof(request), sizeof(response)); - - VERIFY(response.type == static_cast<u32>(Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)); -} - -void GPU::flush_displayed_image(Protocol::Rect const& dirty_rect, ResourceID resource_id) -{ - VERIFY(m_operation_lock.is_locked()); - auto writer = create_scratchspace_writer(); - auto& request = writer.append_structure<Protocol::ResourceFlush>(); - auto& response = writer.append_structure<Protocol::ControlHeader>(); - - populate_virtio_gpu_request_header(request.header, Protocol::CommandType::VIRTIO_GPU_CMD_RESOURCE_FLUSH, VIRTIO_GPU_FLAG_FENCE); - request.resource_id = resource_id.value(); - request.rect = dirty_rect; - - synchronous_virtio_gpu_command(start_of_scratch_space(), sizeof(request), sizeof(response)); - - VERIFY(response.type == static_cast<u32>(Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)); -} - -void GPU::synchronous_virtio_gpu_command(PhysicalAddress buffer_start, size_t request_size, size_t response_size) -{ - VERIFY(m_operation_lock.is_locked()); - VERIFY(m_outstanding_request.is_empty()); - auto& queue = get_queue(CONTROLQ); - { - SpinlockLocker lock(queue.lock()); - VirtIO::QueueChain chain { queue }; - chain.add_buffer_to_chain(buffer_start, request_size, VirtIO::BufferType::DeviceReadable); - chain.add_buffer_to_chain(buffer_start.offset(request_size), response_size, VirtIO::BufferType::DeviceWritable); - supply_chain_and_notify(CONTROLQ, chain); - full_memory_barrier(); - } - m_outstanding_request.wait_forever(); -} - -void GPU::populate_virtio_gpu_request_header(Protocol::ControlHeader& header, Protocol::CommandType ctrl_type, u32 flags) -{ - header.type = static_cast<u32>(ctrl_type); - header.flags = flags; - header.fence_id = 0; - header.context_id = 0; - header.padding = 0; -} - -void GPU::flush_dirty_rectangle(ScanoutID scanout_id, Protocol::Rect const& dirty_rect, ResourceID resource_id) -{ - MutexLocker locker(m_operation_lock); - transfer_framebuffer_data_to_host(scanout_id, dirty_rect, resource_id); - flush_displayed_image(dirty_rect, resource_id); -} - -ResourceID GPU::allocate_resource_id() -{ - VERIFY(m_operation_lock.is_locked()); - m_resource_id_counter = m_resource_id_counter.value() + 1; - return m_resource_id_counter; -} - -void GPU::delete_resource(ResourceID resource_id) -{ - VERIFY(m_operation_lock.is_locked()); - auto writer = create_scratchspace_writer(); - auto& request = writer.append_structure<Protocol::ResourceUnref>(); - auto& response = writer.append_structure<Protocol::ControlHeader>(); - - populate_virtio_gpu_request_header(request.header, Protocol::CommandType::VIRTIO_GPU_CMD_RESOURCE_UNREF, VIRTIO_GPU_FLAG_FENCE); - request.resource_id = resource_id.value(); - - synchronous_virtio_gpu_command(start_of_scratch_space(), sizeof(request), sizeof(response)); - - VERIFY(response.type == static_cast<u32>(Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)); -} - -} diff --git a/Kernel/Graphics/VirtIOGPU/GPU.h b/Kernel/Graphics/VirtIOGPU/GPU.h deleted file mode 100644 index 533ad5a66d..0000000000 --- a/Kernel/Graphics/VirtIOGPU/GPU.h +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (c) 2021, Sahan Fernando <sahan.h.fernando@gmail.com> - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include <AK/BinaryBufferWriter.h> -#include <AK/DistinctNumeric.h> -#include <Kernel/Bus/VirtIO/Device.h> -#include <Kernel/Bus/VirtIO/Queue.h> -#include <Kernel/Devices/BlockDevice.h> -#include <Kernel/Graphics/GraphicsDevice.h> -#include <Kernel/Graphics/VirtIOGPU/Protocol.h> - -#define VIRTIO_GPU_F_VIRGL (1 << 0) -#define VIRTIO_GPU_F_EDID (1 << 1) - -#define VIRTIO_GPU_FLAG_FENCE (1 << 0) - -#define CONTROLQ 0 -#define CURSORQ 1 - -#define MAX_VIRTIOGPU_RESOLUTION_WIDTH 3840 -#define MAX_VIRTIOGPU_RESOLUTION_HEIGHT 2160 - -#define VIRTIO_GPU_EVENT_DISPLAY (1 << 0) - -namespace Kernel::Graphics::VirtIOGPU { - -class Console; -class FramebufferDevice; - -TYPEDEF_DISTINCT_ORDERED_ID(u32, ResourceID); -TYPEDEF_DISTINCT_ORDERED_ID(u32, ScanoutID); - -class GPU final - : public VirtIO::Device - , public RefCounted<GPU> { - friend class FramebufferDevice; - -public: - GPU(GraphicsDevice const&, PCI::DeviceIdentifier const&); - virtual ~GPU() override; - - void create_framebuffer_devices(); - template<typename F> - IterationDecision for_each_framebuffer(F f) - { - for (auto& scanout : m_scanouts) { - if (!scanout.framebuffer) - continue; - IterationDecision decision = f(*scanout.framebuffer, *scanout.console); - if (decision != IterationDecision::Continue) - return decision; - } - return IterationDecision::Continue; - } - - virtual void initialize() override; - - RefPtr<Console> default_console() - { - if (m_default_scanout.has_value()) - return m_scanouts[m_default_scanout.value().value()].console; - return {}; - } - auto& display_info(ScanoutID scanout) const - { - VERIFY(scanout.value() < VIRTIO_GPU_MAX_SCANOUTS); - return m_scanouts[scanout.value()].display_info; - } - auto& display_info(ScanoutID scanout) - { - VERIFY(scanout.value() < VIRTIO_GPU_MAX_SCANOUTS); - return m_scanouts[scanout.value()].display_info; - } - - void flush_dirty_rectangle(ScanoutID, Protocol::Rect const& dirty_rect, ResourceID); - -private: - virtual StringView class_name() const override { return "VirtIOGPU"sv; } - - struct Scanout { - RefPtr<FramebufferDevice> framebuffer; - RefPtr<Console> console; - Protocol::DisplayInfoResponse::Display display_info {}; - }; - - virtual bool handle_device_config_change() override; - virtual void handle_queue_update(u16 queue_index) override; - u32 get_pending_events(); - void clear_pending_events(u32 event_bitmask); - - auto& operation_lock() { return m_operation_lock; } - ResourceID allocate_resource_id(); - - PhysicalAddress start_of_scratch_space() const { return m_scratch_space->physical_page(0)->paddr(); } - AK::BinaryBufferWriter create_scratchspace_writer() - { - return { Bytes(m_scratch_space->vaddr().as_ptr(), m_scratch_space->size()) }; - } - void synchronous_virtio_gpu_command(PhysicalAddress buffer_start, size_t request_size, size_t response_size); - void populate_virtio_gpu_request_header(Protocol::ControlHeader& header, Protocol::CommandType ctrl_type, u32 flags = 0); - - void query_display_information(); - ResourceID create_2d_resource(Protocol::Rect rect); - void delete_resource(ResourceID resource_id); - void ensure_backing_storage(Memory::Region const& region, size_t buffer_offset, size_t buffer_length, ResourceID resource_id); - void detach_backing_storage(ResourceID resource_id); - void set_scanout_resource(ScanoutID scanout, ResourceID resource_id, Protocol::Rect rect); - void transfer_framebuffer_data_to_host(ScanoutID scanout, Protocol::Rect const& rect, ResourceID resource_id); - void flush_displayed_image(Protocol::Rect const& dirty_rect, ResourceID resource_id); - - Optional<ScanoutID> m_default_scanout; - size_t m_num_scanouts { 0 }; - Scanout m_scanouts[VIRTIO_GPU_MAX_SCANOUTS]; - - VirtIO::Configuration const* m_device_configuration { nullptr }; - ResourceID m_resource_id_counter { 0 }; - - NonnullRefPtr<GraphicsDevice> m_adapter; - - // Synchronous commands - WaitQueue m_outstanding_request; - Mutex m_operation_lock; - OwnPtr<Memory::Region> m_scratch_space; -}; - -} diff --git a/Kernel/Graphics/VirtIOGPU/GraphicsAdapter.cpp b/Kernel/Graphics/VirtIOGPU/GraphicsAdapter.cpp index 4043f81156..22f801cc43 100644 --- a/Kernel/Graphics/VirtIOGPU/GraphicsAdapter.cpp +++ b/Kernel/Graphics/VirtIOGPU/GraphicsAdapter.cpp @@ -4,43 +4,53 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include <AK/BinaryBufferWriter.h> #include <Kernel/Bus/PCI/API.h> #include <Kernel/Bus/PCI/IDs.h> #include <Kernel/Graphics/Console/GenericFramebufferConsole.h> #include <Kernel/Graphics/GraphicsManagement.h> -#include <Kernel/Graphics/VirtIOGPU/GPU.h> +#include <Kernel/Graphics/VirtIOGPU/Console.h> +#include <Kernel/Graphics/VirtIOGPU/FramebufferDevice.h> #include <Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h> namespace Kernel::Graphics::VirtIOGPU { +#define DEVICE_EVENTS_READ 0x0 +#define DEVICE_EVENTS_CLEAR 0x4 +#define DEVICE_NUM_SCANOUTS 0x8 + NonnullRefPtr<GraphicsAdapter> GraphicsAdapter::initialize(PCI::DeviceIdentifier const& device_identifier) { VERIFY(device_identifier.hardware_id().vendor_id == PCI::VendorID::VirtIO); - return adopt_ref(*new GraphicsAdapter(device_identifier)); + auto adapter = adopt_ref(*new GraphicsAdapter(device_identifier)); + adapter->initialize(); + return adapter; } GraphicsAdapter::GraphicsAdapter(PCI::DeviceIdentifier const& device_identifier) - : PCI::Device(device_identifier.address()) + : VirtIO::Device(device_identifier) { - m_gpu_device = adopt_ref(*new GPU(*this, device_identifier)).leak_ref(); - m_gpu_device->initialize(); + auto region_or_error = MM.allocate_contiguous_kernel_region(32 * PAGE_SIZE, "VirtGPU Scratch Space", Memory::Region::Access::ReadWrite); + if (region_or_error.is_error()) + TODO(); + m_scratch_space = region_or_error.release_value(); } void GraphicsAdapter::initialize_framebuffer_devices() { - dbgln_if(VIRTIO_DEBUG, "GPU: Initializing framebuffer devices"); + dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Initializing framebuffer devices"); VERIFY(!m_created_framebuffer_devices); - m_gpu_device->create_framebuffer_devices(); + create_framebuffer_devices(); m_created_framebuffer_devices = true; // FIXME: This is a very wrong way to do this... - GraphicsManagement::the().m_console = m_gpu_device->default_console(); + GraphicsManagement::the().m_console = default_console(); } void GraphicsAdapter::enable_consoles() { - dbgln_if(VIRTIO_DEBUG, "GPU: Enabling consoles"); - m_gpu_device->for_each_framebuffer([&](auto& framebuffer, auto& console) { + dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Enabling consoles"); + for_each_framebuffer([&](auto& framebuffer, auto& console) { framebuffer.deactivate_writes(); console.enable(); return IterationDecision::Continue; @@ -49,12 +59,282 @@ void GraphicsAdapter::enable_consoles() void GraphicsAdapter::disable_consoles() { - dbgln_if(VIRTIO_DEBUG, "GPU: Disabling consoles"); - m_gpu_device->for_each_framebuffer([&](auto& framebuffer, auto& console) { + dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Disabling consoles"); + for_each_framebuffer([&](auto& framebuffer, auto& console) { console.disable(); framebuffer.activate_writes(); return IterationDecision::Continue; }); } +void GraphicsAdapter::initialize() +{ + Device::initialize(); + VERIFY(!!m_scratch_space); + if (auto* config = get_config(VirtIO::ConfigurationType::Device)) { + m_device_configuration = config; + bool success = negotiate_features([&](u64 supported_features) { + u64 negotiated = 0; + if (is_feature_set(supported_features, VIRTIO_GPU_F_VIRGL)) + dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: VIRGL is not yet supported!"); + if (is_feature_set(supported_features, VIRTIO_GPU_F_EDID)) + dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: EDID is not yet supported!"); + return negotiated; + }); + if (success) { + read_config_atomic([&]() { + m_num_scanouts = config_read32(*config, DEVICE_NUM_SCANOUTS); + }); + dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: num_scanouts: {}", m_num_scanouts); + success = setup_queues(2); // CONTROLQ + CURSORQ + } + VERIFY(success); + finish_init(); + MutexLocker locker(m_operation_lock); + // Get display information using VIRTIO_GPU_CMD_GET_DISPLAY_INFO + query_display_information(); + } else { + VERIFY_NOT_REACHED(); + } +} + +void GraphicsAdapter::create_framebuffer_devices() +{ + for (size_t i = 0; i < min(m_num_scanouts, VIRTIO_GPU_MAX_SCANOUTS); i++) { + auto& scanout = m_scanouts[i]; + scanout.framebuffer = adopt_ref(*new VirtIOGPU::FramebufferDevice(*this, i)); + scanout.framebuffer->after_inserting(); + scanout.console = Kernel::Graphics::VirtIOGPU::Console::initialize(scanout.framebuffer); + } +} + +bool GraphicsAdapter::handle_device_config_change() +{ + auto events = get_pending_events(); + if (events & VIRTIO_GPU_EVENT_DISPLAY) { + // The host window was resized, in SerenityOS we completely ignore this event + dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Ignoring virtio gpu display resize event"); + clear_pending_events(VIRTIO_GPU_EVENT_DISPLAY); + } + if (events & ~VIRTIO_GPU_EVENT_DISPLAY) { + dbgln("VirtIO::GraphicsAdapter: Got unknown device config change event: {:#x}", events); + return false; + } + return true; +} + +void GraphicsAdapter::handle_queue_update(u16 queue_index) +{ + dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Handle queue update"); + VERIFY(queue_index == CONTROLQ); + + auto& queue = get_queue(CONTROLQ); + SpinlockLocker queue_lock(queue.lock()); + queue.discard_used_buffers(); + m_outstanding_request.wake_all(); +} + +u32 GraphicsAdapter::get_pending_events() +{ + return config_read32(*m_device_configuration, DEVICE_EVENTS_READ); +} + +void GraphicsAdapter::clear_pending_events(u32 event_bitmask) +{ + config_write32(*m_device_configuration, DEVICE_EVENTS_CLEAR, event_bitmask); +} + +void GraphicsAdapter::query_display_information() +{ + VERIFY(m_operation_lock.is_locked()); + auto writer = create_scratchspace_writer(); + auto& request = writer.append_structure<Protocol::ControlHeader>(); + populate_virtio_gpu_request_header(request, Protocol::CommandType::VIRTIO_GPU_CMD_GET_DISPLAY_INFO, VIRTIO_GPU_FLAG_FENCE); + auto& response = writer.append_structure<Protocol::DisplayInfoResponse>(); + + synchronous_virtio_gpu_command(start_of_scratch_space(), sizeof(request), sizeof(response)); + + for (size_t i = 0; i < VIRTIO_GPU_MAX_SCANOUTS; ++i) { + auto& scanout = m_scanouts[i].display_info; + scanout = response.scanout_modes[i]; + dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Scanout {}: enabled: {} x: {}, y: {}, width: {}, height: {}", i, !!scanout.enabled, scanout.rect.x, scanout.rect.y, scanout.rect.width, scanout.rect.height); + if (scanout.enabled && !m_default_scanout.has_value()) + m_default_scanout = i; + } + VERIFY(m_default_scanout.has_value()); +} + +ResourceID GraphicsAdapter::create_2d_resource(Protocol::Rect rect) +{ + VERIFY(m_operation_lock.is_locked()); + auto writer = create_scratchspace_writer(); + auto& request = writer.append_structure<Protocol::ResourceCreate2D>(); + auto& response = writer.append_structure<Protocol::ControlHeader>(); + + populate_virtio_gpu_request_header(request.header, Protocol::CommandType::VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, VIRTIO_GPU_FLAG_FENCE); + + auto resource_id = allocate_resource_id(); + request.resource_id = resource_id.value(); + request.width = rect.width; + request.height = rect.height; + request.format = static_cast<u32>(Protocol::TextureFormat::VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM); + + synchronous_virtio_gpu_command(start_of_scratch_space(), sizeof(request), sizeof(response)); + + VERIFY(response.type == static_cast<u32>(Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)); + dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Allocated 2d resource with id {}", resource_id.value()); + return resource_id; +} + +void GraphicsAdapter::ensure_backing_storage(Memory::Region const& region, size_t buffer_offset, size_t buffer_length, ResourceID resource_id) +{ + VERIFY(m_operation_lock.is_locked()); + + VERIFY(buffer_offset % PAGE_SIZE == 0); + VERIFY(buffer_length % PAGE_SIZE == 0); + auto first_page_index = buffer_offset / PAGE_SIZE; + size_t num_mem_regions = buffer_length / PAGE_SIZE; + + auto writer = create_scratchspace_writer(); + auto& request = writer.append_structure<Protocol::ResourceAttachBacking>(); + const size_t header_block_size = sizeof(request) + num_mem_regions * sizeof(Protocol::MemoryEntry); + + populate_virtio_gpu_request_header(request.header, Protocol::CommandType::VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, VIRTIO_GPU_FLAG_FENCE); + request.resource_id = resource_id.value(); + request.num_entries = num_mem_regions; + for (size_t i = 0; i < num_mem_regions; ++i) { + auto& memory_entry = writer.append_structure<Protocol::MemoryEntry>(); + memory_entry.address = region.physical_page(first_page_index + i)->paddr().get(); + memory_entry.length = PAGE_SIZE; + } + + auto& response = writer.append_structure<Protocol::ControlHeader>(); + + synchronous_virtio_gpu_command(start_of_scratch_space(), header_block_size, sizeof(response)); + + VERIFY(response.type == static_cast<u32>(Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)); + dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Allocated backing storage"); +} + +void GraphicsAdapter::detach_backing_storage(ResourceID resource_id) +{ + VERIFY(m_operation_lock.is_locked()); + auto writer = create_scratchspace_writer(); + auto& request = writer.append_structure<Protocol::ResourceDetachBacking>(); + auto& response = writer.append_structure<Protocol::ControlHeader>(); + + populate_virtio_gpu_request_header(request.header, Protocol::CommandType::VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, VIRTIO_GPU_FLAG_FENCE); + request.resource_id = resource_id.value(); + + synchronous_virtio_gpu_command(start_of_scratch_space(), sizeof(request), sizeof(response)); + + VERIFY(response.type == static_cast<u32>(Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)); + dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Detached backing storage"); +} + +void GraphicsAdapter::set_scanout_resource(ScanoutID scanout, ResourceID resource_id, Protocol::Rect rect) +{ + VERIFY(m_operation_lock.is_locked()); + auto writer = create_scratchspace_writer(); + auto& request = writer.append_structure<Protocol::SetScanOut>(); + auto& response = writer.append_structure<Protocol::ControlHeader>(); + + populate_virtio_gpu_request_header(request.header, Protocol::CommandType::VIRTIO_GPU_CMD_SET_SCANOUT, VIRTIO_GPU_FLAG_FENCE); + request.resource_id = resource_id.value(); + request.scanout_id = scanout.value(); + request.rect = rect; + + synchronous_virtio_gpu_command(start_of_scratch_space(), sizeof(request), sizeof(response)); + + VERIFY(response.type == static_cast<u32>(Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)); + dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Set backing scanout"); +} + +void GraphicsAdapter::transfer_framebuffer_data_to_host(ScanoutID scanout, Protocol::Rect const& dirty_rect, ResourceID resource_id) +{ + VERIFY(m_operation_lock.is_locked()); + auto writer = create_scratchspace_writer(); + auto& request = writer.append_structure<Protocol::TransferToHost2D>(); + auto& response = writer.append_structure<Protocol::ControlHeader>(); + + populate_virtio_gpu_request_header(request.header, Protocol::CommandType::VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, VIRTIO_GPU_FLAG_FENCE); + request.offset = (dirty_rect.x + (dirty_rect.y * m_scanouts[scanout.value()].display_info.rect.width)) * sizeof(u32); + request.resource_id = resource_id.value(); + request.rect = dirty_rect; + + synchronous_virtio_gpu_command(start_of_scratch_space(), sizeof(request), sizeof(response)); + + VERIFY(response.type == static_cast<u32>(Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)); +} + +void GraphicsAdapter::flush_displayed_image(Protocol::Rect const& dirty_rect, ResourceID resource_id) +{ + VERIFY(m_operation_lock.is_locked()); + auto writer = create_scratchspace_writer(); + auto& request = writer.append_structure<Protocol::ResourceFlush>(); + auto& response = writer.append_structure<Protocol::ControlHeader>(); + + populate_virtio_gpu_request_header(request.header, Protocol::CommandType::VIRTIO_GPU_CMD_RESOURCE_FLUSH, VIRTIO_GPU_FLAG_FENCE); + request.resource_id = resource_id.value(); + request.rect = dirty_rect; + + synchronous_virtio_gpu_command(start_of_scratch_space(), sizeof(request), sizeof(response)); + + VERIFY(response.type == static_cast<u32>(Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)); +} + +void GraphicsAdapter::synchronous_virtio_gpu_command(PhysicalAddress buffer_start, size_t request_size, size_t response_size) +{ + VERIFY(m_operation_lock.is_locked()); + VERIFY(m_outstanding_request.is_empty()); + auto& queue = get_queue(CONTROLQ); + { + SpinlockLocker lock(queue.lock()); + VirtIO::QueueChain chain { queue }; + chain.add_buffer_to_chain(buffer_start, request_size, VirtIO::BufferType::DeviceReadable); + chain.add_buffer_to_chain(buffer_start.offset(request_size), response_size, VirtIO::BufferType::DeviceWritable); + supply_chain_and_notify(CONTROLQ, chain); + full_memory_barrier(); + } + m_outstanding_request.wait_forever(); +} + +void GraphicsAdapter::populate_virtio_gpu_request_header(Protocol::ControlHeader& header, Protocol::CommandType ctrl_type, u32 flags) +{ + header.type = static_cast<u32>(ctrl_type); + header.flags = flags; + header.fence_id = 0; + header.context_id = 0; + header.padding = 0; +} + +void GraphicsAdapter::flush_dirty_rectangle(ScanoutID scanout_id, Protocol::Rect const& dirty_rect, ResourceID resource_id) +{ + MutexLocker locker(m_operation_lock); + transfer_framebuffer_data_to_host(scanout_id, dirty_rect, resource_id); + flush_displayed_image(dirty_rect, resource_id); +} + +ResourceID GraphicsAdapter::allocate_resource_id() +{ + VERIFY(m_operation_lock.is_locked()); + m_resource_id_counter = m_resource_id_counter.value() + 1; + return m_resource_id_counter; +} + +void GraphicsAdapter::delete_resource(ResourceID resource_id) +{ + VERIFY(m_operation_lock.is_locked()); + auto writer = create_scratchspace_writer(); + auto& request = writer.append_structure<Protocol::ResourceUnref>(); + auto& response = writer.append_structure<Protocol::ControlHeader>(); + + populate_virtio_gpu_request_header(request.header, Protocol::CommandType::VIRTIO_GPU_CMD_RESOURCE_UNREF, VIRTIO_GPU_FLAG_FENCE); + request.resource_id = resource_id.value(); + + synchronous_virtio_gpu_command(start_of_scratch_space(), sizeof(request), sizeof(response)); + + VERIFY(response.type == static_cast<u32>(Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)); +} + } diff --git a/Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h b/Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h index 69d3f35230..9ffc881595 100644 --- a/Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h +++ b/Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h @@ -6,16 +6,37 @@ #pragma once +#include <AK/BinaryBufferWriter.h> +#include <AK/DistinctNumeric.h> +#include <Kernel/Bus/VirtIO/Device.h> +#include <Kernel/Bus/VirtIO/Queue.h> +#include <Kernel/Devices/BlockDevice.h> +#include <Kernel/Graphics/GraphicsDevice.h> #include <Kernel/Graphics/VirtIOGPU/Console.h> #include <Kernel/Graphics/VirtIOGPU/FramebufferDevice.h> -#include <Kernel/Graphics/VirtIOGPU/GPU.h> +#include <Kernel/Graphics/VirtIOGPU/Protocol.h> namespace Kernel::Graphics::VirtIOGPU { +#define VIRTIO_GPU_F_VIRGL (1 << 0) +#define VIRTIO_GPU_F_EDID (1 << 1) + +#define VIRTIO_GPU_FLAG_FENCE (1 << 0) + +#define CONTROLQ 0 +#define CURSORQ 1 + +#define MAX_VIRTIOGPU_RESOLUTION_WIDTH 3840 +#define MAX_VIRTIOGPU_RESOLUTION_HEIGHT 2160 + +#define VIRTIO_GPU_EVENT_DISPLAY (1 << 0) + +class FramebufferDevice; class GraphicsAdapter final : public GraphicsDevice - , public PCI::Device { + , public VirtIO::Device { AK_MAKE_ETERNAL + friend class FramebufferDevice; public: static NonnullRefPtr<GraphicsAdapter> initialize(PCI::DeviceIdentifier const&); @@ -25,9 +46,45 @@ public: // FIXME: There's a VirtIO VGA GPU variant, so we should consider that virtual bool vga_compatible() const override { return false; } + virtual void initialize() override; + private: + void flush_dirty_rectangle(ScanoutID, Protocol::Rect const& dirty_rect, ResourceID); + + template<typename F> + IterationDecision for_each_framebuffer(F f) + { + for (auto& scanout : m_scanouts) { + if (!scanout.framebuffer) + continue; + IterationDecision decision = f(*scanout.framebuffer, *scanout.console); + if (decision != IterationDecision::Continue) + return decision; + } + return IterationDecision::Continue; + } + + RefPtr<Console> default_console() + { + if (m_default_scanout.has_value()) + return m_scanouts[m_default_scanout.value().value()].console; + return {}; + } + auto& display_info(ScanoutID scanout) const + { + VERIFY(scanout.value() < VIRTIO_GPU_MAX_SCANOUTS); + return m_scanouts[scanout.value()].display_info; + } + auto& display_info(ScanoutID scanout) + { + VERIFY(scanout.value() < VIRTIO_GPU_MAX_SCANOUTS); + return m_scanouts[scanout.value()].display_info; + } + explicit GraphicsAdapter(PCI::DeviceIdentifier const&); + void create_framebuffer_devices(); + virtual void initialize_framebuffer_devices() override; virtual void enable_consoles() override; virtual void disable_consoles() override; @@ -38,7 +95,48 @@ private: virtual bool try_to_set_resolution(size_t, size_t, size_t) override { return false; } virtual bool set_y_offset(size_t, size_t) override { return false; } - RefPtr<GPU> m_gpu_device; + struct Scanout { + RefPtr<Graphics::VirtIOGPU::FramebufferDevice> framebuffer; + RefPtr<Console> console; + Protocol::DisplayInfoResponse::Display display_info {}; + }; + + virtual bool handle_device_config_change() override; + virtual void handle_queue_update(u16 queue_index) override; + u32 get_pending_events(); + void clear_pending_events(u32 event_bitmask); + + auto& operation_lock() { return m_operation_lock; } + ResourceID allocate_resource_id(); + + PhysicalAddress start_of_scratch_space() const { return m_scratch_space->physical_page(0)->paddr(); } + AK::BinaryBufferWriter create_scratchspace_writer() + { + return { Bytes(m_scratch_space->vaddr().as_ptr(), m_scratch_space->size()) }; + } + void synchronous_virtio_gpu_command(PhysicalAddress buffer_start, size_t request_size, size_t response_size); + void populate_virtio_gpu_request_header(Protocol::ControlHeader& header, Protocol::CommandType ctrl_type, u32 flags = 0); + + void query_display_information(); + ResourceID create_2d_resource(Protocol::Rect rect); + void delete_resource(ResourceID resource_id); + void ensure_backing_storage(Memory::Region const& region, size_t buffer_offset, size_t buffer_length, ResourceID resource_id); + void detach_backing_storage(ResourceID resource_id); + void set_scanout_resource(ScanoutID scanout, ResourceID resource_id, Protocol::Rect rect); + void transfer_framebuffer_data_to_host(ScanoutID scanout, Protocol::Rect const& rect, ResourceID resource_id); + void flush_displayed_image(Protocol::Rect const& dirty_rect, ResourceID resource_id); + bool m_created_framebuffer_devices { false }; + Optional<ScanoutID> m_default_scanout; + size_t m_num_scanouts { 0 }; + Scanout m_scanouts[VIRTIO_GPU_MAX_SCANOUTS]; + + VirtIO::Configuration const* m_device_configuration { nullptr }; + ResourceID m_resource_id_counter { 0 }; + + // Synchronous commands + WaitQueue m_outstanding_request; + Mutex m_operation_lock; + OwnPtr<Memory::Region> m_scratch_space; }; } diff --git a/Kernel/Graphics/VirtIOGPU/Protocol.h b/Kernel/Graphics/VirtIOGPU/Protocol.h index 98eae39dce..c715084dd5 100644 --- a/Kernel/Graphics/VirtIOGPU/Protocol.h +++ b/Kernel/Graphics/VirtIOGPU/Protocol.h @@ -10,6 +10,11 @@ #define VIRTIO_GPU_MAX_SCANOUTS 16 +namespace Kernel::Graphics::VirtIOGPU { +TYPEDEF_DISTINCT_ORDERED_ID(u32, ResourceID); +TYPEDEF_DISTINCT_ORDERED_ID(u32, ScanoutID); +}; + namespace Kernel::Graphics::VirtIOGPU::Protocol { // Specification equivalent: enum virtio_gpu_ctrl_type |