3 EFI_GRAPHICS_OUTPUT_PROTOCOL member functions for the VirtIo GPU driver.
5 Copyright (C) 2016, Red Hat, Inc.
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
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.
17 #include <Library/MemoryAllocationLib.h>
19 #include "VirtioGpu.h"
22 Release guest-side and host-side resources that are related to an initialized
25 param[in,out] VgpuGop The VGPU_GOP object to release resources for.
27 On input, the caller is responsible for having called
28 VgpuGop->Gop.SetMode() at least once successfully.
29 (This is equivalent to the requirement that
30 VgpuGop->BackingStore be non-NULL. It is also
31 equivalent to the requirement that VgpuGop->ResourceId
34 On output, resources will be released, and
35 VgpuGop->BackingStore and VgpuGop->ResourceId will be
38 param[in] DisableHead Whether this head (scanout) currently references the
39 resource identified by VgpuGop->ResourceId. Only pass
40 FALSE when VgpuGop->Gop.SetMode() calls this function
41 while switching between modes, and set it to TRUE
46 IN OUT VGPU_GOP
*VgpuGop
,
47 IN BOOLEAN DisableHead
52 ASSERT (VgpuGop
->ResourceId
!= 0);
53 ASSERT (VgpuGop
->BackingStore
!= NULL
);
56 // If any of the following host-side destruction steps fail, we can't get out
57 // of an inconsistent state, so we'll hang. In general errors in object
58 // destruction can hardly be recovered from.
62 // Dissociate head (scanout) #0 from the currently used 2D host resource,
63 // by setting ResourceId=0 for it.
65 Status
= VirtioGpuSetScanout (
66 VgpuGop
->ParentBus
, // VgpuDev
67 0, 0, 0, 0, // X, Y, Width, Height
74 // According to the GPU Device section of the VirtIo specification, the
75 // above operation is valid:
77 // "The driver can use resource_id = 0 to disable a scanout."
79 // However, in practice QEMU does not allow us to disable head (scanout) #0
80 // -- it rejects the command with response code 0x1202
81 // (VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID). Looking at the QEMU source
82 // code, function virtio_gpu_set_scanout() in "hw/display/virtio-gpu.c",
83 // this appears fully intentional, despite not being documented in the
86 // Surprisingly, ignoring the error here, and proceeding to release
87 // host-side resources that presumably underlie head (scanout) #0, work
88 // without any problems -- the driver survives repeated "disconnect" /
89 // "connect -r" commands in the UEFI shell.
91 // So, for now, let's just suppress the error.
98 ASSERT_EFI_ERROR (Status
);
99 if (EFI_ERROR (Status
)) {
105 // Detach backing pages from the currently used 2D host resource.
107 Status
= VirtioGpuResourceDetachBacking (
108 VgpuGop
->ParentBus
, // VgpuDev
109 VgpuGop
->ResourceId
// ResourceId
111 ASSERT_EFI_ERROR (Status
);
112 if (EFI_ERROR (Status
)) {
117 // Unmap and release backing pages.
119 VirtioGpuUnmapAndFreeBackingStore (
120 VgpuGop
->ParentBus
, // VgpuDev
121 VgpuGop
->NumberOfPages
, // NumberOfPages
122 VgpuGop
->BackingStore
, // HostAddress
123 VgpuGop
->BackingStoreMap
// Mapping
125 VgpuGop
->BackingStore
= NULL
;
126 VgpuGop
->NumberOfPages
= 0;
127 VgpuGop
->BackingStoreMap
= NULL
;
130 // Destroy the currently used 2D host resource.
132 Status
= VirtioGpuResourceUnref (
133 VgpuGop
->ParentBus
, // VgpuDev
134 VgpuGop
->ResourceId
// ResourceId
136 ASSERT_EFI_ERROR (Status
);
137 if (EFI_ERROR (Status
)) {
140 VgpuGop
->ResourceId
= 0;
144 // The resolutions supported by this driver.
151 STATIC CONST GOP_RESOLUTION mGopResolutions
[] = {
192 // Macro for casting VGPU_GOP.Gop to VGPU_GOP.
194 #define VGPU_GOP_FROM_GOP(GopPointer) \
195 CR (GopPointer, VGPU_GOP, Gop, VGPU_GOP_SIG)
198 // EFI_GRAPHICS_OUTPUT_PROTOCOL member functions.
204 IN EFI_GRAPHICS_OUTPUT_PROTOCOL
*This
,
205 IN UINT32 ModeNumber
,
206 OUT UINTN
*SizeOfInfo
,
207 OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION
**Info
210 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION
*GopModeInfo
;
212 if (ModeNumber
>= ARRAY_SIZE (mGopResolutions
)) {
213 return EFI_INVALID_PARAMETER
;
216 GopModeInfo
= AllocateZeroPool (sizeof *GopModeInfo
);
217 if (GopModeInfo
== NULL
) {
218 return EFI_OUT_OF_RESOURCES
;
221 GopModeInfo
->HorizontalResolution
= mGopResolutions
[ModeNumber
].Width
;
222 GopModeInfo
->VerticalResolution
= mGopResolutions
[ModeNumber
].Height
;
223 GopModeInfo
->PixelFormat
= PixelBltOnly
;
224 GopModeInfo
->PixelsPerScanLine
= mGopResolutions
[ModeNumber
].Width
;
226 *SizeOfInfo
= sizeof *GopModeInfo
;
235 IN EFI_GRAPHICS_OUTPUT_PROTOCOL
*This
,
240 UINT32 NewResourceId
;
241 UINTN NewNumberOfBytes
;
242 UINTN NewNumberOfPages
;
243 VOID
*NewBackingStore
;
244 EFI_PHYSICAL_ADDRESS NewBackingStoreDeviceAddress
;
245 VOID
*NewBackingStoreMap
;
250 if (ModeNumber
>= ARRAY_SIZE (mGopResolutions
)) {
251 return EFI_UNSUPPORTED
;
254 VgpuGop
= VGPU_GOP_FROM_GOP (This
);
257 // Distinguish the first (internal) call from the other (protocol consumer)
260 if (VgpuGop
->ResourceId
== 0) {
262 // Set up the Gop -> GopMode -> GopModeInfo pointer chain, and the other
263 // (nonzero) constant fields.
265 // No direct framebuffer access is supported, only Blt() is.
267 VgpuGop
->Gop
.Mode
= &VgpuGop
->GopMode
;
269 VgpuGop
->GopMode
.MaxMode
= (UINT32
)(ARRAY_SIZE (mGopResolutions
));
270 VgpuGop
->GopMode
.Info
= &VgpuGop
->GopModeInfo
;
271 VgpuGop
->GopMode
.SizeOfInfo
= sizeof VgpuGop
->GopModeInfo
;
273 VgpuGop
->GopModeInfo
.PixelFormat
= PixelBltOnly
;
276 // This is the first time we create a host side resource.
281 // We already have an active host side resource. Create the new one without
282 // interfering with the current one, so that we can cleanly bail out on
283 // error, without disturbing the current graphics mode.
285 // The formula below will alternate between IDs 1 and 2.
287 NewResourceId
= 3 - VgpuGop
->ResourceId
;
291 // Create the 2D host resource.
293 Status
= VirtioGpuResourceCreate2d (
294 VgpuGop
->ParentBus
, // VgpuDev
295 NewResourceId
, // ResourceId
296 VirtioGpuFormatB8G8R8X8Unorm
, // Format
297 mGopResolutions
[ModeNumber
].Width
, // Width
298 mGopResolutions
[ModeNumber
].Height
// Height
300 if (EFI_ERROR (Status
)) {
305 // Allocate, zero and map guest backing store, for bus master common buffer
308 NewNumberOfBytes
= mGopResolutions
[ModeNumber
].Width
*
309 mGopResolutions
[ModeNumber
].Height
* sizeof (UINT32
);
310 NewNumberOfPages
= EFI_SIZE_TO_PAGES (NewNumberOfBytes
);
311 Status
= VirtioGpuAllocateZeroAndMapBackingStore (
312 VgpuGop
->ParentBus
, // VgpuDev
313 NewNumberOfPages
, // NumberOfPages
314 &NewBackingStore
, // HostAddress
315 &NewBackingStoreDeviceAddress
, // DeviceAddress
316 &NewBackingStoreMap
// Mapping
318 if (EFI_ERROR (Status
)) {
319 goto DestroyHostResource
;
323 // Attach backing store to the host resource.
325 Status
= VirtioGpuResourceAttachBacking (
326 VgpuGop
->ParentBus
, // VgpuDev
327 NewResourceId
, // ResourceId
328 NewBackingStoreDeviceAddress
, // BackingStoreDeviceAddress
329 NewNumberOfPages
// NumberOfPages
331 if (EFI_ERROR (Status
)) {
332 goto UnmapAndFreeBackingStore
;
336 // Point head (scanout) #0 to the host resource.
338 Status
= VirtioGpuSetScanout (
339 VgpuGop
->ParentBus
, // VgpuDev
342 mGopResolutions
[ModeNumber
].Width
, // Width
343 mGopResolutions
[ModeNumber
].Height
, // Height
345 NewResourceId
// ResourceId
347 if (EFI_ERROR (Status
)) {
348 goto DetachBackingStore
;
352 // If this is not the first (i.e., internal) call, then we have to (a) flush
353 // the new resource to head (scanout) #0, after having flipped the latter to
354 // the former above, plus (b) release the old resources.
356 if (VgpuGop
->ResourceId
!= 0) {
357 Status
= VirtioGpuResourceFlush (
358 VgpuGop
->ParentBus
, // VgpuDev
361 mGopResolutions
[ModeNumber
].Width
, // Width
362 mGopResolutions
[ModeNumber
].Height
, // Height
363 NewResourceId
// ResourceId
365 if (EFI_ERROR (Status
)) {
367 // Flip head (scanout) #0 back to the current resource. If this fails, we
368 // cannot continue, as this error occurs on the error path and is
369 // therefore non-recoverable.
371 Status2
= VirtioGpuSetScanout (
372 VgpuGop
->ParentBus
, // VgpuDev
375 mGopResolutions
[This
->Mode
->Mode
].Width
, // Width
376 mGopResolutions
[This
->Mode
->Mode
].Height
, // Height
378 VgpuGop
->ResourceId
// ResourceId
380 ASSERT_EFI_ERROR (Status2
);
381 if (EFI_ERROR (Status2
)) {
384 goto DetachBackingStore
;
388 // Flush successful; release the old resources (without disabling head
391 ReleaseGopResources (VgpuGop
, FALSE
/* DisableHead */);
395 // This is either the first (internal) call when we have no old resources
396 // yet, or we've changed the mode successfully and released the old
399 ASSERT (VgpuGop
->ResourceId
== 0);
400 ASSERT (VgpuGop
->BackingStore
== NULL
);
402 VgpuGop
->ResourceId
= NewResourceId
;
403 VgpuGop
->BackingStore
= NewBackingStore
;
404 VgpuGop
->NumberOfPages
= NewNumberOfPages
;
405 VgpuGop
->BackingStoreMap
= NewBackingStoreMap
;
408 // Populate Mode and ModeInfo (mutable fields only).
410 VgpuGop
->GopMode
.Mode
= ModeNumber
;
411 VgpuGop
->GopModeInfo
.HorizontalResolution
=
412 mGopResolutions
[ModeNumber
].Width
;
413 VgpuGop
->GopModeInfo
.VerticalResolution
= mGopResolutions
[ModeNumber
].Height
;
414 VgpuGop
->GopModeInfo
.PixelsPerScanLine
= mGopResolutions
[ModeNumber
].Width
;
418 Status2
= VirtioGpuResourceDetachBacking (VgpuGop
->ParentBus
, NewResourceId
);
419 ASSERT_EFI_ERROR (Status2
);
420 if (EFI_ERROR (Status2
)) {
424 UnmapAndFreeBackingStore
:
425 VirtioGpuUnmapAndFreeBackingStore (
426 VgpuGop
->ParentBus
, // VgpuDev
427 NewNumberOfPages
, // NumberOfPages
428 NewBackingStore
, // HostAddress
429 NewBackingStoreMap
// Mapping
433 Status2
= VirtioGpuResourceUnref (VgpuGop
->ParentBus
, NewResourceId
);
434 ASSERT_EFI_ERROR (Status2
);
435 if (EFI_ERROR (Status2
)) {
446 IN EFI_GRAPHICS_OUTPUT_PROTOCOL
*This
,
447 IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL
*BltBuffer
, OPTIONAL
448 IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation
,
451 IN UINTN DestinationX
,
452 IN UINTN DestinationY
,
455 IN UINTN Delta OPTIONAL
459 UINT32 CurrentHorizontal
;
460 UINT32 CurrentVertical
;
463 UINTN ResourceOffset
;
466 VgpuGop
= VGPU_GOP_FROM_GOP (This
);
467 CurrentHorizontal
= VgpuGop
->GopModeInfo
.HorizontalResolution
;
468 CurrentVertical
= VgpuGop
->GopModeInfo
.VerticalResolution
;
471 // We can avoid pixel format conversion in the guest because the internal
472 // representation of EFI_GRAPHICS_OUTPUT_BLT_PIXEL and that of
473 // VirtioGpuFormatB8G8R8X8Unorm are identical.
475 SegmentSize
= Width
* sizeof (UINT32
);
478 // Delta is relevant for operations that read a rectangle from, or write a
479 // rectangle to, BltBuffer.
481 // In these cases, Delta is the stride of BltBuffer, in bytes. If Delta is
482 // zero, then Width is the entire width of BltBuffer, and the stride is
483 // supposed to be calculated from Width.
485 if (BltOperation
== EfiBltVideoToBltBuffer
||
486 BltOperation
== EfiBltBufferToVideo
) {
493 // For operations that write to the display, check if the destination fits
496 if (BltOperation
== EfiBltVideoFill
||
497 BltOperation
== EfiBltBufferToVideo
||
498 BltOperation
== EfiBltVideoToVideo
) {
499 if (DestinationX
> CurrentHorizontal
||
500 Width
> CurrentHorizontal
- DestinationX
||
501 DestinationY
> CurrentVertical
||
502 Height
> CurrentVertical
- DestinationY
) {
503 return EFI_INVALID_PARAMETER
;
508 // For operations that read from the display, check if the source fits onto
511 if (BltOperation
== EfiBltVideoToBltBuffer
||
512 BltOperation
== EfiBltVideoToVideo
) {
513 if (SourceX
> CurrentHorizontal
||
514 Width
> CurrentHorizontal
- SourceX
||
515 SourceY
> CurrentVertical
||
516 Height
> CurrentVertical
- SourceY
) {
517 return EFI_INVALID_PARAMETER
;
522 // Render the request. For requests that do not modify the display, there
523 // won't be further steps.
525 switch (BltOperation
) {
526 case EfiBltVideoFill
:
528 // Write data from the BltBuffer pixel (0, 0) directly to every pixel of
529 // the video display rectangle (DestinationX, DestinationY) (DestinationX +
530 // Width, DestinationY + Height). Only one pixel will be used from the
531 // BltBuffer. Delta is NOT used.
533 for (Y
= 0; Y
< Height
; ++Y
) {
535 VgpuGop
->BackingStore
+
536 (DestinationY
+ Y
) * CurrentHorizontal
+ DestinationX
,
543 case EfiBltVideoToBltBuffer
:
545 // Read data from the video display rectangle (SourceX, SourceY) (SourceX +
546 // Width, SourceY + Height) and place it in the BltBuffer rectangle
547 // (DestinationX, DestinationY ) (DestinationX + Width, DestinationY +
548 // Height). If DestinationX or DestinationY is not zero then Delta must be
549 // set to the length in bytes of a row in the BltBuffer.
551 for (Y
= 0; Y
< Height
; ++Y
) {
554 (DestinationY
+ Y
) * Delta
+ DestinationX
* sizeof *BltBuffer
,
555 VgpuGop
->BackingStore
+
556 (SourceY
+ Y
) * CurrentHorizontal
+ SourceX
,
562 case EfiBltBufferToVideo
:
564 // Write data from the BltBuffer rectangle (SourceX, SourceY) (SourceX +
565 // Width, SourceY + Height) directly to the video display rectangle
566 // (DestinationX, DestinationY) (DestinationX + Width, DestinationY +
567 // Height). If SourceX or SourceY is not zero then Delta must be set to the
568 // length in bytes of a row in the BltBuffer.
570 for (Y
= 0; Y
< Height
; ++Y
) {
572 VgpuGop
->BackingStore
+
573 (DestinationY
+ Y
) * CurrentHorizontal
+ DestinationX
,
575 (SourceY
+ Y
) * Delta
+ SourceX
* sizeof *BltBuffer
,
581 case EfiBltVideoToVideo
:
583 // Copy from the video display rectangle (SourceX, SourceY) (SourceX +
584 // Width, SourceY + Height) to the video display rectangle (DestinationX,
585 // DestinationY) (DestinationX + Width, DestinationY + Height). The
586 // BltBuffer and Delta are not used in this mode.
588 // A single invocation of CopyMem() handles overlap between source and
589 // destination (that is, within a single line), but for multiple
590 // invocations, we must handle overlaps.
592 if (SourceY
< DestinationY
) {
597 VgpuGop
->BackingStore
+
598 (DestinationY
+ Y
) * CurrentHorizontal
+ DestinationX
,
599 VgpuGop
->BackingStore
+
600 (SourceY
+ Y
) * CurrentHorizontal
+ SourceX
,
605 for (Y
= 0; Y
< Height
; ++Y
) {
607 VgpuGop
->BackingStore
+
608 (DestinationY
+ Y
) * CurrentHorizontal
+ DestinationX
,
609 VgpuGop
->BackingStore
+
610 (SourceY
+ Y
) * CurrentHorizontal
+ SourceX
,
618 return EFI_INVALID_PARAMETER
;
622 // For operations that wrote to the display, submit the updated area to the
623 // host -- update the host resource from guest memory.
625 ResourceOffset
= sizeof (UINT32
) * (DestinationY
* CurrentHorizontal
+
627 Status
= VirtioGpuTransferToHost2d (
628 VgpuGop
->ParentBus
, // VgpuDev
629 (UINT32
)DestinationX
, // X
630 (UINT32
)DestinationY
, // Y
631 (UINT32
)Width
, // Width
632 (UINT32
)Height
, // Height
633 ResourceOffset
, // Offset
634 VgpuGop
->ResourceId
// ResourceId
636 if (EFI_ERROR (Status
)) {
641 // Flush the updated resource to the display.
643 Status
= VirtioGpuResourceFlush (
644 VgpuGop
->ParentBus
, // VgpuDev
645 (UINT32
)DestinationX
, // X
646 (UINT32
)DestinationY
, // Y
647 (UINT32
)Width
, // Width
648 (UINT32
)Height
, // Height
649 VgpuGop
->ResourceId
// ResourceId
655 // Template for initializing VGPU_GOP.Gop.
657 CONST EFI_GRAPHICS_OUTPUT_PROTOCOL mGopTemplate
= {
661 NULL
// Mode, to be overwritten in the actual protocol instance