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

Authored and Committed by lersek 6 years ago
    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>
    
        
file modified
+37 -20