Home | History | Annotate | Download | only in VirtioGpuDxe
      1 /** @file
      2 
      3   VirtIo GPU initialization, and commands (primitives) for the GPU device.
      4 
      5   Copyright (C) 2016, Red Hat, Inc.
      6 
      7   This program and the accompanying materials are licensed and made available
      8   under the terms and conditions of the BSD License which accompanies this
      9   distribution. The full text of the license may be found at
     10   http://opensource.org/licenses/bsd-license.php
     11 
     12   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
     13   WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     14 
     15 **/
     16 
     17 #include <Library/VirtioLib.h>
     18 
     19 #include "VirtioGpu.h"
     20 
     21 /**
     22   Configure the VirtIo GPU device that underlies VgpuDev.
     23 
     24   @param[in,out] VgpuDev  The VGPU_DEV object to set up VirtIo messaging for.
     25                           On input, the caller is responsible for having
     26                           initialized VgpuDev->VirtIo. On output, VgpuDev->Ring
     27                           has been initialized, and synchronous VirtIo GPU
     28                           commands (primitives) can be submitted to the device.
     29 
     30   @retval EFI_SUCCESS      VirtIo GPU configuration successful.
     31 
     32   @retval EFI_UNSUPPORTED  The host-side configuration of the VirtIo GPU is not
     33                            supported by this driver.
     34 
     35   @retval                  Error codes from underlying functions.
     36 **/
     37 EFI_STATUS
     38 VirtioGpuInit (
     39   IN OUT VGPU_DEV *VgpuDev
     40   )
     41 {
     42   UINT8      NextDevStat;
     43   EFI_STATUS Status;
     44   UINT64     Features;
     45   UINT16     QueueSize;
     46 
     47   //
     48   // Execute virtio-v1.0-cs04, 3.1.1 Driver Requirements: Device
     49   // Initialization.
     50   //
     51   // 1. Reset the device.
     52   //
     53   NextDevStat = 0;
     54   Status = VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);
     55   if (EFI_ERROR (Status)) {
     56     goto Failed;
     57   }
     58 
     59   //
     60   // 2. Set the ACKNOWLEDGE status bit [...]
     61   //
     62   NextDevStat |= VSTAT_ACK;
     63   Status = VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);
     64   if (EFI_ERROR (Status)) {
     65     goto Failed;
     66   }
     67 
     68   //
     69   // 3. Set the DRIVER status bit [...]
     70   //
     71   NextDevStat |= VSTAT_DRIVER;
     72   Status = VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);
     73   if (EFI_ERROR (Status)) {
     74     goto Failed;
     75   }
     76 
     77   //
     78   // 4. Read device feature bits...
     79   //
     80   Status = VgpuDev->VirtIo->GetDeviceFeatures (VgpuDev->VirtIo, &Features);
     81   if (EFI_ERROR (Status)) {
     82     goto Failed;
     83   }
     84   if ((Features & VIRTIO_F_VERSION_1) == 0) {
     85     Status = EFI_UNSUPPORTED;
     86     goto Failed;
     87   }
     88   //
     89   // We only want the most basic 2D features.
     90   //
     91   Features &= VIRTIO_F_VERSION_1;
     92 
     93   //
     94   // ... and write the subset of feature bits understood by the [...] driver to
     95   // the device. [...]
     96   // 5. Set the FEATURES_OK status bit.
     97   // 6. Re-read device status to ensure the FEATURES_OK bit is still set [...]
     98   //
     99   Status = Virtio10WriteFeatures (VgpuDev->VirtIo, Features, &NextDevStat);
    100   if (EFI_ERROR (Status)) {
    101     goto Failed;
    102   }
    103 
    104   //
    105   // 7. Perform device-specific setup, including discovery of virtqueues for
    106   // the device [...]
    107   //
    108   Status = VgpuDev->VirtIo->SetQueueSel (VgpuDev->VirtIo,
    109                               VIRTIO_GPU_CONTROL_QUEUE);
    110   if (EFI_ERROR (Status)) {
    111     goto Failed;
    112   }
    113   Status = VgpuDev->VirtIo->GetQueueNumMax (VgpuDev->VirtIo, &QueueSize);
    114   if (EFI_ERROR (Status)) {
    115     goto Failed;
    116   }
    117 
    118   //
    119   // We implement each VirtIo GPU command that we use with two descriptors:
    120   // request, response.
    121   //
    122   if (QueueSize < 2) {
    123     Status = EFI_UNSUPPORTED;
    124     goto Failed;
    125   }
    126 
    127   //
    128   // [...] population of virtqueues [...]
    129   //
    130   Status = VirtioRingInit (QueueSize, &VgpuDev->Ring);
    131   if (EFI_ERROR (Status)) {
    132     goto Failed;
    133   }
    134   Status = VgpuDev->VirtIo->SetQueueAddress (VgpuDev->VirtIo, &VgpuDev->Ring);
    135   if (EFI_ERROR (Status)) {
    136     goto ReleaseQueue;
    137   }
    138 
    139   //
    140   // 8. Set the DRIVER_OK status bit.
    141   //
    142   NextDevStat |= VSTAT_DRIVER_OK;
    143   Status = VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);
    144   if (EFI_ERROR (Status)) {
    145     goto ReleaseQueue;
    146   }
    147 
    148   return EFI_SUCCESS;
    149 
    150 ReleaseQueue:
    151   VirtioRingUninit (&VgpuDev->Ring);
    152 
    153 Failed:
    154   //
    155   // If any of these steps go irrecoverably wrong, the driver SHOULD set the
    156   // FAILED status bit to indicate that it has given up on the device (it can
    157   // reset the device later to restart if desired). [...]
    158   //
    159   // VirtIo access failure here should not mask the original error.
    160   //
    161   NextDevStat |= VSTAT_FAILED;
    162   VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);
    163 
    164   return Status;
    165 }
    166 
    167 /**
    168   De-configure the VirtIo GPU device that underlies VgpuDev.
    169 
    170   @param[in,out] VgpuDev  The VGPU_DEV object to tear down VirtIo messaging
    171                           for. On input, the caller is responsible for having
    172                           called VirtioGpuInit(). On output, VgpuDev->Ring has
    173                           been uninitialized; VirtIo GPU commands (primitives)
    174                           can no longer be submitted to the device.
    175 **/
    176 VOID
    177 VirtioGpuUninit (
    178   IN OUT VGPU_DEV *VgpuDev
    179   )
    180 {
    181   //
    182   // Resetting the VirtIo device makes it release its resources and forget its
    183   // configuration.
    184   //
    185   VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, 0);
    186   VirtioRingUninit (&VgpuDev->Ring);
    187 }
    188 
    189 /**
    190   EFI_EVENT_NOTIFY function for the VGPU_DEV.ExitBoot event. It resets the
    191   VirtIo device, causing it to release its resources and to forget its
    192   configuration.
    193 
    194   This function may only be called (that is, VGPU_DEV.ExitBoot may only be
    195   signaled) after VirtioGpuInit() returns and before VirtioGpuUninit() is
    196   called.
    197 
    198   @param[in] Event    Event whose notification function is being invoked.
    199 
    200   @param[in] Context  Pointer to the associated VGPU_DEV object.
    201 **/
    202 VOID
    203 EFIAPI
    204 VirtioGpuExitBoot (
    205   IN EFI_EVENT Event,
    206   IN VOID      *Context
    207   )
    208 {
    209   VGPU_DEV *VgpuDev;
    210 
    211   VgpuDev = Context;
    212   VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, 0);
    213 }
    214 
    215 /**
    216   Internal utility function that sends a request to the VirtIo GPU device
    217   model, awaits the answer from the host, and returns a status.
    218 
    219   @param[in,out] VgpuDev  The VGPU_DEV object that represents the VirtIo GPU
    220                           device. The caller is responsible to have
    221                           successfully invoked VirtioGpuInit() on VgpuDev
    222                           previously, while VirtioGpuUninit() must not have
    223                           been called on VgpuDev.
    224 
    225   @param[in] RequestType  The type of the request. The caller is responsible
    226                           for providing a VirtioGpuCmd* RequestType which, on
    227                           success, elicits a VirtioGpuRespOkNodata response
    228                           from the host.
    229 
    230   @param[in] Fence        Whether to enable fencing for this request. Fencing
    231                           forces the host to complete the command before
    232                           producing a response. If Fence is TRUE, then
    233                           VgpuDev->FenceId is consumed, and incremented.
    234 
    235   @param[in,out] Header   Pointer to the caller-allocated request object. The
    236                           request must start with VIRTIO_GPU_CONTROL_HEADER.
    237                           This function overwrites all fields of Header before
    238                           submitting the request to the host:
    239 
    240                           - it sets Type from RequestType,
    241 
    242                           - it sets Flags and FenceId based on Fence,
    243 
    244                           - it zeroes CtxId and Padding.
    245 
    246   @param[in] RequestSize  Size of the entire caller-allocated request object,
    247                           including the leading VIRTIO_GPU_CONTROL_HEADER.
    248 
    249   @retval EFI_SUCCESS            Operation successful.
    250 
    251   @retval EFI_DEVICE_ERROR       The host rejected the request. The host error
    252                                  code has been logged on the EFI_D_ERROR level.
    253 
    254   @return                        Codes for unexpected errors in VirtIo
    255                                  messaging.
    256 **/
    257 STATIC
    258 EFI_STATUS
    259 VirtioGpuSendCommand (
    260   IN OUT VGPU_DEV                           *VgpuDev,
    261   IN     VIRTIO_GPU_CONTROL_TYPE            RequestType,
    262   IN     BOOLEAN                            Fence,
    263   IN OUT volatile VIRTIO_GPU_CONTROL_HEADER *Header,
    264   IN     UINTN                              RequestSize
    265   )
    266 {
    267   DESC_INDICES                       Indices;
    268   volatile VIRTIO_GPU_CONTROL_HEADER Response;
    269   EFI_STATUS                         Status;
    270   UINT32                             ResponseSize;
    271 
    272   //
    273   // Initialize Header.
    274   //
    275   Header->Type      = RequestType;
    276   if (Fence) {
    277     Header->Flags   = VIRTIO_GPU_FLAG_FENCE;
    278     Header->FenceId = VgpuDev->FenceId++;
    279   } else {
    280     Header->Flags   = 0;
    281     Header->FenceId = 0;
    282   }
    283   Header->CtxId     = 0;
    284   Header->Padding   = 0;
    285 
    286   ASSERT (RequestSize >= sizeof *Header);
    287   ASSERT (RequestSize <= MAX_UINT32);
    288 
    289   //
    290   // Compose the descriptor chain.
    291   //
    292   VirtioPrepare (&VgpuDev->Ring, &Indices);
    293   VirtioAppendDesc (&VgpuDev->Ring, (UINTN)Header, (UINT32)RequestSize,
    294     VRING_DESC_F_NEXT, &Indices);
    295   VirtioAppendDesc (&VgpuDev->Ring, (UINTN)&Response, sizeof Response,
    296     VRING_DESC_F_WRITE, &Indices);
    297 
    298   //
    299   // Send the command.
    300   //
    301   Status = VirtioFlush (VgpuDev->VirtIo, VIRTIO_GPU_CONTROL_QUEUE,
    302              &VgpuDev->Ring, &Indices, &ResponseSize);
    303   if (EFI_ERROR (Status)) {
    304     return Status;
    305   }
    306 
    307   //
    308   // Parse the response.
    309   //
    310   if (ResponseSize != sizeof Response) {
    311     DEBUG ((EFI_D_ERROR, "%a: malformed response to Request=0x%x\n",
    312       __FUNCTION__, (UINT32)RequestType));
    313     return EFI_PROTOCOL_ERROR;
    314   }
    315 
    316   if (Response.Type == VirtioGpuRespOkNodata) {
    317     return EFI_SUCCESS;
    318   }
    319 
    320   DEBUG ((EFI_D_ERROR, "%a: Request=0x%x Response=0x%x\n", __FUNCTION__,
    321     (UINT32)RequestType, Response.Type));
    322   return EFI_DEVICE_ERROR;
    323 }
    324 
    325 /**
    326   The following functions send requests to the VirtIo GPU device model, await
    327   the answer from the host, and return a status. They share the following
    328   interface details:
    329 
    330   @param[in,out] VgpuDev  The VGPU_DEV object that represents the VirtIo GPU
    331                           device. The caller is responsible to have
    332                           successfully invoked VirtioGpuInit() on VgpuDev
    333                           previously, while VirtioGpuUninit() must not have
    334                           been called on VgpuDev.
    335 
    336   @retval EFI_INVALID_PARAMETER  Invalid command-specific parameters were
    337                                  detected by this driver.
    338 
    339   @retval EFI_SUCCESS            Operation successful.
    340 
    341   @retval EFI_DEVICE_ERROR       The host rejected the request. The host error
    342                                  code has been logged on the EFI_D_ERROR level.
    343 
    344   @return                        Codes for unexpected errors in VirtIo
    345                                  messaging.
    346 
    347   For the command-specific parameters, please consult the GPU Device section of
    348   the VirtIo 1.0 specification (see references in
    349   "OvmfPkg/Include/IndustryStandard/VirtioGpu.h").
    350 **/
    351 EFI_STATUS
    352 VirtioGpuResourceCreate2d (
    353   IN OUT VGPU_DEV           *VgpuDev,
    354   IN     UINT32             ResourceId,
    355   IN     VIRTIO_GPU_FORMATS Format,
    356   IN     UINT32             Width,
    357   IN     UINT32             Height
    358   )
    359 {
    360   volatile VIRTIO_GPU_RESOURCE_CREATE_2D Request;
    361 
    362   if (ResourceId == 0) {
    363     return EFI_INVALID_PARAMETER;
    364   }
    365 
    366   Request.ResourceId = ResourceId;
    367   Request.Format     = (UINT32)Format;
    368   Request.Width      = Width;
    369   Request.Height     = Height;
    370 
    371   return VirtioGpuSendCommand (
    372            VgpuDev,
    373            VirtioGpuCmdResourceCreate2d,
    374            FALSE,                        // Fence
    375            &Request.Header,
    376            sizeof Request
    377            );
    378 }
    379 
    380 EFI_STATUS
    381 VirtioGpuResourceUnref (
    382   IN OUT VGPU_DEV *VgpuDev,
    383   IN     UINT32   ResourceId
    384   )
    385 {
    386   volatile VIRTIO_GPU_RESOURCE_UNREF Request;
    387 
    388   if (ResourceId == 0) {
    389     return EFI_INVALID_PARAMETER;
    390   }
    391 
    392   Request.ResourceId = ResourceId;
    393   Request.Padding    = 0;
    394 
    395   return VirtioGpuSendCommand (
    396            VgpuDev,
    397            VirtioGpuCmdResourceUnref,
    398            FALSE,                     // Fence
    399            &Request.Header,
    400            sizeof Request
    401            );
    402 }
    403 
    404 EFI_STATUS
    405 VirtioGpuResourceAttachBacking (
    406   IN OUT VGPU_DEV *VgpuDev,
    407   IN     UINT32   ResourceId,
    408   IN     VOID     *FirstBackingPage,
    409   IN     UINTN    NumberOfPages
    410   )
    411 {
    412   volatile VIRTIO_GPU_RESOURCE_ATTACH_BACKING Request;
    413 
    414   if (ResourceId == 0) {
    415     return EFI_INVALID_PARAMETER;
    416   }
    417 
    418   Request.ResourceId    = ResourceId;
    419   Request.NrEntries     = 1;
    420   Request.Entry.Addr    = (UINTN)FirstBackingPage;
    421   Request.Entry.Length  = (UINT32)EFI_PAGES_TO_SIZE (NumberOfPages);
    422   Request.Entry.Padding = 0;
    423 
    424   return VirtioGpuSendCommand (
    425            VgpuDev,
    426            VirtioGpuCmdResourceAttachBacking,
    427            FALSE,                             // Fence
    428            &Request.Header,
    429            sizeof Request
    430            );
    431 }
    432 
    433 EFI_STATUS
    434 VirtioGpuResourceDetachBacking (
    435   IN OUT VGPU_DEV *VgpuDev,
    436   IN     UINT32   ResourceId
    437   )
    438 {
    439   volatile VIRTIO_GPU_RESOURCE_DETACH_BACKING Request;
    440 
    441   if (ResourceId == 0) {
    442     return EFI_INVALID_PARAMETER;
    443   }
    444 
    445   Request.ResourceId = ResourceId;
    446   Request.Padding    = 0;
    447 
    448   //
    449   // In this case, we set Fence to TRUE, because after this function returns,
    450   // the caller might reasonably want to repurpose the backing pages
    451   // immediately. Thus we should ensure that the host releases all references
    452   // to the backing pages before we return.
    453   //
    454   return VirtioGpuSendCommand (
    455            VgpuDev,
    456            VirtioGpuCmdResourceDetachBacking,
    457            TRUE,                              // Fence
    458            &Request.Header,
    459            sizeof Request
    460            );
    461 }
    462 
    463 EFI_STATUS
    464 VirtioGpuSetScanout (
    465   IN OUT VGPU_DEV *VgpuDev,
    466   IN     UINT32   X,
    467   IN     UINT32   Y,
    468   IN     UINT32   Width,
    469   IN     UINT32   Height,
    470   IN     UINT32   ScanoutId,
    471   IN     UINT32   ResourceId
    472   )
    473 {
    474   volatile VIRTIO_GPU_SET_SCANOUT Request;
    475 
    476   //
    477   // Unlike for most other commands, ResourceId=0 is valid; it
    478   // is used to disable a scanout.
    479   //
    480   Request.Rectangle.X      = X;
    481   Request.Rectangle.Y      = Y;
    482   Request.Rectangle.Width  = Width;
    483   Request.Rectangle.Height = Height;
    484   Request.ScanoutId        = ScanoutId;
    485   Request.ResourceId       = ResourceId;
    486 
    487   return VirtioGpuSendCommand (
    488            VgpuDev,
    489            VirtioGpuCmdSetScanout,
    490            FALSE,                  // Fence
    491            &Request.Header,
    492            sizeof Request
    493            );
    494 }
    495 
    496 EFI_STATUS
    497 VirtioGpuTransferToHost2d (
    498   IN OUT VGPU_DEV *VgpuDev,
    499   IN     UINT32   X,
    500   IN     UINT32   Y,
    501   IN     UINT32   Width,
    502   IN     UINT32   Height,
    503   IN     UINT64   Offset,
    504   IN     UINT32   ResourceId
    505   )
    506 {
    507   volatile VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D Request;
    508 
    509   if (ResourceId == 0) {
    510     return EFI_INVALID_PARAMETER;
    511   }
    512 
    513   Request.Rectangle.X      = X;
    514   Request.Rectangle.Y      = Y;
    515   Request.Rectangle.Width  = Width;
    516   Request.Rectangle.Height = Height;
    517   Request.Offset           = Offset;
    518   Request.ResourceId       = ResourceId;
    519   Request.Padding          = 0;
    520 
    521   return VirtioGpuSendCommand (
    522            VgpuDev,
    523            VirtioGpuCmdTransferToHost2d,
    524            FALSE,                        // Fence
    525            &Request.Header,
    526            sizeof Request
    527            );
    528 }
    529 
    530 EFI_STATUS
    531 VirtioGpuResourceFlush (
    532   IN OUT VGPU_DEV *VgpuDev,
    533   IN     UINT32   X,
    534   IN     UINT32   Y,
    535   IN     UINT32   Width,
    536   IN     UINT32   Height,
    537   IN     UINT32   ResourceId
    538   )
    539 {
    540   volatile VIRTIO_GPU_RESOURCE_FLUSH Request;
    541 
    542   if (ResourceId == 0) {
    543     return EFI_INVALID_PARAMETER;
    544   }
    545 
    546   Request.Rectangle.X      = X;
    547   Request.Rectangle.Y      = Y;
    548   Request.Rectangle.Width  = Width;
    549   Request.Rectangle.Height = Height;
    550   Request.ResourceId       = ResourceId;
    551   Request.Padding          = 0;
    552 
    553   return VirtioGpuSendCommand (
    554            VgpuDev,
    555            VirtioGpuCmdResourceFlush,
    556            FALSE,                     // Fence
    557            &Request.Header,
    558            sizeof Request
    559            );
    560 }
    561