https://bugzilla.tianocore.org/show_bug.cgi?id=1373 Replace BSD 2-Clause License with BSD+Patent License. This change is based on the following emails: https://lists.01.org/pipermail/edk2-devel/2019-February/036260.html https://lists.01.org/pipermail/edk2-devel/2018-October/030385.html RFCs with detailed process for the license change: V3: https://lists.01.org/pipermail/edk2-devel/2019-March/038116.html V2: https://lists.01.org/pipermail/edk2-devel/2019-March/037669.html V1: https://lists.01.org/pipermail/edk2-devel/2019-March/037500.html Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Michael D Kinney <michael.d.kinney@intel.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com>
		
			
				
	
	
		
			657 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			657 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
| 
 | |
|   EFI_GRAPHICS_OUTPUT_PROTOCOL member functions for the VirtIo GPU driver.
 | |
| 
 | |
|   Copyright (C) 2016, Red Hat, Inc.
 | |
| 
 | |
|   SPDX-License-Identifier: BSD-2-Clause-Patent
 | |
| 
 | |
| **/
 | |
| 
 | |
| #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 ();
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Unmap and release backing pages.
 | |
|   //
 | |
|   VirtioGpuUnmapAndFreeBackingStore (
 | |
|     VgpuGop->ParentBus,      // VgpuDev
 | |
|     VgpuGop->NumberOfPages,  // NumberOfPages
 | |
|     VgpuGop->BackingStore,   // HostAddress
 | |
|     VgpuGop->BackingStoreMap // Mapping
 | |
|     );
 | |
|   VgpuGop->BackingStore  = NULL;
 | |
|   VgpuGop->NumberOfPages = 0;
 | |
|   VgpuGop->BackingStoreMap = NULL;
 | |
| 
 | |
|   //
 | |
|   // 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 >= ARRAY_SIZE (mGopResolutions)) {
 | |
|     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_PHYSICAL_ADDRESS NewBackingStoreDeviceAddress;
 | |
|   VOID                 *NewBackingStoreMap;
 | |
| 
 | |
|   EFI_STATUS Status;
 | |
|   EFI_STATUS Status2;
 | |
| 
 | |
|   if (ModeNumber >= ARRAY_SIZE (mGopResolutions)) {
 | |
|     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)(ARRAY_SIZE (mGopResolutions));
 | |
|     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, zero and map guest backing store, for bus master common buffer
 | |
|   // operation.
 | |
|   //
 | |
|   NewNumberOfBytes = mGopResolutions[ModeNumber].Width *
 | |
|                      mGopResolutions[ModeNumber].Height * sizeof (UINT32);
 | |
|   NewNumberOfPages = EFI_SIZE_TO_PAGES (NewNumberOfBytes);
 | |
|   Status = VirtioGpuAllocateZeroAndMapBackingStore (
 | |
|              VgpuGop->ParentBus,            // VgpuDev
 | |
|              NewNumberOfPages,              // NumberOfPages
 | |
|              &NewBackingStore,              // HostAddress
 | |
|              &NewBackingStoreDeviceAddress, // DeviceAddress
 | |
|              &NewBackingStoreMap            // Mapping
 | |
|              );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     goto DestroyHostResource;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Attach backing store to the host resource.
 | |
|   //
 | |
|   Status = VirtioGpuResourceAttachBacking (
 | |
|              VgpuGop->ParentBus,           // VgpuDev
 | |
|              NewResourceId,                // ResourceId
 | |
|              NewBackingStoreDeviceAddress, // BackingStoreDeviceAddress
 | |
|              NewNumberOfPages              // NumberOfPages
 | |
|              );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     goto UnmapAndFreeBackingStore;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // 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;
 | |
|   VgpuGop->BackingStoreMap = NewBackingStoreMap;
 | |
| 
 | |
|   //
 | |
|   // 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 ();
 | |
|   }
 | |
| 
 | |
| UnmapAndFreeBackingStore:
 | |
|   VirtioGpuUnmapAndFreeBackingStore (
 | |
|     VgpuGop->ParentBus, // VgpuDev
 | |
|     NewNumberOfPages,   // NumberOfPages
 | |
|     NewBackingStore,    // HostAddress
 | |
|     NewBackingStoreMap  // Mapping
 | |
|     );
 | |
| 
 | |
| 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
 | |
| };
 |