From a66ea3b5578ccebf22c2d1d673273f0dffc84ffa Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Thu, 18 Aug 2016 17:00:03 +0200 Subject: [PATCH] OvmfPkg/VirtioGpuDxe: provide functions for sending VirtIo GPU commands In this patch we add a "workhorse" function called VirtioGpuSendCommand(), and implement seven simple RPCs atop, for the command types listed in "OvmfPkg/Include/IndustryStandard/VirtioGpu.h". These functions will be called by our EFI_GRAPHICS_OUTPUT_PROTOCOL implementation. Cc: Ard Biesheuvel Cc: Jordan Justen Ref: https://tianocore.acgmultimedia.com/show_bug.cgi?id=66 Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Laszlo Ersek Reviewed-by: Jordan Justen --- OvmfPkg/VirtioGpuDxe/Commands.c | 347 ++++++++++++++++++++++++++++++- OvmfPkg/VirtioGpuDxe/VirtioGpu.h | 93 +++++++++ 2 files changed, 439 insertions(+), 1 deletion(-) diff --git a/OvmfPkg/VirtioGpuDxe/Commands.c b/OvmfPkg/VirtioGpuDxe/Commands.c index 804de950ff..b369dc3a7a 100644 --- a/OvmfPkg/VirtioGpuDxe/Commands.c +++ b/OvmfPkg/VirtioGpuDxe/Commands.c @@ -14,7 +14,6 @@ **/ -#include #include #include "VirtioGpu.h" @@ -212,3 +211,349 @@ VirtioGpuExitBoot ( 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); + + // + // Compose the descriptor chain. + // + VirtioPrepare (&VgpuDev->Ring, &Indices); + VirtioAppendDesc (&VgpuDev->Ring, (UINTN)Header, 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 + ); +} diff --git a/OvmfPkg/VirtioGpuDxe/VirtioGpu.h b/OvmfPkg/VirtioGpuDxe/VirtioGpu.h index 97767dba70..f883992248 100644 --- a/OvmfPkg/VirtioGpuDxe/VirtioGpu.h +++ b/OvmfPkg/VirtioGpuDxe/VirtioGpu.h @@ -17,6 +17,7 @@ #ifndef _VIRTIO_GPU_DXE_H_ #define _VIRTIO_GPU_DXE_H_ +#include #include #include #include @@ -58,6 +59,11 @@ typedef struct { // EFI_EVENT ExitBoot; + // + // Common running counter for all VirtIo GPU requests that ask for fencing. + // + UINT64 FenceId; + // // The Child field references the GOP wrapper structure. If this pointer is // NULL, then the hybrid driver has bound (i.e., started) the @@ -171,4 +177,91 @@ VirtioGpuExitBoot ( IN VOID *Context ); +/** + 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 + ); + +EFI_STATUS +VirtioGpuResourceUnref ( + IN OUT VGPU_DEV *VgpuDev, + IN UINT32 ResourceId + ); + +EFI_STATUS +VirtioGpuResourceAttachBacking ( + IN OUT VGPU_DEV *VgpuDev, + IN UINT32 ResourceId, + IN VOID *FirstBackingPage, + IN UINTN NumberOfPages + ); + +EFI_STATUS +VirtioGpuResourceDetachBacking ( + IN OUT VGPU_DEV *VgpuDev, + IN UINT32 ResourceId + ); + +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 + ); + +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 + ); + +EFI_STATUS +VirtioGpuResourceFlush ( + IN OUT VGPU_DEV *VgpuDev, + IN UINT32 X, + IN UINT32 Y, + IN UINT32 Width, + IN UINT32 Height, + IN UINT32 ResourceId + ); + #endif // _VIRTIO_GPU_DXE_H_