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