blob: 383a529e694d64816e51f1e6740b1670a06a14a9 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#pragma once
#include <fbl/intrusive_hash_table.h>
#include <fbl/mutex.h>
#include <fbl/unique_ptr.h>
#include <machina/virtio.h>
#include <virtio/gpu.h>
#include <zircon/compiler.h>
#include <zircon/device/display.h>
#include <zircon/types.h>
#define VIRTIO_GPU_Q_CONTROLQ 0
#define VIRTIO_GPU_Q_CURSORQ 1
#define VIRTIO_GPU_Q_COUNT 2
class VirtioGpu;
using ResourceId = uint32_t;
using ScanoutId = uint32_t;
/* A scanout represents a display that GPU resources can be rendered to.
*
* Each scanout will own a single device under /dev/class/framebuffer/
*/
class GpuScanout {
public:
GpuScanout(uint32_t width, uint32_t height, uint32_t format, uint8_t* buffer)
: width_(width), height_(height), format_(format), buffer_(buffer) {}
virtual ~GpuScanout() = default;
uint32_t width() const { return width_; }
uint32_t height() const { return height_; }
uint32_t format() const { return format_; }
uint8_t* buffer() const { return buffer_; }
virtual void FlushRegion(const virtio_gpu_rect_t& rect) {}
private:
uint32_t width_;
uint32_t height_;
uint32_t format_;
uint8_t* buffer_;
};
class FramebufferScanout : public GpuScanout {
public:
static zx_status_t Create(const char* framebuffer, fbl::unique_ptr<GpuScanout>* out);
FramebufferScanout(int fd, const ioctl_display_get_fb_t& fb, uint8_t* buffer)
: GpuScanout(fb.info.width, fb.info.height, VirtioPixelFormat(fb.info.format), buffer),
fd_(fd) {}
virtual ~FramebufferScanout();
void FlushRegion(const virtio_gpu_rect_t& rect) override;
private:
static uint32_t VirtioPixelFormat(uint32_t zx_format);
int fd_ = 0;
};
/* A resource corresponds to a single display buffer. */
class GpuResource : public fbl::SinglyLinkedListable<fbl::unique_ptr<GpuResource>> {
public:
// The driver will provide a scatter-gather list of memory pages to back
// the framebuffer in guest physical memory.
struct BackingPages : public fbl::SinglyLinkedListable<fbl::unique_ptr<BackingPages>> {
uint64_t addr;
uint32_t length;
BackingPages(uint64_t addr_, uint32_t length_)
: addr(addr_), length(length_) {}
};
// Fix the number of hash table buckets to 1 because linux and zircon
// virtcons only use a single resource.
static constexpr size_t kNumHashTableBuckets = 1;
using HashTable = fbl::HashTable<ResourceId,
fbl::unique_ptr<GpuResource>,
fbl::SinglyLinkedList<fbl::unique_ptr<GpuResource>>,
size_t,
kNumHashTableBuckets>;
GpuResource(VirtioGpu* gpu, const virtio_gpu_resource_create_2d_t* args);
uint32_t width() const { return width_; }
uint32_t height() const { return height_; }
uint32_t format() const { return format_; }
virtio_gpu_ctrl_type SetScanout(GpuScanout* scanout);
// Handle a VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING command for this
// resource.
virtio_gpu_ctrl_type AttachBacking(const virtio_gpu_mem_entry_t* mem_entries,
uint32_t num_entries);
// Handle a VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING command for this
// resource.
virtio_gpu_ctrl_type DetachBacking();
// Handle a VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D command for this
// resource.
virtio_gpu_ctrl_type TransferToHost2D(const virtio_gpu_transfer_to_host_2d_t* request);
// Handle a VIRTIO_GPU_CMD_RESOURCE_FLUSH command for this
// resource.
virtio_gpu_ctrl_type Flush(const virtio_gpu_resource_flush_t* request);
// Handle a VIRTIO_GPU_CMD_SET_SCANOUT command for this
// resource.
virtio_gpu_ctrl_type Flush(GpuScanout* scanout);
// Trait implementation for fbl::HashTable
ResourceId GetKey() const { return res_id_; }
static size_t GetHash(ResourceId key) { return key; }
private:
// Copies bytes from the linked list of backing pages in guest memory into
// a host resource.
void CopyBytes(uint64_t offset, uint8_t* dest, size_t size);
VirtioGpu* gpu_;
GpuScanout* scanout_;
ResourceId res_id_;
uint32_t width_;
uint32_t height_;
uint32_t format_;
fbl::SinglyLinkedList<fbl::unique_ptr<BackingPages>> backing_;
};
/* Virtio 2D GPU device. */
class VirtioGpu : public VirtioDevice {
public:
static constexpr uint8_t kBytesPerPixel = 4;
VirtioGpu(uintptr_t guest_physmem_addr, size_t guest_physmem_size);
~VirtioGpu() override = default;
virtio_queue_t& control_queue() { return queues_[VIRTIO_GPU_Q_CONTROLQ]; }
virtio_queue_t& cursor_queue() { return queues_[VIRTIO_GPU_Q_CURSORQ]; }
// Opens the framebuffer device located at |path| and starts processing
// any descriptors that become available in the queues.
zx_status_t Init(const char* path);
// Adds a scanout to the GPU.
//
// Currently only a single scanout is supported. ZX_ERR_ALREADY_EXISTS will
// be returned if this method is called multiple times.
zx_status_t AddScanout(fbl::unique_ptr<GpuScanout> scanout);
zx_status_t HandleGpuCommand(virtio_queue_t* queue, uint16_t head, uint32_t* used);
protected:
static zx_status_t QueueHandler(virtio_queue_t* queue, uint16_t head, uint32_t* used,
void* ctx);
// VIRTIO_GPU_CMD_GET_DISPLAY_INFO
void GetDisplayInfo(const virtio_gpu_ctrl_hdr_t* request,
virtio_gpu_resp_display_info_t* response);
// VIRTIO_GPU_CMD_RESOURCE_CREATE_2D
void ResourceCreate2D(const virtio_gpu_resource_create_2d_t* request,
virtio_gpu_ctrl_hdr_t* response);
// VIRTIO_GPU_CMD_RESOURCE_UNREF
void ResourceUnref(const virtio_gpu_resource_unref_t* request,
virtio_gpu_ctrl_hdr_t* response);
// VIRTIO_GPU_CMD_SET_SCANOUT
void SetScanout(const virtio_gpu_set_scanout_t* request, virtio_gpu_ctrl_hdr_t* response);
// VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING
void ResourceAttachBacking(const virtio_gpu_resource_attach_backing_t* request,
const virtio_gpu_mem_entry_t* mem_entries,
virtio_gpu_ctrl_hdr_t* response);
// VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING
void ResourceDetachBacking(const virtio_gpu_resource_detach_backing_t* request,
virtio_gpu_ctrl_hdr_t* response);
// VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D
void TransferToHost2D(const virtio_gpu_transfer_to_host_2d_t* request,
virtio_gpu_ctrl_hdr_t* response);
// VIRTIO_GPU_CMD_RESOURCE_FLUSH
void ResourceFlush(const virtio_gpu_resource_flush_t* request,
virtio_gpu_ctrl_hdr_t* response);
private:
fbl::unique_ptr<GpuScanout> scanout_;
GpuResource::HashTable resources_;
virtio_queue_t queues_[VIRTIO_GPU_Q_COUNT];
virtio_gpu_config_t config_ = {};
};