diff --git a/OvmfPkg/VirtioGpuDxe/Commands.c b/OvmfPkg/VirtioGpuDxe/Commands.c new file mode 100644 index 0000000000..804de950ff --- /dev/null +++ b/OvmfPkg/VirtioGpuDxe/Commands.c @@ -0,0 +1,214 @@ +/** @file + + VirtIo GPU initialization, and commands (primitives) for the GPU device. + + Copyright (C) 2016, Red Hat, Inc. + + This program and the accompanying materials are licensed and made available + under the terms and conditions of the BSD License which accompanies this + distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT + WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include + +#include "VirtioGpu.h" + +/** + Configure the VirtIo GPU device that underlies VgpuDev. + + @param[in,out] VgpuDev The VGPU_DEV object to set up VirtIo messaging for. + On input, the caller is responsible for having + initialized VgpuDev->VirtIo. On output, VgpuDev->Ring + has been initialized, and synchronous VirtIo GPU + commands (primitives) can be submitted to the device. + + @retval EFI_SUCCESS VirtIo GPU configuration successful. + + @retval EFI_UNSUPPORTED The host-side configuration of the VirtIo GPU is not + supported by this driver. + + @retval Error codes from underlying functions. +**/ +EFI_STATUS +VirtioGpuInit ( + IN OUT VGPU_DEV *VgpuDev + ) +{ + UINT8 NextDevStat; + EFI_STATUS Status; + UINT64 Features; + UINT16 QueueSize; + + // + // Execute virtio-v1.0-cs04, 3.1.1 Driver Requirements: Device + // Initialization. + // + // 1. Reset the device. + // + NextDevStat = 0; + Status = VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat); + if (EFI_ERROR (Status)) { + goto Failed; + } + + // + // 2. Set the ACKNOWLEDGE status bit [...] + // + NextDevStat |= VSTAT_ACK; + Status = VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat); + if (EFI_ERROR (Status)) { + goto Failed; + } + + // + // 3. Set the DRIVER status bit [...] + // + NextDevStat |= VSTAT_DRIVER; + Status = VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat); + if (EFI_ERROR (Status)) { + goto Failed; + } + + // + // 4. Read device feature bits... + // + Status = VgpuDev->VirtIo->GetDeviceFeatures (VgpuDev->VirtIo, &Features); + if (EFI_ERROR (Status)) { + goto Failed; + } + if ((Features & VIRTIO_F_VERSION_1) == 0) { + Status = EFI_UNSUPPORTED; + goto Failed; + } + // + // We only want the most basic 2D features. + // + Features &= VIRTIO_F_VERSION_1; + + // + // ... and write the subset of feature bits understood by the [...] driver to + // the device. [...] + // 5. Set the FEATURES_OK status bit. + // 6. Re-read device status to ensure the FEATURES_OK bit is still set [...] + // + Status = Virtio10WriteFeatures (VgpuDev->VirtIo, Features, &NextDevStat); + if (EFI_ERROR (Status)) { + goto Failed; + } + + // + // 7. Perform device-specific setup, including discovery of virtqueues for + // the device [...] + // + Status = VgpuDev->VirtIo->SetQueueSel (VgpuDev->VirtIo, + VIRTIO_GPU_CONTROL_QUEUE); + if (EFI_ERROR (Status)) { + goto Failed; + } + Status = VgpuDev->VirtIo->GetQueueNumMax (VgpuDev->VirtIo, &QueueSize); + if (EFI_ERROR (Status)) { + goto Failed; + } + + // + // We implement each VirtIo GPU command that we use with two descriptors: + // request, response. + // + if (QueueSize < 2) { + Status = EFI_UNSUPPORTED; + goto Failed; + } + + // + // [...] population of virtqueues [...] + // + Status = VirtioRingInit (QueueSize, &VgpuDev->Ring); + if (EFI_ERROR (Status)) { + goto Failed; + } + Status = VgpuDev->VirtIo->SetQueueAddress (VgpuDev->VirtIo, &VgpuDev->Ring); + if (EFI_ERROR (Status)) { + goto ReleaseQueue; + } + + // + // 8. Set the DRIVER_OK status bit. + // + NextDevStat |= VSTAT_DRIVER_OK; + Status = VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat); + if (EFI_ERROR (Status)) { + goto ReleaseQueue; + } + + return EFI_SUCCESS; + +ReleaseQueue: + VirtioRingUninit (&VgpuDev->Ring); + +Failed: + // + // If any of these steps go irrecoverably wrong, the driver SHOULD set the + // FAILED status bit to indicate that it has given up on the device (it can + // reset the device later to restart if desired). [...] + // + // VirtIo access failure here should not mask the original error. + // + NextDevStat |= VSTAT_FAILED; + VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat); + + return Status; +} + +/** + De-configure the VirtIo GPU device that underlies VgpuDev. + + @param[in,out] VgpuDev The VGPU_DEV object to tear down VirtIo messaging + for. On input, the caller is responsible for having + called VirtioGpuInit(). On output, VgpuDev->Ring has + been uninitialized; VirtIo GPU commands (primitives) + can no longer be submitted to the device. +**/ +VOID +VirtioGpuUninit ( + IN OUT VGPU_DEV *VgpuDev + ) +{ + // + // Resetting the VirtIo device makes it release its resources and forget its + // configuration. + // + VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, 0); + VirtioRingUninit (&VgpuDev->Ring); +} + +/** + EFI_EVENT_NOTIFY function for the VGPU_DEV.ExitBoot event. It resets the + VirtIo device, causing it to release its resources and to forget its + configuration. + + This function may only be called (that is, VGPU_DEV.ExitBoot may only be + signaled) after VirtioGpuInit() returns and before VirtioGpuUninit() is + called. + + @param[in] Event Event whose notification function is being invoked. + + @param[in] Context Pointer to the associated VGPU_DEV object. +**/ +VOID +EFIAPI +VirtioGpuExitBoot ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + VGPU_DEV *VgpuDev; + + VgpuDev = Context; + VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, 0); +} diff --git a/OvmfPkg/VirtioGpuDxe/DriverBinding.c b/OvmfPkg/VirtioGpuDxe/DriverBinding.c index b902a07871..bdea55ef7d 100644 --- a/OvmfPkg/VirtioGpuDxe/DriverBinding.c +++ b/OvmfPkg/VirtioGpuDxe/DriverBinding.c @@ -646,13 +646,25 @@ VirtioGpuDriverBindingStart ( goto FreeVgpuDev; } + Status = VirtioGpuInit (VgpuDev); + if (EFI_ERROR (Status)) { + goto FreeVgpuDevBusName; + } + + Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK, + VirtioGpuExitBoot, VgpuDev /* NotifyContext */, + &VgpuDev->ExitBoot); + if (EFI_ERROR (Status)) { + goto UninitGpu; + } + // // Install the VGPU_DEV "protocol interface" on ControllerHandle. // Status = gBS->InstallProtocolInterface (&ControllerHandle, &gEfiCallerIdGuid, EFI_NATIVE_INTERFACE, VgpuDev); if (EFI_ERROR (Status)) { - goto FreeVgpuDevBusName; + goto CloseExitBoot; } if (RemainingDevicePath != NULL && IsDevicePathEnd (RemainingDevicePath)) { @@ -693,6 +705,16 @@ UninstallVgpuDev: VgpuDev); } +CloseExitBoot: + if (VirtIoBoundJustNow) { + gBS->CloseEvent (VgpuDev->ExitBoot); + } + +UninitGpu: + if (VirtIoBoundJustNow) { + VirtioGpuUninit (VgpuDev); + } + FreeVgpuDevBusName: if (VirtIoBoundJustNow) { FreeUnicodeStringTable (VgpuDev->BusName); @@ -761,6 +783,10 @@ VirtioGpuDriverBindingStop ( &gEfiCallerIdGuid, VgpuDev); ASSERT_EFI_ERROR (Status); + Status = gBS->CloseEvent (VgpuDev->ExitBoot); + ASSERT_EFI_ERROR (Status); + + VirtioGpuUninit (VgpuDev); FreeUnicodeStringTable (VgpuDev->BusName); FreePool (VgpuDev); diff --git a/OvmfPkg/VirtioGpuDxe/VirtioGpu.h b/OvmfPkg/VirtioGpuDxe/VirtioGpu.h index ca5805df84..97767dba70 100644 --- a/OvmfPkg/VirtioGpuDxe/VirtioGpu.h +++ b/OvmfPkg/VirtioGpuDxe/VirtioGpu.h @@ -48,6 +48,16 @@ typedef struct { // EFI_UNICODE_STRING_TABLE *BusName; + // + // VirtIo ring used for VirtIo communication. + // + VRING Ring; + + // + // Event to be signaled at ExitBootServices(). + // + EFI_EVENT ExitBoot; + // // The Child field references the GOP wrapper structure. If this pointer is // NULL, then the hybrid driver has bound (i.e., started) the @@ -103,4 +113,62 @@ struct VGPU_GOP_STRUCT { UINT8 Gop; }; +// +// VirtIo GPU initialization, and commands (primitives) for the GPU device. +// +/** + Configure the VirtIo GPU device that underlies VgpuDev. + + @param[in,out] VgpuDev The VGPU_DEV object to set up VirtIo messaging for. + On input, the caller is responsible for having + initialized VgpuDev->VirtIo. On output, VgpuDev->Ring + has been initialized, and synchronous VirtIo GPU + commands (primitives) can be submitted to the device. + + @retval EFI_SUCCESS VirtIo GPU configuration successful. + + @retval EFI_UNSUPPORTED The host-side configuration of the VirtIo GPU is not + supported by this driver. + + @retval Error codes from underlying functions. +**/ +EFI_STATUS +VirtioGpuInit ( + IN OUT VGPU_DEV *VgpuDev + ); + +/** + De-configure the VirtIo GPU device that underlies VgpuDev. + + @param[in,out] VgpuDev The VGPU_DEV object to tear down VirtIo messaging + for. On input, the caller is responsible for having + called VirtioGpuInit(). On output, VgpuDev->Ring has + been uninitialized; VirtIo GPU commands (primitives) + can no longer be submitted to the device. +**/ +VOID +VirtioGpuUninit ( + IN OUT VGPU_DEV *VgpuDev + ); + +/** + EFI_EVENT_NOTIFY function for the VGPU_DEV.ExitBoot event. It resets the + VirtIo device, causing it to release its resources and to forget its + configuration. + + This function may only be called (that is, VGPU_DEV.ExitBoot may only be + signaled) after VirtioGpuInit() returns and before VirtioGpuUninit() is + called. + + @param[in] Event Event whose notification function is being invoked. + + @param[in] Context Pointer to the associated VGPU_DEV object. +**/ +VOID +EFIAPI +VirtioGpuExitBoot ( + IN EFI_EVENT Event, + IN VOID *Context + ); + #endif // _VIRTIO_GPU_DXE_H_ diff --git a/OvmfPkg/VirtioGpuDxe/VirtioGpu.inf b/OvmfPkg/VirtioGpuDxe/VirtioGpu.inf index 948350dbce..7a6269eded 100644 --- a/OvmfPkg/VirtioGpuDxe/VirtioGpu.inf +++ b/OvmfPkg/VirtioGpuDxe/VirtioGpu.inf @@ -24,6 +24,7 @@ ENTRY_POINT = VirtioGpuEntryPoint [Sources] + Commands.c DriverBinding.c VirtioGpu.h @@ -40,6 +41,7 @@ UefiBootServicesTableLib UefiDriverEntryPoint UefiLib + VirtioLib [Protocols] gEfiDevicePathProtocolGuid ## TO_START ## BY_START