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
49 // Execute virtio-v1.0-cs04, 3.1.1 Driver Requirements: Device
52 // 1. Reset the device.
55 Status
= VgpuDev
->VirtIo
->SetDeviceStatus (VgpuDev
->VirtIo
, NextDevStat
);
56 if (EFI_ERROR (Status
)) {
61 // 2. Set the ACKNOWLEDGE status bit [...]
63 NextDevStat
|= VSTAT_ACK
;
64 Status
= VgpuDev
->VirtIo
->SetDeviceStatus (VgpuDev
->VirtIo
, NextDevStat
);
65 if (EFI_ERROR (Status
)) {
70 // 3. Set the DRIVER status bit [...]
72 NextDevStat
|= VSTAT_DRIVER
;
73 Status
= VgpuDev
->VirtIo
->SetDeviceStatus (VgpuDev
->VirtIo
, NextDevStat
);
74 if (EFI_ERROR (Status
)) {
79 // 4. Read device feature bits...
81 Status
= VgpuDev
->VirtIo
->GetDeviceFeatures (VgpuDev
->VirtIo
, &Features
);
82 if (EFI_ERROR (Status
)) {
85 if ((Features
& VIRTIO_F_VERSION_1
) == 0) {
86 Status
= EFI_UNSUPPORTED
;
90 // We only want the most basic 2D features.
92 Features
&= VIRTIO_F_VERSION_1
;
95 // ... and write the subset of feature bits understood by the [...] driver to
97 // 5. Set the FEATURES_OK status bit.
98 // 6. Re-read device status to ensure the FEATURES_OK bit is still set [...]
100 Status
= Virtio10WriteFeatures (VgpuDev
->VirtIo
, Features
, &NextDevStat
);
101 if (EFI_ERROR (Status
)) {
106 // 7. Perform device-specific setup, including discovery of virtqueues for
109 Status
= VgpuDev
->VirtIo
->SetQueueSel (VgpuDev
->VirtIo
,
110 VIRTIO_GPU_CONTROL_QUEUE
);
111 if (EFI_ERROR (Status
)) {
114 Status
= VgpuDev
->VirtIo
->GetQueueNumMax (VgpuDev
->VirtIo
, &QueueSize
);
115 if (EFI_ERROR (Status
)) {
120 // We implement each VirtIo GPU command that we use with two descriptors:
121 // request, response.
124 Status
= EFI_UNSUPPORTED
;
129 // [...] population of virtqueues [...]
131 Status
= VirtioRingInit (VgpuDev
->VirtIo
, QueueSize
, &VgpuDev
->Ring
);
132 if (EFI_ERROR (Status
)) {
135 Status
= VgpuDev
->VirtIo
->SetQueueAddress (VgpuDev
->VirtIo
, &VgpuDev
->Ring
);
136 if (EFI_ERROR (Status
)) {
141 // 8. Set the DRIVER_OK status bit.
143 NextDevStat
|= VSTAT_DRIVER_OK
;
144 Status
= VgpuDev
->VirtIo
->SetDeviceStatus (VgpuDev
->VirtIo
, NextDevStat
);
145 if (EFI_ERROR (Status
)) {
152 VirtioRingUninit (VgpuDev
->VirtIo
, &VgpuDev
->Ring
);
156 // If any of these steps go irrecoverably wrong, the driver SHOULD set the
157 // FAILED status bit to indicate that it has given up on the device (it can
158 // reset the device later to restart if desired). [...]
160 // VirtIo access failure here should not mask the original error.
162 NextDevStat
|= VSTAT_FAILED
;
163 VgpuDev
->VirtIo
->SetDeviceStatus (VgpuDev
->VirtIo
, NextDevStat
);
169 De-configure the VirtIo GPU device that underlies VgpuDev.
171 @param[in,out] VgpuDev The VGPU_DEV object to tear down VirtIo messaging
172 for. On input, the caller is responsible for having
173 called VirtioGpuInit(). On output, VgpuDev->Ring has
174 been uninitialized; VirtIo GPU commands (primitives)
175 can no longer be submitted to the device.
179 IN OUT VGPU_DEV
*VgpuDev
183 // Resetting the VirtIo device makes it release its resources and forget its
186 VgpuDev
->VirtIo
->SetDeviceStatus (VgpuDev
->VirtIo
, 0);
187 VirtioRingUninit (VgpuDev
->VirtIo
, &VgpuDev
->Ring
);
191 EFI_EVENT_NOTIFY function for the VGPU_DEV.ExitBoot event. It resets the
192 VirtIo device, causing it to release its resources and to forget its
195 This function may only be called (that is, VGPU_DEV.ExitBoot may only be
196 signaled) after VirtioGpuInit() returns and before VirtioGpuUninit() is
199 @param[in] Event Event whose notification function is being invoked.
201 @param[in] Context Pointer to the associated VGPU_DEV object.
213 VgpuDev
->VirtIo
->SetDeviceStatus (VgpuDev
->VirtIo
, 0);
217 Internal utility function that sends a request to the VirtIo GPU device
218 model, awaits the answer from the host, and returns a status.
220 @param[in,out] VgpuDev The VGPU_DEV object that represents the VirtIo GPU
221 device. The caller is responsible to have
222 successfully invoked VirtioGpuInit() on VgpuDev
223 previously, while VirtioGpuUninit() must not have
224 been called on VgpuDev.
226 @param[in] RequestType The type of the request. The caller is responsible
227 for providing a VirtioGpuCmd* RequestType which, on
228 success, elicits a VirtioGpuRespOkNodata response
231 @param[in] Fence Whether to enable fencing for this request. Fencing
232 forces the host to complete the command before
233 producing a response. If Fence is TRUE, then
234 VgpuDev->FenceId is consumed, and incremented.
236 @param[in,out] Header Pointer to the caller-allocated request object. The
237 request must start with VIRTIO_GPU_CONTROL_HEADER.
238 This function overwrites all fields of Header before
239 submitting the request to the host:
241 - it sets Type from RequestType,
243 - it sets Flags and FenceId based on Fence,
245 - it zeroes CtxId and Padding.
247 @param[in] RequestSize Size of the entire caller-allocated request object,
248 including the leading VIRTIO_GPU_CONTROL_HEADER.
250 @retval EFI_SUCCESS Operation successful.
252 @retval EFI_DEVICE_ERROR The host rejected the request. The host error
253 code has been logged on the EFI_D_ERROR level.
255 @return Codes for unexpected errors in VirtIo
260 VirtioGpuSendCommand (
261 IN OUT VGPU_DEV
*VgpuDev
,
262 IN VIRTIO_GPU_CONTROL_TYPE RequestType
,
264 IN OUT
volatile VIRTIO_GPU_CONTROL_HEADER
*Header
,
268 DESC_INDICES Indices
;
269 volatile VIRTIO_GPU_CONTROL_HEADER Response
;
274 // Initialize Header.
276 Header
->Type
= RequestType
;
278 Header
->Flags
= VIRTIO_GPU_FLAG_FENCE
;
279 Header
->FenceId
= VgpuDev
->FenceId
++;
287 ASSERT (RequestSize
>= sizeof *Header
);
288 ASSERT (RequestSize
<= MAX_UINT32
);
291 // Compose the descriptor chain.
293 VirtioPrepare (&VgpuDev
->Ring
, &Indices
);
294 VirtioAppendDesc (&VgpuDev
->Ring
, (UINTN
)Header
, (UINT32
)RequestSize
,
295 VRING_DESC_F_NEXT
, &Indices
);
296 VirtioAppendDesc (&VgpuDev
->Ring
, (UINTN
)&Response
, sizeof Response
,
297 VRING_DESC_F_WRITE
, &Indices
);
302 Status
= VirtioFlush (VgpuDev
->VirtIo
, VIRTIO_GPU_CONTROL_QUEUE
,
303 &VgpuDev
->Ring
, &Indices
, &ResponseSize
);
304 if (EFI_ERROR (Status
)) {
309 // Parse the response.
311 if (ResponseSize
!= sizeof Response
) {
312 DEBUG ((EFI_D_ERROR
, "%a: malformed response to Request=0x%x\n",
313 __FUNCTION__
, (UINT32
)RequestType
));
314 return EFI_PROTOCOL_ERROR
;
317 if (Response
.Type
== VirtioGpuRespOkNodata
) {
321 DEBUG ((EFI_D_ERROR
, "%a: Request=0x%x Response=0x%x\n", __FUNCTION__
,
322 (UINT32
)RequestType
, Response
.Type
));
323 return EFI_DEVICE_ERROR
;
327 The following functions send requests to the VirtIo GPU device model, await
328 the answer from the host, and return a status. They share the following
331 @param[in,out] VgpuDev The VGPU_DEV object that represents the VirtIo GPU
332 device. The caller is responsible to have
333 successfully invoked VirtioGpuInit() on VgpuDev
334 previously, while VirtioGpuUninit() must not have
335 been called on VgpuDev.
337 @retval EFI_INVALID_PARAMETER Invalid command-specific parameters were
338 detected by this driver.
340 @retval EFI_SUCCESS Operation successful.
342 @retval EFI_DEVICE_ERROR The host rejected the request. The host error
343 code has been logged on the EFI_D_ERROR level.
345 @return Codes for unexpected errors in VirtIo
348 For the command-specific parameters, please consult the GPU Device section of
349 the VirtIo 1.0 specification (see references in
350 "OvmfPkg/Include/IndustryStandard/VirtioGpu.h").
353 VirtioGpuResourceCreate2d (
354 IN OUT VGPU_DEV
*VgpuDev
,
355 IN UINT32 ResourceId
,
356 IN VIRTIO_GPU_FORMATS Format
,
361 volatile VIRTIO_GPU_RESOURCE_CREATE_2D Request
;
363 if (ResourceId
== 0) {
364 return EFI_INVALID_PARAMETER
;
367 Request
.ResourceId
= ResourceId
;
368 Request
.Format
= (UINT32
)Format
;
369 Request
.Width
= Width
;
370 Request
.Height
= Height
;
372 return VirtioGpuSendCommand (
374 VirtioGpuCmdResourceCreate2d
,
382 VirtioGpuResourceUnref (
383 IN OUT VGPU_DEV
*VgpuDev
,
387 volatile VIRTIO_GPU_RESOURCE_UNREF Request
;
389 if (ResourceId
== 0) {
390 return EFI_INVALID_PARAMETER
;
393 Request
.ResourceId
= ResourceId
;
396 return VirtioGpuSendCommand (
398 VirtioGpuCmdResourceUnref
,
406 VirtioGpuResourceAttachBacking (
407 IN OUT VGPU_DEV
*VgpuDev
,
408 IN UINT32 ResourceId
,
409 IN VOID
*FirstBackingPage
,
410 IN UINTN NumberOfPages
413 volatile VIRTIO_GPU_RESOURCE_ATTACH_BACKING Request
;
415 if (ResourceId
== 0) {
416 return EFI_INVALID_PARAMETER
;
419 Request
.ResourceId
= ResourceId
;
420 Request
.NrEntries
= 1;
421 Request
.Entry
.Addr
= (UINTN
)FirstBackingPage
;
422 Request
.Entry
.Length
= (UINT32
)EFI_PAGES_TO_SIZE (NumberOfPages
);
423 Request
.Entry
.Padding
= 0;
425 return VirtioGpuSendCommand (
427 VirtioGpuCmdResourceAttachBacking
,
435 VirtioGpuResourceDetachBacking (
436 IN OUT VGPU_DEV
*VgpuDev
,
440 volatile VIRTIO_GPU_RESOURCE_DETACH_BACKING Request
;
442 if (ResourceId
== 0) {
443 return EFI_INVALID_PARAMETER
;
446 Request
.ResourceId
= ResourceId
;
450 // In this case, we set Fence to TRUE, because after this function returns,
451 // the caller might reasonably want to repurpose the backing pages
452 // immediately. Thus we should ensure that the host releases all references
453 // to the backing pages before we return.
455 return VirtioGpuSendCommand (
457 VirtioGpuCmdResourceDetachBacking
,
465 VirtioGpuSetScanout (
466 IN OUT VGPU_DEV
*VgpuDev
,
475 volatile VIRTIO_GPU_SET_SCANOUT Request
;
478 // Unlike for most other commands, ResourceId=0 is valid; it
479 // is used to disable a scanout.
481 Request
.Rectangle
.X
= X
;
482 Request
.Rectangle
.Y
= Y
;
483 Request
.Rectangle
.Width
= Width
;
484 Request
.Rectangle
.Height
= Height
;
485 Request
.ScanoutId
= ScanoutId
;
486 Request
.ResourceId
= ResourceId
;
488 return VirtioGpuSendCommand (
490 VirtioGpuCmdSetScanout
,
498 VirtioGpuTransferToHost2d (
499 IN OUT VGPU_DEV
*VgpuDev
,
508 volatile VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D Request
;
510 if (ResourceId
== 0) {
511 return EFI_INVALID_PARAMETER
;
514 Request
.Rectangle
.X
= X
;
515 Request
.Rectangle
.Y
= Y
;
516 Request
.Rectangle
.Width
= Width
;
517 Request
.Rectangle
.Height
= Height
;
518 Request
.Offset
= Offset
;
519 Request
.ResourceId
= ResourceId
;
522 return VirtioGpuSendCommand (
524 VirtioGpuCmdTransferToHost2d
,
532 VirtioGpuResourceFlush (
533 IN OUT VGPU_DEV
*VgpuDev
,
541 volatile VIRTIO_GPU_RESOURCE_FLUSH Request
;
543 if (ResourceId
== 0) {
544 return EFI_INVALID_PARAMETER
;
547 Request
.Rectangle
.X
= X
;
548 Request
.Rectangle
.Y
= Y
;
549 Request
.Rectangle
.Width
= Width
;
550 Request
.Rectangle
.Height
= Height
;
551 Request
.ResourceId
= ResourceId
;
554 return VirtioGpuSendCommand (
556 VirtioGpuCmdResourceFlush
,