/** 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; }
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; }