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
285 VirtioGpuSendCommand (
286 IN OUT VGPU_DEV
*VgpuDev
,
287 IN VIRTIO_GPU_CONTROL_TYPE RequestType
,
289 IN OUT
volatile VIRTIO_GPU_CONTROL_HEADER
*Header
,
293 DESC_INDICES Indices
;
294 volatile VIRTIO_GPU_CONTROL_HEADER Response
;
299 // Initialize Header.
301 Header
->Type
= RequestType
;
303 Header
->Flags
= VIRTIO_GPU_FLAG_FENCE
;
304 Header
->FenceId
= VgpuDev
->FenceId
++;
312 ASSERT (RequestSize
>= sizeof *Header
);
313 ASSERT (RequestSize
<= MAX_UINT32
);
316 // Compose the descriptor chain.
318 VirtioPrepare (&VgpuDev
->Ring
, &Indices
);
319 VirtioAppendDesc (&VgpuDev
->Ring
, (UINTN
)Header
, (UINT32
)RequestSize
,
320 VRING_DESC_F_NEXT
, &Indices
);
321 VirtioAppendDesc (&VgpuDev
->Ring
, (UINTN
)&Response
, sizeof Response
,
322 VRING_DESC_F_WRITE
, &Indices
);
327 Status
= VirtioFlush (VgpuDev
->VirtIo
, VIRTIO_GPU_CONTROL_QUEUE
,
328 &VgpuDev
->Ring
, &Indices
, &ResponseSize
);
329 if (EFI_ERROR (Status
)) {
334 // Parse the response.
336 if (ResponseSize
!= sizeof Response
) {
337 DEBUG ((EFI_D_ERROR
, "%a: malformed response to Request=0x%x\n",
338 __FUNCTION__
, (UINT32
)RequestType
));
339 return EFI_PROTOCOL_ERROR
;
342 if (Response
.Type
== VirtioGpuRespOkNodata
) {
346 DEBUG ((EFI_D_ERROR
, "%a: Request=0x%x Response=0x%x\n", __FUNCTION__
,
347 (UINT32
)RequestType
, Response
.Type
));
348 return EFI_DEVICE_ERROR
;
352 The following functions send requests to the VirtIo GPU device model, await
353 the answer from the host, and return a status. They share the following
356 @param[in,out] VgpuDev The VGPU_DEV object that represents the VirtIo GPU
357 device. The caller is responsible to have
358 successfully invoked VirtioGpuInit() on VgpuDev
359 previously, while VirtioGpuUninit() must not have
360 been called on VgpuDev.
362 @retval EFI_INVALID_PARAMETER Invalid command-specific parameters were
363 detected by this driver.
365 @retval EFI_SUCCESS Operation successful.
367 @retval EFI_DEVICE_ERROR The host rejected the request. The host error
368 code has been logged on the EFI_D_ERROR level.
370 @return Codes for unexpected errors in VirtIo
373 For the command-specific parameters, please consult the GPU Device section of
374 the VirtIo 1.0 specification (see references in
375 "OvmfPkg/Include/IndustryStandard/VirtioGpu.h").
378 VirtioGpuResourceCreate2d (
379 IN OUT VGPU_DEV
*VgpuDev
,
380 IN UINT32 ResourceId
,
381 IN VIRTIO_GPU_FORMATS Format
,
386 volatile VIRTIO_GPU_RESOURCE_CREATE_2D Request
;
388 if (ResourceId
== 0) {
389 return EFI_INVALID_PARAMETER
;
392 Request
.ResourceId
= ResourceId
;
393 Request
.Format
= (UINT32
)Format
;
394 Request
.Width
= Width
;
395 Request
.Height
= Height
;
397 return VirtioGpuSendCommand (
399 VirtioGpuCmdResourceCreate2d
,
407 VirtioGpuResourceUnref (
408 IN OUT VGPU_DEV
*VgpuDev
,
412 volatile VIRTIO_GPU_RESOURCE_UNREF Request
;
414 if (ResourceId
== 0) {
415 return EFI_INVALID_PARAMETER
;
418 Request
.ResourceId
= ResourceId
;
421 return VirtioGpuSendCommand (
423 VirtioGpuCmdResourceUnref
,
431 VirtioGpuResourceAttachBacking (
432 IN OUT VGPU_DEV
*VgpuDev
,
433 IN UINT32 ResourceId
,
434 IN VOID
*FirstBackingPage
,
435 IN UINTN NumberOfPages
438 volatile VIRTIO_GPU_RESOURCE_ATTACH_BACKING Request
;
440 if (ResourceId
== 0) {
441 return EFI_INVALID_PARAMETER
;
444 Request
.ResourceId
= ResourceId
;
445 Request
.NrEntries
= 1;
446 Request
.Entry
.Addr
= (UINTN
)FirstBackingPage
;
447 Request
.Entry
.Length
= (UINT32
)EFI_PAGES_TO_SIZE (NumberOfPages
);
448 Request
.Entry
.Padding
= 0;
450 return VirtioGpuSendCommand (
452 VirtioGpuCmdResourceAttachBacking
,
460 VirtioGpuResourceDetachBacking (
461 IN OUT VGPU_DEV
*VgpuDev
,
465 volatile VIRTIO_GPU_RESOURCE_DETACH_BACKING Request
;
467 if (ResourceId
== 0) {
468 return EFI_INVALID_PARAMETER
;
471 Request
.ResourceId
= ResourceId
;
475 // In this case, we set Fence to TRUE, because after this function returns,
476 // the caller might reasonably want to repurpose the backing pages
477 // immediately. Thus we should ensure that the host releases all references
478 // to the backing pages before we return.
480 return VirtioGpuSendCommand (
482 VirtioGpuCmdResourceDetachBacking
,
490 VirtioGpuSetScanout (
491 IN OUT VGPU_DEV
*VgpuDev
,
500 volatile VIRTIO_GPU_SET_SCANOUT Request
;
503 // Unlike for most other commands, ResourceId=0 is valid; it
504 // is used to disable a scanout.
506 Request
.Rectangle
.X
= X
;
507 Request
.Rectangle
.Y
= Y
;
508 Request
.Rectangle
.Width
= Width
;
509 Request
.Rectangle
.Height
= Height
;
510 Request
.ScanoutId
= ScanoutId
;
511 Request
.ResourceId
= ResourceId
;
513 return VirtioGpuSendCommand (
515 VirtioGpuCmdSetScanout
,
523 VirtioGpuTransferToHost2d (
524 IN OUT VGPU_DEV
*VgpuDev
,
533 volatile VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D Request
;
535 if (ResourceId
== 0) {
536 return EFI_INVALID_PARAMETER
;
539 Request
.Rectangle
.X
= X
;
540 Request
.Rectangle
.Y
= Y
;
541 Request
.Rectangle
.Width
= Width
;
542 Request
.Rectangle
.Height
= Height
;
543 Request
.Offset
= Offset
;
544 Request
.ResourceId
= ResourceId
;
547 return VirtioGpuSendCommand (
549 VirtioGpuCmdTransferToHost2d
,
557 VirtioGpuResourceFlush (
558 IN OUT VGPU_DEV
*VgpuDev
,
566 volatile VIRTIO_GPU_RESOURCE_FLUSH Request
;
568 if (ResourceId
== 0) {
569 return EFI_INVALID_PARAMETER
;
572 Request
.Rectangle
.X
= X
;
573 Request
.Rectangle
.Y
= Y
;
574 Request
.Rectangle
.Width
= Width
;
575 Request
.Rectangle
.Height
= Height
;
576 Request
.ResourceId
= ResourceId
;
579 return VirtioGpuSendCommand (
581 VirtioGpuCmdResourceFlush
,