OvmfPkg/VirtioGpuDxe: map backing store to bus master device address

VirtioGpuDxe is a UEFI Bus driver (not a Device driver). This is because a
UEFI graphics driver is expected to produce its GraphicsOutput protocol
instance(s) on new child handle(s) of the video controller handle, one
child handle (plus GOP) per video output (or, one child handle plus GOP
per combination of multiple video outputs).

In VirtioGpuDxe, we support a single VirtIo GPU head (scanout), namely
head#0. This means that, with regard to a specific VirtIo GPU device, the
driver may be in one of three states, at any time:

[1] VirtioGpuDxe has not bound the device at all,

[2] VirtioGpuDxe has bound the device, but not produced the sole child
    handle for head#0,

[3] VirtioGpuDxe has bound the device, and produced the sole child handle
    for head#0, with a GOP instance on the child handle.

(Which state the driver is in wrt. a given VirtIo GPU device depends on
the VirtioGpuDriverBindingStart() / VirtioGpuDriverBindingStop()
invocations issued by the ConnectController() / DisconnectController()
boot services. In turn those come from BDS or e.g. the UEFI shell.)

The concept of "current video mode" is technically tied to the GOP (i.e.,
the child handle, state [3] only), not the VirtIo GPU controller handle.
This is why we manage the storage that backs the current video mode in our
EFI_GRAPHICS_OUTPUT_PROTOCOL.SetMode() member implementation.

GopSetMode() is first called *internally*, when we enter state [3] (that
is, when we produce the child handle + GOP on it):

  VirtioGpuDriverBindingStart() [DriverBinding.c]
    InitVgpuGop()               [DriverBinding.c]
      VgpuGop->Gop.SetMode()    [Gop.c]

When this happens, we allocate the backing store *without* having a
preexistent backing store (due to no preexistent video mode and GOP).
Skipping VirtIo GPU details not relevant for this patch, we just note that
the backing store is exposed *permanently* to the VirtIo GPU device, with
the RESOURCE_ATTACH_BACKING command.

When external clients call the EFI_GRAPHICS_OUTPUT_PROTOCOL.Blt() member
function -- called GopBlt() in this driver --, in state [3], the function
operates on the backing store, and sends only small messages to the VirtIo
GPU device.

When external clients call GopSetMode() for switching between video modes
-- in state [3] --, then

- a new backing store is allocated and exposed to the device (attached to
  a new host-side VirtIo GPU resource),

- head#0 is flipped to the new backing store,

- on success, the ReleaseGopResources() function both detaches the
  previous backing store from the VirtIo GPU device, an releases it. The
  new backing store address and size are saved in our GOP object. (In
  other words, we "commit" to the new video mode.)

When the DisconnectController() boot service asks us to leave state [3] --
we can leave it directly only for state [2] --, then the
ReleaseGopResources() function is called on a different path:

  VirtioGpuDriverBindingStop() [DriverBinding.c]
    UninitVgpuGop()            [DriverBinding.c]
      ReleaseGopResources()    [Gop.c]

In this case, the backing store being released is still in use (we're not
leaving it for a new mode -- head#0 has not been flipped "away" from it),
so in ReleaseGopResources() we disable head#0 first.

(The ReleaseGopResources() function is called the same way on the error
path in InitVgpuGop(), if the first -- internal -- VgpuGop->Gop.SetMode()
call succeeds, but the rest of InitVgpuGop() fails.)

Based on the above, for IOMMU-compatibility,

- in GopSetMode(), don't just allocate, but also map the backing store of
  the nascent video mode to a device address, for bus master common buffer
  operation,

- (the VirtioGpuAllocateZeroAndMapBackingStore() helper function
  introduced in the last patch takes care of zeroing internally,)

- pass the device address to the VirtIo GPU device in the
  RESOURCE_ATTACH_BACKING command,

- if GopSetMode() succeeds, save the mapping token,

- if GopSetMode() fails, don't just free but also unmap the still-born
  backing store,

- in ReleaseGopResources(), don't just free but also unmap the backing
  store -- which is the previous backing store if we're mode-switching,
  and the current backing store if we're leaving state [3].

Finally, ExitBootServices() may be called when the driver is in either
state [1], [2] or [3], wrt. a given VirtIo GPU device. (Of course we are
only notified in states [2] and [3].) If we get the notification in state
[3], then the current video mode's backing store has to be unmapped, but
not released. (We must not change the UEFI memory map.)

Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Tested-by: Brijesh Singh <brijesh.singh@amd.com>
This commit is contained in:
Laszlo Ersek 2017-08-26 18:12:15 +02:00
parent 5c8f091bd8
commit f10ae92366
3 changed files with 64 additions and 19 deletions

View File

@ -355,6 +355,27 @@ VirtioGpuExitBoot (
VgpuDev = Context; VgpuDev = Context;
VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, 0); VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, 0);
//
// If VirtioGpuDriverBindingStart() and VirtioGpuDriverBindingStop() have
// been called thus far in such a sequence that right now our (sole) child
// handle exists -- with the GOP on it standing for head (scanout) #0 --,
// then we have to unmap the current video mode's backing store.
//
if (VgpuDev->Child != NULL) {
//
// The current video mode is guaranteed to have a valid and mapped backing
// store, due to the first Gop.SetMode() call, made internally in
// InitVgpuGop().
//
ASSERT (VgpuDev->Child->BackingStore != NULL);
VgpuDev->VirtIo->UnmapSharedBuffer (
VgpuDev->VirtIo,
VgpuDev->Child->BackingStoreMap
);
}
VgpuDev->VirtIo->UnmapSharedBuffer (VgpuDev->VirtIo, VgpuDev->RingMap); VgpuDev->VirtIo->UnmapSharedBuffer (VgpuDev->VirtIo, VgpuDev->RingMap);
} }

