mirror of https://github.com/acidanthera/audk.git
561 lines
16 KiB
C
561 lines
16 KiB
C
/** @file
|
|
|
|
VirtIo GPU initialization, and commands (primitives) for the GPU device.
|
|
|
|
Copyright (C) 2016, Red Hat, Inc.
|
|
|
|
This program and the accompanying materials are licensed and made available
|
|
under the terms and conditions of the BSD License which accompanies this
|
|
distribution. The full text of the license may be found at
|
|
http://opensource.org/licenses/bsd-license.php
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
|
|
WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
|
|
**/
|
|
|
|
#include <Library/VirtioLib.h>
|
|
|
|
#include "VirtioGpu.h"
|
|
|
|
/**
|
|
Configure the VirtIo GPU device that underlies VgpuDev.
|
|
|
|
@param[in,out] VgpuDev The VGPU_DEV object to set up VirtIo messaging for.
|
|
On input, the caller is responsible for having
|
|
initialized VgpuDev->VirtIo. On output, VgpuDev->Ring
|
|
has been initialized, and synchronous VirtIo GPU
|
|
commands (primitives) can be submitted to the device.
|
|
|
|
@retval EFI_SUCCESS VirtIo GPU configuration successful.
|
|
|
|
@retval EFI_UNSUPPORTED The host-side configuration of the VirtIo GPU is not
|
|
supported by this driver.
|
|
|
|
@retval Error codes from underlying functions.
|
|
**/
|
|
EFI_STATUS
|
|
VirtioGpuInit (
|
|
IN OUT VGPU_DEV *VgpuDev
|
|
)
|
|
{
|
|
UINT8 NextDevStat;
|
|
EFI_STATUS Status;
|
|
UINT64 Features;
|
|
UINT16 QueueSize;
|
|
|
|
//
|
|
// Execute virtio-v1.0-cs04, 3.1.1 Driver Requirements: Device
|
|
// Initialization.
|
|
//
|
|
// 1. Reset the device.
|
|
//
|
|
NextDevStat = 0;
|
|
Status = VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Failed;
|
|
}
|
|
|
|
//
|
|
// 2. Set the ACKNOWLEDGE status bit [...]
|
|
//
|
|
NextDevStat |= VSTAT_ACK;
|
|
Status = VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Failed;
|
|
}
|
|
|
|
//
|
|
// 3. Set the DRIVER status bit [...]
|
|
//
|
|
NextDevStat |= VSTAT_DRIVER;
|
|
Status = VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Failed;
|
|
}
|
|
|
|
//
|
|
// 4. Read device feature bits...
|
|
//
|
|
Status = VgpuDev->VirtIo->GetDeviceFeatures (VgpuDev->VirtIo, &Features);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Failed;
|
|
}
|
|
if ((Features & VIRTIO_F_VERSION_1) == 0) {
|
|
Status = EFI_UNSUPPORTED;
|
|
goto Failed;
|
|
}
|
|
//
|
|
// We only want the most basic 2D features.
|
|
//
|
|
Features &= VIRTIO_F_VERSION_1;
|
|
|
|
//
|
|
// ... and write the subset of feature bits understood by the [...] driver to
|
|
// the device. [...]
|
|
// 5. Set the FEATURES_OK status bit.
|
|
// 6. Re-read device status to ensure the FEATURES_OK bit is still set [...]
|
|
//
|
|
Status = Virtio10WriteFeatures (VgpuDev->VirtIo, Features, &NextDevStat);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Failed;
|
|
}
|
|
|
|
//
|
|
// 7. Perform device-specific setup, including discovery of virtqueues for
|
|
// the device [...]
|
|
//
|
|
Status = VgpuDev->VirtIo->SetQueueSel (VgpuDev->VirtIo,
|
|
VIRTIO_GPU_CONTROL_QUEUE);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Failed;
|
|
}
|
|
Status = VgpuDev->VirtIo->GetQueueNumMax (VgpuDev->VirtIo, &QueueSize);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Failed;
|
|
}
|
|
|
|
//
|
|
// We implement each VirtIo GPU command that we use with two descriptors:
|
|
// request, response.
|
|
//
|
|
if (QueueSize < 2) {
|
|
Status = EFI_UNSUPPORTED;
|
|
goto Failed;
|
|
}
|
|
|
|
//
|
|
// [...] population of virtqueues [...]
|
|
//
|
|
Status = VirtioRingInit (QueueSize, &VgpuDev->Ring);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Failed;
|
|
}
|
|
Status = VgpuDev->VirtIo->SetQueueAddress (VgpuDev->VirtIo, &VgpuDev->Ring);
|
|
if (EFI_ERROR (Status)) {
|
|
goto ReleaseQueue;
|
|
}
|
|
|
|
//
|
|
// 8. Set the DRIVER_OK status bit.
|
|
//
|
|
NextDevStat |= VSTAT_DRIVER_OK;
|
|
Status = VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);
|
|
if (EFI_ERROR (Status)) {
|
|
goto ReleaseQueue;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
ReleaseQueue:
|
|
VirtioRingUninit (&VgpuDev->Ring);
|
|
|
|
Failed:
|
|
//
|
|
// If any of these steps go irrecoverably wrong, the driver SHOULD set the
|
|
// FAILED status bit to indicate that it has given up on the device (it can
|
|
// reset the device later to restart if desired). [...]
|
|
//
|
|
// VirtIo access failure here should not mask the original error.
|
|
//
|
|
NextDevStat |= VSTAT_FAILED;
|
|
VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
De-configure the VirtIo GPU device that underlies VgpuDev.
|
|
|
|
@param[in,out] VgpuDev The VGPU_DEV object to tear down VirtIo messaging
|
|
for. On input, the caller is responsible for having
|
|
called VirtioGpuInit(). On output, VgpuDev->Ring has
|
|
been uninitialized; VirtIo GPU commands (primitives)
|
|
can no longer be submitted to the device.
|
|
**/
|
|
VOID
|
|
VirtioGpuUninit (
|
|
IN OUT VGPU_DEV *VgpuDev
|
|
)
|
|
{
|
|
//
|
|
// Resetting the VirtIo device makes it release its resources and forget its
|
|
// configuration.
|
|
//
|
|
VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, 0);
|
|
VirtioRingUninit (&VgpuDev->Ring);
|
|
}
|
|
|
|
/**
|
|
EFI_EVENT_NOTIFY function for the VGPU_DEV.ExitBoot event. It resets the
|
|
VirtIo device, causing it to release its resources and to forget its
|
|
configuration.
|
|
|
|
This function may only be called (that is, VGPU_DEV.ExitBoot may only be
|
|
signaled) after VirtioGpuInit() returns and before VirtioGpuUninit() is
|
|
called.
|
|
|
|
@param[in] Event Event whose notification function is being invoked.
|
|
|
|
@param[in] Context Pointer to the associated VGPU_DEV object.
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
VirtioGpuExitBoot (
|
|
IN EFI_EVENT Event,
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
VGPU_DEV *VgpuDev;
|
|
|
|
VgpuDev = Context;
|
|
VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, 0);
|
|
}
|
|
|
|
/**
|
|
Internal utility function that sends a request to the VirtIo GPU device
|
|
model, awaits the answer from the host, and returns a status.
|
|
|
|
@param[in,out] VgpuDev The VGPU_DEV object that represents the VirtIo GPU
|
|
device. The caller is responsible to have
|
|
successfully invoked VirtioGpuInit() on VgpuDev
|
|
previously, while VirtioGpuUninit() must not have
|
|
been called on VgpuDev.
|
|
|
|
@param[in] RequestType The type of the request. The caller is responsible
|
|
for providing a VirtioGpuCmd* RequestType which, on
|
|
success, elicits a VirtioGpuRespOkNodata response
|
|
from the host.
|
|
|
|
@param[in] Fence Whether to enable fencing for this request. Fencing
|
|
forces the host to complete the command before
|
|
producing a response. If Fence is TRUE, then
|
|
VgpuDev->FenceId is consumed, and incremented.
|
|
|
|
@param[in,out] Header Pointer to the caller-allocated request object. The
|
|
request must start with VIRTIO_GPU_CONTROL_HEADER.
|
|
This function overwrites all fields of Header before
|
|
submitting the request to the host:
|
|
|
|
- it sets Type from RequestType,
|
|
|
|
- it sets Flags and FenceId based on Fence,
|
|
|
|
- it zeroes CtxId and Padding.
|
|
|
|
@param[in] RequestSize Size of the entire caller-allocated request object,
|
|
including the leading VIRTIO_GPU_CONTROL_HEADER.
|
|
|
|
@retval EFI_SUCCESS Operation successful.
|
|
|
|
@retval EFI_DEVICE_ERROR The host rejected the request. The host error
|
|
code has been logged on the EFI_D_ERROR level.
|
|
|
|
@return Codes for unexpected errors in VirtIo
|
|
messaging.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
VirtioGpuSendCommand (
|
|
IN OUT VGPU_DEV *VgpuDev,
|
|
IN VIRTIO_GPU_CONTROL_TYPE RequestType,
|
|
IN BOOLEAN Fence,
|
|
IN OUT volatile VIRTIO_GPU_CONTROL_HEADER *Header,
|
|
IN UINTN RequestSize
|
|
)
|
|
{
|
|
DESC_INDICES Indices;
|
|
volatile VIRTIO_GPU_CONTROL_HEADER Response;
|
|
EFI_STATUS Status;
|
|
UINT32 ResponseSize;
|
|
|
|
//
|
|
// Initialize Header.
|
|
//
|
|
Header->Type = RequestType;
|
|
if (Fence) {
|
|
Header->Flags = VIRTIO_GPU_FLAG_FENCE;
|
|
Header->FenceId = VgpuDev->FenceId++;
|
|
} else {
|
|
Header->Flags = 0;
|
|
Header->FenceId = 0;
|
|
}
|
|
Header->CtxId = 0;
|
|
Header->Padding = 0;
|
|
|
|
ASSERT (RequestSize >= sizeof *Header);
|
|
ASSERT (RequestSize <= MAX_UINT32);
|
|
|
|
//
|
|
// Compose the descriptor chain.
|
|
//
|
|
VirtioPrepare (&VgpuDev->Ring, &Indices);
|
|
VirtioAppendDesc (&VgpuDev->Ring, (UINTN)Header, (UINT32)RequestSize,
|
|
VRING_DESC_F_NEXT, &Indices);
|
|
VirtioAppendDesc (&VgpuDev->Ring, (UINTN)&Response, sizeof Response,
|
|
VRING_DESC_F_WRITE, &Indices);
|
|
|
|
//
|
|
// Send the command.
|
|
//
|
|
Status = VirtioFlush (VgpuDev->VirtIo, VIRTIO_GPU_CONTROL_QUEUE,
|
|
&VgpuDev->Ring, &Indices, &ResponseSize);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Parse the response.
|
|
//
|
|
if (ResponseSize != sizeof Response) {
|
|
DEBUG ((EFI_D_ERROR, "%a: malformed response to Request=0x%x\n",
|
|
__FUNCTION__, (UINT32)RequestType));
|
|
return EFI_PROTOCOL_ERROR;
|
|
}
|
|
|
|
if (Response.Type == VirtioGpuRespOkNodata) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
DEBUG ((EFI_D_ERROR, "%a: Request=0x%x Response=0x%x\n", __FUNCTION__,
|
|
(UINT32)RequestType, Response.Type));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
/**
|
|
The following functions send requests to the VirtIo GPU device model, await
|
|
the answer from the host, and return a status. They share the following
|
|
interface details:
|
|
|
|
@param[in,out] VgpuDev The VGPU_DEV object that represents the VirtIo GPU
|
|
device. The caller is responsible to have
|
|
successfully invoked VirtioGpuInit() on VgpuDev
|
|
previously, while VirtioGpuUninit() must not have
|
|
been called on VgpuDev.
|
|
|
|
@retval EFI_INVALID_PARAMETER Invalid command-specific parameters were
|
|
detected by this driver.
|
|
|
|
@retval EFI_SUCCESS Operation successful.
|
|
|
|
@retval EFI_DEVICE_ERROR The host rejected the request. The host error
|
|
code has been logged on the EFI_D_ERROR level.
|
|
|
|
@return Codes for unexpected errors in VirtIo
|
|
messaging.
|
|
|
|
For the command-specific parameters, please consult the GPU Device section of
|
|
the VirtIo 1.0 specification (see references in
|
|
"OvmfPkg/Include/IndustryStandard/VirtioGpu.h").
|
|
**/
|
|
EFI_STATUS
|
|
VirtioGpuResourceCreate2d (
|
|
IN OUT VGPU_DEV *VgpuDev,
|
|
IN UINT32 ResourceId,
|
|
IN VIRTIO_GPU_FORMATS Format,
|
|
IN UINT32 Width,
|
|
IN UINT32 Height
|
|
)
|
|
{
|
|
volatile VIRTIO_GPU_RESOURCE_CREATE_2D Request;
|
|
|
|
if (ResourceId == 0) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Request.ResourceId = ResourceId;
|
|
Request.Format = (UINT32)Format;
|
|
Request.Width = Width;
|
|
Request.Height = Height;
|
|
|
|
return VirtioGpuSendCommand (
|
|
VgpuDev,
|
|
VirtioGpuCmdResourceCreate2d,
|
|
FALSE, // Fence
|
|
&Request.Header,
|
|
sizeof Request
|
|
);
|
|
}
|
|
|
|
EFI_STATUS
|
|
VirtioGpuResourceUnref (
|
|
IN OUT VGPU_DEV *VgpuDev,
|
|
IN UINT32 ResourceId
|
|
)
|
|
{
|
|
volatile VIRTIO_GPU_RESOURCE_UNREF Request;
|
|
|
|
if (ResourceId == 0) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Request.ResourceId = ResourceId;
|
|
Request.Padding = 0;
|
|
|
|
return VirtioGpuSendCommand (
|
|
VgpuDev,
|
|
VirtioGpuCmdResourceUnref,
|
|
FALSE, // Fence
|
|
&Request.Header,
|
|
sizeof Request
|
|
);
|
|
}
|
|
|
|
EFI_STATUS
|
|
VirtioGpuResourceAttachBacking (
|
|
IN OUT VGPU_DEV *VgpuDev,
|
|
IN UINT32 ResourceId,
|
|
IN VOID *FirstBackingPage,
|
|
IN UINTN NumberOfPages
|
|
)
|
|
{
|
|
volatile VIRTIO_GPU_RESOURCE_ATTACH_BACKING Request;
|
|
|
|
if (ResourceId == 0) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Request.ResourceId = ResourceId;
|
|
Request.NrEntries = 1;
|
|
Request.Entry.Addr = (UINTN)FirstBackingPage;
|
|
Request.Entry.Length = (UINT32)EFI_PAGES_TO_SIZE (NumberOfPages);
|
|
Request.Entry.Padding = 0;
|
|
|
|
return VirtioGpuSendCommand (
|
|
VgpuDev,
|
|
VirtioGpuCmdResourceAttachBacking,
|
|
FALSE, // Fence
|
|
&Request.Header,
|
|
sizeof Request
|
|
);
|
|
}
|
|
|
|
EFI_STATUS
|
|
VirtioGpuResourceDetachBacking (
|
|
IN OUT VGPU_DEV *VgpuDev,
|
|
IN UINT32 ResourceId
|
|
)
|
|
{
|
|
volatile VIRTIO_GPU_RESOURCE_DETACH_BACKING Request;
|
|
|
|
if (ResourceId == 0) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Request.ResourceId = ResourceId;
|
|
Request.Padding = 0;
|
|
|
|
//
|
|
// In this case, we set Fence to TRUE, because after this function returns,
|
|
// the caller might reasonably want to repurpose the backing pages
|
|
// immediately. Thus we should ensure that the host releases all references
|
|
// to the backing pages before we return.
|
|
//
|
|
return VirtioGpuSendCommand (
|
|
VgpuDev,
|
|
VirtioGpuCmdResourceDetachBacking,
|
|
TRUE, // Fence
|
|
&Request.Header,
|
|
sizeof Request
|
|
);
|
|
}
|
|
|
|
EFI_STATUS
|
|
VirtioGpuSetScanout (
|
|
IN OUT VGPU_DEV *VgpuDev,
|
|
IN UINT32 X,
|
|
IN UINT32 Y,
|
|
IN UINT32 Width,
|
|
IN UINT32 Height,
|
|
IN UINT32 ScanoutId,
|
|
IN UINT32 ResourceId
|
|
)
|
|
{
|
|
volatile VIRTIO_GPU_SET_SCANOUT Request;
|
|
|
|
//
|
|
// Unlike for most other commands, ResourceId=0 is valid; it
|
|
// is used to disable a scanout.
|
|
//
|
|
Request.Rectangle.X = X;
|
|
Request.Rectangle.Y = Y;
|
|
Request.Rectangle.Width = Width;
|
|
Request.Rectangle.Height = Height;
|
|
Request.ScanoutId = ScanoutId;
|
|
Request.ResourceId = ResourceId;
|
|
|
|
return VirtioGpuSendCommand (
|
|
VgpuDev,
|
|
VirtioGpuCmdSetScanout,
|
|
FALSE, // Fence
|
|
&Request.Header,
|
|
sizeof Request
|
|
);
|
|
}
|
|
|
|
EFI_STATUS
|
|
VirtioGpuTransferToHost2d (
|
|
IN OUT VGPU_DEV *VgpuDev,
|
|
IN UINT32 X,
|
|
IN UINT32 Y,
|
|
IN UINT32 Width,
|
|
IN UINT32 Height,
|
|
IN UINT64 Offset,
|
|
IN UINT32 ResourceId
|
|
)
|
|
{
|
|
volatile VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D Request;
|
|
|
|
if (ResourceId == 0) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Request.Rectangle.X = X;
|
|
Request.Rectangle.Y = Y;
|
|
Request.Rectangle.Width = Width;
|
|
Request.Rectangle.Height = Height;
|
|
Request.Offset = Offset;
|
|
Request.ResourceId = ResourceId;
|
|
Request.Padding = 0;
|
|
|
|
return VirtioGpuSendCommand (
|
|
VgpuDev,
|
|
VirtioGpuCmdTransferToHost2d,
|
|
FALSE, // Fence
|
|
&Request.Header,
|
|
sizeof Request
|
|
);
|
|
}
|
|
|
|
EFI_STATUS
|
|
VirtioGpuResourceFlush (
|
|
IN OUT VGPU_DEV *VgpuDev,
|
|
IN UINT32 X,
|
|
IN UINT32 Y,
|
|
IN UINT32 Width,
|
|
IN UINT32 Height,
|
|
IN UINT32 ResourceId
|
|
)
|
|
{
|
|
volatile VIRTIO_GPU_RESOURCE_FLUSH Request;
|
|
|
|
if (ResourceId == 0) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Request.Rectangle.X = X;
|
|
Request.Rectangle.Y = Y;
|
|
Request.Rectangle.Width = Width;
|
|
Request.Rectangle.Height = Height;
|
|
Request.ResourceId = ResourceId;
|
|
Request.Padding = 0;
|
|
|
|
return VirtioGpuSendCommand (
|
|
VgpuDev,
|
|
VirtioGpuCmdResourceFlush,
|
|
FALSE, // Fence
|
|
&Request.Header,
|
|
sizeof Request
|
|
);
|
|
}
|