OvmfPkg/VirtioGpuDxe: initialize and tear down VirtIo GPU device

This patch implements the steps listed in section "3.1.1 Driver
Requirements: Device Initialization" of the Virtio V1.0 Committee Spec 04.
The VirtIo GPU is brought up in VirtioGpuDriverBindingStart(), and down in
VirtioGpuDriverBindingStop().

We also add an ExitBootServices() callback that resets the device. This
ensures that the device model abandons any guest memory areas when we
transfer control to the guest OS.

Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Ref: https://tianocore.acgmultimedia.com/show_bug.cgi?id=66
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
This commit is contained in:
Laszlo Ersek 2016-08-18 01:31:27 +02:00
parent 92f200c2d6
commit c5f235bbf2
4 changed files with 311 additions and 1 deletions

View File

@ -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 <IndustryStandard/VirtioGpu.h>
#include <Library/VirtioLib.h>
#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);
}

View File

@ -646,13 +646,25 @@ VirtioGpuDriverBindingStart (
goto FreeVgpuDev; 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. // Install the VGPU_DEV "protocol interface" on ControllerHandle.
// //
Status = gBS->InstallProtocolInterface (&ControllerHandle, Status = gBS->InstallProtocolInterface (&ControllerHandle,
&gEfiCallerIdGuid, EFI_NATIVE_INTERFACE, VgpuDev); &gEfiCallerIdGuid, EFI_NATIVE_INTERFACE, VgpuDev);
if (EFI_ERROR (Status)) { if (EFI_ERROR (Status)) {
goto FreeVgpuDevBusName; goto CloseExitBoot;
} }
if (RemainingDevicePath != NULL && IsDevicePathEnd (RemainingDevicePath)) { if (RemainingDevicePath != NULL && IsDevicePathEnd (RemainingDevicePath)) {
@ -693,6 +705,16 @@ UninstallVgpuDev:
VgpuDev); VgpuDev);
} }
CloseExitBoot:
if (VirtIoBoundJustNow) {
gBS->CloseEvent (VgpuDev->ExitBoot);
}
UninitGpu:
if (VirtIoBoundJustNow) {
VirtioGpuUninit (VgpuDev);
}
FreeVgpuDevBusName: FreeVgpuDevBusName:
if (VirtIoBoundJustNow) { if (VirtIoBoundJustNow) {
FreeUnicodeStringTable (VgpuDev->BusName); FreeUnicodeStringTable (VgpuDev->BusName);
@ -761,6 +783,10 @@ VirtioGpuDriverBindingStop (
&gEfiCallerIdGuid, VgpuDev); &gEfiCallerIdGuid, VgpuDev);
ASSERT_EFI_ERROR (Status); ASSERT_EFI_ERROR (Status);
Status = gBS->CloseEvent (VgpuDev->ExitBoot);
ASSERT_EFI_ERROR (Status);
VirtioGpuUninit (VgpuDev);
FreeUnicodeStringTable (VgpuDev->BusName); FreeUnicodeStringTable (VgpuDev->BusName);
FreePool (VgpuDev); FreePool (VgpuDev);

View File

@ -48,6 +48,16 @@ typedef struct {
// //
EFI_UNICODE_STRING_TABLE *BusName; 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 // The Child field references the GOP wrapper structure. If this pointer is
// NULL, then the hybrid driver has bound (i.e., started) the // NULL, then the hybrid driver has bound (i.e., started) the
@ -103,4 +113,62 @@ struct VGPU_GOP_STRUCT {
UINT8 Gop; 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_ #endif // _VIRTIO_GPU_DXE_H_

View File

@ -24,6 +24,7 @@
ENTRY_POINT = VirtioGpuEntryPoint ENTRY_POINT = VirtioGpuEntryPoint
[Sources] [Sources]
Commands.c
DriverBinding.c DriverBinding.c
VirtioGpu.h VirtioGpu.h
@ -40,6 +41,7 @@
UefiBootServicesTableLib UefiBootServicesTableLib
UefiDriverEntryPoint UefiDriverEntryPoint
UefiLib UefiLib
VirtioLib
[Protocols] [Protocols]
gEfiDevicePathProtocolGuid ## TO_START ## BY_START gEfiDevicePathProtocolGuid ## TO_START ## BY_START