View File

@ -114,11 +114,17 @@ ReleaseGopResources (
} }
// //
// Release backing pages. // Unmap and release backing pages.
// //
FreePages (VgpuGop->BackingStore, VgpuGop->NumberOfPages); VirtioGpuUnmapAndFreeBackingStore (
VgpuGop->ParentBus, // VgpuDev
VgpuGop->NumberOfPages, // NumberOfPages
VgpuGop->BackingStore, // HostAddress
VgpuGop->BackingStoreMap // Mapping
);
VgpuGop->BackingStore = NULL; VgpuGop->BackingStore = NULL;
VgpuGop->NumberOfPages = 0; VgpuGop->NumberOfPages = 0;
VgpuGop->BackingStoreMap = NULL;
// //
// Destroy the currently used 2D host resource. // Destroy the currently used 2D host resource.
@ -235,6 +241,9 @@ GopSetMode (
UINTN NewNumberOfBytes; UINTN NewNumberOfBytes;
UINTN NewNumberOfPages; UINTN NewNumberOfPages;
VOID *NewBackingStore; VOID *NewBackingStore;
EFI_PHYSICAL_ADDRESS NewBackingStoreDeviceAddress;
VOID *NewBackingStoreMap;
EFI_STATUS Status; EFI_STATUS Status;
EFI_STATUS Status2; EFI_STATUS Status2;
@ -293,20 +302,22 @@ GopSetMode (
} }
// //
// Allocate guest backing store. // Allocate, zero and map guest backing store, for bus master common buffer
// operation.
// //
NewNumberOfBytes = mGopResolutions[ModeNumber].Width * NewNumberOfBytes = mGopResolutions[ModeNumber].Width *
mGopResolutions[ModeNumber].Height * sizeof (UINT32); mGopResolutions[ModeNumber].Height * sizeof (UINT32);
NewNumberOfPages = EFI_SIZE_TO_PAGES (NewNumberOfBytes); NewNumberOfPages = EFI_SIZE_TO_PAGES (NewNumberOfBytes);
NewBackingStore = AllocatePages (NewNumberOfPages); Status = VirtioGpuAllocateZeroAndMapBackingStore (
if (NewBackingStore == NULL) { VgpuGop->ParentBus, // VgpuDev
Status = EFI_OUT_OF_RESOURCES; NewNumberOfPages, // NumberOfPages
&NewBackingStore, // HostAddress
&NewBackingStoreDeviceAddress, // DeviceAddress
&NewBackingStoreMap // Mapping
);
if (EFI_ERROR (Status)) {
goto DestroyHostResource; goto DestroyHostResource;
} }
//
// Fill visible part of backing store with black.
//
ZeroMem (NewBackingStore, NewNumberOfBytes);
// //
// Attach backing store to the host resource. // Attach backing store to the host resource.
@ -314,11 +325,11 @@ GopSetMode (
Status = VirtioGpuResourceAttachBacking ( Status = VirtioGpuResourceAttachBacking (
VgpuGop->ParentBus, // VgpuDev VgpuGop->ParentBus, // VgpuDev
NewResourceId, // ResourceId NewResourceId, // ResourceId
(UINTN)NewBackingStore, // BackingStoreDeviceAddress NewBackingStoreDeviceAddress, // BackingStoreDeviceAddress
NewNumberOfPages // NumberOfPages NewNumberOfPages // NumberOfPages
); );
if (EFI_ERROR (Status)) { if (EFI_ERROR (Status)) {
goto FreeBackingStore; goto UnmapAndFreeBackingStore;
} }
// //
@ -391,6 +402,7 @@ GopSetMode (
VgpuGop->ResourceId = NewResourceId; VgpuGop->ResourceId = NewResourceId;
VgpuGop->BackingStore = NewBackingStore; VgpuGop->BackingStore = NewBackingStore;
VgpuGop->NumberOfPages = NewNumberOfPages; VgpuGop->NumberOfPages = NewNumberOfPages;
VgpuGop->BackingStoreMap = NewBackingStoreMap;
// //
// Populate Mode and ModeInfo (mutable fields only). // Populate Mode and ModeInfo (mutable fields only).
@ -409,8 +421,13 @@ DetachBackingStore:
CpuDeadLoop (); CpuDeadLoop ();
} }
FreeBackingStore: UnmapAndFreeBackingStore:
FreePages (NewBackingStore, NewNumberOfPages); VirtioGpuUnmapAndFreeBackingStore (
VgpuGop->ParentBus, // VgpuDev
NewNumberOfPages, // NumberOfPages
NewBackingStore, // HostAddress
NewBackingStoreMap // Mapping
);
DestroyHostResource: DestroyHostResource:
Status2 = VirtioGpuResourceUnref (VgpuGop->ParentBus, NewResourceId); Status2 = VirtioGpuResourceUnref (VgpuGop->ParentBus, NewResourceId);

View File

@ -150,6 +150,13 @@ struct VGPU_GOP_STRUCT {
// //
UINT32 *BackingStore; UINT32 *BackingStore;
UINTN NumberOfPages; UINTN NumberOfPages;
//
// Token associated with BackingStore's mapping for bus master common
// buffer operation. BackingStoreMap is valid if, and only if,
// BackingStore is non-NULL.
//
VOID *BackingStoreMap;
}; };
// //