mirror of
https://github.com/RGBCube/serenity
synced 2025-07-13 16:57:36 +00:00

This happens to be a sad truth for the VirtIOGPU driver - it lacked any error propagation measures and generally relied on clunky assumptions that most operations with the GPU device are infallible, although in reality much of them could fail, so we do need to handle errors. To fix this, synchronous GPU commands no longer rely on the wait queue mechanism anymore, so instead we introduce a timeout-based mechanism, similar to how other Kernel drivers use a polling based mechanism with the assumption that hardware could get stuck in an error state and we could abort gracefully. Then, we change most of the VirtIOGraphicsAdapter methods to propagate errors properly to the original callers, to ensure that if a synchronous GPU command failed, either the Kernel or userspace could do something meaningful about this situation.
122 lines
5.7 KiB
C++
122 lines
5.7 KiB
C++
/*
|
|
* 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/Graphics/GenericGraphicsAdapter.h>
|
|
#include <Kernel/Graphics/VirtIOGPU/Protocol.h>
|
|
|
|
namespace Kernel {
|
|
|
|
#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 VirtIODisplayConnector;
|
|
class VirtIOGPU3DDevice;
|
|
class VirtIOGraphicsAdapter final
|
|
: public GenericGraphicsAdapter
|
|
, public VirtIO::Device {
|
|
friend class VirtIODisplayConnector;
|
|
friend class VirtIOGPU3DDevice;
|
|
|
|
public:
|
|
static NonnullLockRefPtr<VirtIOGraphicsAdapter> initialize(PCI::DeviceIdentifier const&);
|
|
|
|
virtual void initialize() override;
|
|
|
|
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);
|
|
ErrorOr<void> flush_displayed_image(Badge<VirtIODisplayConnector>, VirtIODisplayConnector&, Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect, bool main_buffer);
|
|
ErrorOr<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);
|
|
|
|
ErrorOr<void> initialize_3d_device();
|
|
|
|
ErrorOr<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 };
|
|
};
|
|
|
|
LockRefPtr<VirtIODisplayConnector> display_connector;
|
|
PhysicalBuffer main_buffer;
|
|
PhysicalBuffer back_buffer;
|
|
};
|
|
|
|
VirtIOGraphicsAdapter(PCI::DeviceIdentifier const&, Bitmap&& active_context_ids, NonnullOwnPtr<Memory::Region> scratch_space_region);
|
|
|
|
ErrorOr<void> initialize_adapter();
|
|
|
|
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);
|
|
|
|
// 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
|
|
ErrorOr<Graphics::VirtIOGPU::ContextID> create_context();
|
|
ErrorOr<void> attach_resource_to_context(Graphics::VirtIOGPU::ResourceID resource_id, Graphics::VirtIOGPU::ContextID context_id);
|
|
ErrorOr<void> submit_command_buffer(Graphics::VirtIOGPU::ContextID, Function<size_t(Bytes)> buffer_writer);
|
|
Graphics::VirtIOGPU::Protocol::TextureFormat get_framebuffer_format() const { return Graphics::VirtIOGPU::Protocol::TextureFormat::VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM; }
|
|
|
|
auto& operation_lock() { return m_operation_lock; }
|
|
Graphics::VirtIOGPU::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()) };
|
|
}
|
|
ErrorOr<void> synchronous_virtio_gpu_command(size_t microseconds_timeout, PhysicalAddress buffer_start, size_t request_size, size_t response_size);
|
|
|
|
ErrorOr<Graphics::VirtIOGPU::ResourceID> create_2d_resource(Graphics::VirtIOGPU::Protocol::Rect rect);
|
|
ErrorOr<Graphics::VirtIOGPU::ResourceID> create_3d_resource(Graphics::VirtIOGPU::Protocol::Resource3DSpecification const& resource_3d_specification);
|
|
ErrorOr<void> delete_resource(Graphics::VirtIOGPU::ResourceID resource_id);
|
|
ErrorOr<void> ensure_backing_storage(Graphics::VirtIOGPU::ResourceID resource_id, Memory::Region const& region, size_t buffer_offset, size_t buffer_length);
|
|
ErrorOr<void> detach_backing_storage(Graphics::VirtIOGPU::ResourceID resource_id);
|
|
ErrorOr<void> set_scanout_resource(Graphics::VirtIOGPU::ScanoutID scanout, Graphics::VirtIOGPU::ResourceID resource_id, Graphics::VirtIOGPU::Protocol::Rect rect);
|
|
ErrorOr<void> transfer_framebuffer_data_to_host(Graphics::VirtIOGPU::ScanoutID scanout, Graphics::VirtIOGPU::ResourceID resource_id, Graphics::VirtIOGPU::Protocol::Rect const& rect);
|
|
ErrorOr<void> flush_displayed_image(Graphics::VirtIOGPU::ResourceID resource_id, Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect);
|
|
ErrorOr<void> query_and_set_edid(u32 scanout_id, VirtIODisplayConnector& display_connector);
|
|
|
|
size_t m_num_scanouts { 0 };
|
|
Scanout m_scanouts[VIRTIO_GPU_MAX_SCANOUTS];
|
|
|
|
VirtIO::Configuration const* m_device_configuration { nullptr };
|
|
// Note: Resource ID 0 is invalid, and we must not allocate 0 as the first resource ID.
|
|
Atomic<u32> m_resource_id_counter { 1 };
|
|
SpinlockProtected<Bitmap> m_active_context_ids { LockRank::None };
|
|
LockRefPtr<VirtIOGPU3DDevice> m_3d_device;
|
|
bool m_has_virgl_support { false };
|
|
|
|
Spinlock m_operation_lock { LockRank::None };
|
|
NonnullOwnPtr<Memory::Region> m_scratch_space;
|
|
};
|
|
}
|