3 VirtIo GPU initialization, and commands (primitives) for the GPU device.
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/VirtioLib.h>
19 #include "VirtioGpu.h"
22 Configure the VirtIo GPU device that underlies VgpuDev.
24 @param[in,out] VgpuDev The VGPU_DEV object to set up VirtIo messaging for.
25 On input, the caller is responsible for having
26 initialized VgpuDev->VirtIo. On output, VgpuDev->Ring
27 has been initialized, and synchronous VirtIo GPU
28 commands (primitives) can be submitted to the device.
30 @retval EFI_SUCCESS VirtIo GPU configuration successful.
32 @retval EFI_UNSUPPORTED The host-side configuration of the VirtIo GPU is not
33 supported by this driver.
35 @retval Error codes from underlying functions.
39 IN OUT VGPU_DEV
*VgpuDev
48 // Execute virtio-v1.0-cs04, 3.1.1 Driver Requirements: Device
51 // 1. Reset the device.
54 Status
= VgpuDev
->VirtIo
->SetDeviceStatus (VgpuDev
->VirtIo
, NextDevStat
);
55 if (EFI_ERROR (Status
)) {
60 // 2. Set the ACKNOWLEDGE status bit [...]
62 NextDevStat
|= VSTAT_ACK
;
63 Status
= VgpuDev
->VirtIo
->SetDeviceStatus (VgpuDev
->VirtIo
, NextDevStat
);
64 if (EFI_ERROR (Status
)) {
69 // 3. Set the DRIVER status bit [...]
71 NextDevStat
|= VSTAT_DRIVER
;
72 Status
= VgpuDev
->VirtIo
->SetDeviceStatus (VgpuDev
->VirtIo
, NextDevStat
);
73 if (EFI_ERROR (Status
)) {
78 // 4. Read device feature bits...
80 Status
= VgpuDev
->VirtIo
->GetDeviceFeatures (VgpuDev
->VirtIo
, &Features
);
81 if (EFI_ERROR (Status
)) {
84 if ((Features
& VIRTIO_F_VERSION_1
) == 0) {
85 Status
= EFI_UNSUPPORTED
;
89 // We only want the most basic 2D features.
91 Features
&= VIRTIO_F_VERSION_1
;
94 // ... and write the subset of feature bits understood by the [...] driver to
96 // 5. Set the FEATURES_OK status bit.
97 // 6. Re-read device status to ensure the FEATURES_OK bit is still set [...]
99 Status
= Virtio10WriteFeatures (VgpuDev
->VirtIo
, Features
, &NextDevStat
);
100 if (EFI_ERROR (Status
)) {
105 // 7. Perform device-specific setup, including discovery of virtqueues for
108 Status
= VgpuDev
->VirtIo
->SetQueueSel (VgpuDev
->VirtIo
,
109 VIRTIO_GPU_CONTROL_QUEUE
);
110 if (EFI_ERROR (Status
)) {
113 Status
= VgpuDev
->VirtIo
->GetQueueNumMax (VgpuDev
->VirtIo
, &QueueSize
);
114 if (EFI_ERROR (Status
)) {
119 // We implement each VirtIo GPU command that we use with two descriptors:
120 // request, response.
123 Status
= EFI_UNSUPPORTED
;
128 // [...] population of virtqueues [...]
130 Status
= VirtioRingInit (QueueSize
, &VgpuDev
->Ring
);
131 if (EFI_ERROR (Status
)) {
134 Status
= VgpuDev
->VirtIo
->SetQueueAddress (VgpuDev
->VirtIo
, &VgpuDev
->Ring
);
135 if (EFI_ERROR (Status
)) {
140 // 8. Set the DRIVER_OK status bit.
142 NextDevStat
|= VSTAT_DRIVER_OK
;
143 Status
= VgpuDev
->VirtIo
->SetDeviceStatus (VgpuDev
->VirtIo
, NextDevStat
);
144 if (EFI_ERROR (Status
)) {
151 VirtioRingUninit (&VgpuDev
->Ring
);
155 // If any of these steps go irrecoverably wrong, the driver SHOULD set the
156 // FAILED status bit to indicate that it has given up on the device (it can
157 // reset the device later to restart if desired). [...]
159 // VirtIo access failure here should not mask the original error.
161 NextDevStat
|= VSTAT_FAILED
;
162 VgpuDev
->VirtIo
->SetDeviceStatus (VgpuDev
->VirtIo
, NextDevStat
);
168 De-configure the VirtIo GPU device that underlies VgpuDev.
170 @param[in,out] VgpuDev The VGPU_DEV object to tear down VirtIo messaging
171 for. On input, the caller is responsible for having
172 called VirtioGpuInit(). On output, VgpuDev->Ring has
173 been uninitialized; VirtIo GPU commands (primitives)
174 can no longer be submitted to the device.
178 IN OUT VGPU_DEV
*VgpuDev
182 // Resetting the VirtIo device makes it release its resources and forget its
185 VgpuDev
->VirtIo
->SetDeviceStatus (VgpuDev
->VirtIo
, 0);
186 VirtioRingUninit (&VgpuDev
->Ring
);
190 EFI_EVENT_NOTIFY function for the VGPU_DEV.ExitBoot event. It resets the
191 VirtIo device, causing it to release its resources and to forget its
194 This function may only be called (that is, VGPU_DEV.ExitBoot may only be
195 signaled) after VirtioGpuInit() returns and before VirtioGpuUninit() is
198 @param[in] Event Event whose notification function is being invoked.
200 @param[in] Context Pointer to the associated VGPU_DEV object.
212 VgpuDev
->VirtIo
->SetDeviceStatus (VgpuDev
->VirtIo
, 0);
216 Internal utility function that sends a request to the VirtIo GPU device
217 model, awaits the answer from the host, and returns a status.
219 @param[in,out] VgpuDev The VGPU_DEV object that represents the VirtIo GPU
220 device. The caller is responsible to have
221 successfully invoked VirtioGpuInit() on VgpuDev
222 previously, while VirtioGpuUninit() must not have
223 been called on VgpuDev.
225 @param[in] RequestType The type of the request. The caller is responsible
226 for providing a VirtioGpuCmd* RequestType which, on
227 success, elicits a VirtioGpuRespOkNodata response
230 @param[in] Fence Whether to enable fencing for this request. Fencing
231 forces the host to complete the command before
232 producing a response. If Fence is TRUE, then
233 VgpuDev->FenceId is consumed, and incremented.
235 @param[in,out] Header Pointer to the caller-allocated request object. The
236 request must start with VIRTIO_GPU_CONTROL_HEADER.
237 This function overwrites all fields of Header before
238 submitting the request to the host:
240 - it sets Type from RequestType,
242 - it sets Flags and FenceId based on Fence,
244 - it zeroes CtxId and Padding.
246 @param[in] RequestSize Size of the entire caller-allocated request object,
247 including the leading VIRTIO_GPU_CONTROL_HEADER.
249 @retval EFI_SUCCESS Operation successful.
251 @retval EFI_DEVICE_ERROR The host rejected the request. The host error
252 code has been logged on the EFI_D_ERROR level.
254 @return Codes for unexpected errors in VirtIo
259 VirtioGpuSendCommand (
260 IN OUT VGPU_DEV
*VgpuDev
,
261 IN VIRTIO_GPU_CONTROL_TYPE RequestType
,
263 IN OUT
volatile VIRTIO_GPU_CONTROL_HEADER
*Header
,
267 DESC_INDICES Indices
;
268 volatile VIRTIO_GPU_CONTROL_HEADER Response
;
273 // Initialize Header.
275 Header
->Type
= RequestType
;
277 Header
->Flags
= VIRTIO_GPU_FLAG_FENCE
;
278 Header
->FenceId
= VgpuDev
->FenceId
++;
286 ASSERT (RequestSize
>= sizeof *Header
);
287 ASSERT (RequestSize
<= MAX_UINT32
);
290 // Compose the descriptor chain.
292 VirtioPrepare (&VgpuDev
->Ring
, &Indices
);
293 VirtioAppendDesc (&VgpuDev
->Ring
, (UINTN
)Header
, (UINT32
)RequestSize
,
294 VRING_DESC_F_NEXT
, &Indices
);
295 VirtioAppendDesc (&VgpuDev
->Ring
, (UINTN
)&Response
, sizeof Response
,
296 VRING_DESC_F_WRITE
, &Indices
);
301 Status
= VirtioFlush (VgpuDev
->VirtIo
, VIRTIO_GPU_CONTROL_QUEUE
,
302 &VgpuDev
->Ring
, &Indices
, &ResponseSize
);
303 if (EFI_ERROR (Status
)) {
308 // Parse the response.
310 if (ResponseSize
!= sizeof Response
) {
311 DEBUG ((EFI_D_ERROR
, "%a: malformed response to Request=0x%x\n",
312 __FUNCTION__
, (UINT32
)RequestType
));
313 return EFI_PROTOCOL_ERROR
;
316 if (Response
.Type
== VirtioGpuRespOkNodata
) {
320 DEBUG ((EFI_D_ERROR
, "%a: Request=0x%x Response=0x%x\n", __FUNCTION__
,
321 (UINT32
)RequestType
, Response
.Type
));
322 return EFI_DEVICE_ERROR
;
326 The following functions send requests to the VirtIo GPU device model, await
327 the answer from the host, and return a status. They share the following
330 @param[in,out] VgpuDev The VGPU_DEV object that represents the VirtIo GPU
331 device. The caller is responsible to have
332 successfully invoked VirtioGpuInit() on VgpuDev
333 previously, while VirtioGpuUninit() must not have
334 been called on VgpuDev.
336 @retval EFI_INVALID_PARAMETER Invalid command-specific parameters were
337 detected by this driver.
339 @retval EFI_SUCCESS Operation successful.
341 @retval EFI_DEVICE_ERROR The host rejected the request. The host error
342 code has been logged on the EFI_D_ERROR level.
344 @return Codes for unexpected errors in VirtIo
347 For the command-specific parameters, please consult the GPU Device section of
348 the VirtIo 1.0 specification (see references in
349 "OvmfPkg/Include/IndustryStandard/VirtioGpu.h").
352 VirtioGpuResourceCreate2d (
353 IN OUT VGPU_DEV
*VgpuDev
,
354 IN UINT32 ResourceId
,
355 IN VIRTIO_GPU_FORMATS Format
,
360 volatile VIRTIO_GPU_RESOURCE_CREATE_2D Request
;
362 if (ResourceId
== 0) {
363 return EFI_INVALID_PARAMETER
;
366 Request
.ResourceId
= ResourceId
;
367 Request
.Format
= (UINT32
)Format
;
368 Request
.Width
= Width
;
369 Request
.Height
= Height
;
371 return VirtioGpuSendCommand (
373 VirtioGpuCmdResourceCreate2d
,
381 VirtioGpuResourceUnref (
382 IN OUT VGPU_DEV
*VgpuDev
,
386 volatile VIRTIO_GPU_RESOURCE_UNREF Request
;
388 if (ResourceId
== 0) {
389 return EFI_INVALID_PARAMETER
;
392 Request
.ResourceId
= ResourceId
;
395 return VirtioGpuSendCommand (
397 VirtioGpuCmdResourceUnref
,
405 VirtioGpuResourceAttachBacking (
406 IN OUT VGPU_DEV
*VgpuDev
,
407 IN UINT32 ResourceId
,
408 IN VOID
*FirstBackingPage
,
409 IN UINTN NumberOfPages
412 volatile VIRTIO_GPU_RESOURCE_ATTACH_BACKING Request
;
414 if (ResourceId
== 0) {
415 return EFI_INVALID_PARAMETER
;
418 Request
.ResourceId
= ResourceId
;
419 Request
.NrEntries
= 1;
420 Request
.Entry
.Addr
= (UINTN
)FirstBackingPage
;
421 Request
.Entry
.Length
= (UINT32
)EFI_PAGES_TO_SIZE (NumberOfPages
);
422 Request
.Entry
.Padding
= 0;
424 return VirtioGpuSendCommand (
426 VirtioGpuCmdResourceAttachBacking
,
434 VirtioGpuResourceDetachBacking (
435 IN OUT VGPU_DEV
*VgpuDev
,
439 volatile VIRTIO_GPU_RESOURCE_DETACH_BACKING Request
;
441 if (ResourceId
== 0) {
442 return EFI_INVALID_PARAMETER
;
445 Request
.ResourceId
= ResourceId
;
449 // In this case, we set Fence to TRUE, because after this function returns,
450 // the caller might reasonably want to repurpose the backing pages
451 // immediately. Thus we should ensure that the host releases all references
452 // to the backing pages before we return.
454 return VirtioGpuSendCommand (
456 VirtioGpuCmdResourceDetachBacking
,
464 VirtioGpuSetScanout (
465 IN OUT VGPU_DEV
*VgpuDev
,
474 volatile VIRTIO_GPU_SET_SCANOUT Request
;
477 // Unlike for most other commands, ResourceId=0 is valid; it
478 // is used to disable a scanout.
480 Request
.Rectangle
.X
= X
;
481 Request
.Rectangle
.Y
= Y
;
482 Request
.Rectangle
.Width
= Width
;
483 Request
.Rectangle
.Height
= Height
;
484 Request
.ScanoutId
= ScanoutId
;
485 Request
.ResourceId
= ResourceId
;
487 return VirtioGpuSendCommand (
489 VirtioGpuCmdSetScanout
,
497 VirtioGpuTransferToHost2d (
498 IN OUT VGPU_DEV
*VgpuDev
,
507 volatile VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D Request
;
509 if (ResourceId
== 0) {
510 return EFI_INVALID_PARAMETER
;
513 Request
.Rectangle
.X
= X
;
514 Request
.Rectangle
.Y
= Y
;
515 Request
.Rectangle
.Width
= Width
;
516 Request
.Rectangle
.Height
= Height
;
517 Request
.Offset
= Offset
;
518 Request
.ResourceId
= ResourceId
;
521 return VirtioGpuSendCommand (
523 VirtioGpuCmdTransferToHost2d
,
531 VirtioGpuResourceFlush (
532 IN OUT VGPU_DEV
*VgpuDev
,
540 volatile VIRTIO_GPU_RESOURCE_FLUSH Request
;
542 if (ResourceId
== 0) {
543 return EFI_INVALID_PARAMETER
;
546 Request
.Rectangle
.X
= X
;
547 Request
.Rectangle
.Y
= Y
;
548 Request
.Rectangle
.Width
= Width
;
549 Request
.Rectangle
.Height
= Height
;
550 Request
.ResourceId
= ResourceId
;
553 return VirtioGpuSendCommand (
555 VirtioGpuCmdResourceFlush
,