/**
  Produces and returns an RNG value using either the default or specified RNG
  algorithm.

  @param[in]  This                    A pointer to the EFI_RNG_PROTOCOL
                                      instance.
  @param[in]  RNGAlgorithm            A pointer to the EFI_RNG_ALGORITHM that
                                      identifies the RNG algorithm to use. May
                                      be NULL in which case the function will
                                      use its default RNG algorithm.
  @param[in]  RNGValueLength          The length in bytes of the memory buffer
                                      pointed to by RNGValue. The driver shall
                                      return exactly this numbers of bytes.
  @param[out] RNGValue                A caller-allocated memory buffer filled
                                      by the driver with the resulting RNG
                                      value.

  @retval EFI_SUCCESS                 The RNG value was returned successfully.
  @retval EFI_UNSUPPORTED             The algorithm specified by RNGAlgorithm
                                      is not supported by this driver.
  @retval EFI_DEVICE_ERROR            An RNG value could not be retrieved due
                                      to a hardware or firmware error.
  @retval EFI_NOT_READY               There is not enough random data available
                                      to satisfy the length requested by
                                      RNGValueLength.
  @retval EFI_INVALID_PARAMETER       RNGValue is NULL or RNGValueLength is
                                      zero.

**/
STATIC
EFI_STATUS
EFIAPI
VirtioRngGetRNG (
  IN EFI_RNG_PROTOCOL            *This,
  IN EFI_RNG_ALGORITHM           *RNGAlgorithm, OPTIONAL
  IN UINTN                       RNGValueLength,
  OUT UINT8                      *RNGValue
  )
{
  VIRTIO_RNG_DEV            *Dev;
  DESC_INDICES              Indices;
  volatile UINT8            *Buffer;
  UINTN                     Index;
  UINT32                    Len;
  UINT32                    BufferSize;
  EFI_STATUS                Status;
  EFI_PHYSICAL_ADDRESS      DeviceAddress;
  VOID                      *Mapping;

  if (This == NULL || RNGValueLength == 0 || RNGValue == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // We only support the raw algorithm, so reject requests for anything else
  //
  if (RNGAlgorithm != NULL &&
      !CompareGuid (RNGAlgorithm, &gEfiRngAlgorithmRaw)) {
    return EFI_UNSUPPORTED;
  }

  Buffer = (volatile UINT8 *)AllocatePool (RNGValueLength);
  if (Buffer == NULL) {
    return EFI_DEVICE_ERROR;
  }

  Dev = VIRTIO_ENTROPY_SOURCE_FROM_RNG (This);
  //
  // Map Buffer's system phyiscal address to device address
  //
  Status = VirtioMapAllBytesInSharedBuffer (
             Dev->VirtIo,
             VirtioOperationBusMasterWrite,
             (VOID *)Buffer,
             RNGValueLength,
             &DeviceAddress,
             &Mapping
             );
  if (EFI_ERROR (Status)) {
    Status = EFI_DEVICE_ERROR;
    goto FreeBuffer;
  }

  //
  // The Virtio RNG device may return less data than we asked it to, and can
  // only return MAX_UINT32 bytes per invocation. So loop as long as needed to
  // get all the entropy we were asked for.
  //
  for (Index = 0; Index < RNGValueLength; Index += Len) {
    BufferSize = (UINT32)MIN (RNGValueLength - Index, (UINTN)MAX_UINT32);

    VirtioPrepare (&Dev->Ring, &Indices);
    VirtioAppendDesc (&Dev->Ring,
      DeviceAddress + Index,
      BufferSize,
      VRING_DESC_F_WRITE,
      &Indices);

    if (VirtioFlush (Dev->VirtIo, 0, &Dev->Ring, &Indices, &Len) !=
        EFI_SUCCESS) {
      Status = EFI_DEVICE_ERROR;
      goto UnmapBuffer;
    }
    ASSERT (Len > 0);
    ASSERT (Len <= BufferSize);
  }

  //
  // Unmap the device buffer before accessing it.
  //
  Status = Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Mapping);
  if (EFI_ERROR (Status)) {
    Status = EFI_DEVICE_ERROR;
    goto FreeBuffer;
  }

  for (Index = 0; Index < RNGValueLength; Index++) {
    RNGValue[Index] = Buffer[Index];
  }
  Status = EFI_SUCCESS;

UnmapBuffer:
  //
  // If we are reached here due to the error then unmap the buffer otherwise
  // the buffer is already unmapped after VirtioFlush().
  //
  if (EFI_ERROR (Status)) {
    Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Mapping);
  }

