+/** @file\r
+\r
+ VirtIo GPU initialization, and commands (primitives) for the GPU device.\r
+\r
+ Copyright (C) 2016, Red Hat, Inc.\r
+\r
+ This program and the accompanying materials are licensed and made available\r
+ under the terms and conditions of the BSD License which accompanies this\r
+ distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT\r
+ WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <IndustryStandard/VirtioGpu.h>\r
+#include <Library/VirtioLib.h>\r
+\r
+#include "VirtioGpu.h"\r
+\r
+/**\r
+ Configure the VirtIo GPU device that underlies VgpuDev.\r
+\r
+ @param[in,out] VgpuDev The VGPU_DEV object to set up VirtIo messaging for.\r
+ On input, the caller is responsible for having\r
+ initialized VgpuDev->VirtIo. On output, VgpuDev->Ring\r
+ has been initialized, and synchronous VirtIo GPU\r
+ commands (primitives) can be submitted to the device.\r
+\r
+ @retval EFI_SUCCESS VirtIo GPU configuration successful.\r
+\r
+ @retval EFI_UNSUPPORTED The host-side configuration of the VirtIo GPU is not\r
+ supported by this driver.\r
+\r
+ @retval Error codes from underlying functions.\r
+**/\r
+EFI_STATUS\r
+VirtioGpuInit (\r
+ IN OUT VGPU_DEV *VgpuDev\r
+ )\r
+{\r
+ UINT8 NextDevStat;\r
+ EFI_STATUS Status;\r
+ UINT64 Features;\r
+ UINT16 QueueSize;\r
+\r
+ //\r
+ // Execute virtio-v1.0-cs04, 3.1.1 Driver Requirements: Device\r
+ // Initialization.\r
+ //\r
+ // 1. Reset the device.\r
+ //\r
+ NextDevStat = 0;\r
+ Status = VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Failed;\r
+ }\r
+\r
+ //\r
+ // 2. Set the ACKNOWLEDGE status bit [...]\r
+ //\r
+ NextDevStat |= VSTAT_ACK;\r
+ Status = VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Failed;\r
+ }\r
+\r
+ //\r
+ // 3. Set the DRIVER status bit [...]\r
+ //\r
+ NextDevStat |= VSTAT_DRIVER;\r
+ Status = VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Failed;\r
+ }\r
+\r
+ //\r
+ // 4. Read device feature bits...\r
+ //\r
+ Status = VgpuDev->VirtIo->GetDeviceFeatures (VgpuDev->VirtIo, &Features);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Failed;\r
+ }\r
+ if ((Features & VIRTIO_F_VERSION_1) == 0) {\r
+ Status = EFI_UNSUPPORTED;\r
+ goto Failed;\r
+ }\r
+ //\r
+ // We only want the most basic 2D features.\r
+ //\r
+ Features &= VIRTIO_F_VERSION_1;\r
+\r
+ //\r
+ // ... and write the subset of feature bits understood by the [...] driver to\r
+ // the device. [...]\r
+ // 5. Set the FEATURES_OK status bit.\r
+ // 6. Re-read device status to ensure the FEATURES_OK bit is still set [...]\r
+ //\r
+ Status = Virtio10WriteFeatures (VgpuDev->VirtIo, Features, &NextDevStat);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Failed;\r
+ }\r
+\r
+ //\r
+ // 7. Perform device-specific setup, including discovery of virtqueues for\r
+ // the device [...]\r
+ //\r
+ Status = VgpuDev->VirtIo->SetQueueSel (VgpuDev->VirtIo,\r
+ VIRTIO_GPU_CONTROL_QUEUE);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Failed;\r
+ }\r
+ Status = VgpuDev->VirtIo->GetQueueNumMax (VgpuDev->VirtIo, &QueueSize);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Failed;\r
+ }\r
+\r
+ //\r
+ // We implement each VirtIo GPU command that we use with two descriptors:\r
+ // request, response.\r
+ //\r
+ if (QueueSize < 2) {\r
+ Status = EFI_UNSUPPORTED;\r
+ goto Failed;\r
+ }\r
+\r
+ //\r
+ // [...] population of virtqueues [...]\r
+ //\r
+ Status = VirtioRingInit (QueueSize, &VgpuDev->Ring);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Failed;\r
+ }\r
+ Status = VgpuDev->VirtIo->SetQueueAddress (VgpuDev->VirtIo, &VgpuDev->Ring);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ReleaseQueue;\r
+ }\r
+\r
+ //\r
+ // 8. Set the DRIVER_OK status bit.\r
+ //\r
+ NextDevStat |= VSTAT_DRIVER_OK;\r
+ Status = VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ReleaseQueue;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+\r
+ReleaseQueue:\r
+ VirtioRingUninit (&VgpuDev->Ring);\r
+\r
+Failed:\r
+ //\r
+ // If any of these steps go irrecoverably wrong, the driver SHOULD set the\r
+ // FAILED status bit to indicate that it has given up on the device (it can\r
+ // reset the device later to restart if desired). [...]\r
+ //\r
+ // VirtIo access failure here should not mask the original error.\r
+ //\r
+ NextDevStat |= VSTAT_FAILED;\r
+ VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ De-configure the VirtIo GPU device that underlies VgpuDev.\r
+\r
+ @param[in,out] VgpuDev The VGPU_DEV object to tear down VirtIo messaging\r
+ for. On input, the caller is responsible for having\r
+ called VirtioGpuInit(). On output, VgpuDev->Ring has\r
+ been uninitialized; VirtIo GPU commands (primitives)\r
+ can no longer be submitted to the device.\r
+**/\r
+VOID\r
+VirtioGpuUninit (\r
+ IN OUT VGPU_DEV *VgpuDev\r
+ )\r
+{\r
+ //\r
+ // Resetting the VirtIo device makes it release its resources and forget its\r
+ // configuration.\r
+ //\r
+ VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, 0);\r
+ VirtioRingUninit (&VgpuDev->Ring);\r
+}\r
+\r
+/**\r
+ EFI_EVENT_NOTIFY function for the VGPU_DEV.ExitBoot event. It resets the\r
+ VirtIo device, causing it to release its resources and to forget its\r
+ configuration.\r
+\r
+ This function may only be called (that is, VGPU_DEV.ExitBoot may only be\r
+ signaled) after VirtioGpuInit() returns and before VirtioGpuUninit() is\r
+ called.\r
+\r
+ @param[in] Event Event whose notification function is being invoked.\r
+\r
+ @param[in] Context Pointer to the associated VGPU_DEV object.\r
+**/\r
+VOID\r
+EFIAPI\r
+VirtioGpuExitBoot (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ VGPU_DEV *VgpuDev;\r
+\r
+ VgpuDev = Context;\r
+ VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, 0);\r
+}\r