/** @file | |
This driver produces Virtio Device Protocol instances for Virtio MMIO devices. | |
Copyright (C) 2012, Red Hat, Inc. | |
Copyright (c) 2012, Intel Corporation. All rights reserved.<BR> | |
Copyright (C) 2013, ARM Ltd. | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "VirtioMmioDevice.h" | |
EFI_STATUS | |
EFIAPI | |
VirtioMmioGetDeviceFeatures ( | |
IN VIRTIO_DEVICE_PROTOCOL *This, | |
OUT UINT64 *DeviceFeatures | |
) | |
{ | |
VIRTIO_MMIO_DEVICE *Device; | |
UINT32 LowBits, HighBits; | |
if (DeviceFeatures == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This); | |
if (Device->Version == VIRTIO_MMIO_DEVICE_VERSION_0_95) { | |
*DeviceFeatures = VIRTIO_CFG_READ (Device, VIRTIO_MMIO_OFFSET_HOST_FEATURES); | |
} else { | |
VIRTIO_CFG_WRITE (Device, VIRTIO_MMIO_OFFSET_HOST_FEATURES_SEL, 0); | |
LowBits = VIRTIO_CFG_READ (Device, VIRTIO_MMIO_OFFSET_HOST_FEATURES); | |
VIRTIO_CFG_WRITE (Device, VIRTIO_MMIO_OFFSET_HOST_FEATURES_SEL, 1); | |
HighBits = VIRTIO_CFG_READ (Device, VIRTIO_MMIO_OFFSET_HOST_FEATURES); | |
*DeviceFeatures = LShiftU64 (HighBits, 32) | LowBits; | |
} | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
VirtioMmioGetQueueSize ( | |
IN VIRTIO_DEVICE_PROTOCOL *This, | |
OUT UINT16 *QueueNumMax | |
) | |
{ | |
VIRTIO_MMIO_DEVICE *Device; | |
if (QueueNumMax == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This); | |
*QueueNumMax = VIRTIO_CFG_READ (Device, VIRTIO_MMIO_OFFSET_QUEUE_NUM_MAX) & 0xFFFF; | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
VirtioMmioGetDeviceStatus ( | |
IN VIRTIO_DEVICE_PROTOCOL *This, | |
OUT UINT8 *DeviceStatus | |
) | |
{ | |
VIRTIO_MMIO_DEVICE *Device; | |
if (DeviceStatus == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This); | |
*DeviceStatus = VIRTIO_CFG_READ (Device, VIRTIO_MMIO_OFFSET_STATUS) & 0xFF; | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
VirtioMmioSetQueueSize ( | |
IN VIRTIO_DEVICE_PROTOCOL *This, | |
IN UINT16 QueueSize | |
) | |
{ | |
VIRTIO_MMIO_DEVICE *Device; | |
Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This); | |
if (Device->Version == VIRTIO_MMIO_DEVICE_VERSION_0_95) { | |
VIRTIO_CFG_WRITE (Device, VIRTIO_MMIO_OFFSET_QUEUE_NUM, QueueSize); | |
} else { | |
Device->QueueNum = QueueSize; | |
} | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
VirtioMmioSetDeviceStatus ( | |
IN VIRTIO_DEVICE_PROTOCOL *This, | |
IN UINT8 DeviceStatus | |
) | |
{ | |
VIRTIO_MMIO_DEVICE *Device; | |
Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This); | |
VIRTIO_CFG_WRITE (Device, VIRTIO_MMIO_OFFSET_STATUS, DeviceStatus); | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
VirtioMmioSetQueueNotify ( | |
IN VIRTIO_DEVICE_PROTOCOL *This, | |
IN UINT16 QueueNotify | |
) | |
{ | |
VIRTIO_MMIO_DEVICE *Device; | |
Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This); | |
VIRTIO_CFG_WRITE (Device, VIRTIO_MMIO_OFFSET_QUEUE_NOTIFY, QueueNotify); | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
VirtioMmioSetQueueAlignment ( | |
IN VIRTIO_DEVICE_PROTOCOL *This, | |
IN UINT32 Alignment | |
) | |
{ | |
VIRTIO_MMIO_DEVICE *Device; | |
Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This); | |
if (Device->Version == VIRTIO_MMIO_DEVICE_VERSION_0_95) { | |
VIRTIO_CFG_WRITE (Device, VIRTIO_MMIO_OFFSET_QUEUE_ALIGN, Alignment); | |
} | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
VirtioMmioSetPageSize ( | |
IN VIRTIO_DEVICE_PROTOCOL *This, | |
IN UINT32 PageSize | |
) | |
{ | |
VIRTIO_MMIO_DEVICE *Device; | |
if (PageSize != EFI_PAGE_SIZE) { | |
return EFI_UNSUPPORTED; | |
} | |
Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This); | |
if (Device->Version == VIRTIO_MMIO_DEVICE_VERSION_0_95) { | |
VIRTIO_CFG_WRITE (Device, VIRTIO_MMIO_OFFSET_GUEST_PAGE_SIZE, PageSize); | |
} | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
VirtioMmioSetQueueSel ( | |
IN VIRTIO_DEVICE_PROTOCOL *This, | |
IN UINT16 Sel | |
) | |
{ | |
VIRTIO_MMIO_DEVICE *Device; | |
Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This); | |
VIRTIO_CFG_WRITE (Device, VIRTIO_MMIO_OFFSET_QUEUE_SEL, Sel); | |
if (Device->Version == VIRTIO_MMIO_DEVICE_VERSION_0_95) { | |
Device->QueueNum = VIRTIO_CFG_READ (Device, VIRTIO_MMIO_OFFSET_QUEUE_NUM_MAX) & 0xFFFF; | |
} | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
VirtioMmioSetQueueAddress ( | |
IN VIRTIO_DEVICE_PROTOCOL *This, | |
IN VRING *Ring, | |
IN UINT64 RingBaseShift | |
) | |
{ | |
VIRTIO_MMIO_DEVICE *Device; | |
UINT64 Address; | |
ASSERT (RingBaseShift == 0); | |
Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This); | |
if (Device->Version == VIRTIO_MMIO_DEVICE_VERSION_0_95) { | |
VIRTIO_CFG_WRITE ( | |
Device, | |
VIRTIO_MMIO_OFFSET_QUEUE_PFN, | |
(UINT32)((UINTN)Ring->Base >> EFI_PAGE_SHIFT) | |
); | |
} else { | |
VIRTIO_CFG_WRITE (Device, VIRTIO_MMIO_OFFSET_QUEUE_NUM, Device->QueueNum); | |
Address = (UINTN)Ring->Base; | |
VIRTIO_CFG_WRITE ( | |
Device, | |
VIRTIO_MMIO_OFFSET_QUEUE_DESC_LO, | |
(UINT32)Address | |
); | |
VIRTIO_CFG_WRITE ( | |
Device, | |
VIRTIO_MMIO_OFFSET_QUEUE_DESC_HI, | |
(UINT32)RShiftU64 (Address, 32) | |
); | |
Address = (UINTN)Ring->Avail.Flags; | |
VIRTIO_CFG_WRITE ( | |
Device, | |
VIRTIO_MMIO_OFFSET_QUEUE_AVAIL_LO, | |
(UINT32)Address | |
); | |
VIRTIO_CFG_WRITE ( | |
Device, | |
VIRTIO_MMIO_OFFSET_QUEUE_AVAIL_HI, | |
(UINT32)RShiftU64 (Address, 32) | |
); | |
Address = (UINTN)Ring->Used.Flags; | |
VIRTIO_CFG_WRITE ( | |
Device, | |
VIRTIO_MMIO_OFFSET_QUEUE_USED_LO, | |
(UINT32)Address | |
); | |
VIRTIO_CFG_WRITE ( | |
Device, | |
VIRTIO_MMIO_OFFSET_QUEUE_USED_HI, | |
(UINT32)RShiftU64 (Address, 32) | |
); | |
VIRTIO_CFG_WRITE (Device, VIRTIO_MMIO_OFFSET_QUEUE_READY, 1); | |
} | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
VirtioMmioSetGuestFeatures ( | |
IN VIRTIO_DEVICE_PROTOCOL *This, | |
IN UINT64 Features | |
) | |
{ | |
VIRTIO_MMIO_DEVICE *Device; | |
Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This); | |
if (Device->Version == VIRTIO_MMIO_DEVICE_VERSION_0_95) { | |
if (Features > MAX_UINT32) { | |
return EFI_UNSUPPORTED; | |
} | |
VIRTIO_CFG_WRITE ( | |
Device, | |
VIRTIO_MMIO_OFFSET_GUEST_FEATURES, | |
(UINT32)Features | |
); | |
} else { | |
VIRTIO_CFG_WRITE (Device, VIRTIO_MMIO_OFFSET_GUEST_FEATURES_SEL, 0); | |
VIRTIO_CFG_WRITE ( | |
Device, | |
VIRTIO_MMIO_OFFSET_GUEST_FEATURES, | |
(UINT32)Features | |
); | |
VIRTIO_CFG_WRITE (Device, VIRTIO_MMIO_OFFSET_GUEST_FEATURES_SEL, 1); | |
VIRTIO_CFG_WRITE ( | |
Device, | |
VIRTIO_MMIO_OFFSET_GUEST_FEATURES, | |
(UINT32)RShiftU64 (Features, 32) | |
); | |
} | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
VirtioMmioDeviceWrite ( | |
IN VIRTIO_DEVICE_PROTOCOL *This, | |
IN UINTN FieldOffset, | |
IN UINTN FieldSize, | |
IN UINT64 Value | |
) | |
{ | |
UINTN DstBaseAddress; | |
VIRTIO_MMIO_DEVICE *Device; | |
Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This); | |
// | |
// Double-check fieldsize | |
// | |
if ((FieldSize != 1) && (FieldSize != 2) && | |
(FieldSize != 4) && (FieldSize != 8)) | |
{ | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Compute base address | |
// | |
DstBaseAddress = Device->BaseAddress + | |
VIRTIO_DEVICE_SPECIFIC_CONFIGURATION_OFFSET_MMIO + FieldOffset; | |
// | |
// The device-specific memory area of Virtio-MMIO can only be written in | |
// byte accesses. This is not currently in the Virtio spec. | |
// | |
MmioWriteBuffer8 (DstBaseAddress, FieldSize, (UINT8 *)&Value); | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
VirtioMmioDeviceRead ( | |
IN VIRTIO_DEVICE_PROTOCOL *This, | |
IN UINTN FieldOffset, | |
IN UINTN FieldSize, | |
IN UINTN BufferSize, | |
OUT VOID *Buffer | |
) | |
{ | |
UINTN SrcBaseAddress; | |
VIRTIO_MMIO_DEVICE *Device; | |
Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This); | |
// | |
// Parameter validation | |
// | |
ASSERT (FieldSize == BufferSize); | |
// | |
// Double-check fieldsize | |
// | |
if ((FieldSize != 1) && (FieldSize != 2) && | |
(FieldSize != 4) && (FieldSize != 8)) | |
{ | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Compute base address | |
// | |
SrcBaseAddress = Device->BaseAddress + | |
VIRTIO_DEVICE_SPECIFIC_CONFIGURATION_OFFSET_MMIO + FieldOffset; | |
// | |
// The device-specific memory area of Virtio-MMIO can only be read in | |
// byte reads. This is not currently in the Virtio spec. | |
// | |
MmioReadBuffer8 (SrcBaseAddress, BufferSize, Buffer); | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
VirtioMmioAllocateSharedPages ( | |
IN VIRTIO_DEVICE_PROTOCOL *This, | |
IN UINTN NumPages, | |
OUT VOID **HostAddress | |
) | |
{ | |
VOID *Buffer; | |
Buffer = AllocatePages (NumPages); | |
if (Buffer == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
*HostAddress = Buffer; | |
return EFI_SUCCESS; | |
} | |
VOID | |
EFIAPI | |
VirtioMmioFreeSharedPages ( | |
IN VIRTIO_DEVICE_PROTOCOL *This, | |
IN UINTN NumPages, | |
IN VOID *HostAddress | |
) | |
{ | |
FreePages (HostAddress, NumPages); | |
} | |
EFI_STATUS | |
EFIAPI | |
VirtioMmioMapSharedBuffer ( | |
IN VIRTIO_DEVICE_PROTOCOL *This, | |
IN VIRTIO_MAP_OPERATION Operation, | |
IN VOID *HostAddress, | |
IN OUT UINTN *NumberOfBytes, | |
OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, | |
OUT VOID **Mapping | |
) | |
{ | |
*DeviceAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress; | |
*Mapping = NULL; | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
VirtioMmioUnmapSharedBuffer ( | |
IN VIRTIO_DEVICE_PROTOCOL *This, | |
IN VOID *Mapping | |
) | |
{ | |
return EFI_SUCCESS; | |
} |