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"
|
#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 device path node that describes the Video Output Device Attributes for
|
||||||
// the single head (UEFI child handle) that we support.
|
// the single head (UEFI child handle) that we support.
|
||||||
|
@ -356,9 +346,11 @@ InitVgpuGop (
|
||||||
//
|
//
|
||||||
// Initialize our Graphics Output Protocol.
|
// 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)) {
|
if (EFI_ERROR (Status)) {
|
||||||
goto CloseVirtIoByChild;
|
goto CloseVirtIoByChild;
|
||||||
}
|
}
|
||||||
|
@ -367,7 +359,7 @@ InitVgpuGop (
|
||||||
// Install the Graphics Output Protocol on the child handle.
|
// Install the Graphics Output Protocol on the child handle.
|
||||||
//
|
//
|
||||||
Status = gBS->InstallProtocolInterface (&VgpuGop->GopHandle,
|
Status = gBS->InstallProtocolInterface (&VgpuGop->GopHandle,
|
||||||
&mDummyGraphicsOutputProtocolGuid, EFI_NATIVE_INTERFACE,
|
&gEfiGraphicsOutputProtocolGuid, EFI_NATIVE_INTERFACE,
|
||||||
&VgpuGop->Gop);
|
&VgpuGop->Gop);
|
||||||
if (EFI_ERROR (Status)) {
|
if (EFI_ERROR (Status)) {
|
||||||
goto UninitGop;
|
goto UninitGop;
|
||||||
|
@ -381,9 +373,7 @@ InitVgpuGop (
|
||||||
return EFI_SUCCESS;
|
return EFI_SUCCESS;
|
||||||
|
|
||||||
UninitGop:
|
UninitGop:
|
||||||
//
|
ReleaseGopResources (VgpuGop, TRUE /* DisableHead */);
|
||||||
// Nothing, for now.
|
|
||||||
//
|
|
||||||
|
|
||||||
CloseVirtIoByChild:
|
CloseVirtIoByChild:
|
||||||
gBS->CloseProtocol (ParentBusController, &gVirtioDeviceProtocolGuid,
|
gBS->CloseProtocol (ParentBusController, &gVirtioDeviceProtocolGuid,
|
||||||
|
@ -439,16 +429,13 @@ UninitVgpuGop (
|
||||||
|
|
||||||
VgpuGop = ParentBus->Child;
|
VgpuGop = ParentBus->Child;
|
||||||
Status = gBS->UninstallProtocolInterface (VgpuGop->GopHandle,
|
Status = gBS->UninstallProtocolInterface (VgpuGop->GopHandle,
|
||||||
&mDummyGraphicsOutputProtocolGuid, &VgpuGop->Gop);
|
&gEfiGraphicsOutputProtocolGuid, &VgpuGop->Gop);
|
||||||
ASSERT_EFI_ERROR (Status);
|
ASSERT_EFI_ERROR (Status);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Uninitialize VgpuGop->Gop.
|
// Uninitialize VgpuGop->Gop.
|
||||||
//
|
//
|
||||||
// Nothing, for now.
|
ReleaseGopResources (VgpuGop, TRUE /* DisableHead */);
|
||||||
//
|
|
||||||
Status = EFI_SUCCESS;
|
|
||||||
ASSERT_EFI_ERROR (Status);
|
|
||||||
|
|
||||||
Status = gBS->CloseProtocol (ParentBusController, &gVirtioDeviceProtocolGuid,
|
Status = gBS->CloseProtocol (ParentBusController, &gVirtioDeviceProtocolGuid,
|
||||||
DriverBindingHandle, VgpuGop->GopHandle);
|
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 <IndustryStandard/VirtioGpu.h>
|
||||||
#include <Library/DebugLib.h>
|
#include <Library/DebugLib.h>
|
||||||
#include <Library/UefiLib.h>
|
#include <Library/UefiLib.h>
|
||||||
|
#include <Protocol/GraphicsOutput.h>
|
||||||
#include <Protocol/VirtioDevice.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
|
// The Gop field is installed on the child handle as Graphics Output Protocol
|
||||||
// interface.
|
// 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
|
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_
|
#endif // _VIRTIO_GPU_DXE_H_
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
[Sources]
|
[Sources]
|
||||||
Commands.c
|
Commands.c
|
||||||
DriverBinding.c
|
DriverBinding.c
|
||||||
|
Gop.c
|
||||||
VirtioGpu.h
|
VirtioGpu.h
|
||||||
|
|
||||||
[Packages]
|
[Packages]
|
||||||
|
@ -45,5 +46,6 @@
|
||||||
|
|
||||||
[Protocols]
|
[Protocols]
|
||||||
gEfiDevicePathProtocolGuid ## TO_START ## BY_START
|
gEfiDevicePathProtocolGuid ## TO_START ## BY_START
|
||||||
|
gEfiGraphicsOutputProtocolGuid ## BY_START
|
||||||
gEfiPciIoProtocolGuid ## TO_START
|
gEfiPciIoProtocolGuid ## TO_START
|
||||||
gVirtioDeviceProtocolGuid ## TO_START
|
gVirtioDeviceProtocolGuid ## TO_START
|
||||||
|
|
Loading…
Reference in New Issue