OvmfPkg: VirtioScsiDxe: reset device at ExitBootServices()

(1) VirtioLib allocates the virtio ring in EfiBootServicesData memory.
    (This is intentional.) Code that executes after ExitBootServices() is
    permitted to reuse such memory.

(2) The hypervisor is allowed to look at, and act upon, a live virtio ring
    at any time, even without explicit virtio kicks from the guest.

Should boot loader code or kernel code, running between ExitBootServices()
and the kernel's own virtio drivers resetting the device, overwrite the
pages that used to contain the virtio ring before ExitBootServices(), QEMU
could theoretically interpret that unrelated data as garbage ring
contents, and abort the guest.

Although we have seen no such reports, better be prudent and reset the
device in an ExitBootServices() event handler. Among other things, this
causes QEMU to forget about the device's virtio ring.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>

git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@18623 6f19259b-4bc3-4df7-8a09-765794883524
This commit is contained in:
Laszlo Ersek 2015-10-16 19:52:18 +00:00 committed by lersek
parent f3e34b9d8c
commit fbc80813ee
2 changed files with 39 additions and 2 deletions

View File

@ -933,7 +933,6 @@ Failed:
} }
STATIC STATIC
VOID VOID
EFIAPI EFIAPI
@ -960,6 +959,32 @@ VirtioScsiUninit (
} }
//
// Event notification function enqueued by ExitBootServices().
//
STATIC
VOID
EFIAPI
VirtioScsiExitBoot (
IN EFI_EVENT Event,
IN VOID *Context
)
{
VSCSI_DEV *Dev;
//
// Reset the device. This causes the hypervisor to forget about the virtio
// ring.
//
// We allocated said ring in EfiBootServicesData type memory, and code
// executing after ExitBootServices() is permitted to overwrite it.
//
Dev = Context;
Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);
}
// //
// Probe, start and stop functions of this driver, called by the DXE core for // Probe, start and stop functions of this driver, called by the DXE core for
// specific devices. // specific devices.
@ -1050,6 +1075,12 @@ VirtioScsiDriverBindingStart (
goto CloseVirtIo; goto CloseVirtIo;
} }
Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,
&VirtioScsiExitBoot, Dev, &Dev->ExitBoot);
if (EFI_ERROR (Status)) {
goto UninitDev;
}
// //
// Setup complete, attempt to export the driver instance's PassThru // Setup complete, attempt to export the driver instance's PassThru
// interface. // interface.
@ -1059,11 +1090,14 @@ VirtioScsiDriverBindingStart (
&gEfiExtScsiPassThruProtocolGuid, EFI_NATIVE_INTERFACE, &gEfiExtScsiPassThruProtocolGuid, EFI_NATIVE_INTERFACE,
&Dev->PassThru); &Dev->PassThru);
if (EFI_ERROR (Status)) { if (EFI_ERROR (Status)) {
goto UninitDev; goto CloseExitBoot;
} }
return EFI_SUCCESS; return EFI_SUCCESS;
CloseExitBoot:
gBS->CloseEvent (Dev->ExitBoot);
UninitDev: UninitDev:
VirtioScsiUninit (Dev); VirtioScsiUninit (Dev);
@ -1114,6 +1148,8 @@ VirtioScsiDriverBindingStop (
return Status; return Status;
} }
gBS->CloseEvent (Dev->ExitBoot);
VirtioScsiUninit (Dev); VirtioScsiUninit (Dev);
gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid, gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,

View File

@ -52,6 +52,7 @@ typedef struct {
// ---------------- ------------------ ---------- // ---------------- ------------------ ----------
UINT32 Signature; // DriverBindingStart 0 UINT32 Signature; // DriverBindingStart 0
VIRTIO_DEVICE_PROTOCOL *VirtIo; // DriverBindingStart 0 VIRTIO_DEVICE_PROTOCOL *VirtIo; // DriverBindingStart 0
EFI_EVENT ExitBoot; // DriverBindingStart 0
BOOLEAN InOutSupported; // VirtioScsiInit 1 BOOLEAN InOutSupported; // VirtioScsiInit 1
UINT16 MaxTarget; // VirtioScsiInit 1 UINT16 MaxTarget; // VirtioScsiInit 1
UINT32 MaxLun; // VirtioScsiInit 1 UINT32 MaxLun; // VirtioScsiInit 1