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