Home | History | Annotate | Download | only in VirtioGpuDxe
      1 /** @file
      2 
      3   EFI_GRAPHICS_OUTPUT_PROTOCOL member functions for the VirtIo GPU driver.
      4 
      5   Copyright (C) 2016, Red Hat, Inc.
      6 
      7   This program and the accompanying materials are licensed and made available
      8   under the terms and conditions of the BSD License which accompanies this
      9   distribution. The full text of the license may be found at
     10   http://opensource.org/licenses/bsd-license.php
     11 
     12   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
     13   WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     14 
     15 **/
     16 
     17 #include <Library/BaseMemoryLib.h>
     18 #include <Library/MemoryAllocationLib.h>
     19 
     20 #include "VirtioGpu.h"
     21 
     22 /**
     23   Release guest-side and host-side resources that are related to an initialized
     24   VGPU_GOP.Gop.
     25 
     26   param[in,out] VgpuGop  The VGPU_GOP object to release resources for.
     27 
     28                          On input, the caller is responsible for having called
     29                          VgpuGop->Gop.SetMode() at least once successfully.
     30                          (This is equivalent to the requirement that
     31                          VgpuGop->BackingStore be non-NULL. It is also
     32                          equivalent to the requirement that VgpuGop->ResourceId
     33                          be nonzero.)
     34 
     35                          On output, resources will be released, and
     36                          VgpuGop->BackingStore and VgpuGop->ResourceId will be
     37                          nulled.
     38 
     39   param[in] DisableHead  Whether this head (scanout) currently references the
     40                          resource identified by VgpuGop->ResourceId. Only pass
     41                          FALSE when VgpuGop->Gop.SetMode() calls this function
     42                          while switching between modes, and set it to TRUE
     43                          every other time.
     44 **/
     45 VOID
     46 ReleaseGopResources (
     47   IN OUT VGPU_GOP *VgpuGop,
     48   IN     BOOLEAN  DisableHead
     49   )
     50 {
     51   EFI_STATUS Status;
     52 
     53   ASSERT (VgpuGop->ResourceId != 0);
     54   ASSERT (VgpuGop->BackingStore != NULL);
     55 
     56   //
     57   // If any of the following host-side destruction steps fail, we can't get out
     58   // of an inconsistent state, so we'll hang. In general errors in object
     59   // destruction can hardly be recovered from.
     60   //
     61   if (DisableHead) {
     62     //
     63     // Dissociate head (scanout) #0 from the currently used 2D host resource,
     64     // by setting ResourceId=0 for it.
     65     //
     66     Status = VirtioGpuSetScanout (
     67                VgpuGop->ParentBus, // VgpuDev
     68                0, 0, 0, 0,         // X, Y, Width, Height
     69                0,                  // ScanoutId
     70                0                   // ResourceId
     71                );
     72     //
     73     // HACK BEGINS HERE
     74     //
     75     // According to the GPU Device section of the VirtIo specification, the
     76     // above operation is valid:
     77     //
     78     // "The driver can use resource_id = 0 to disable a scanout."
     79     //
     80     // However, in practice QEMU does not allow us to disable head (scanout) #0
     81     // -- it rejects the command with response code 0x1202
     82     // (VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID). Looking at the QEMU source
     83     // code, function virtio_gpu_set_scanout() in "hw/display/virtio-gpu.c",
     84     // this appears fully intentional, despite not being documented in the
     85     // spec.
     86     //
     87     // Surprisingly, ignoring the error here, and proceeding to release
     88     // host-side resources that presumably underlie head (scanout) #0, work
     89     // without any problems -- the driver survives repeated "disconnect" /
     90     // "connect -r" commands in the UEFI shell.
     91     //
     92     // So, for now, let's just suppress the error.
     93     //
     94     Status = EFI_SUCCESS;
     95     //
     96     // HACK ENDS HERE
     97     //
     98 
     99     ASSERT_EFI_ERROR (Status);
    100     if (EFI_ERROR (Status)) {
    101       CpuDeadLoop ();
    102     }
    103   }
    104 
    105   //
    106   // Detach backing pages from the currently used 2D host resource.
    107   //
    108   Status = VirtioGpuResourceDetachBacking (
    109              VgpuGop->ParentBus, // VgpuDev
    110              VgpuGop->ResourceId // ResourceId
    111              );
    112   ASSERT_EFI_ERROR (Status);
    113   if (EFI_ERROR (Status)) {
    114     CpuDeadLoop ();
    115   }
    116 
    117   //
    118   // Release backing pages.
    119   //
    120   FreePages (VgpuGop->BackingStore, VgpuGop->NumberOfPages);
    121   VgpuGop->BackingStore  = NULL;
    122   VgpuGop->NumberOfPages = 0;
    123 
    124   //
    125   // Destroy the currently used 2D host resource.
    126   //
    127   Status = VirtioGpuResourceUnref (
    128              VgpuGop->ParentBus, // VgpuDev
    129              VgpuGop->ResourceId // ResourceId
    130              );
    131   ASSERT_EFI_ERROR (Status);
    132   if (EFI_ERROR (Status)) {
    133     CpuDeadLoop ();
    134   }
    135   VgpuGop->ResourceId = 0;
    136 }
    137 
    138 //
    139 // The resolutions supported by this driver.
    140 //
    141 typedef struct {
    142   UINT32 Width;
    143   UINT32 Height;
    144 } GOP_RESOLUTION;
    145 
    146 STATIC CONST GOP_RESOLUTION mGopResolutions[] = {
    147   {  640,  480 },
    148   {  800,  480 },
    149   {  800,  600 },
    150   {  832,  624 },
    151   {  960,  640 },
    152   { 1024,  600 },
    153   { 1024,  768 },
    154   { 1152,  864 },
    155   { 1152,  870 },
    156   { 1280,  720 },
    157   { 1280,  760 },
    158   { 1280,  768 },
    159   { 1280,  800 },
    160   { 1280,  960 },
    161   { 1280, 1024 },
    162   { 1360,  768 },
    163   { 1366,  768 },
    164   { 1400, 1050 },
    165   { 1440,  900 },
    166   { 1600,  900 },
    167   { 1600, 1200 },
    168   { 1680, 1050 },
    169   { 1920, 1080 },
    170   { 1920, 1200 },
    171   { 1920, 1440 },
    172   { 2000, 2000 },
    173   { 2048, 1536 },
    174   { 2048, 2048 },
    175   { 2560, 1440 },
    176   { 2560, 1600 },
    177   { 2560, 2048 },
    178   { 2800, 2100 },
    179   { 3200, 2400 },
    180   { 3840, 2160 },
    181   { 4096, 2160 },
    182   { 7680, 4320 },
    183   { 8192, 4320 },
    184 };
    185 
    186 //
    187 // Macro for casting VGPU_GOP.Gop to VGPU_GOP.
    188 //
    189 #define VGPU_GOP_FROM_GOP(GopPointer) \
    190           CR (GopPointer, VGPU_GOP, Gop, VGPU_GOP_SIG)
    191 
    192 //
    193 // EFI_GRAPHICS_OUTPUT_PROTOCOL member functions.
    194 //
    195 STATIC
    196 EFI_STATUS
    197 EFIAPI
    198 GopQueryMode (
    199   IN  EFI_GRAPHICS_OUTPUT_PROTOCOL         *This,
    200   IN  UINT32                               ModeNumber,
    201   OUT UINTN                                *SizeOfInfo,
    202   OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info
    203   )
    204 {
    205   EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *GopModeInfo;
    206 
    207   if (ModeNumber >= ARRAY_SIZE (mGopResolutions)) {
    208     return EFI_INVALID_PARAMETER;
    209   }
    210 
    211   GopModeInfo = AllocateZeroPool (sizeof *GopModeInfo);
    212   if (GopModeInfo == NULL) {
    213     return EFI_OUT_OF_RESOURCES;
    214   }
    215 
    216   GopModeInfo->HorizontalResolution = mGopResolutions[ModeNumber].Width;
    217   GopModeInfo->VerticalResolution   = mGopResolutions[ModeNumber].Height;
    218   GopModeInfo->PixelFormat          = PixelBltOnly;
    219   GopModeInfo->PixelsPerScanLine    = mGopResolutions[ModeNumber].Width;
    220 
    221   *SizeOfInfo = sizeof *GopModeInfo;
    222   *Info = GopModeInfo;
    223   return EFI_SUCCESS;
    224 }
    225 
    226 STATIC
    227 EFI_STATUS
    228 EFIAPI
    229 GopSetMode (
    230   IN  EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
    231   IN  UINT32                       ModeNumber
    232   )
    233 {
    234   VGPU_GOP   *VgpuGop;
    235   UINT32     NewResourceId;
    236   UINTN      NewNumberOfBytes;
    237   UINTN      NewNumberOfPages;
    238   VOID       *NewBackingStore;
    239   EFI_STATUS Status;
    240   EFI_STATUS Status2;
    241 
    242   if (ModeNumber >= ARRAY_SIZE (mGopResolutions)) {
    243     return EFI_UNSUPPORTED;
    244   }
    245 
    246   VgpuGop = VGPU_GOP_FROM_GOP (This);
    247 
    248   //
    249   // Distinguish the first (internal) call from the other (protocol consumer)
    250   // calls.
    251   //
    252   if (VgpuGop->ResourceId == 0) {
    253     //
    254     // Set up the Gop -> GopMode -> GopModeInfo pointer chain, and the other
    255     // (nonzero) constant fields.
    256     //
    257     // No direct framebuffer access is supported, only Blt() is.
    258     //
    259     VgpuGop->Gop.Mode = &VgpuGop->GopMode;
    260 
    261     VgpuGop->GopMode.MaxMode         = (UINT32)(ARRAY_SIZE (mGopResolutions));
    262     VgpuGop->GopMode.Info            = &VgpuGop->GopModeInfo;
    263     VgpuGop->GopMode.SizeOfInfo      = sizeof VgpuGop->GopModeInfo;
    264 
    265     VgpuGop->GopModeInfo.PixelFormat = PixelBltOnly;
    266 
    267     //
    268     // This is the first time we create a host side resource.
    269     //
    270     NewResourceId = 1;
    271   } else {
    272     //
    273     // We already have an active host side resource. Create the new one without
    274     // interfering with the current one, so that we can cleanly bail out on
    275     // error, without disturbing the current graphics mode.
    276     //
    277     // The formula below will alternate between IDs 1 and 2.
    278     //
    279     NewResourceId = 3 - VgpuGop->ResourceId;
    280   }
    281 
    282   //
    283   // Create the 2D host resource.
    284   //
    285   Status = VirtioGpuResourceCreate2d (
    286              VgpuGop->ParentBus,                // VgpuDev
    287              NewResourceId,                     // ResourceId
    288              VirtioGpuFormatB8G8R8X8Unorm,      // Format
    289              mGopResolutions[ModeNumber].Width, // Width
    290              mGopResolutions[ModeNumber].Height // Height
    291              );
    292   if (EFI_ERROR (Status)) {
    293     return Status;
    294   }
    295 
    296   //
    297   // Allocate guest backing store.
    298   //
    299   NewNumberOfBytes = mGopResolutions[ModeNumber].Width *
    300                      mGopResolutions[ModeNumber].Height * sizeof (UINT32);
    301   NewNumberOfPages = EFI_SIZE_TO_PAGES (NewNumberOfBytes);
    302   NewBackingStore = AllocatePages (NewNumberOfPages);
    303   if (NewBackingStore == NULL) {
    304     Status = EFI_OUT_OF_RESOURCES;
    305     goto DestroyHostResource;
    306   }
    307   //
    308   // Fill visible part of backing store with black.
    309   //
    310   ZeroMem (NewBackingStore, NewNumberOfBytes);
    311 
    312   //
    313   // Attach backing store to the host resource.
    314   //
    315   Status = VirtioGpuResourceAttachBacking (
    316              VgpuGop->ParentBus, // VgpuDev
    317              NewResourceId,      // ResourceId
    318              NewBackingStore,    // FirstBackingPage
    319              NewNumberOfPages    // NumberOfPages
    320              );
    321   if (EFI_ERROR (Status)) {
    322     goto FreeBackingStore;
    323   }
    324 
    325   //
    326   // Point head (scanout) #0 to the host resource.
    327   //
    328   Status = VirtioGpuSetScanout (
    329              VgpuGop->ParentBus,                 // VgpuDev
    330              0,                                  // X
    331              0,                                  // Y
    332              mGopResolutions[ModeNumber].Width,  // Width
    333              mGopResolutions[ModeNumber].Height, // Height
    334              0,                                  // ScanoutId
    335              NewResourceId                       // ResourceId
    336              );
    337   if (EFI_ERROR (Status)) {
    338     goto DetachBackingStore;
    339   }
    340 
    341   //
    342   // If this is not the first (i.e., internal) call, then we have to (a) flush
    343   // the new resource to head (scanout) #0, after having flipped the latter to
    344   // the former above, plus (b) release the old resources.
    345   //
    346   if (VgpuGop->ResourceId != 0) {
    347     Status = VirtioGpuResourceFlush (
    348                VgpuGop->ParentBus,                 // VgpuDev
    349                0,                                  // X
    350                0,                                  // Y
    351                mGopResolutions[ModeNumber].Width,  // Width
    352                mGopResolutions[ModeNumber].Height, // Height
    353                NewResourceId                       // ResourceId
    354                );
    355     if (EFI_ERROR (Status)) {
    356       //
    357       // Flip head (scanout) #0 back to the current resource. If this fails, we
    358       // cannot continue, as this error occurs on the error path and is
    359       // therefore non-recoverable.
    360       //
    361       Status2 = VirtioGpuSetScanout (
    362                   VgpuGop->ParentBus,                       // VgpuDev
    363                   0,                                        // X
    364                   0,                                        // Y
    365                   mGopResolutions[This->Mode->Mode].Width,  // Width
    366                   mGopResolutions[This->Mode->Mode].Height, // Height
    367                   0,                                        // ScanoutId
    368                   VgpuGop->ResourceId                       // ResourceId
    369                   );
    370       ASSERT_EFI_ERROR (Status2);
    371       if (EFI_ERROR (Status2)) {
    372         CpuDeadLoop ();
    373       }
    374       goto DetachBackingStore;
    375     }
    376 
    377     //
    378     // Flush successful; release the old resources (without disabling head
    379     // (scanout) #0).
    380     //
    381     ReleaseGopResources (VgpuGop, FALSE /* DisableHead */);
    382   }
    383 
    384   //
    385   // This is either the first (internal) call when we have no old resources
    386   // yet, or we've changed the mode successfully and released the old
    387   // resources.
    388   //
    389   ASSERT (VgpuGop->ResourceId == 0);
    390   ASSERT (VgpuGop->BackingStore == NULL);
    391 
    392   VgpuGop->ResourceId = NewResourceId;
    393   VgpuGop->BackingStore = NewBackingStore;
    394   VgpuGop->NumberOfPages = NewNumberOfPages;
    395 
    396   //
    397   // Populate Mode and ModeInfo (mutable fields only).
    398   //
    399   VgpuGop->GopMode.Mode = ModeNumber;
    400   VgpuGop->GopModeInfo.HorizontalResolution =
    401                                              mGopResolutions[ModeNumber].Width;
    402   VgpuGop->GopModeInfo.VerticalResolution = mGopResolutions[ModeNumber].Height;
    403   VgpuGop->GopModeInfo.PixelsPerScanLine = mGopResolutions[ModeNumber].Width;
    404   return EFI_SUCCESS;
    405 
    406 DetachBackingStore:
    407   Status2 = VirtioGpuResourceDetachBacking (VgpuGop->ParentBus, NewResourceId);
    408   ASSERT_EFI_ERROR (Status2);
    409   if (EFI_ERROR (Status2)) {
    410     CpuDeadLoop ();
    411   }
    412 
    413 FreeBackingStore:
    414   FreePages (NewBackingStore, NewNumberOfPages);
    415 
    416 DestroyHostResource:
    417   Status2 = VirtioGpuResourceUnref (VgpuGop->ParentBus, NewResourceId);
    418   ASSERT_EFI_ERROR (Status2);
    419   if (EFI_ERROR (Status2)) {
    420     CpuDeadLoop ();
    421   }
    422 
    423   return Status;
    424 }
    425 
    426 STATIC
    427 EFI_STATUS
    428 EFIAPI
    429 GopBlt (
    430   IN  EFI_GRAPHICS_OUTPUT_PROTOCOL      *This,
    431   IN  EFI_GRAPHICS_OUTPUT_BLT_PIXEL     *BltBuffer,   OPTIONAL
    432   IN  EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation,
    433   IN  UINTN                             SourceX,
    434   IN  UINTN                             SourceY,
    435   IN  UINTN                             DestinationX,
    436   IN  UINTN                             DestinationY,
    437   IN  UINTN                             Width,
    438   IN  UINTN                             Height,
    439   IN  UINTN                             Delta         OPTIONAL
    440   )
    441 {
    442   VGPU_GOP   *VgpuGop;
    443   UINT32     CurrentHorizontal;
    444   UINT32     CurrentVertical;
    445   UINTN      SegmentSize;
    446   UINTN      Y;
    447   UINTN      ResourceOffset;
    448   EFI_STATUS Status;
    449 
    450   VgpuGop = VGPU_GOP_FROM_GOP (This);
    451   CurrentHorizontal = VgpuGop->GopModeInfo.HorizontalResolution;
    452   CurrentVertical   = VgpuGop->GopModeInfo.VerticalResolution;
    453 
    454   //
    455   // We can avoid pixel format conversion in the guest because the internal
    456   // representation of EFI_GRAPHICS_OUTPUT_BLT_PIXEL and that of
    457   // VirtioGpuFormatB8G8R8X8Unorm are identical.
    458   //
    459   SegmentSize = Width * sizeof (UINT32);
    460 
    461   //
    462   // Delta is relevant for operations that read a rectangle from, or write a
    463   // rectangle to, BltBuffer.
    464   //
    465   // In these cases, Delta is the stride of BltBuffer, in bytes. If Delta is
    466   // zero, then Width is the entire width of BltBuffer, and the stride is
    467   // supposed to be calculated from Width.
    468   //
    469   if (BltOperation == EfiBltVideoToBltBuffer ||
    470       BltOperation == EfiBltBufferToVideo) {
    471     if (Delta == 0) {
    472       Delta = SegmentSize;
    473     }
    474   }
    475 
    476   //
    477   // For operations that write to the display, check if the destination fits
    478   // onto the display.
    479   //
    480   if (BltOperation == EfiBltVideoFill ||
    481       BltOperation == EfiBltBufferToVideo ||
    482       BltOperation == EfiBltVideoToVideo) {
    483     if (DestinationX > CurrentHorizontal ||
    484         Width > CurrentHorizontal - DestinationX ||
    485         DestinationY > CurrentVertical ||
    486         Height > CurrentVertical - DestinationY) {
    487       return EFI_INVALID_PARAMETER;
    488     }
    489   }
    490 
    491   //
    492   // For operations that read from the display, check if the source fits onto
    493   // the display.
    494   //
    495   if (BltOperation == EfiBltVideoToBltBuffer ||
    496       BltOperation == EfiBltVideoToVideo) {
    497     if (SourceX > CurrentHorizontal ||
    498         Width > CurrentHorizontal - SourceX ||
    499         SourceY > CurrentVertical ||
    500         Height > CurrentVertical - SourceY) {
    501       return EFI_INVALID_PARAMETER;
    502     }
    503   }
    504 
    505   //
    506   // Render the request. For requests that do not modify the display, there
    507   // won't be further steps.
    508   //
    509   switch (BltOperation) {
    510   case EfiBltVideoFill:
    511     //
    512     // Write data from the BltBuffer pixel (0, 0) directly to every pixel of
    513     // the video display rectangle (DestinationX, DestinationY) (DestinationX +
    514     // Width, DestinationY + Height). Only one pixel will be used from the
    515     // BltBuffer. Delta is NOT used.
    516     //
    517     for (Y = 0; Y < Height; ++Y) {
    518       SetMem32 (
    519         VgpuGop->BackingStore +
    520           (DestinationY + Y) * CurrentHorizontal + DestinationX,
    521         SegmentSize,
    522         *(UINT32 *)BltBuffer
    523         );
    524     }
    525     break;
    526 
    527   case EfiBltVideoToBltBuffer:
    528     //
    529     // Read data from the video display rectangle (SourceX, SourceY) (SourceX +
    530     // Width, SourceY + Height) and place it in the BltBuffer rectangle
    531     // (DestinationX, DestinationY ) (DestinationX + Width, DestinationY +
    532     // Height). If DestinationX or DestinationY is not zero then Delta must be
    533     // set to the length in bytes of a row in the BltBuffer.
    534     //
    535     for (Y = 0; Y < Height; ++Y) {
    536       CopyMem (
    537         (UINT8 *)BltBuffer +
    538           (DestinationY + Y) * Delta + DestinationX * sizeof *BltBuffer,
    539         VgpuGop->BackingStore +
    540           (SourceY + Y) * CurrentHorizontal + SourceX,
    541         SegmentSize
    542         );
    543     }
    544     return EFI_SUCCESS;
    545 
    546   case EfiBltBufferToVideo:
    547     //
    548     // Write data from the BltBuffer rectangle (SourceX, SourceY) (SourceX +
    549     // Width, SourceY + Height) directly to the video display rectangle
    550     // (DestinationX, DestinationY) (DestinationX + Width, DestinationY +
    551     // Height). If SourceX or SourceY is not zero then Delta must be set to the
    552     // length in bytes of a row in the BltBuffer.
    553     //
    554     for (Y = 0; Y < Height; ++Y) {
    555       CopyMem (
    556         VgpuGop->BackingStore +
    557           (DestinationY + Y) * CurrentHorizontal + DestinationX,
    558         (UINT8 *)BltBuffer +
    559           (SourceY + Y) * Delta + SourceX * sizeof *BltBuffer,
    560         SegmentSize
    561         );
    562     }
    563     break;
    564 
    565   case EfiBltVideoToVideo:
    566     //
    567     // Copy from the video display rectangle (SourceX, SourceY) (SourceX +
    568     // Width, SourceY + Height) to the video display rectangle (DestinationX,
    569     // DestinationY) (DestinationX + Width, DestinationY + Height). The
    570     // BltBuffer and Delta are not used in this mode.
    571     //
    572     // A single invocation of CopyMem() handles overlap between source and
    573     // destination (that is, within a single line), but for multiple
    574     // invocations, we must handle overlaps.
    575     //
    576     if (SourceY < DestinationY) {
    577       Y = Height;
    578       while (Y > 0) {
    579         --Y;
    580         CopyMem (
    581           VgpuGop->BackingStore +
    582             (DestinationY + Y) * CurrentHorizontal + DestinationX,
    583           VgpuGop->BackingStore +
    584             (SourceY + Y) * CurrentHorizontal + SourceX,
    585           SegmentSize
    586           );
    587       }
    588     } else {
    589       for (Y = 0; Y < Height; ++Y) {
    590         CopyMem (
    591           VgpuGop->BackingStore +
    592             (DestinationY + Y) * CurrentHorizontal + DestinationX,
    593           VgpuGop->BackingStore +
    594             (SourceY + Y) * CurrentHorizontal + SourceX,
    595           SegmentSize
    596           );
    597       }
    598     }
    599     break;
    600 
    601   default:
    602     return EFI_INVALID_PARAMETER;
    603   }
    604 
    605   //
    606   // For operations that wrote to the display, submit the updated area to the
    607   // host -- update the host resource from guest memory.
    608   //
    609   ResourceOffset = sizeof (UINT32) * (DestinationY * CurrentHorizontal +
    610                                       DestinationX);
    611   Status = VirtioGpuTransferToHost2d (
    612              VgpuGop->ParentBus,   // VgpuDev
    613              (UINT32)DestinationX, // X
    614              (UINT32)DestinationY, // Y
    615              (UINT32)Width,        // Width
    616              (UINT32)Height,       // Height
    617              ResourceOffset,       // Offset
    618              VgpuGop->ResourceId   // ResourceId
    619              );
    620   if (EFI_ERROR (Status)) {
    621     return Status;
    622   }
    623 
    624   //
    625   // Flush the updated resource to the display.
    626   //
    627   Status = VirtioGpuResourceFlush (
    628              VgpuGop->ParentBus,   // VgpuDev
    629              (UINT32)DestinationX, // X
    630              (UINT32)DestinationY, // Y
    631              (UINT32)Width,        // Width
    632              (UINT32)Height,       // Height
    633              VgpuGop->ResourceId   // ResourceId
    634              );
    635   return Status;
    636 }
    637 
    638 //
    639 // Template for initializing VGPU_GOP.Gop.
    640 //
    641 CONST EFI_GRAPHICS_OUTPUT_PROTOCOL mGopTemplate = {
    642   GopQueryMode,
    643   GopSetMode,
    644   GopBlt,
    645   NULL          // Mode, to be overwritten in the actual protocol instance
    646 };
    647