Why is the virtio-mmio implementation of the protocol a library,
instead of a driver binary?
The UEFI driver model would encourage to create a virtio-mmio driver
instead of a library. But the reasons why I created a library are:
- A virtio-mmio driver would imply an additional protocol that would
probably have a single attribute field:
typedef struct {
  PHYSICAL_ADDRESS       BaseAddress;
} VIRTIO_MMIO_DEVICE_PROTOCOL;
- There is no (easy) way to scan the available VirtIo devices on a
platform. So, the UEFI firmware for this platform would need a driver
to produce instances for every virtio devices it wants to expose in
UEFI. A single call to a helper library (ie: VirtioMmioDeviceLib)
make the porting easier.
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Olivier Martin <olivier.martin@arm.com>
v5:
- typo fix in VirtioMmioInstallDevice() comment block
- plug MmioDevice leak in VirtioMmioUninstallDevice()
- return EFI_INVALID_PARAMETER in VirtioMmioGetQueueAddress() if
  QueueAddress is NULL
- VirtioMmioSetQueueSize(): fix return value (it's a status code)
- VirtioMmioSetPageSize(): check against EFI_PAGE_SIZE with "if" plus
  EFI_UNSUPPORTED, rather than ASSERT()
- VirtioMmioDeviceWrite(), VirtioMmioDeviceRead(): remove redundant
  (FieldSize > 8) checks
- VirtioMmioDeviceLib.inf: drop UefiDriverEntryPoint library dependency
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@14965 6f19259b-4bc3-4df7-8a09-765794883524
		
	
		
			
				
	
	
		
			311 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			311 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @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.
 | |
| 
 | |
|   This program and the accompanying materials are licensed and made available
 | |
|   under the terms and conditions of the BSD License which accompanies this
 | |
|   distribution. The full text of the license may be found at
 | |
|   http://opensource.org/licenses/bsd-license.php
 | |
| 
 | |
|   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
 | |
|   WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 | |
| 
 | |
| **/
 | |
| 
 | |
| #include "VirtioMmioDevice.h"
 | |
| 
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| VirtioMmioGetDeviceFeatures (
 | |
|   IN VIRTIO_DEVICE_PROTOCOL *This,
 | |
|   OUT UINT32                *DeviceFeatures
 | |
|   )
 | |
| {
 | |
|   VIRTIO_MMIO_DEVICE *Device;
 | |
| 
 | |
|   if (DeviceFeatures == NULL) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This);
 | |
| 
 | |
|   *DeviceFeatures = VIRTIO_CFG_READ (Device, VIRTIO_MMIO_OFFSET_HOST_FEATURES);
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| VirtioMmioGetQueueAddress (
 | |
|   IN  VIRTIO_DEVICE_PROTOCOL *This,
 | |
|   OUT UINT32                 *QueueAddress
 | |
|   )
 | |
| {
 | |
|   VIRTIO_MMIO_DEVICE *Device;
 | |
| 
 | |
|   if (QueueAddress == NULL) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This);
 | |
| 
 | |
|   *QueueAddress = VIRTIO_CFG_READ (Device, VIRTIO_MMIO_OFFSET_QUEUE_PFN);
 | |
| 
 | |
|   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 (
 | |
|   VIRTIO_DEVICE_PROTOCOL *This,
 | |
|   UINT16                  QueueSize
 | |
|   )
 | |
| {
 | |
|   VIRTIO_MMIO_DEVICE *Device;
 | |
| 
 | |
|   Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This);
 | |
| 
 | |
|   VIRTIO_CFG_WRITE (Device, VIRTIO_MMIO_OFFSET_QUEUE_NUM, QueueSize);
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| VirtioMmioSetDeviceStatus (
 | |
|   VIRTIO_DEVICE_PROTOCOL *This,
 | |
|   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 (
 | |
|   VIRTIO_DEVICE_PROTOCOL *This,
 | |
|   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 (
 | |
|   VIRTIO_DEVICE_PROTOCOL *This,
 | |
|   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 (
 | |
|   VIRTIO_DEVICE_PROTOCOL *This,
 | |
|   UINT32                  PageSize
 | |
|   )
 | |
| {
 | |
|   VIRTIO_MMIO_DEVICE *Device;
 | |
| 
 | |
|   if (PageSize != EFI_PAGE_SIZE) {
 | |
|     return EFI_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This);
 | |
| 
 | |
|   VIRTIO_CFG_WRITE (Device, VIRTIO_MMIO_OFFSET_GUEST_PAGE_SIZE, PageSize);
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| VirtioMmioSetQueueSel (
 | |
|   VIRTIO_DEVICE_PROTOCOL *This,
 | |
|   UINT16                  Sel
 | |
|   )
 | |
| {
 | |
|   VIRTIO_MMIO_DEVICE *Device;
 | |
| 
 | |
|   Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This);
 | |
| 
 | |
|   VIRTIO_CFG_WRITE (Device, VIRTIO_MMIO_OFFSET_QUEUE_SEL, Sel);
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| EFI_STATUS
 | |
| VirtioMmioSetQueueAddress (
 | |
|   VIRTIO_DEVICE_PROTOCOL *This,
 | |
|   UINT32                  Address
 | |
|   )
 | |
| {
 | |
|   VIRTIO_MMIO_DEVICE *Device;
 | |
| 
 | |
|   Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This);
 | |
| 
 | |
|   VIRTIO_CFG_WRITE (Device, VIRTIO_MMIO_OFFSET_QUEUE_PFN, Address);
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| VirtioMmioSetGuestFeatures (
 | |
|   VIRTIO_DEVICE_PROTOCOL *This,
 | |
|   UINT32                  Features
 | |
|   )
 | |
| {
 | |
|   VIRTIO_MMIO_DEVICE *Device;
 | |
| 
 | |
|   Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This);
 | |
| 
 | |
|   VIRTIO_CFG_WRITE (Device, VIRTIO_MMIO_OFFSET_GUEST_FEATURES, Features);
 | |
| 
 | |
|   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;
 | |
| }
 |