From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 86F5D1A1E25 for ; Fri, 19 Aug 2016 05:49:55 -0700 (PDT) Received: from int-mx11.intmail.prod.int.phx2.redhat.com (int-mx11.intmail.prod.int.phx2.redhat.com [10.5.11.24]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 1FBBB81F01; Fri, 19 Aug 2016 12:49:55 +0000 (UTC) Received: from lacos-laptop-7.usersys.redhat.com (ovpn-116-13.phx2.redhat.com [10.3.116.13]) by int-mx11.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id u7JCnaSp011583; Fri, 19 Aug 2016 08:49:54 -0400 From: Laszlo Ersek To: edk2-devel-01 Cc: Ard Biesheuvel , Jordan Justen Date: Fri, 19 Aug 2016 14:49:31 +0200 Message-Id: <20160819124932.29711-11-lersek@redhat.com> In-Reply-To: <20160819124932.29711-1-lersek@redhat.com> References: <20160819124932.29711-1-lersek@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.68 on 10.5.11.24 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.25]); Fri, 19 Aug 2016 12:49:55 +0000 (UTC) Subject: [PATCH 10/11] OvmfPkg/VirtioGpuDxe: implement EFI_GRAPHICS_OUTPUT_PROTOCOL X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 19 Aug 2016 12:49:55 -0000 Content-Transfer-Encoding: quoted-printable 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 Cc: Jordan Justen Ref: https://tianocore.acgmultimedia.com/show_bug.cgi?id=3D66 Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Laszlo Ersek --- OvmfPkg/VirtioGpuDxe/VirtioGpu.inf | 2 + OvmfPkg/VirtioGpuDxe/VirtioGpu.h | 64 +- OvmfPkg/VirtioGpuDxe/DriverBinding.c | 29 +- OvmfPkg/VirtioGpuDxe/Gop.c | 647 ++++++++++++++++++++ 4 files changed, 719 insertions(+), 23 deletions(-) diff --git a/OvmfPkg/VirtioGpuDxe/VirtioGpu.inf b/OvmfPkg/VirtioGpuDxe/Virt= ioGpu.inf index 7a6269eded51..04bc2964c223 100644 --- a/OvmfPkg/VirtioGpuDxe/VirtioGpu.inf +++ b/OvmfPkg/VirtioGpuDxe/VirtioGpu.inf @@ -23,12 +23,13 @@ [Defines] VERSION_STRING =3D 1.0=0D ENTRY_POINT =3D VirtioGpuEntryPoint=0D =0D [Sources]=0D Commands.c=0D DriverBinding.c=0D + Gop.c=0D VirtioGpu.h=0D =0D [Packages]=0D MdePkg/MdePkg.dec=0D OvmfPkg/OvmfPkg.dec=0D =0D @@ -42,8 +43,9 @@ [LibraryClasses] UefiDriverEntryPoint=0D UefiLib=0D VirtioLib=0D =0D [Protocols]=0D gEfiDevicePathProtocolGuid ## TO_START ## BY_START=0D + gEfiGraphicsOutputProtocolGuid ## BY_START=0D gEfiPciIoProtocolGuid ## TO_START=0D gVirtioDeviceProtocolGuid ## TO_START=0D diff --git a/OvmfPkg/VirtioGpuDxe/VirtioGpu.h b/OvmfPkg/VirtioGpuDxe/Virtio= Gpu.h index f8839922487c..078b7d44d83e 100644 --- a/OvmfPkg/VirtioGpuDxe/VirtioGpu.h +++ b/OvmfPkg/VirtioGpuDxe/VirtioGpu.h @@ -17,12 +17,13 @@ #ifndef _VIRTIO_GPU_DXE_H_=0D #define _VIRTIO_GPU_DXE_H_=0D =0D #include =0D #include =0D #include =0D +#include =0D #include =0D =0D //=0D // Forward declaration of VGPU_GOP.=0D //=0D typedef struct VGPU_GOP_STRUCT VGPU_GOP;=0D @@ -111,15 +112,40 @@ struct VGPU_GOP_STRUCT { EFI_DEVICE_PATH_PROTOCOL *GopDevicePath;=0D =0D //=0D // The Gop field is installed on the child handle as Graphics Output Pro= tocol=0D // interface.=0D //=0D - // For now it is just a placeholder.=0D + EFI_GRAPHICS_OUTPUT_PROTOCOL Gop;=0D +=0D //=0D - UINT8 Gop;=0D + // Referenced by Gop.Mode, GopMode provides a summary about the supporte= d=0D + // graphics modes, and the current mode.=0D + //=0D + EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE GopMode;=0D +=0D + //=0D + // Referenced by GopMode.Info, GopModeInfo provides detailed information= =0D + // about the current mode.=0D + //=0D + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION GopModeInfo;=0D +=0D + //=0D + // Identifier of the 2D host resource that is in use by this head (scano= ut)=0D + // of the VirtIo GPU device. Zero until the first successful -- internal= --=0D + // Gop.SetMode() call, never zero afterwards.=0D + //=0D + UINT32 ResourceId;=0D +=0D + //=0D + // A number of whole pages providing the backing store for the 2D host=0D + // resource identified by ResourceId above. NULL until the first success= ful=0D + // -- internal -- Gop.SetMode() call, never NULL afterwards.=0D + //=0D + UINT32 *BackingStore;=0D + UINTN NumberOfPages;=0D };=0D =0D //=0D // VirtIo GPU initialization, and commands (primitives) for the GPU device= .=0D //=0D /**=0D @@ -261,7 +287,41 @@ VirtioGpuResourceFlush ( IN UINT32 Y,=0D IN UINT32 Width,=0D IN UINT32 Height,=0D IN UINT32 ResourceId=0D );=0D =0D +/**=0D + Release guest-side and host-side resources that are related to an initia= lized=0D + VGPU_GOP.Gop.=0D +=0D + param[in,out] VgpuGop The VGPU_GOP object to release resources for.=0D +=0D + On input, the caller is responsible for having ca= lled=0D + VgpuGop->Gop.SetMode() at least once successfully= .=0D + (This is equivalent to the requirement that=0D + VgpuGop->BackingStore be non-NULL. It is also=0D + equivalent to the requirement that VgpuGop->Resou= rceId=0D + be nonzero.)=0D +=0D + On output, resources will be released, and=0D + VgpuGop->BackingStore and VgpuGop->ResourceId wil= l be=0D + nulled.=0D +=0D + param[in] DisableHead Whether this head (scanout) currently references = the=0D + resource identified by VgpuGop->ResourceId. Only = pass=0D + FALSE when VgpuGop->Gop.SetMode() calls this func= tion=0D + while switching between modes, and set it to TRUE= =0D + every other time.=0D +**/=0D +VOID=0D +ReleaseGopResources (=0D + IN OUT VGPU_GOP *VgpuGop,=0D + IN BOOLEAN DisableHead=0D + );=0D +=0D +//=0D +// Template for initializing VGPU_GOP.Gop.=0D +//=0D +extern CONST EFI_GRAPHICS_OUTPUT_PROTOCOL mGopTemplate;=0D +=0D #endif // _VIRTIO_GPU_DXE_H_=0D diff --git a/OvmfPkg/VirtioGpuDxe/DriverBinding.c b/OvmfPkg/VirtioGpuDxe/Dr= iverBinding.c index bdea55ef7dbf..33c1ad3b3110 100644 --- a/OvmfPkg/VirtioGpuDxe/DriverBinding.c +++ b/OvmfPkg/VirtioGpuDxe/DriverBinding.c @@ -26,22 +26,12 @@ #include =0D #include =0D =0D #include "VirtioGpu.h"=0D =0D //=0D -// Dummy Graphics Output Protocol GUID: a temporary placeholder for the EF= I=0D -// counterpart. It will be replaced with the real thing as soon as we impl= ement=0D -// the EFI GOP. Refer to VGPU_GOP.Gop.=0D -//=0D -STATIC EFI_GUID mDummyGraphicsOutputProtocolGuid =3D {=0D - 0x4983f8dc, 0x2782, 0x415b,=0D - { 0x91, 0xf5, 0x2c, 0xeb, 0x48, 0x4a, 0x0f, 0xe9 }=0D -};=0D -=0D -//=0D // The device path node that describes the Video Output Device Attributes = for=0D // the single head (UEFI child handle) that we support.=0D //=0D // The ACPI_DISPLAY_ADR() macro corresponds to Table B-2, section "B.4.2 _= DOD"=0D // in the ACPI 3.0b spec, or more recently, to Table B-379, section "B.3.2= =0D // _DOD" in the ACPI 6.0 spec.=0D @@ -353,24 +343,26 @@ InitVgpuGop ( }=0D ASSERT (ParentVirtIo =3D=3D ParentBus->VirtIo);=0D =0D //=0D // Initialize our Graphics Output Protocol.=0D //=0D - // This means "nothing" for now.=0D + // Fill in the function members of VgpuGop->Gop from the template, then = set=0D + // up the rest of the GOP infrastructure by calling SetMode() right now.= =0D //=0D - Status =3D EFI_SUCCESS;=0D + CopyMem (&VgpuGop->Gop, &mGopTemplate, sizeof mGopTemplate);=0D + Status =3D VgpuGop->Gop.SetMode (&VgpuGop->Gop, 0);=0D if (EFI_ERROR (Status)) {=0D goto CloseVirtIoByChild;=0D }=0D =0D //=0D // Install the Graphics Output Protocol on the child handle.=0D //=0D Status =3D gBS->InstallProtocolInterface (&VgpuGop->GopHandle,=0D - &mDummyGraphicsOutputProtocolGuid, EFI_NATIVE_INTERFACE,= =0D + &gEfiGraphicsOutputProtocolGuid, EFI_NATIVE_INTERFACE,=0D &VgpuGop->Gop);=0D if (EFI_ERROR (Status)) {=0D goto UninitGop;=0D }=0D =0D //=0D @@ -378,15 +370,13 @@ InitVgpuGop ( //=0D gBS->RestoreTPL (OldTpl);=0D ParentBus->Child =3D VgpuGop;=0D return EFI_SUCCESS;=0D =0D UninitGop:=0D - //=0D - // Nothing, for now.=0D - //=0D + ReleaseGopResources (VgpuGop, TRUE /* DisableHead */);=0D =0D CloseVirtIoByChild:=0D gBS->CloseProtocol (ParentBusController, &gVirtioDeviceProtocolGuid,=0D DriverBindingHandle, VgpuGop->GopHandle);=0D =0D UninstallDevicePath:=0D @@ -436,22 +426,19 @@ UninitVgpuGop ( {=0D VGPU_GOP *VgpuGop;=0D EFI_STATUS Status;=0D =0D VgpuGop =3D ParentBus->Child;=0D Status =3D gBS->UninstallProtocolInterface (VgpuGop->GopHandle,=0D - &mDummyGraphicsOutputProtocolGuid, &VgpuGop->Gop);=0D + &gEfiGraphicsOutputProtocolGuid, &VgpuGop->Gop);=0D ASSERT_EFI_ERROR (Status);=0D =0D //=0D // Uninitialize VgpuGop->Gop.=0D //=0D - // Nothing, for now.=0D - //=0D - Status =3D EFI_SUCCESS;=0D - ASSERT_EFI_ERROR (Status);=0D + ReleaseGopResources (VgpuGop, TRUE /* DisableHead */);=0D =0D Status =3D gBS->CloseProtocol (ParentBusController, &gVirtioDeviceProtoc= olGuid,=0D DriverBindingHandle, VgpuGop->GopHandle);=0D ASSERT_EFI_ERROR (Status);=0D =0D Status =3D gBS->UninstallProtocolInterface (VgpuGop->GopHandle,=0D diff --git a/OvmfPkg/VirtioGpuDxe/Gop.c b/OvmfPkg/VirtioGpuDxe/Gop.c new file mode 100644 index 000000000000..c6ff9ed57461 --- /dev/null +++ b/OvmfPkg/VirtioGpuDxe/Gop.c @@ -0,0 +1,647 @@ +/** @file=0D +=0D + EFI_GRAPHICS_OUTPUT_PROTOCOL member functions for the VirtIo GPU driver.= =0D +=0D + Copyright (C) 2016, Red Hat, Inc.=0D +=0D + This program and the accompanying materials are licensed and made availa= ble=0D + under the terms and conditions of the BSD License which accompanies this= =0D + distribution. The full text of the license may be found at=0D + http://opensource.org/licenses/bsd-license.php=0D +=0D + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WI= THOUT=0D + WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.=0D +=0D +**/=0D +=0D +#include =0D +#include =0D +=0D +#include "VirtioGpu.h"=0D +=0D +/**=0D + Release guest-side and host-side resources that are related to an initia= lized=0D + VGPU_GOP.Gop.=0D +=0D + param[in,out] VgpuGop The VGPU_GOP object to release resources for.=0D +=0D + On input, the caller is responsible for having ca= lled=0D + VgpuGop->Gop.SetMode() at least once successfully= .=0D + (This is equivalent to the requirement that=0D + VgpuGop->BackingStore be non-NULL. It is also=0D + equivalent to the requirement that VgpuGop->Resou= rceId=0D + be nonzero.)=0D +=0D + On output, resources will be released, and=0D + VgpuGop->BackingStore and VgpuGop->ResourceId wil= l be=0D + nulled.=0D +=0D + param[in] DisableHead Whether this head (scanout) currently references = the=0D + resource identified by VgpuGop->ResourceId. Only = pass=0D + FALSE when VgpuGop->Gop.SetMode() calls this func= tion=0D + while switching between modes, and set it to TRUE= =0D + every other time.=0D +**/=0D +VOID=0D +ReleaseGopResources (=0D + IN OUT VGPU_GOP *VgpuGop,=0D + IN BOOLEAN DisableHead=0D + )=0D +{=0D + EFI_STATUS Status;=0D +=0D + ASSERT (VgpuGop->ResourceId !=3D 0);=0D + ASSERT (VgpuGop->BackingStore !=3D NULL);=0D +=0D + //=0D + // If any of the following host-side destruction steps fail, we can't ge= t out=0D + // of an inconsistent state, so we'll hang. In general errors in object= =0D + // destruction can hardly be recovered from.=0D + //=0D + if (DisableHead) {=0D + //=0D + // Dissociate head (scanout) #0 from the currently used 2D host resour= ce,=0D + // by setting ResourceId=3D0 for it.=0D + //=0D + Status =3D VirtioGpuSetScanout (=0D + VgpuGop->ParentBus, // VgpuDev=0D + 0, 0, 0, 0, // X, Y, Width, Height=0D + 0, // ScanoutId=0D + 0 // ResourceId=0D + );=0D + //=0D + // HACK BEGINS HERE=0D + //=0D + // According to the GPU Device section of the VirtIo specification, th= e=0D + // above operation is valid:=0D + //=0D + // "The driver can use resource_id =3D 0 to disable a scanout."=0D + //=0D + // However, in practice QEMU does not allow us to disable head (scanou= t) #0=0D + // -- it rejects the command with response code 0x1202=0D + // (VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID). Looking at the QEMU sourc= e=0D + // code, function virtio_gpu_set_scanout() in "hw/display/virtio-gpu.c= ",=0D + // this appears fully intentional, despite not being documented in the= =0D + // spec.=0D + //=0D + // Surprisingly, ignoring the error here, and proceeding to release=0D + // host-side resources that presumably underlie head (scanout) #0, wor= k=0D + // without any problems -- the driver survives repeated "disconnect" /= =0D + // "connect -r" commands in the UEFI shell.=0D + //=0D + // So, for now, let's just suppress the error.=0D + //=0D + Status =3D EFI_SUCCESS;=0D + //=0D + // HACK ENDS HERE=0D + //=0D +=0D + ASSERT_EFI_ERROR (Status);=0D + if (EFI_ERROR (Status)) {=0D + CpuDeadLoop ();=0D + }=0D + }=0D +=0D + //=0D + // Detach backing pages from the currently used 2D host resource.=0D + //=0D + Status =3D VirtioGpuResourceDetachBacking (=0D + VgpuGop->ParentBus, // VgpuDev=0D + VgpuGop->ResourceId // ResourceId=0D + );=0D + ASSERT_EFI_ERROR (Status);=0D + if (EFI_ERROR (Status)) {=0D + CpuDeadLoop ();=0D + }=0D +=0D + //=0D + // Release backing pages.=0D + //=0D + FreePages (VgpuGop->BackingStore, VgpuGop->NumberOfPages);=0D + VgpuGop->BackingStore =3D NULL;=0D + VgpuGop->NumberOfPages =3D 0;=0D +=0D + //=0D + // Destroy the currently used 2D host resource.=0D + //=0D + Status =3D VirtioGpuResourceUnref (=0D + VgpuGop->ParentBus, // VgpuDev=0D + VgpuGop->ResourceId // ResourceId=0D + );=0D + ASSERT_EFI_ERROR (Status);=0D + if (EFI_ERROR (Status)) {=0D + CpuDeadLoop ();=0D + }=0D + VgpuGop->ResourceId =3D 0;=0D +}=0D +=0D +//=0D +// The resolutions supported by this driver.=0D +//=0D +typedef struct {=0D + UINT32 Width;=0D + UINT32 Height;=0D +} GOP_RESOLUTION;=0D +=0D +STATIC CONST GOP_RESOLUTION mGopResolutions[] =3D {=0D + { 640, 480 },=0D + { 800, 480 },=0D + { 800, 600 },=0D + { 832, 624 },=0D + { 960, 640 },=0D + { 1024, 600 },=0D + { 1024, 768 },=0D + { 1152, 864 },=0D + { 1152, 870 },=0D + { 1280, 720 },=0D + { 1280, 760 },=0D + { 1280, 768 },=0D + { 1280, 800 },=0D + { 1280, 960 },=0D + { 1280, 1024 },=0D + { 1360, 768 },=0D + { 1366, 768 },=0D + { 1400, 1050 },=0D + { 1440, 900 },=0D + { 1600, 900 },=0D + { 1600, 1200 },=0D + { 1680, 1050 },=0D + { 1920, 1080 },=0D + { 1920, 1200 },=0D + { 1920, 1440 },=0D + { 2000, 2000 },=0D + { 2048, 1536 },=0D + { 2048, 2048 },=0D + { 2560, 1440 },=0D + { 2560, 1600 },=0D + { 2560, 2048 },=0D + { 2800, 2100 },=0D + { 3200, 2400 },=0D + { 3840, 2160 },=0D + { 4096, 2160 },=0D + { 7680, 4320 },=0D + { 8192, 4320 },=0D +};=0D +=0D +//=0D +// Macro for casting VGPU_GOP.Gop to VGPU_GOP.=0D +//=0D +#define VGPU_GOP_FROM_GOP(GopPointer) \=0D + CR (GopPointer, VGPU_GOP, Gop, VGPU_GOP_SIG)=0D +=0D +//=0D +// EFI_GRAPHICS_OUTPUT_PROTOCOL member functions.=0D +//=0D +STATIC=0D +EFI_STATUS=0D +EFIAPI=0D +GopQueryMode (=0D + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,=0D + IN UINT32 ModeNumber,=0D + OUT UINTN *SizeOfInfo,=0D + OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info=0D + )=0D +{=0D + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *GopModeInfo;=0D +=0D + if (ModeNumber >=3D sizeof mGopResolutions / sizeof mGopResolutions[0]) = {=0D + return EFI_INVALID_PARAMETER;=0D + }=0D +=0D + GopModeInfo =3D AllocateZeroPool (sizeof *GopModeInfo);=0D + if (GopModeInfo =3D=3D NULL) {=0D + return EFI_OUT_OF_RESOURCES;=0D + }=0D +=0D + GopModeInfo->HorizontalResolution =3D mGopResolutions[ModeNumber].Width;= =0D + GopModeInfo->VerticalResolution =3D mGopResolutions[ModeNumber].Height= ;=0D + GopModeInfo->PixelFormat =3D PixelBltOnly;=0D + GopModeInfo->PixelsPerScanLine =3D mGopResolutions[ModeNumber].Width;= =0D +=0D + *SizeOfInfo =3D sizeof *GopModeInfo;=0D + *Info =3D GopModeInfo;=0D + return EFI_SUCCESS;=0D +}=0D +=0D +STATIC=0D +EFI_STATUS=0D +EFIAPI=0D +GopSetMode (=0D + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,=0D + IN UINT32 ModeNumber=0D + )=0D +{=0D + VGPU_GOP *VgpuGop;=0D + UINT32 NewResourceId;=0D + UINTN NewNumberOfBytes;=0D + UINTN NewNumberOfPages;=0D + VOID *NewBackingStore;=0D + EFI_STATUS Status;=0D + EFI_STATUS Status2;=0D +=0D + if (ModeNumber >=3D sizeof mGopResolutions / sizeof mGopResolutions[0]) = {=0D + return EFI_UNSUPPORTED;=0D + }=0D +=0D + VgpuGop =3D VGPU_GOP_FROM_GOP (This);=0D +=0D + //=0D + // Distinguish the first (internal) call from the other (protocol consum= er)=0D + // calls.=0D + //=0D + if (VgpuGop->ResourceId =3D=3D 0) {=0D + //=0D + // Set up the Gop -> GopMode -> GopModeInfo pointer chain, and the oth= er=0D + // (nonzero) constant fields.=0D + //=0D + // No direct framebuffer access is supported, only Blt() is.=0D + //=0D + VgpuGop->Gop.Mode =3D &VgpuGop->GopMode;=0D +=0D + VgpuGop->GopMode.MaxMode =3D (UINT32)(sizeof mGopResolutions /= =0D + sizeof mGopResolutions[0])= ;=0D + VgpuGop->GopMode.Info =3D &VgpuGop->GopModeInfo;=0D + VgpuGop->GopMode.SizeOfInfo =3D sizeof VgpuGop->GopModeInfo;=0D +=0D + VgpuGop->GopModeInfo.PixelFormat =3D PixelBltOnly;=0D +=0D + //=0D + // This is the first time we create a host side resource.=0D + //=0D + NewResourceId =3D 1;=0D + } else {=0D + //=0D + // We already have an active host side resource. Create the new one wi= thout=0D + // interfering with the current one, so that we can cleanly bail out o= n=0D + // error, without disturbing the current graphics mode.=0D + //=0D + // The formula below will alternate between IDs 1 and 2.=0D + //=0D + NewResourceId =3D 3 - VgpuGop->ResourceId;=0D + }=0D +=0D + //=0D + // Create the 2D host resource.=0D + //=0D + Status =3D VirtioGpuResourceCreate2d (=0D + VgpuGop->ParentBus, // VgpuDev=0D + NewResourceId, // ResourceId=0D + VirtioGpuFormatB8G8R8X8Unorm, // Format=0D + mGopResolutions[ModeNumber].Width, // Width=0D + mGopResolutions[ModeNumber].Height // Height=0D + );=0D + if (EFI_ERROR (Status)) {=0D + return Status;=0D + }=0D +=0D + //=0D + // Allocate guest backing store.=0D + //=0D + NewNumberOfBytes =3D mGopResolutions[ModeNumber].Width *=0D + mGopResolutions[ModeNumber].Height * sizeof (UINT32);= =0D + NewNumberOfPages =3D EFI_SIZE_TO_PAGES (NewNumberOfBytes);=0D + NewBackingStore =3D AllocatePages (NewNumberOfPages);=0D + if (NewBackingStore =3D=3D NULL) {=0D + Status =3D EFI_OUT_OF_RESOURCES;=0D + goto DestroyHostResource;=0D + }=0D + //=0D + // Fill visible part of backing store with black.=0D + //=0D + ZeroMem (NewBackingStore, NewNumberOfBytes);=0D +=0D + //=0D + // Attach backing store to the host resource.=0D + //=0D + Status =3D VirtioGpuResourceAttachBacking (=0D + VgpuGop->ParentBus, // VgpuDev=0D + NewResourceId, // ResourceId=0D + NewBackingStore, // FirstBackingPage=0D + NewNumberOfPages // NumberOfPages=0D + );=0D + if (EFI_ERROR (Status)) {=0D + goto FreeBackingStore;=0D + }=0D +=0D + //=0D + // Point head (scanout) #0 to the host resource.=0D + //=0D + Status =3D VirtioGpuSetScanout (=0D + VgpuGop->ParentBus, // VgpuDev=0D + 0, // X=0D + 0, // Y=0D + mGopResolutions[ModeNumber].Width, // Width=0D + mGopResolutions[ModeNumber].Height, // Height=0D + 0, // ScanoutId=0D + NewResourceId // ResourceId=0D + );=0D + if (EFI_ERROR (Status)) {=0D + goto DetachBackingStore;=0D + }=0D +=0D + //=0D + // If this is not the first (i.e., internal) call, then we have to (a) f= lush=0D + // the new resource to head (scanout) #0, after having flipped the latte= r to=0D + // the former above, plus (b) release the old resources.=0D + //=0D + if (VgpuGop->ResourceId !=3D 0) {=0D + Status =3D VirtioGpuResourceFlush (=0D + VgpuGop->ParentBus, // VgpuDev=0D + 0, // X=0D + 0, // Y=0D + mGopResolutions[ModeNumber].Width, // Width=0D + mGopResolutions[ModeNumber].Height, // Height=0D + NewResourceId // ResourceId=0D + );=0D + if (EFI_ERROR (Status)) {=0D + //=0D + // Flip head (scanout) #0 back to the current resource. If this fail= s, we=0D + // cannot continue, as this error occurs on the error path and is=0D + // therefore non-recoverable.=0D + //=0D + Status2 =3D VirtioGpuSetScanout (=0D + VgpuGop->ParentBus, // VgpuDev=0D + 0, // X=0D + 0, // Y=0D + mGopResolutions[This->Mode->Mode].Width, // Width=0D + mGopResolutions[This->Mode->Mode].Height, // Height=0D + 0, // ScanoutId=0D + VgpuGop->ResourceId // ResourceId= =0D + );=0D + ASSERT_EFI_ERROR (Status2);=0D + if (EFI_ERROR (Status2)) {=0D + CpuDeadLoop ();=0D + }=0D + goto DetachBackingStore;=0D + }=0D +=0D + //=0D + // Flush successful; release the old resources (without disabling head= =0D + // (scanout) #0).=0D + //=0D + ReleaseGopResources (VgpuGop, FALSE /* DisableHead */);=0D + }=0D +=0D + //=0D + // This is either the first (internal) call when we have no old resource= s=0D + // yet, or we've changed the mode successfully and released the old=0D + // resources.=0D + //=0D + ASSERT (VgpuGop->ResourceId =3D=3D 0);=0D + ASSERT (VgpuGop->BackingStore =3D=3D NULL);=0D +=0D + VgpuGop->ResourceId =3D NewResourceId;=0D + VgpuGop->BackingStore =3D NewBackingStore;=0D + VgpuGop->NumberOfPages =3D NewNumberOfPages;=0D +=0D + //=0D + // Populate Mode and ModeInfo (mutable fields only).=0D + //=0D + VgpuGop->GopMode.Mode =3D ModeNumber;=0D + VgpuGop->GopModeInfo.HorizontalResolution =3D=0D + mGopResolutions[ModeNumber].W= idth;=0D + VgpuGop->GopModeInfo.VerticalResolution =3D mGopResolutions[ModeNumber].= Height;=0D + VgpuGop->GopModeInfo.PixelsPerScanLine =3D mGopResolutions[ModeNumber].W= idth;=0D + return EFI_SUCCESS;=0D +=0D +DetachBackingStore:=0D + Status2 =3D VirtioGpuResourceDetachBacking (VgpuGop->ParentBus, NewResou= rceId);=0D + ASSERT_EFI_ERROR (Status2);=0D + if (EFI_ERROR (Status2)) {=0D + CpuDeadLoop ();=0D + }=0D +=0D +FreeBackingStore:=0D + FreePages (NewBackingStore, NewNumberOfPages);=0D +=0D +DestroyHostResource:=0D + Status2 =3D VirtioGpuResourceUnref (VgpuGop->ParentBus, NewResourceId);= =0D + ASSERT_EFI_ERROR (Status2);=0D + if (EFI_ERROR (Status2)) {=0D + CpuDeadLoop ();=0D + }=0D +=0D + return Status;=0D +}=0D +=0D +STATIC=0D +EFI_STATUS=0D +EFIAPI=0D +GopBlt (=0D + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,=0D + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL=0D + IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation,=0D + IN UINTN SourceX,=0D + IN UINTN SourceY,=0D + IN UINTN DestinationX,=0D + IN UINTN DestinationY,=0D + IN UINTN Width,=0D + IN UINTN Height,=0D + IN UINTN Delta OPTIONAL=0D + )=0D +{=0D + VGPU_GOP *VgpuGop;=0D + UINT32 CurrentHorizontal;=0D + UINT32 CurrentVertical;=0D + UINTN SegmentSize;=0D + UINTN Y;=0D + UINTN ResourceOffset;=0D + EFI_STATUS Status;=0D +=0D + VgpuGop =3D VGPU_GOP_FROM_GOP (This);=0D + CurrentHorizontal =3D VgpuGop->GopModeInfo.HorizontalResolution;=0D + CurrentVertical =3D VgpuGop->GopModeInfo.VerticalResolution;=0D +=0D + //=0D + // We can avoid pixel format conversion in the guest because the interna= l=0D + // representation of EFI_GRAPHICS_OUTPUT_BLT_PIXEL and that of=0D + // VirtioGpuFormatB8G8R8X8Unorm are identical.=0D + //=0D + SegmentSize =3D Width * sizeof (UINT32);=0D +=0D + //=0D + // Delta is relevant for operations that read a rectangle from, or write= a=0D + // rectangle to, BltBuffer.=0D + //=0D + // In these cases, Delta is the stride of BltBuffer, in bytes. If Delta = is=0D + // zero, then Width is the entire width of BltBuffer, and the stride is= =0D + // supposed to be calculated from Width.=0D + //=0D + if (BltOperation =3D=3D EfiBltVideoToBltBuffer ||=0D + BltOperation =3D=3D EfiBltBufferToVideo) {=0D + if (Delta =3D=3D 0) {=0D + Delta =3D SegmentSize;=0D + }=0D + }=0D +=0D + //=0D + // For operations that write to the display, check if the destination fi= ts=0D + // onto the display.=0D + //=0D + if (BltOperation =3D=3D EfiBltVideoFill ||=0D + BltOperation =3D=3D EfiBltBufferToVideo ||=0D + BltOperation =3D=3D EfiBltVideoToVideo) {=0D + if (DestinationX > CurrentHorizontal ||=0D + Width > CurrentHorizontal - DestinationX ||=0D + DestinationY > CurrentVertical ||=0D + Height > CurrentVertical - DestinationY) {=0D + return EFI_INVALID_PARAMETER;=0D + }=0D + }=0D +=0D + //=0D + // For operations that read from the display, check if the source fits o= nto=0D + // the display.=0D + //=0D + if (BltOperation =3D=3D EfiBltVideoToBltBuffer ||=0D + BltOperation =3D=3D EfiBltVideoToVideo) {=0D + if (SourceX > CurrentHorizontal ||=0D + Width > CurrentHorizontal - SourceX ||=0D + SourceY > CurrentVertical ||=0D + Height > CurrentVertical - SourceY) {=0D + return EFI_INVALID_PARAMETER;=0D + }=0D + }=0D +=0D + //=0D + // Render the request. For requests that do not modify the display, ther= e=0D + // won't be further steps.=0D + //=0D + switch (BltOperation) {=0D + case EfiBltVideoFill:=0D + //=0D + // Write data from the BltBuffer pixel (0, 0) directly to every pixel = of=0D + // the video display rectangle (DestinationX, DestinationY) (Destinati= onX +=0D + // Width, DestinationY + Height). Only one pixel will be used from the= =0D + // BltBuffer. Delta is NOT used.=0D + //=0D + for (Y =3D 0; Y < Height; ++Y) {=0D + SetMem32 (=0D + VgpuGop->BackingStore +=0D + (DestinationY + Y) * CurrentHorizontal + DestinationX,=0D + SegmentSize,=0D + *(UINT32 *)BltBuffer=0D + );=0D + }=0D + break;=0D +=0D + case EfiBltVideoToBltBuffer:=0D + //=0D + // Read data from the video display rectangle (SourceX, SourceY) (Sour= ceX +=0D + // Width, SourceY + Height) and place it in the BltBuffer rectangle=0D + // (DestinationX, DestinationY ) (DestinationX + Width, DestinationY += =0D + // Height). If DestinationX or DestinationY is not zero then Delta mus= t be=0D + // set to the length in bytes of a row in the BltBuffer.=0D + //=0D + for (Y =3D 0; Y < Height; ++Y) {=0D + CopyMem (=0D + (UINT8 *)BltBuffer +=0D + (DestinationY + Y) * Delta + DestinationX * sizeof *BltBuffer,=0D + VgpuGop->BackingStore +=0D + (SourceY + Y) * CurrentHorizontal + SourceX,=0D + SegmentSize=0D + );=0D + }=0D + return EFI_SUCCESS;=0D +=0D + case EfiBltBufferToVideo:=0D + //=0D + // Write data from the BltBuffer rectangle (SourceX, SourceY) (SourceX= +=0D + // Width, SourceY + Height) directly to the video display rectangle=0D + // (DestinationX, DestinationY) (DestinationX + Width, DestinationY += =0D + // Height). If SourceX or SourceY is not zero then Delta must be set t= o the=0D + // length in bytes of a row in the BltBuffer.=0D + //=0D + for (Y =3D 0; Y < Height; ++Y) {=0D + CopyMem (=0D + VgpuGop->BackingStore +=0D + (DestinationY + Y) * CurrentHorizontal + DestinationX,=0D + (UINT8 *)BltBuffer +=0D + (SourceY + Y) * Delta + SourceX * sizeof *BltBuffer,=0D + SegmentSize=0D + );=0D + }=0D + break;=0D +=0D + case EfiBltVideoToVideo:=0D + //=0D + // Copy from the video display rectangle (SourceX, SourceY) (SourceX += =0D + // Width, SourceY + Height) to the video display rectangle (Destinatio= nX,=0D + // DestinationY) (DestinationX + Width, DestinationY + Height). The=0D + // BltBuffer and Delta are not used in this mode.=0D + //=0D + // A single invocation of CopyMem() handles overlap between source and= =0D + // destination (that is, within a single line), but for multiple=0D + // invocations, we must handle overlaps.=0D + //=0D + if (SourceY < DestinationY) {=0D + Y =3D Height;=0D + while (Y > 0) {=0D + --Y;=0D + CopyMem (=0D + VgpuGop->BackingStore +=0D + (DestinationY + Y) * CurrentHorizontal + DestinationX,=0D + VgpuGop->BackingStore +=0D + (SourceY + Y) * CurrentHorizontal + SourceX,=0D + SegmentSize=0D + );=0D + }=0D + } else {=0D + for (Y =3D 0; Y < Height; ++Y) {=0D + CopyMem (=0D + VgpuGop->BackingStore +=0D + (DestinationY + Y) * CurrentHorizontal + DestinationX,=0D + VgpuGop->BackingStore +=0D + (SourceY + Y) * CurrentHorizontal + SourceX,=0D + SegmentSize=0D + );=0D + }=0D + }=0D + break;=0D +=0D + default:=0D + return EFI_INVALID_PARAMETER;=0D + }=0D +=0D + //=0D + // For operations that wrote to the display, submit the updated area to = the=0D + // host -- update the host resource from guest memory.=0D + //=0D + ResourceOffset =3D sizeof (UINT32) * (DestinationY * CurrentHorizontal += =0D + DestinationX);=0D + Status =3D VirtioGpuTransferToHost2d (=0D + VgpuGop->ParentBus, // VgpuDev=0D + (UINT32)DestinationX, // X=0D + (UINT32)DestinationY, // Y=0D + (UINT32)Width, // Width=0D + (UINT32)Height, // Height=0D + ResourceOffset, // Offset=0D + VgpuGop->ResourceId // ResourceId=0D + );=0D + if (EFI_ERROR (Status)) {=0D + return Status;=0D + }=0D +=0D + //=0D + // Flush the updated resource to the display.=0D + //=0D + Status =3D VirtioGpuResourceFlush (=0D + VgpuGop->ParentBus, // VgpuDev=0D + (UINT32)DestinationX, // X=0D + (UINT32)DestinationY, // Y=0D + (UINT32)Width, // Width=0D + (UINT32)Height, // Height=0D + VgpuGop->ResourceId // ResourceId=0D + );=0D + return Status;=0D +}=0D +=0D +//=0D +// Template for initializing VGPU_GOP.Gop.=0D +//=0D +CONST EFI_GRAPHICS_OUTPUT_PROTOCOL mGopTemplate =3D {=0D + GopQueryMode,=0D + GopSetMode,=0D + GopBlt,=0D + NULL // Mode, to be overwritten in the actual protocol instance= =0D +};=0D --=20 2.9.2