FreeBuffer:
  FreePool ((VOID *)Buffer);
  return Status;
}
示例#2
0
STATIC
EFI_STATUS
EFIAPI
SynchronousRequest (
  IN              VBLK_DEV *Dev,
  IN              EFI_LBA  Lba,
  IN              UINTN    BufferSize,
  IN OUT volatile VOID     *Buffer,
  IN              BOOLEAN  RequestIsWrite
  )
{
  UINT32                  BlockSize;
  volatile VIRTIO_BLK_REQ Request;
  volatile UINT8          *HostStatus;
  VOID                    *HostStatusBuffer;
  DESC_INDICES            Indices;
  VOID                    *RequestMapping;
  VOID                    *StatusMapping;
  VOID                    *BufferMapping;
  EFI_PHYSICAL_ADDRESS    BufferDeviceAddress;
  EFI_PHYSICAL_ADDRESS    HostStatusDeviceAddress;
  EFI_PHYSICAL_ADDRESS    RequestDeviceAddress;
  EFI_STATUS              Status;
  EFI_STATUS              UnmapStatus;

  BlockSize = Dev->BlockIoMedia.BlockSize;

  //
  // Set BufferMapping and BufferDeviceAddress to suppress incorrect
  // compiler/analyzer warnings.
  //
  BufferMapping       = NULL;
  BufferDeviceAddress = 0;

  //
  // ensured by VirtioBlkInit()
  //
  ASSERT (BlockSize > 0);
  ASSERT (BlockSize % 512 == 0);

  //
  // ensured by contract above, plus VerifyReadWriteRequest()
  //
  ASSERT (BufferSize % BlockSize == 0);

  //
  // Prepare virtio-blk request header, setting zero size for flush.
  // IO Priority is homogeneously 0.
  //
  Request.Type   = RequestIsWrite ?
                   (BufferSize == 0 ? VIRTIO_BLK_T_FLUSH : VIRTIO_BLK_T_OUT) :
                   VIRTIO_BLK_T_IN;
  Request.IoPrio = 0;
  Request.Sector = MultU64x32(Lba, BlockSize / 512);

  //
  // Host status is bi-directional (we preset with a value and expect the
  // device to update it). Allocate a host status buffer which can be mapped
  // to access equally by both processor and the device.
  //
  Status = Dev->VirtIo->AllocateSharedPages (
                          Dev->VirtIo,
                          EFI_SIZE_TO_PAGES (sizeof *HostStatus),
                          &HostStatusBuffer
                          );
  if (EFI_ERROR (Status)) {
    return EFI_DEVICE_ERROR;
  }

  HostStatus = HostStatusBuffer;

  //
  // Map virtio-blk request header (must be done after request header is
  // populated)
  //
  Status = VirtioMapAllBytesInSharedBuffer (
             Dev->VirtIo,
             VirtioOperationBusMasterRead,
             (VOID *) &Request,
             sizeof Request,
             &RequestDeviceAddress,
             &RequestMapping
             );
  if (EFI_ERROR (Status)) {
    Status = EFI_DEVICE_ERROR;
    goto FreeHostStatusBuffer;
  }

  //
  // Map data buffer
  //
  if (BufferSize > 0) {
    Status = VirtioMapAllBytesInSharedBuffer (
               Dev->VirtIo,
               (RequestIsWrite ?
                VirtioOperationBusMasterRead :
                VirtioOperationBusMasterWrite),
               (VOID *) Buffer,
               BufferSize,
               &BufferDeviceAddress,
               &BufferMapping
               );
    if (EFI_ERROR (Status)) {
      Status = EFI_DEVICE_ERROR;
      goto UnmapRequestBuffer;
    }
  }

  //
  // preset a host status for ourselves that we do not accept as success
  //
  *HostStatus = VIRTIO_BLK_S_IOERR;

  //
  // Map the Status Buffer with VirtioOperationBusMasterCommonBuffer so that
  // both processor and device can access it.
  //
  Status = VirtioMapAllBytesInSharedBuffer (
             Dev->VirtIo,
             VirtioOperationBusMasterCommonBuffer,
             HostStatusBuffer,
             sizeof *HostStatus,
             &HostStatusDeviceAddress,
             &StatusMapping
             );
  if (EFI_ERROR (Status)) {
    Status = EFI_DEVICE_ERROR;
    goto UnmapDataBuffer;
  }

  VirtioPrepare (&Dev->Ring, &Indices);

  //
  // ensured by VirtioBlkInit() -- this predicate, in combination with the
  // lock-step progress, ensures we don't have to track free descriptors.
  //
  ASSERT (Dev->Ring.QueueSize >= 3);

  //
  // virtio-blk header in first desc
  //
  VirtioAppendDesc (
    &Dev->Ring,
    RequestDeviceAddress,
    sizeof Request,
    VRING_DESC_F_NEXT,
    &Indices
    );

  //
  // data buffer for read/write in second desc
  //
  if (BufferSize > 0) {
    //
    // From virtio-0.9.5, 2.3.2 Descriptor Table:
    // "no descriptor chain may be more than 2^32 bytes long in total".
    //
    // The predicate is ensured by the call contract above (for flush), or
    // VerifyReadWriteRequest() (for read/write). It also implies that
    // converting BufferSize to UINT32 will not truncate it.
    //
    ASSERT (BufferSize <= SIZE_1GB);

    //
    // VRING_DESC_F_WRITE is interpreted from the host's point of view.
    //
    VirtioAppendDesc (
      &Dev->Ring,
      BufferDeviceAddress,
      (UINT32) BufferSize,
      VRING_DESC_F_NEXT | (RequestIsWrite ? 0 : VRING_DESC_F_WRITE),
      &Indices
      );
  }

  //
  // host status in last (second or third) desc
  //
  VirtioAppendDesc (
    &Dev->Ring,
    HostStatusDeviceAddress,
    sizeof *HostStatus,
    VRING_DESC_F_WRITE,
    &Indices
    );

  //
  // virtio-blk's only virtqueue is #0, called "requestq" (see Appendix D).
  //
  if (VirtioFlush (Dev->VirtIo, 0, &Dev->Ring, &Indices,
        NULL) == EFI_SUCCESS &&
      *HostStatus == VIRTIO_BLK_S_OK) {
    Status = EFI_SUCCESS;
  } else {
    Status = EFI_DEVICE_ERROR;
  }

  Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, StatusMapping);

UnmapDataBuffer:
  if (BufferSize > 0) {
    UnmapStatus = Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, BufferMapping);
    if (EFI_ERROR (UnmapStatus) && !RequestIsWrite && !EFI_ERROR (Status)) {
      //
      // Data from the bus master may not reach the caller; fail the request.
      //
      Status = EFI_DEVICE_ERROR;
    }
  }

UnmapRequestBuffer:
  Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, RequestMapping);

FreeHostStatusBuffer:
  Dev->VirtIo->FreeSharedPages (
                 Dev->VirtIo,
                 EFI_SIZE_TO_PAGES (sizeof *HostStatus),
                 HostStatusBuffer
                 );

  return Status;
}