mirror of https://github.com/acidanthera/audk.git
OvmfPkg/VirtioGpuDxe: implement EFI_GRAPHICS_OUTPUT_PROTOCOL
In this patch we replace our "dummy" Graphics Output Protocol interface with the real one. We exploit that EFI_GRAPHICS_OUTPUT_BLT_PIXEL and VirtioGpuFormatB8G8R8X8Unorm have identical representations; this lets us forego any pixel format conversions in the guest. For messaging the VirtIo GPU device, we use the primitives introduced in the previous patch. Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Cc: Jordan Justen <jordan.l.justen@intel.com> Ref: https://tianocore.acgmultimedia.com/show_bug.cgi?id=66 Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Laszlo Ersek <lersek@redhat.com> Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
This commit is contained in:
parent
a66ea3b557
commit
8731debefd
|
@ -28,16 +28,6 @@
|
|||
|
||||
#include "VirtioGpu.h"
|
||||
|
||||
//
|
||||
// Dummy Graphics Output Protocol GUID: a temporary placeholder for the EFI
|
||||
// counterpart. It will be replaced with the real thing as soon as we implement
|
||||
// the EFI GOP. Refer to VGPU_GOP.Gop.
|
||||
//
|
||||
STATIC EFI_GUID mDummyGraphicsOutputProtocolGuid = {
|
||||
0x4983f8dc, 0x2782, 0x415b,
|
||||
{ 0x91, 0xf5, 0x2c, 0xeb, 0x48, 0x4a, 0x0f, 0xe9 }
|
||||
};
|
||||
|
||||
//
|
||||
// The device path node that describes the Video Output Device Attributes for
|
||||
// the single head (UEFI child handle) that we support.
|
||||
|
@ -356,9 +346,11 @@ InitVgpuGop (
|
|||
//
|
||||
// Initialize our Graphics Output Protocol.
|
||||
//
|
||||
// This means "nothing" for now.
|
||||
// Fill in the function members of VgpuGop->Gop from the template, then set
|
||||
// up the rest of the GOP infrastructure by calling SetMode() right now.
|
||||
//
|
||||
Status = EFI_SUCCESS;
|
||||
CopyMem (&VgpuGop->Gop, &mGopTemplate, sizeof mGopTemplate);
|
||||
Status = VgpuGop->Gop.SetMode (&VgpuGop->Gop, 0);
|
||||
if (EFI_ERROR (Status)) {
|
||||
goto CloseVirtIoByChild;
|
||||
}
|
||||
|
@ -367,7 +359,7 @@ InitVgpuGop (
|
|||
// Install the Graphics Output Protocol on the child handle.
|
||||
//
|
||||
Status = gBS->InstallProtocolInterface (&VgpuGop->GopHandle,
|
||||
&mDummyGraphicsOutputProtocolGuid, EFI_NATIVE_INTERFACE,
|
||||
&gEfiGraphicsOutputProtocolGuid, EFI_NATIVE_INTERFACE,
|
||||
&VgpuGop->Gop);
|
||||
if (EFI_ERROR (Status)) {
|
||||
goto UninitGop;
|
||||
|
@ -381,9 +373,7 @@ InitVgpuGop (
|
|||
return EFI_SUCCESS;
|
||||
|
||||
UninitGop:
|
||||
//
|
||||
// Nothing, for now.
|
||||
//
|
||||
ReleaseGopResources (VgpuGop, TRUE /* DisableHead */);
|
||||
|
||||
CloseVirtIoByChild:
|
||||
gBS->CloseProtocol (ParentBusController, &gVirtioDeviceProtocolGuid,
|
||||
|
@ -439,16 +429,13 @@ UninitVgpuGop (
|
|||
|
||||
VgpuGop = ParentBus->Child;
|
||||
Status = gBS->UninstallProtocolInterface (VgpuGop->GopHandle,
|
||||
&mDummyGraphicsOutputProtocolGuid, &VgpuGop->Gop);
|
||||
&gEfiGraphicsOutputProtocolGuid, &VgpuGop->Gop);
|
||||
ASSERT_EFI_ERROR (Status);
|
||||
|
||||
//
|
||||
// Uninitialize VgpuGop->Gop.
|
||||
//
|
||||
// Nothing, for now.
|
||||
//
|
||||
Status = EFI_SUCCESS;
|
||||
ASSERT_EFI_ERROR (Status);
|
||||
ReleaseGopResources (VgpuGop, TRUE /* DisableHead */);
|
||||
|
||||
Status = gBS->CloseProtocol (ParentBusController, &gVirtioDeviceProtocolGuid,
|
||||
DriverBindingHandle, VgpuGop->GopHandle);
|
||||
|
|
|
@ -0,0 +1,647 @@
|
|||
/** @file
|
||||
|
||||
EFI_GRAPHICS_OUTPUT_PROTOCOL member functions for the VirtIo GPU driver.
|
||||
|
||||
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/BaseMemoryLib.h>
|
||||
#include <Library/MemoryAllocationLib.h>
|
||||
|
||||
#include "VirtioGpu.h"
|
||||
|
||||
/**
|
||||
Release guest-side and host-side resources that are related to an initialized
|
||||
VGPU_GOP.Gop.
|
||||
|
||||
param[in,out] VgpuGop The VGPU_GOP object to release resources for.
|
||||
|
||||
On input, the caller is responsible for having called
|
||||
VgpuGop->Gop.SetMode() at least once successfully.
|
||||
(This is equivalent to the requirement that
|
||||
VgpuGop->BackingStore be non-NULL. It is also
|
||||
equivalent to the requirement that VgpuGop->ResourceId
|
||||
be nonzero.)
|
||||
|
||||
On output, resources will be released, and
|
||||
VgpuGop->BackingStore and VgpuGop->ResourceId will be
|
||||
nulled.
|
||||
|
||||
param[in] DisableHead Whether this head (scanout) currently references the
|
||||
resource identified by VgpuGop->ResourceId. Only pass
|
||||
FALSE when VgpuGop->Gop.SetMode() calls this function
|
||||
while switching between modes, and set it to TRUE
|
||||
every other time.
|
||||
**/
|
||||
VOID
|
||||
ReleaseGopResources (
|
||||
IN OUT VGPU_GOP *VgpuGop,
|
||||
IN BOOLEAN DisableHead
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
|
||||
ASSERT (VgpuGop->ResourceId != 0);
|
||||
ASSERT (VgpuGop->BackingStore != NULL);
|
||||
|
||||
//
|
||||
// If any of the following host-side destruction steps fail, we can't get out
|
||||
// of an inconsistent state, so we'll hang. In general errors in object
|
||||
// destruction can hardly be recovered from.
|
||||
//
|
||||
if (DisableHead) {
|
||||
//
|
||||
// Dissociate head (scanout) #0 from the currently used 2D host resource,
|
||||
// by setting ResourceId=0 for it.
|
||||
//
|
||||
Status = VirtioGpuSetScanout (
|
||||
VgpuGop->ParentBus, // VgpuDev
|
||||
0, 0, 0, 0, // X, Y, Width, Height
|
||||
0, // ScanoutId
|
||||
0 // ResourceId
|
||||
);
|
||||
//
|
||||
// HACK BEGINS HERE
|
||||
//
|
||||
// According to the GPU Device section of the VirtIo specification, the
|
||||
// above operation is valid:
|
||||
//
|
||||
// "The driver can use resource_id = 0 to disable a scanout."
|
||||
//
|
||||
// However, in practice QEMU does not allow us to disable head (scanout) #0
|
||||
// -- it rejects the command with response code 0x1202
|
||||
// (VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID). Looking at the QEMU source
|
||||
// code, function virtio_gpu_set_scanout() in "hw/display/virtio-gpu.c",
|
||||
// this appears fully intentional, despite not being documented in the
|
||||
// spec.
|
||||
//
|
||||
// Surprisingly, ignoring the error here, and proceeding to release
|
||||
// host-side resources that presumably underlie head (scanout) #0, work
|
||||
// without any problems -- the driver survives repeated "disconnect" /
|
||||
// "connect -r" commands in the UEFI shell.
|
||||
//
|
||||
// So, for now, let's just suppress the error.
|
||||
//
|
||||
Status = EFI_SUCCESS;
|
||||
//
|
||||
// HACK ENDS HERE
|
||||
//
|
||||
|
||||
ASSERT_EFI_ERROR (Status);
|
||||
if (EFI_ERROR (Status)) {
|
||||
CpuDeadLoop ();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Detach backing pages from the currently used 2D host resource.
|
||||
//
|
||||
Status = VirtioGpuResourceDetachBacking (
|
||||
VgpuGop->ParentBus, // VgpuDev
|
||||
VgpuGop->ResourceId // ResourceId
|
||||
);
|
||||
ASSERT_EFI_ERROR (Status);
|
||||
if (EFI_ERROR (Status)) {
|
||||
CpuDeadLoop ();
|
||||
}
|
||||
|
||||
//
|
||||
// Release backing pages.
|
||||
//
|
||||
FreePages (VgpuGop->BackingStore, VgpuGop->NumberOfPages);
|
||||
VgpuGop->BackingStore = NULL;
|
||||
VgpuGop->NumberOfPages = 0;
|
||||
|
||||
//
|
||||
// Destroy the currently used 2D host resource.
|
||||
//
|
||||
Status = VirtioGpuResourceUnref (
|
||||
VgpuGop->ParentBus, // VgpuDev
|
||||
VgpuGop->ResourceId // ResourceId
|
||||
);
|
||||
ASSERT_EFI_ERROR (Status);
|
||||
if (EFI_ERROR (Status)) {
|
||||
CpuDeadLoop ();
|
||||
}
|
||||
VgpuGop->ResourceId = 0;
|
||||
}
|
||||
|
||||
//
|
||||
// The resolutions supported by this driver.
|
||||
//
|
||||
typedef struct {
|
||||
UINT32 Width;
|
||||
UINT32 Height;
|
||||
} GOP_RESOLUTION;
|
||||
|
||||
STATIC CONST GOP_RESOLUTION mGopResolutions[] = {
|
||||
{ 640, 480 },
|
||||
{ 800, 480 },
|
||||
{ 800, 600 },
|
||||
{ 832, 624 },
|
||||
{ 960, 640 },
|
||||
{ 1024, 600 },
|
||||
{ 1024, 768 },
|
||||
{ 1152, 864 },
|
||||
{ 1152, 870 },
|
||||
{ 1280, 720 },
|
||||
{ 1280, 760 },
|
||||
{ 1280, 768 },
|
||||
{ 1280, 800 },
|
||||
{ 1280, 960 },
|
||||
{ 1280, 1024 },
|
||||
{ 1360, 768 },
|
||||
{ 1366, 768 },
|
||||
{ 1400, 1050 },
|
||||
{ 1440, 900 },
|
||||
{ 1600, 900 },
|
||||
{ 1600, 1200 },
|
||||
{ 1680, 1050 },
|
||||
{ 1920, 1080 },
|
||||
{ 1920, 1200 },
|
||||
{ 1920, 1440 },
|
||||
{ 2000, 2000 },
|
||||
{ 2048, 1536 },
|
||||
{ 2048, 2048 },
|
||||
{ 2560, 1440 },
|
||||
{ 2560, 1600 },
|
||||
{ 2560, 2048 },
|
||||
{ 2800, 2100 },
|
||||
{ 3200, 2400 },
|
||||
{ 3840, 2160 },
|
||||
{ 4096, 2160 },
|
||||
{ 7680, 4320 },
|
||||
{ 8192, 4320 },
|
||||
};
|
||||
|
||||
//
|
||||
// Macro for casting VGPU_GOP.Gop to VGPU_GOP.
|
||||
//
|
||||
#define VGPU_GOP_FROM_GOP(GopPointer) \
|
||||
CR (GopPointer, VGPU_GOP, Gop, VGPU_GOP_SIG)
|
||||
|
||||
//
|
||||
// EFI_GRAPHICS_OUTPUT_PROTOCOL member functions.
|
||||
//
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
GopQueryMode (
|
||||
IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
|
||||
IN UINT32 ModeNumber,
|
||||
OUT UINTN *SizeOfInfo,
|
||||
OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info
|
||||
)
|
||||
{
|
||||
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *GopModeInfo;
|
||||
|
||||
if (ModeNumber >= sizeof mGopResolutions / sizeof mGopResolutions[0]) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
GopModeInfo = AllocateZeroPool (sizeof *GopModeInfo);
|
||||
if (GopModeInfo == NULL) {
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
GopModeInfo->HorizontalResolution = mGopResolutions[ModeNumber].Width;
|
||||
GopModeInfo->VerticalResolution = mGopResolutions[ModeNumber].Height;
|
||||
GopModeInfo->PixelFormat = PixelBltOnly;
|
||||
GopModeInfo->PixelsPerScanLine = mGopResolutions[ModeNumber].Width;
|
||||
|
||||
*SizeOfInfo = sizeof *GopModeInfo;
|
||||
*Info = GopModeInfo;
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
GopSetMode (
|
||||
IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
|
||||
IN UINT32 ModeNumber
|
||||
)
|
||||
{
|
||||
VGPU_GOP *VgpuGop;
|
||||
UINT32 NewResourceId;
|
||||
UINTN NewNumberOfBytes;
|
||||
UINTN NewNumberOfPages;
|
||||
VOID *NewBackingStore;
|
||||
EFI_STATUS Status;
|
||||
EFI_STATUS Status2;
|
||||
|
||||
if (ModeNumber >= sizeof mGopResolutions / sizeof mGopResolutions[0]) {
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
VgpuGop = VGPU_GOP_FROM_GOP (This);
|
||||
|
||||
//
|
||||
// Distinguish the first (internal) call from the other (protocol consumer)
|
||||
// calls.
|
||||
//
|
||||
if (VgpuGop->ResourceId == 0) {
|
||||
//
|
||||
// Set up the Gop -> GopMode -> GopModeInfo pointer chain, and the other
|
||||
// (nonzero) constant fields.
|
||||
//
|
||||
// No direct framebuffer access is supported, only Blt() is.
|
||||
//
|
||||
VgpuGop->Gop.Mode = &VgpuGop->GopMode;
|
||||
|
||||
VgpuGop->GopMode.MaxMode = (UINT32)(sizeof mGopResolutions /
|
||||
sizeof mGopResolutions[0]);
|
||||
VgpuGop->GopMode.Info = &VgpuGop->GopModeInfo;
|
||||
VgpuGop->GopMode.SizeOfInfo = sizeof VgpuGop->GopModeInfo;
|
||||
|
||||
VgpuGop->GopModeInfo.PixelFormat = PixelBltOnly;
|
||||
|
||||
//
|
||||
// This is the first time we create a host side resource.
|
||||
//
|
||||
NewResourceId = 1;
|
||||
} else {
|
||||
//
|
||||
// We already have an active host side resource. Create the new one without
|
||||
// interfering with the current one, so that we can cleanly bail out on
|
||||
// error, without disturbing the current graphics mode.
|
||||
//
|
||||
// The formula below will alternate between IDs 1 and 2.
|
||||
//
|
||||
NewResourceId = 3 - VgpuGop->ResourceId;
|
||||
}
|
||||
|
||||
//
|
||||
// Create the 2D host resource.
|
||||
//
|
||||
Status = VirtioGpuResourceCreate2d (
|
||||
VgpuGop->ParentBus, // VgpuDev
|
||||
NewResourceId, // ResourceId
|
||||
VirtioGpuFormatB8G8R8X8Unorm, // Format
|
||||
mGopResolutions[ModeNumber].Width, // Width
|
||||
mGopResolutions[ModeNumber].Height // Height
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
|
||||
//
|
||||
// Allocate guest backing store.
|
||||
//
|
||||
NewNumberOfBytes = mGopResolutions[ModeNumber].Width *
|
||||
mGopResolutions[ModeNumber].Height * sizeof (UINT32);
|
||||
NewNumberOfPages = EFI_SIZE_TO_PAGES (NewNumberOfBytes);
|
||||
NewBackingStore = AllocatePages (NewNumberOfPages);
|
||||
if (NewBackingStore == NULL) {
|
||||
Status = EFI_OUT_OF_RESOURCES;
|
||||
goto DestroyHostResource;
|
||||
}
|
||||
//
|
||||
// Fill visible part of backing store with black.
|
||||
//
|
||||
ZeroMem (NewBackingStore, NewNumberOfBytes);
|
||||
|
||||
//
|
||||
// Attach backing store to the host resource.
|
||||
//
|
||||
Status = VirtioGpuResourceAttachBacking (
|
||||
VgpuGop->ParentBus, // VgpuDev
|
||||
NewResourceId, // ResourceId
|
||||
NewBackingStore, // FirstBackingPage
|
||||
NewNumberOfPages // NumberOfPages
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
goto FreeBackingStore;
|
||||
}
|
||||
|
||||
//
|
||||
// Point head (scanout) #0 to the host resource.
|
||||
//
|
||||
Status = VirtioGpuSetScanout (
|
||||
VgpuGop->ParentBus, // VgpuDev
|
||||
0, // X
|
||||
0, // Y
|
||||
mGopResolutions[ModeNumber].Width, // Width
|
||||
mGopResolutions[ModeNumber].Height, // Height
|
||||
0, // ScanoutId
|
||||
NewResourceId // ResourceId
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
goto DetachBackingStore;
|
||||
}
|
||||
|
||||
//
|
||||
// If this is not the first (i.e., internal) call, then we have to (a) flush
|
||||
// the new resource to head (scanout) #0, after having flipped the latter to
|
||||
// the former above, plus (b) release the old resources.
|
||||
//
|
||||
if (VgpuGop->ResourceId != 0) {
|
||||
Status = VirtioGpuResourceFlush (
|
||||
VgpuGop->ParentBus, // VgpuDev
|
||||
0, // X
|
||||
0, // Y
|
||||
mGopResolutions[ModeNumber].Width, // Width
|
||||
mGopResolutions[ModeNumber].Height, // Height
|
||||
NewResourceId // ResourceId
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
//
|
||||
// Flip head (scanout) #0 back to the current resource. If this fails, we
|
||||
// cannot continue, as this error occurs on the error path and is
|
||||
// therefore non-recoverable.
|
||||
//
|
||||
Status2 = VirtioGpuSetScanout (
|
||||
VgpuGop->ParentBus, // VgpuDev
|
||||
0, // X
|
||||
0, // Y
|
||||
mGopResolutions[This->Mode->Mode].Width, // Width
|
||||
mGopResolutions[This->Mode->Mode].Height, // Height
|
||||
0, // ScanoutId
|
||||
VgpuGop->ResourceId // ResourceId
|
||||
);
|
||||
ASSERT_EFI_ERROR (Status2);
|
||||
if (EFI_ERROR (Status2)) {
|
||||
CpuDeadLoop ();
|
||||
}
|
||||
goto DetachBackingStore;
|
||||
}
|
||||
|
||||
//
|
||||
// Flush successful; release the old resources (without disabling head
|
||||
// (scanout) #0).
|
||||
//
|
||||
ReleaseGopResources (VgpuGop, FALSE /* DisableHead */);
|
||||
}
|
||||
|
||||
//
|
||||
// This is either the first (internal) call when we have no old resources
|
||||
// yet, or we've changed the mode successfully and released the old
|
||||
// resources.
|
||||
//
|
||||
ASSERT (VgpuGop->ResourceId == 0);
|
||||
ASSERT (VgpuGop->BackingStore == NULL);
|
||||
|
||||
VgpuGop->ResourceId = NewResourceId;
|
||||
VgpuGop->BackingStore = NewBackingStore;
|
||||
VgpuGop->NumberOfPages = NewNumberOfPages;
|
||||
|
||||
//
|
||||
// Populate Mode and ModeInfo (mutable fields only).
|
||||
//
|
||||
VgpuGop->GopMode.Mode = ModeNumber;
|
||||
VgpuGop->GopModeInfo.HorizontalResolution =
|
||||
mGopResolutions[ModeNumber].Width;
|
||||
VgpuGop->GopModeInfo.VerticalResolution = mGopResolutions[ModeNumber].Height;
|
||||
VgpuGop->GopModeInfo.PixelsPerScanLine = mGopResolutions[ModeNumber].Width;
|
||||
return EFI_SUCCESS;
|
||||
|
||||
DetachBackingStore:
|
||||
Status2 = VirtioGpuResourceDetachBacking (VgpuGop->ParentBus, NewResourceId);
|
||||
ASSERT_EFI_ERROR (Status2);
|
||||
if (EFI_ERROR (Status2)) {
|
||||
CpuDeadLoop ();
|
||||
}
|
||||
|
||||
FreeBackingStore:
|
||||
FreePages (NewBackingStore, NewNumberOfPages);
|
||||
|
||||
DestroyHostResource:
|
||||
Status2 = VirtioGpuResourceUnref (VgpuGop->ParentBus, NewResourceId);
|
||||
ASSERT_EFI_ERROR (Status2);
|
||||
if (EFI_ERROR (Status2)) {
|
||||
CpuDeadLoop ();
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
GopBlt (
|
||||
IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
|
||||
IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL
|
||||
IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation,
|
||||
IN UINTN SourceX,
|
||||
IN UINTN SourceY,
|
||||
IN UINTN DestinationX,
|
||||
IN UINTN DestinationY,
|
||||
IN UINTN Width,
|
||||
IN UINTN Height,
|
||||
IN UINTN Delta OPTIONAL
|
||||
)
|
||||
{
|
||||
VGPU_GOP *VgpuGop;
|
||||
UINT32 CurrentHorizontal;
|
||||
UINT32 CurrentVertical;
|
||||
UINTN SegmentSize;
|
||||
UINTN Y;
|
||||
UINTN ResourceOffset;
|
||||
EFI_STATUS Status;
|
||||
|
||||
VgpuGop = VGPU_GOP_FROM_GOP (This);
|
||||
CurrentHorizontal = VgpuGop->GopModeInfo.HorizontalResolution;
|
||||
CurrentVertical = VgpuGop->GopModeInfo.VerticalResolution;
|
||||
|
||||
//
|
||||
// We can avoid pixel format conversion in the guest because the internal
|
||||
// representation of EFI_GRAPHICS_OUTPUT_BLT_PIXEL and that of
|
||||
// VirtioGpuFormatB8G8R8X8Unorm are identical.
|
||||
//
|
||||
SegmentSize = Width * sizeof (UINT32);
|
||||
|
||||
//
|
||||
// Delta is relevant for operations that read a rectangle from, or write a
|
||||
// rectangle to, BltBuffer.
|
||||
//
|
||||
// In these cases, Delta is the stride of BltBuffer, in bytes. If Delta is
|
||||
// zero, then Width is the entire width of BltBuffer, and the stride is
|
||||
// supposed to be calculated from Width.
|
||||
//
|
||||
if (BltOperation == EfiBltVideoToBltBuffer ||
|
||||
BltOperation == EfiBltBufferToVideo) {
|
||||
if (Delta == 0) {
|
||||
Delta = SegmentSize;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// For operations that write to the display, check if the destination fits
|
||||
// onto the display.
|
||||
//
|
||||
if (BltOperation == EfiBltVideoFill ||
|
||||
BltOperation == EfiBltBufferToVideo ||
|
||||
BltOperation == EfiBltVideoToVideo) {
|
||||
if (DestinationX > CurrentHorizontal ||
|
||||
Width > CurrentHorizontal - DestinationX ||
|
||||
DestinationY > CurrentVertical ||
|
||||
Height > CurrentVertical - DestinationY) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// For operations that read from the display, check if the source fits onto
|
||||
// the display.
|
||||
//
|
||||
if (BltOperation == EfiBltVideoToBltBuffer ||
|
||||
BltOperation == EfiBltVideoToVideo) {
|
||||
if (SourceX > CurrentHorizontal ||
|
||||
Width > CurrentHorizontal - SourceX ||
|
||||
SourceY > CurrentVertical ||
|
||||
Height > CurrentVertical - SourceY) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Render the request. For requests that do not modify the display, there
|
||||
// won't be further steps.
|
||||
//
|
||||
switch (BltOperation) {
|
||||
case EfiBltVideoFill:
|
||||
//
|
||||
// Write data from the BltBuffer pixel (0, 0) directly to every pixel of
|
||||
// the video display rectangle (DestinationX, DestinationY) (DestinationX +
|
||||
// Width, DestinationY + Height). Only one pixel will be used from the
|
||||
// BltBuffer. Delta is NOT used.
|
||||
//
|
||||
for (Y = 0; Y < Height; ++Y) {
|
||||
SetMem32 (
|
||||
VgpuGop->BackingStore +
|
||||
(DestinationY + Y) * CurrentHorizontal + DestinationX,
|
||||
SegmentSize,
|
||||
*(UINT32 *)BltBuffer
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case EfiBltVideoToBltBuffer:
|
||||
//
|
||||
// Read data from the video display rectangle (SourceX, SourceY) (SourceX +
|
||||
// Width, SourceY + Height) and place it in the BltBuffer rectangle
|
||||
// (DestinationX, DestinationY ) (DestinationX + Width, DestinationY +
|
||||
// Height). If DestinationX or DestinationY is not zero then Delta must be
|
||||
// set to the length in bytes of a row in the BltBuffer.
|
||||
//
|
||||
for (Y = 0; Y < Height; ++Y) {
|
||||
CopyMem (
|
||||
(UINT8 *)BltBuffer +
|
||||
(DestinationY + Y) * Delta + DestinationX * sizeof *BltBuffer,
|
||||
VgpuGop->BackingStore +
|
||||
(SourceY + Y) * CurrentHorizontal + SourceX,
|
||||
SegmentSize
|
||||
);
|
||||
}
|
||||
return EFI_SUCCESS;
|
||||
|
||||
case EfiBltBufferToVideo:
|
||||
//
|
||||
// Write data from the BltBuffer rectangle (SourceX, SourceY) (SourceX +
|
||||
// Width, SourceY + Height) directly to the video display rectangle
|
||||
// (DestinationX, DestinationY) (DestinationX + Width, DestinationY +
|
||||
// Height). If SourceX or SourceY is not zero then Delta must be set to the
|
||||
// length in bytes of a row in the BltBuffer.
|
||||
//
|
||||
for (Y = 0; Y < Height; ++Y) {
|
||||
CopyMem (
|
||||
VgpuGop->BackingStore +
|
||||
(DestinationY + Y) * CurrentHorizontal + DestinationX,
|
||||
(UINT8 *)BltBuffer +
|
||||
(SourceY + Y) * Delta + SourceX * sizeof *BltBuffer,
|
||||
SegmentSize
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case EfiBltVideoToVideo:
|
||||
//
|
||||
// Copy from the video display rectangle (SourceX, SourceY) (SourceX +
|
||||
// Width, SourceY + Height) to the video display rectangle (DestinationX,
|
||||
// DestinationY) (DestinationX + Width, DestinationY + Height). The
|
||||
// BltBuffer and Delta are not used in this mode.
|
||||
//
|
||||
// A single invocation of CopyMem() handles overlap between source and
|
||||
// destination (that is, within a single line), but for multiple
|
||||
// invocations, we must handle overlaps.
|
||||
//
|
||||
if (SourceY < DestinationY) {
|
||||
Y = Height;
|
||||
while (Y > 0) {
|
||||
--Y;
|
||||
CopyMem (
|
||||
VgpuGop->BackingStore +
|
||||
(DestinationY + Y) * CurrentHorizontal + DestinationX,
|
||||
VgpuGop->BackingStore +
|
||||
(SourceY + Y) * CurrentHorizontal + SourceX,
|
||||
SegmentSize
|
||||
);
|
||||
}
|
||||
} else {
|
||||
for (Y = 0; Y < Height; ++Y) {
|
||||
CopyMem (
|
||||
VgpuGop->BackingStore +
|
||||
(DestinationY + Y) * CurrentHorizontal + DestinationX,
|
||||
VgpuGop->BackingStore +
|
||||
(SourceY + Y) * CurrentHorizontal + SourceX,
|
||||
SegmentSize
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
//
|
||||
// For operations that wrote to the display, submit the updated area to the
|
||||
// host -- update the host resource from guest memory.
|
||||
//
|
||||
ResourceOffset = sizeof (UINT32) * (DestinationY * CurrentHorizontal +
|
||||
DestinationX);
|
||||
Status = VirtioGpuTransferToHost2d (
|
||||
VgpuGop->ParentBus, // VgpuDev
|
||||
(UINT32)DestinationX, // X
|
||||
(UINT32)DestinationY, // Y
|
||||
(UINT32)Width, // Width
|
||||
(UINT32)Height, // Height
|
||||
ResourceOffset, // Offset
|
||||
VgpuGop->ResourceId // ResourceId
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
|
||||
//
|
||||
// Flush the updated resource to the display.
|
||||
//
|
||||
Status = VirtioGpuResourceFlush (
|
||||
VgpuGop->ParentBus, // VgpuDev
|
||||
(UINT32)DestinationX, // X
|
||||
(UINT32)DestinationY, // Y
|
||||
(UINT32)Width, // Width
|
||||
(UINT32)Height, // Height
|
||||
VgpuGop->ResourceId // ResourceId
|
||||
);
|
||||
return Status;
|
||||
}
|
||||
|
||||
//
|
||||
// Template for initializing VGPU_GOP.Gop.
|
||||
//
|
||||
CONST EFI_GRAPHICS_OUTPUT_PROTOCOL mGopTemplate = {
|
||||
GopQueryMode,
|
||||
GopSetMode,
|
||||
GopBlt,
|
||||
NULL // Mode, to be overwritten in the actual protocol instance
|
||||
};
|
|
@ -20,6 +20,7 @@
|
|||
#include <IndustryStandard/VirtioGpu.h>
|
||||
#include <Library/DebugLib.h>
|
||||
#include <Library/UefiLib.h>
|
||||
#include <Protocol/GraphicsOutput.h>
|
||||
#include <Protocol/VirtioDevice.h>
|
||||
|
||||
//
|
||||
|
@ -114,9 +115,34 @@ struct VGPU_GOP_STRUCT {
|
|||
// The Gop field is installed on the child handle as Graphics Output Protocol
|
||||
// interface.
|
||||
//
|
||||
// For now it is just a placeholder.
|
||||
EFI_GRAPHICS_OUTPUT_PROTOCOL Gop;
|
||||
|
||||
//
|
||||
UINT8 Gop;
|
||||
// Referenced by Gop.Mode, GopMode provides a summary about the supported
|
||||
// graphics modes, and the current mode.
|
||||
//
|
||||
EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE GopMode;
|
||||
|
||||
//
|
||||
// Referenced by GopMode.Info, GopModeInfo provides detailed information
|
||||
// about the current mode.
|
||||
//
|
||||
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION GopModeInfo;
|
||||
|
||||
//
|
||||
// Identifier of the 2D host resource that is in use by this head (scanout)
|
||||
// of the VirtIo GPU device. Zero until the first successful -- internal --
|
||||
// Gop.SetMode() call, never zero afterwards.
|
||||
//
|
||||
UINT32 ResourceId;
|
||||
|
||||
//
|
||||
// A number of whole pages providing the backing store for the 2D host
|
||||
// resource identified by ResourceId above. NULL until the first successful
|
||||
// -- internal -- Gop.SetMode() call, never NULL afterwards.
|
||||
//
|
||||
UINT32 *BackingStore;
|
||||
UINTN NumberOfPages;
|
||||
};
|
||||
|
||||
//
|
||||
|
@ -264,4 +290,38 @@ VirtioGpuResourceFlush (
|
|||
IN UINT32 ResourceId
|
||||
);
|
||||
|
||||
/**
|
||||
Release guest-side and host-side resources that are related to an initialized
|
||||
VGPU_GOP.Gop.
|
||||
|
||||
param[in,out] VgpuGop The VGPU_GOP object to release resources for.
|
||||
|
||||
On input, the caller is responsible for having called
|
||||
VgpuGop->Gop.SetMode() at least once successfully.
|
||||
(This is equivalent to the requirement that
|
||||
VgpuGop->BackingStore be non-NULL. It is also
|
||||
equivalent to the requirement that VgpuGop->ResourceId
|
||||
be nonzero.)
|
||||
|
||||
On output, resources will be released, and
|
||||
VgpuGop->BackingStore and VgpuGop->ResourceId will be
|
||||
nulled.
|
||||
|
||||
param[in] DisableHead Whether this head (scanout) currently references the
|
||||
resource identified by VgpuGop->ResourceId. Only pass
|
||||
FALSE when VgpuGop->Gop.SetMode() calls this function
|
||||
while switching between modes, and set it to TRUE
|
||||
every other time.
|
||||
**/
|
||||
VOID
|
||||
ReleaseGopResources (
|
||||
IN OUT VGPU_GOP *VgpuGop,
|
||||
IN BOOLEAN DisableHead
|
||||
);
|
||||
|
||||
//
|
||||
// Template for initializing VGPU_GOP.Gop.
|
||||
//
|
||||
extern CONST EFI_GRAPHICS_OUTPUT_PROTOCOL mGopTemplate;
|
||||
|
||||
#endif // _VIRTIO_GPU_DXE_H_
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
[Sources]
|
||||
Commands.c
|
||||
DriverBinding.c
|
||||
Gop.c
|
||||
VirtioGpu.h
|
||||
|
||||
[Packages]
|
||||
|
@ -45,5 +46,6 @@
|
|||
|
||||
[Protocols]
|
||||
gEfiDevicePathProtocolGuid ## TO_START ## BY_START
|
||||
gEfiGraphicsOutputProtocolGuid ## BY_START
|
||||
gEfiPciIoProtocolGuid ## TO_START
|
||||
gVirtioDeviceProtocolGuid ## TO_START
|
||||
|
|
Loading…
Reference in New Issue