3 VirtIo GPU initialization, and commands (primitives) for the GPU device.
5 Copyright (C) 2016, Red Hat, Inc.
6 Copyright (c) 2017, AMD Inc, All rights reserved.<BR>
8 This program and the accompanying materials are licensed and made available
9 under the terms and conditions of the BSD License which accompanies this
10 distribution. The full text of the license may be found at
11 http://opensource.org/licenses/bsd-license.php
13 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
14 WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 #include <Library/VirtioLib.h>
20 #include "VirtioGpu.h"
23 Configure the VirtIo GPU device that underlies VgpuDev.
25 @param[in,out] VgpuDev The VGPU_DEV object to set up VirtIo messaging for.
26 On input, the caller is responsible for having
27 initialized VgpuDev->VirtIo. On output, VgpuDev->Ring
28 has been initialized, and synchronous VirtIo GPU
29 commands (primitives) can be submitted to the device.
31 @retval EFI_SUCCESS VirtIo GPU configuration successful.
33 @retval EFI_UNSUPPORTED The host-side configuration of the VirtIo GPU is not
34 supported by this driver.
36 @retval Error codes from underlying functions.
40 IN OUT VGPU_DEV
*VgpuDev
50 // Execute virtio-v1.0-cs04, 3.1.1 Driver Requirements: Device
53 // 1. Reset the device.
56 Status
= VgpuDev
->VirtIo
->SetDeviceStatus (VgpuDev
->VirtIo
, NextDevStat
);
57 if (EFI_ERROR (Status
)) {
62 // 2. Set the ACKNOWLEDGE status bit [...]
64 NextDevStat
|= VSTAT_ACK
;
65 Status
= VgpuDev
->VirtIo
->SetDeviceStatus (VgpuDev
->VirtIo
, NextDevStat
);
66 if (EFI_ERROR (Status
)) {
71 // 3. Set the DRIVER status bit [...]
73 NextDevStat
|= VSTAT_DRIVER
;
74 Status
= VgpuDev
->VirtIo
->SetDeviceStatus (VgpuDev
->VirtIo
, NextDevStat
);
75 if (EFI_ERROR (Status
)) {
80 // 4. Read device feature bits...
82 Status
= VgpuDev
->VirtIo
->GetDeviceFeatures (VgpuDev
->VirtIo
, &Features
);
83 if (EFI_ERROR (Status
)) {
86 if ((Features
& VIRTIO_F_VERSION_1
) == 0) {
87 Status
= EFI_UNSUPPORTED
;
91 // We only want the most basic 2D features.
93 Features
&= VIRTIO_F_VERSION_1
;
96 // ... and write the subset of feature bits understood by the [...] driver to
98 // 5. Set the FEATURES_OK status bit.
99 // 6. Re-read device status to ensure the FEATURES_OK bit is still set [...]
101 Status
= Virtio10WriteFeatures (VgpuDev
->VirtIo
, Features
, &NextDevStat
);
102 if (EFI_ERROR (Status
)) {
107 // 7. Perform device-specific setup, including discovery of virtqueues for
110 Status
= VgpuDev
->VirtIo
->SetQueueSel (VgpuDev
->VirtIo
,
111 VIRTIO_GPU_CONTROL_QUEUE
);
112 if (EFI_ERROR (Status
)) {
115 Status
= VgpuDev
->VirtIo
->GetQueueNumMax (VgpuDev
->VirtIo
, &QueueSize
);
116 if (EFI_ERROR (Status
)) {
121 // We implement each VirtIo GPU command that we use with two descriptors:
122 // request, response.
125 Status
= EFI_UNSUPPORTED
;
130 // [...] population of virtqueues [...]
132 Status
= VirtioRingInit (VgpuDev
->VirtIo
, QueueSize
, &VgpuDev
->Ring
);
133 if (EFI_ERROR (Status
)) {
137 // If anything fails from here on, we have to release the ring.
139 Status
= VirtioRingMap (
145 if (EFI_ERROR (Status
)) {
149 // If anything fails from here on, we have to unmap the ring.
151 Status
= VgpuDev
->VirtIo
->SetQueueAddress (
156 if (EFI_ERROR (Status
)) {
161 // 8. Set the DRIVER_OK status bit.
163 NextDevStat
|= VSTAT_DRIVER_OK
;
164 Status
= VgpuDev
->VirtIo
->SetDeviceStatus (VgpuDev
->VirtIo
, NextDevStat
);
165 if (EFI_ERROR (Status
)) {
172 VgpuDev
->VirtIo
->UnmapSharedBuffer (VgpuDev
->VirtIo
, VgpuDev
->RingMap
);
175 VirtioRingUninit (VgpuDev
->VirtIo
, &VgpuDev
->Ring
);
179 // If any of these steps go irrecoverably wrong, the driver SHOULD set the
180 // FAILED status bit to indicate that it has given up on the device (it can
181 // reset the device later to restart if desired). [...]
183 // VirtIo access failure here should not mask the original error.
185 NextDevStat
|= VSTAT_FAILED
;
186 VgpuDev
->VirtIo
->SetDeviceStatus (VgpuDev
->VirtIo
, NextDevStat
);
192 De-configure the VirtIo GPU device that underlies VgpuDev.
194 @param[in,out] VgpuDev The VGPU_DEV object to tear down VirtIo messaging
195 for. On input, the caller is responsible for having
196 called VirtioGpuInit(). On output, VgpuDev->Ring has
197 been uninitialized; VirtIo GPU commands (primitives)
198 can no longer be submitted to the device.
202 IN OUT VGPU_DEV
*VgpuDev
206 // Resetting the VirtIo device makes it release its resources and forget its
209 VgpuDev
->VirtIo
->SetDeviceStatus (VgpuDev
->VirtIo
, 0);
210 VgpuDev
->VirtIo
->UnmapSharedBuffer (VgpuDev
->VirtIo
, VgpuDev
->RingMap
);
211 VirtioRingUninit (VgpuDev
->VirtIo
, &VgpuDev
->Ring
);
215 EFI_EVENT_NOTIFY function for the VGPU_DEV.ExitBoot event. It resets the
216 VirtIo device, causing it to release its resources and to forget its
219 This function may only be called (that is, VGPU_DEV.ExitBoot may only be
220 signaled) after VirtioGpuInit() returns and before VirtioGpuUninit() is
223 @param[in] Event Event whose notification function is being invoked.
225 @param[in] Context Pointer to the associated VGPU_DEV object.
237 VgpuDev
->VirtIo
->SetDeviceStatus (VgpuDev
->VirtIo
, 0);
238 VgpuDev
->VirtIo
->UnmapSharedBuffer (VgpuDev
->VirtIo
, VgpuDev
->RingMap
);
242 Internal utility function that sends a request to the VirtIo GPU device
243 model, awaits the answer from the host, and returns a status.
245 @param[in,out] VgpuDev The VGPU_DEV object that represents the VirtIo GPU
246 device. The caller is responsible to have
247 successfully invoked VirtioGpuInit() on VgpuDev
248 previously, while VirtioGpuUninit() must not have
249 been called on VgpuDev.
251 @param[in] RequestType The type of the request. The caller is responsible
252 for providing a VirtioGpuCmd* RequestType which, on
253 success, elicits a VirtioGpuRespOkNodata response
256 @param[in] Fence Whether to enable fencing for this request. Fencing
257 forces the host to complete the command before
258 producing a response. If Fence is TRUE, then
259 VgpuDev->FenceId is consumed, and incremented.
261 @param[in,out] Header Pointer to the caller-allocated request object. The
262 request must start with VIRTIO_GPU_CONTROL_HEADER.
263 This function overwrites all fields of Header before
264 submitting the request to the host:
266 - it sets Type from RequestType,
268 - it sets Flags and FenceId based on Fence,
270 - it zeroes CtxId and Padding.
272 @param[in] RequestSize Size of the entire caller-allocated request object,
273 including the leading VIRTIO_GPU_CONTROL_HEADER.
275 @retval EFI_SUCCESS Operation successful.
277 @retval EFI_DEVICE_ERROR The host rejected the request. The host error
278 code has been logged on the EFI_D_ERROR level.
280 @return Codes for unexpected errors in VirtIo
281 messaging, or request/response
286 VirtioGpuSendCommand (
287 IN OUT VGPU_DEV
*VgpuDev
,
288 IN VIRTIO_GPU_CONTROL_TYPE RequestType
,
290 IN OUT
volatile VIRTIO_GPU_CONTROL_HEADER
*Header
,
294 DESC_INDICES Indices
;
295 volatile VIRTIO_GPU_CONTROL_HEADER Response
;
298 EFI_PHYSICAL_ADDRESS RequestDeviceAddress
;
300 EFI_PHYSICAL_ADDRESS ResponseDeviceAddress
;
304 // Initialize Header.
306 Header
->Type
= RequestType
;
308 Header
->Flags
= VIRTIO_GPU_FLAG_FENCE
;
309 Header
->FenceId
= VgpuDev
->FenceId
++;
317 ASSERT (RequestSize
>= sizeof *Header
);
318 ASSERT (RequestSize
<= MAX_UINT32
);
321 // Map request and response to bus master device addresses.
323 Status
= VirtioMapAllBytesInSharedBuffer (
325 VirtioOperationBusMasterRead
,
328 &RequestDeviceAddress
,
331 if (EFI_ERROR (Status
)) {
334 Status
= VirtioMapAllBytesInSharedBuffer (
336 VirtioOperationBusMasterWrite
,
339 &ResponseDeviceAddress
,
342 if (EFI_ERROR (Status
)) {
347 // Compose the descriptor chain.
349 VirtioPrepare (&VgpuDev
->Ring
, &Indices
);
352 RequestDeviceAddress
,
359 ResponseDeviceAddress
,
360 (UINT32
)sizeof Response
,
368 Status
= VirtioFlush (VgpuDev
->VirtIo
, VIRTIO_GPU_CONTROL_QUEUE
,
369 &VgpuDev
->Ring
, &Indices
, &ResponseSize
);
370 if (EFI_ERROR (Status
)) {
375 // Verify response size.
377 if (ResponseSize
!= sizeof Response
) {
378 DEBUG ((EFI_D_ERROR
, "%a: malformed response to Request=0x%x\n",
379 __FUNCTION__
, (UINT32
)RequestType
));
380 Status
= EFI_PROTOCOL_ERROR
;
385 // Unmap response and request, in reverse order of mapping. On error, the
386 // respective mapping is invalidated anyway, only the data may not have been
387 // committed to system memory (in case of VirtioOperationBusMasterWrite).
389 Status
= VgpuDev
->VirtIo
->UnmapSharedBuffer (VgpuDev
->VirtIo
, ResponseMap
);
390 if (EFI_ERROR (Status
)) {
393 Status
= VgpuDev
->VirtIo
->UnmapSharedBuffer (VgpuDev
->VirtIo
, RequestMap
);
394 if (EFI_ERROR (Status
)) {
399 // Parse the response.
401 if (Response
.Type
== VirtioGpuRespOkNodata
) {
405 DEBUG ((EFI_D_ERROR
, "%a: Request=0x%x Response=0x%x\n", __FUNCTION__
,
406 (UINT32
)RequestType
, Response
.Type
));
407 return EFI_DEVICE_ERROR
;
410 VgpuDev
->VirtIo
->UnmapSharedBuffer (VgpuDev
->VirtIo
, ResponseMap
);
413 VgpuDev
->VirtIo
->UnmapSharedBuffer (VgpuDev
->VirtIo
, RequestMap
);
419 The following functions send requests to the VirtIo GPU device model, await
420 the answer from the host, and return a status. They share the following
423 @param[in,out] VgpuDev The VGPU_DEV object that represents the VirtIo GPU
424 device. The caller is responsible to have
425 successfully invoked VirtioGpuInit() on VgpuDev
426 previously, while VirtioGpuUninit() must not have
427 been called on VgpuDev.
429 @retval EFI_INVALID_PARAMETER Invalid command-specific parameters were
430 detected by this driver.
432 @retval EFI_SUCCESS Operation successful.
434 @retval EFI_DEVICE_ERROR The host rejected the request. The host error
435 code has been logged on the EFI_D_ERROR level.
437 @return Codes for unexpected errors in VirtIo
440 For the command-specific parameters, please consult the GPU Device section of
441 the VirtIo 1.0 specification (see references in
442 "OvmfPkg/Include/IndustryStandard/VirtioGpu.h").
445 VirtioGpuResourceCreate2d (
446 IN OUT VGPU_DEV
*VgpuDev
,
447 IN UINT32 ResourceId
,
448 IN VIRTIO_GPU_FORMATS Format
,
453 volatile VIRTIO_GPU_RESOURCE_CREATE_2D Request
;
455 if (ResourceId
== 0) {
456 return EFI_INVALID_PARAMETER
;
459 Request
.ResourceId
= ResourceId
;
460 Request
.Format
= (UINT32
)Format
;
461 Request
.Width
= Width
;
462 Request
.Height
= Height
;
464 return VirtioGpuSendCommand (
466 VirtioGpuCmdResourceCreate2d
,
474 VirtioGpuResourceUnref (
475 IN OUT VGPU_DEV
*VgpuDev
,
479 volatile VIRTIO_GPU_RESOURCE_UNREF Request
;
481 if (ResourceId
== 0) {
482 return EFI_INVALID_PARAMETER
;
485 Request
.ResourceId
= ResourceId
;
488 return VirtioGpuSendCommand (
490 VirtioGpuCmdResourceUnref
,
498 VirtioGpuResourceAttachBacking (
499 IN OUT VGPU_DEV
*VgpuDev
,
500 IN UINT32 ResourceId
,
501 IN EFI_PHYSICAL_ADDRESS BackingStoreDeviceAddress
,
502 IN UINTN NumberOfPages
505 volatile VIRTIO_GPU_RESOURCE_ATTACH_BACKING Request
;
507 if (ResourceId
== 0) {
508 return EFI_INVALID_PARAMETER
;
511 Request
.ResourceId
= ResourceId
;
512 Request
.NrEntries
= 1;
513 Request
.Entry
.Addr
= BackingStoreDeviceAddress
;
514 Request
.Entry
.Length
= (UINT32
)EFI_PAGES_TO_SIZE (NumberOfPages
);
515 Request
.Entry
.Padding
= 0;
517 return VirtioGpuSendCommand (
519 VirtioGpuCmdResourceAttachBacking
,
527 VirtioGpuResourceDetachBacking (
528 IN OUT VGPU_DEV
*VgpuDev
,
532 volatile VIRTIO_GPU_RESOURCE_DETACH_BACKING Request
;
534 if (ResourceId
== 0) {
535 return EFI_INVALID_PARAMETER
;
538 Request
.ResourceId
= ResourceId
;
542 // In this case, we set Fence to TRUE, because after this function returns,
543 // the caller might reasonably want to repurpose the backing pages
544 // immediately. Thus we should ensure that the host releases all references
545 // to the backing pages before we return.
547 return VirtioGpuSendCommand (
549 VirtioGpuCmdResourceDetachBacking
,
557 VirtioGpuSetScanout (
558 IN OUT VGPU_DEV
*VgpuDev
,
567 volatile VIRTIO_GPU_SET_SCANOUT Request
;
570 // Unlike for most other commands, ResourceId=0 is valid; it
571 // is used to disable a scanout.
573 Request
.Rectangle
.X
= X
;
574 Request
.Rectangle
.Y
= Y
;
575 Request
.Rectangle
.Width
= Width
;
576 Request
.Rectangle
.Height
= Height
;
577 Request
.ScanoutId
= ScanoutId
;
578 Request
.ResourceId
= ResourceId
;
580 return VirtioGpuSendCommand (
582 VirtioGpuCmdSetScanout
,
590 VirtioGpuTransferToHost2d (
591 IN OUT VGPU_DEV
*VgpuDev
,
600 volatile VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D Request
;
602 if (ResourceId
== 0) {
603 return EFI_INVALID_PARAMETER
;
606 Request
.Rectangle
.X
= X
;
607 Request
.Rectangle
.Y
= Y
;
608 Request
.Rectangle
.Width
= Width
;
609 Request
.Rectangle
.Height
= Height
;
610 Request
.Offset
= Offset
;
611 Request
.ResourceId
= ResourceId
;
614 return VirtioGpuSendCommand (
616 VirtioGpuCmdTransferToHost2d
,
624 VirtioGpuResourceFlush (
625 IN OUT VGPU_DEV
*VgpuDev
,
633 volatile VIRTIO_GPU_RESOURCE_FLUSH Request
;
635 if (ResourceId
== 0) {
636 return EFI_INVALID_PARAMETER
;
639 Request
.Rectangle
.X
= X
;
640 Request
.Rectangle
.Y
= Y
;
641 Request
.Rectangle
.Width
= Width
;
642 Request
.Rectangle
.Height
= Height
;
643 Request
.ResourceId
= ResourceId
;
646 return VirtioGpuSendCommand (
648 VirtioGpuCmdResourceFlush
,