diff --git a/Kernel/Graphics/VirtIOGPU/GPU3DDevice.cpp b/Kernel/Graphics/VirtIOGPU/GPU3DDevice.cpp index 7114216da5..0367376237 100644 --- a/Kernel/Graphics/VirtIOGPU/GPU3DDevice.cpp +++ b/Kernel/Graphics/VirtIOGPU/GPU3DDevice.cpp @@ -15,6 +15,12 @@ namespace Kernel::Graphics::VirtIOGPU { +GPU3DDevice::PerContextState::PerContextState(ContextID context_id, OwnPtr transfer_buffer_region) + : m_context_id(context_id) + , m_transfer_buffer_region(move(transfer_buffer_region)) +{ +} + GPU3DDevice::GPU3DDevice(GraphicsAdapter& graphics_adapter) : CharacterDevice(28, 0) , m_graphics_adapter(graphics_adapter) @@ -24,41 +30,68 @@ GPU3DDevice::GPU3DDevice(GraphicsAdapter& graphics_adapter) // Setup memory transfer region auto region_result = MM.allocate_kernel_region( NUM_TRANSFER_REGION_PAGES * PAGE_SIZE, - "VIRGL3D upload buffer", + "VIRGL3D kernel upload buffer", Memory::Region::Access::ReadWrite, AllocationStrategy::AllocateNow); VERIFY(!region_result.is_error()); m_transfer_buffer_region = region_result.release_value(); } -ErrorOr GPU3DDevice::ioctl(OpenFileDescription&, unsigned request, Userspace arg) +void GPU3DDevice::detach(OpenFileDescription& description) +{ + m_context_state_lookup.remove(&description); + CharacterDevice::detach(description); +} + +ErrorOr> GPU3DDevice::get_context_for_description(OpenFileDescription& description) +{ + auto res = m_context_state_lookup.get(&description); + if (!res.has_value()) + return EBADF; + return res.value(); +} + +ErrorOr GPU3DDevice::ioctl(OpenFileDescription& description, unsigned request, Userspace arg) { // TODO: We really should have ioctls for destroying resources as well switch (request) { + case VIRGL_IOCTL_CREATE_CONTEXT: { + if (m_context_state_lookup.contains(&description)) + return EEXIST; + MutexLocker locker(m_graphics_adapter.operation_lock()); + // TODO: Delete the context if it fails to be set in m_context_state_lookup + auto context_id = m_graphics_adapter.create_context(); + RefPtr per_context_state = TRY(PerContextState::try_create(context_id)); + auto ref = RefPtr(description); + TRY(m_context_state_lookup.try_set(ref, per_context_state)); + return {}; + } case VIRGL_IOCTL_TRANSFER_DATA: { + auto& transfer_buffer_region = TRY(get_context_for_description(description))->transfer_buffer_region(); auto user_transfer_descriptor = static_ptr_cast(arg); auto transfer_descriptor = TRY(copy_typed_from_user(user_transfer_descriptor)); if (transfer_descriptor.direction == VIRGL_DATA_DIR_GUEST_TO_HOST) { if (transfer_descriptor.offset_in_region + transfer_descriptor.num_bytes > NUM_TRANSFER_REGION_PAGES * PAGE_SIZE) { return EOVERFLOW; } - auto target = m_transfer_buffer_region->vaddr().offset(transfer_descriptor.offset_in_region).as_ptr(); + auto target = transfer_buffer_region.vaddr().offset(transfer_descriptor.offset_in_region).as_ptr(); return copy_from_user(target, transfer_descriptor.data, transfer_descriptor.num_bytes); } else if (transfer_descriptor.direction == VIRGL_DATA_DIR_HOST_TO_GUEST) { if (transfer_descriptor.offset_in_region + transfer_descriptor.num_bytes > NUM_TRANSFER_REGION_PAGES * PAGE_SIZE) { return EOVERFLOW; } - auto source = m_transfer_buffer_region->vaddr().offset(transfer_descriptor.offset_in_region).as_ptr(); + auto source = transfer_buffer_region.vaddr().offset(transfer_descriptor.offset_in_region).as_ptr(); return copy_to_user(transfer_descriptor.data, source, transfer_descriptor.num_bytes); } else { return EINVAL; } } case VIRGL_IOCTL_SUBMIT_CMD: { + auto context_id = TRY(get_context_for_description(description))->context_id(); MutexLocker locker(m_graphics_adapter.operation_lock()); auto user_command_buffer = static_ptr_cast(arg); auto command_buffer = TRY(copy_typed_from_user(user_command_buffer)); - m_graphics_adapter.submit_command_buffer(m_kernel_context_id, [&](Bytes buffer) { + m_graphics_adapter.submit_command_buffer(context_id, [&](Bytes buffer) { auto num_bytes = command_buffer.num_elems * sizeof(u32); VERIFY(num_bytes <= buffer.size()); MUST(copy_from_user(buffer.data(), command_buffer.data, num_bytes)); @@ -67,6 +100,7 @@ ErrorOr GPU3DDevice::ioctl(OpenFileDescription&, unsigned request, Userspa return {}; } case VIRGL_IOCTL_CREATE_RESOURCE: { + auto per_context_state = TRY(get_context_for_description(description)); auto user_spec = static_ptr_cast(arg); VirGL3DResourceSpec spec = TRY(copy_typed_from_user(user_spec)); @@ -84,8 +118,8 @@ ErrorOr GPU3DDevice::ioctl(OpenFileDescription&, unsigned request, Userspa }; MutexLocker locker(m_graphics_adapter.operation_lock()); auto resource_id = m_graphics_adapter.create_3d_resource(resource_spec).value(); - m_graphics_adapter.attach_resource_to_context(resource_id, m_kernel_context_id); - m_graphics_adapter.ensure_backing_storage(resource_id, *m_transfer_buffer_region, 0, NUM_TRANSFER_REGION_PAGES * PAGE_SIZE); + m_graphics_adapter.attach_resource_to_context(resource_id, per_context_state->context_id()); + m_graphics_adapter.ensure_backing_storage(resource_id, per_context_state->transfer_buffer_region(), 0, NUM_TRANSFER_REGION_PAGES * PAGE_SIZE); spec.created_resource_id = resource_id; // FIXME: We should delete the resource we just created if we fail to copy the resource id out return copy_to_user(static_ptr_cast(arg), &spec); diff --git a/Kernel/Graphics/VirtIOGPU/GPU3DDevice.h b/Kernel/Graphics/VirtIOGPU/GPU3DDevice.h index 2e41e9ef79..a8a432355f 100644 --- a/Kernel/Graphics/VirtIOGPU/GPU3DDevice.h +++ b/Kernel/Graphics/VirtIOGPU/GPU3DDevice.h @@ -91,6 +91,27 @@ public: GPU3DDevice() = delete; explicit GPU3DDevice(GraphicsAdapter& graphics_adapter); + class PerContextState : public RefCounted { + public: + static ErrorOr> try_create(ContextID context_id) + { + auto region_result = TRY(MM.allocate_kernel_region( + NUM_TRANSFER_REGION_PAGES * PAGE_SIZE, + "VIRGL3D userspace upload buffer", + Memory::Region::Access::ReadWrite, + AllocationStrategy::AllocateNow)); + return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) PerContextState(context_id, move(region_result)))); + } + ContextID context_id() { return m_context_id; } + Memory::Region& transfer_buffer_region() { return *m_transfer_buffer_region; } + + private: + PerContextState() = delete; + explicit PerContextState(ContextID context_id, OwnPtr transfer_buffer_region); + ContextID m_context_id; + OwnPtr m_transfer_buffer_region; + }; + virtual bool can_read(const OpenFileDescription&, u64) const override { return true; } virtual bool can_write(const OpenFileDescription&, u64) const override { return true; } virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override { return ENOTSUP; } @@ -98,11 +119,15 @@ public: virtual StringView class_name() const override { return "virgl3d"; } virtual ErrorOr ioctl(OpenFileDescription&, unsigned request, Userspace arg) override; + virtual void detach(OpenFileDescription&) override; private: + ErrorOr> get_context_for_description(OpenFileDescription&); + Kernel::Graphics::VirtIOGPU::GraphicsAdapter& m_graphics_adapter; // Context used for kernel operations (e.g. flushing resources to scanout) ContextID m_kernel_context_id; + HashMap, RefPtr> m_context_state_lookup; // Memory management for backing buffers OwnPtr m_transfer_buffer_region; constexpr static size_t NUM_TRANSFER_REGION_PAGES = 256; diff --git a/Userland/Demos/VirGLDemo/VirGLDemo.cpp b/Userland/Demos/VirGLDemo/VirGLDemo.cpp index f3763a54e1..97d1a7081f 100644 --- a/Userland/Demos/VirGLDemo/VirGLDemo.cpp +++ b/Userland/Demos/VirGLDemo/VirGLDemo.cpp @@ -133,6 +133,8 @@ static void init() // Open the device gpu_fd = open("/dev/gpu0", O_RDWR); VERIFY(gpu_fd >= 0); + // Create a virgl context for this file descriptor + VERIFY(ioctl(gpu_fd, VIRGL_IOCTL_CREATE_CONTEXT) >= 0); // Create a VertexElements resource VirGL3DResourceSpec vbo_spec { .target = to_underlying(Gallium::PipeTextureTarget::BUFFER), // pipe_texture_target diff --git a/Userland/Libraries/LibC/sys/ioctl_numbers.h b/Userland/Libraries/LibC/sys/ioctl_numbers.h index 86709457b5..0c1448a9d9 100644 --- a/Userland/Libraries/LibC/sys/ioctl_numbers.h +++ b/Userland/Libraries/LibC/sys/ioctl_numbers.h @@ -125,6 +125,7 @@ enum IOCtlNumber { SOUNDCARD_IOCTL_GET_SAMPLE_RATE, STORAGE_DEVICE_GET_SIZE, STORAGE_DEVICE_GET_BLOCK_SIZE, + VIRGL_IOCTL_CREATE_CONTEXT, VIRGL_IOCTL_CREATE_RESOURCE, VIRGL_IOCTL_SUBMIT_CMD, VIRGL_IOCTL_TRANSFER_DATA, @@ -175,6 +176,7 @@ enum IOCtlNumber { #define SOUNDCARD_IOCTL_GET_SAMPLE_RATE SOUNDCARD_IOCTL_GET_SAMPLE_RATE #define STORAGE_DEVICE_GET_SIZE STORAGE_DEVICE_GET_SIZE #define STORAGE_DEVICE_GET_BLOCK_SIZE STORAGE_DEVICE_GET_BLOCK_SIZE +#define VIRGL_IOCTL_CREATE_CONTEXT VIRGL_IOCTL_CREATE_CONTEXT #define VIRGL_IOCTL_CREATE_RESOURCE VIRGL_IOCTL_CREATE_RESOURCE #define VIRGL_IOCTL_SUBMIT_CMD VIRGL_IOCTL_SUBMIT_CMD #define VIRGL_IOCTL_TRANSFER_DATA VIRGL_IOCTL_TRANSFER_DATA