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