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