On 09/23/13 15:37, Olivier Martin wrote: > Contributed-under: TianoCore Contribution Agreement 1.0 > Signed-off-by: Olivier Martin <olivier.mar...@arm.com> > --- > OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.c | 652 > +++++++++++++++++++++ > OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.h | 267 +++++++++ > OvmfPkg/VirtioPciDeviceDxe/VirtioPciDeviceDxe.inf | 43 ++ > OvmfPkg/VirtioPciDeviceDxe/VirtioPciFunctions.c | 288 +++++++++ > 4 files changed, 1250 insertions(+), 0 deletions(-) > create mode 100644 OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.c > create mode 100644 OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.h > create mode 100644 OvmfPkg/VirtioPciDeviceDxe/VirtioPciDeviceDxe.inf > create mode 100644 OvmfPkg/VirtioPciDeviceDxe/VirtioPciFunctions.c
I'll probably only mention things here that aren't covered by -- and don't follow from -- points (1) to (3) in my review for v3 03/10. > > diff --git a/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.c > b/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.c > new file mode 100644 > index 0000000..816506d > --- /dev/null > +++ b/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.c > @@ -0,0 +1,652 @@ > +/** @file > + > + This driver produces Virtio Device Protocol instances for Virtio PCI > 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 <IndustryStandard/Pci.h> > +#include <Library/BaseMemoryLib.h> > +#include <Library/DebugLib.h> > +#include <Library/MemoryAllocationLib.h> > +#include <Library/UefiBootServicesTableLib.h> > +#include <Library/UefiLib.h> > + > +#include "VirtioPciDevice.h" > + > +STATIC VIRTIO_DEVICE_PROTOCOL mDeviceProtocolTemplate = { > + 0, // SubSystemDeviceId > + VirtioPciGetDeviceFeatures, // GetDeviceFeatures > + VirtioPciGetGuestFeatures, // GetGuestFeatures > + VirtioPciSetGuestFeatures, // SetGuestFeatures > + VirtioPciGetQueueAddress, // GetQueueAddress > + VirtioPciSetQueueAddress, // SetQueueAddress > + VirtioPciSetQueueSel, // SetQueueSel > + VirtioPciSetQueueNotify, // SetQueueNotify > + VirtioPciSetQueueAlignment, // SetQueueAlignment > + VirtioPciGetQueueSize, // GetQueueNumMax > + VirtioPciSetQueueSize, // SetQueueNum > + VirtioPciGetDeviceStatus, // GetDeviceStatus > + VirtioPciSetDeviceStatus, // SetDeviceStatus > + VirtioPciDeviceWrite, // WriteDevice > + VirtioPciDeviceRead // ReadDevice > +}; > + > +/** > + > + Read a word from Region 0 of the device specified by PciIo. > + > + Region 0 must be an iomem region. This is an internal function for the > + driver-specific VIRTIO_CFG_READ() macros. I think we should rather say "this is an internal function for the PCI implementation of the protocol". The VIRTIO_CFG_READ() macros in the drivers, if there will be such, won't call this directly. > + > + @param[in] PciIo Source PCI device. Stale parameter name; it should be called Dev. > + > + @param[in] FieldOffset Source offset. > + > + @param[in] FieldSize Source field size, must be in { 1, 2, 4, 8 }. > + > + @param[in] BufferSize Number of bytes available in the target buffer. > Must > + equal FieldSize. > + > + @param[out] Buffer Target buffer. > + > + > + @return Status code returned by PciIo->Io.Read(). > + > +**/ > +EFI_STATUS > +EFIAPI > +VirtioPciIoRead ( > + IN VIRTIO_PCI_DEVICE *Dev, > + IN UINTN FieldOffset, > + IN UINTN FieldSize, > + IN UINTN BufferSize, > + OUT VOID *Buffer > + ) > +{ > + UINTN Count; > + EFI_PCI_IO_PROTOCOL_WIDTH Width; > + EFI_PCI_IO_PROTOCOL *PciIo; > + > + ASSERT (FieldSize == BufferSize); > + > + PciIo = Dev->PciIo; > + > + Count = 1; > + switch (FieldSize) { > + case 1: > + Width = EfiPciIoWidthUint8; > + break; > + > + case 2: > + Width = EfiPciIoWidthUint16; > + break; > + > + case 8: > + // The 64bit PCI I/O is broken down into two 32bit reads to prevent > + // any alignment or width issues. > + // The UEFI spec says under EFI_PCI_IO_PROTOCOL.Io.Write(): > + // > + // The I/O operations are carried out exactly as requested. The caller > + // is responsible for any alignment and I/O width issues which the > + // bus, device, platform, or type of I/O might require. For example on > + // some platforms, width requests of EfiPciIoWidthUint64 do not work*/ Runaway "*/" at EOL. > + Count = 2; > + // fall through > + > + case 4: > + Width = EfiPciIoWidthUint32; > + break; > + > + default: > + ASSERT (FALSE); > + return EFI_INVALID_PARAMETER; > + } > + > + return PciIo->Io.Read ( > + PciIo, > + Width, > + PCI_BAR_IDX0, > + FieldOffset, > + Count, > + Buffer > + ); > +} > + > +/** > + > + Write a word into Region 0 of the device specified by PciIo. > + > + @param[in] PciIo Target PCI device. Parameter is called "Dev". > + > + @param[in] FieldOffset Destination offset. > + > + @param[in] FieldSize Destination field size, must be in { 1, 2, 4, 8 }. > + > + @param[in] Value Little endian value to write, converted to UINT64. > + The least significant FieldSize bytes will be used. OK, so this is where the endianness question comes in: > + > + > + @return Status code returned by PciIo->Io.Write(). > + > +**/ > +EFI_STATUS > +EFIAPI > +VirtioPciIoWrite ( > + IN VIRTIO_PCI_DEVICE *Dev, > + IN UINTN FieldOffset, > + IN UINTN FieldSize, > + IN UINT64 Value > + ) > +{ > + UINTN Count; > + EFI_PCI_IO_PROTOCOL_WIDTH Width; > + EFI_PCI_IO_PROTOCOL *PciIo; > + > + PciIo = Dev->PciIo; > + > + Count = 1; > + switch (FieldSize) { > + case 1: > + Width = EfiPciIoWidthUint8; > + break; > + > + case 2: > + Width = EfiPciIoWidthUint16; > + break; > + > + case 8: > + // The 64bit PCI I/O is broken down into two 32bit writes to prevent > + // any alignment or width issues. > + // The UEFI spec says under EFI_PCI_IO_PROTOCOL.Io.Write(): > + // > + // The I/O operations are carried out exactly as requested. The caller > + // is responsible for any alignment and I/O width issues which the > + // bus, device, platform, or type of I/O might require. For example on > + // some platforms, width requests of EfiPciIoWidthUint64 do not work*/ (Runaway "*/" at EOL.) > + Count = 2; > + // fall through > + > + case 4: > + Width = EfiPciIoWidthUint32; > + break; > + > + default: > + ASSERT (FALSE); > + return EFI_INVALID_PARAMETER; > + } > + > + return PciIo->Io.Write ( > + PciIo, > + Width, > + PCI_BAR_IDX0, > + FieldOffset, > + Count, > + &Value > + ); > +} The above matches the leading comment (ie. little-endian Value only). However, this function is used, via VIRTIO_CFG_WRITE(), from both VirtioPciDeviceWrite() and VirtioPciSetGuestFeatures(). The latter is indeed little-endian only (targets a field in the common virtio header), but the former has guest-native byte order. This effectively forces the WriteDevice protocol member to be little endian. I can't predict if virtio-pci is going to be used with a big endian platform. I think some layer should be endianness-aware. The outermost protocol members should likely all communicate in native endianness (ie. plain integer values). Then, - protocol implementations can write such values to the device-specific fields without byte-swapping, but the correct address into Value must be computed for big endian; - protocol implementations must correctly byte-swap Value to little-endian before writing it (always from its lowest address) into a common header field. This is of course all moot if we only support little endian. We can add big endian support later too, in another series; but then the main protocol header should state it's little-endian only. > + > +/** > + > + Device probe function for this driver. > + > + The DXE core calls this function for any given device in order to see if > the > + driver can drive the device. Some references present in OvmfPkg/VirtioBlkDxe/VirtioBlk.c are going lost here, like "Specs relevant in the general sense" etc, but I don't insist on keeping them. > + @param[in] This The EFI_DRIVER_BINDING_PROTOCOL object > + incorporating this driver (independently of > + any device). > + > + @param[in] DeviceHandle The device to probe. > + > + @param[in] RemainingDevicePath Relevant only for bus drivers, ignored. > + > + > + @retval EFI_SUCCESS The driver supports the device being probed. > + > + @retval EFI_UNSUPPORTED Based on virtio-blk PCI discovery, we do not > support > + the device. s/virtio-blk PCI/virtio-pci/ > + > + @return Error codes from the OpenProtocol() boot service > or > + the PciIo protocol. > + > +**/ > + > +EFI_STATUS > +EFIAPI > +VirtioPciDeviceBindingSupported ( > + IN EFI_DRIVER_BINDING_PROTOCOL *This, > + IN EFI_HANDLE DeviceHandle, > + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath > + ) > +{ > + EFI_STATUS Status; > + EFI_PCI_IO_PROTOCOL *PciIo; > + PCI_TYPE00 Pci; > + > + // > + // Attempt to open the device with the PciIo set of interfaces. On success, > + // the protocol is "instantiated" for the PCI device. Covers duplicate open > + // attempts (EFI_ALREADY_STARTED). > + // > + Status = gBS->OpenProtocol ( > + DeviceHandle, // candidate device > + &gEfiPciIoProtocolGuid, // for generic PCI access > + (VOID **)&PciIo, // handle to instantiate > + This->DriverBindingHandle, // requestor driver identity > + DeviceHandle, // ControllerHandle, according > to > + // the UEFI Driver Model > + EFI_OPEN_PROTOCOL_BY_DRIVER // get exclusive PciIo access > to > + // the device; to be released > + ); > + if (EFI_ERROR (Status)) { > + return Status; > + } > + > + // > + // Read entire PCI configuration header for more extensive check ahead. > + // > + Status = PciIo->Pci.Read ( > + PciIo, // (protocol, device) > + // handle > + EfiPciIoWidthUint32, // access width & copy > + // mode > + 0, // Offset > + sizeof Pci / sizeof (UINT32), // Count > + &Pci // target buffer > + ); > + > + if (Status == EFI_SUCCESS) { > + // > + // virtio-0.9.5, 2.1 PCI Discovery > + // > + if ((Pci.Hdr.VendorId == 0x1AF4) && > + (Pci.Hdr.DeviceId >= 0x1000) && > + (Pci.Hdr.DeviceId <= 0x103F) && > + (Pci.Hdr.RevisionID == 0x00)) { > + Status = EFI_SUCCESS; > + } else { > + Status = EFI_UNSUPPORTED; > + } > + } > + > + // > + // We needed PCI IO access only transitorily, to see whether we support the > + // device or not. > + // > + gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid, > + This->DriverBindingHandle, DeviceHandle); > + > + return Status; > +} OK. > + > +/** > + > + Initialize the VirtIo PCI Device > + > + @param[in out] Dev The driver instance to configure. The caller is > + responsible for Device->PciIo's validity (ie. working > IO > + access to the underlying virtio-blk PCI device). The official format is [in,out] -- I must apologize for that, I've been using it inconsistently as well until Jordan educated me. I don't insist on fixing these though. Also, s/virtio-blk PCI device/PCI device/. > + > + @retval EFI_SUCCESS Setup complete. > + > + @retval EFI_UNSUPPORTED The driver is unable to work with the virtio ring > or > + virtio-blk attributes the host provides. This @retval should be dropped. > + > + @return Error codes from PciIo->Pci.Read(). > + > +**/ > +STATIC > +EFI_STATUS > +EFIAPI > +VirtioPciInit ( > + IN OUT VIRTIO_PCI_DEVICE *Device > + ) > +{ > + EFI_STATUS Status; > + EFI_PCI_IO_PROTOCOL *PciIo; > + PCI_TYPE00 Pci; > + > + ASSERT (Device != NULL); > + PciIo = Device->PciIo; > + ASSERT (PciIo != NULL); > + ASSERT (PciIo->Pci.Read != NULL); > + > + Status = PciIo->Pci.Read ( > + PciIo, // (protocol, device) > + // handle > + EfiPciIoWidthUint32, // access width & copy > + // mode > + 0, // Offset > + sizeof (Pci) / sizeof (UINT32), // Count > + &Pci // target buffer > + ); > + if (EFI_ERROR (Status)) { > + return Status; > + } > + Device->VirtioDevice.SubSystemDeviceId = Pci.Device.SubsystemID; (I guess this is also where we'd set the Revision field to something nonzero; for now the AllocateZeroPool() should cover that. OK.) > + > + // Note: We don't support the MSI-X capability. If we did, > + // the offset would become 24 after enabling MSI-X. > + Device->DeviceSpecificConfigurationOffset = 20; Apparently, DeviceSpecificConfigurationOffset is private to the PCI implementation of the protocol. In that case we might as well simply use a constant. But it doesn't hurt, and this comment is worth keeping. OK. > + > + return EFI_SUCCESS; > +} > + > +/** > + > + Uninitialize the internals of a virtio-blk device that has been > successfully > + set up with VirtioPciInit(). > + > + @param[in out] Dev The device to clean up. > + > +**/ > + > +STATIC > +VOID > +EFIAPI > +VirtioPciUninit ( > + IN OUT VIRTIO_PCI_DEVICE *Device > + ) > +{ > + // > + // Reset the virtual device -- see virtio-0.9.5, 2.2.2.1 Device Status. > When > + // VirtioPciSetDeviceStatus() returns, the host will have learned to stay > + // away from the old comms area. > + // > + VirtioPciSetDeviceStatus (&Device->VirtioDevice, 0); > +} I think *the body* of this function should be dropped entirely. The actions taken by this function should mirror VirtioPciInit(); release resources allocated in that function. However, VirtioPciInit() doesn't allocate any resources -- there's nothing to free here. Unlike the original VirtioBlkInit(), VirtioPciInit() above doesn't communicate with the device, so there's no reason to do it in VirtioPciUninit() either. Tearing down the virtio-ring & clearing the status etc. are the responsibility of the individual device driver. Note that the individual device driver instance (block, scsi, net) holds a reference to this protocol instance (just like we're holding a reference to PciIo, with EFI_OPEN_PROTOCOL_BY_DRIVER). Consequently, the VIRTIO_PCI_DEVICE instance cannot be stopped/removed/disconnected until the dependent block/scsi/net driver goes down. Resetting the device is part of *that* teardown. We only manage PCI in this protocol. Of course one could say it doesn't hurt, and if some dependent device driver fails to reset the virtio device before exiting, we're catching it here. I disagree with this notion. Responsibilities must be well separated. I do think the "shell" of this function should be preserved however: for symmetry, and in order to keep the control flow in BindingStart() intact, below. > + > +/** > + > + After we've pronounced support for a specific device in > + DriverBindingSupported(), we start managing said device (passed in by the > + Driver Exeuction Environment) with the following service. > + > + See DriverBindingSupported() for specification references. > + > + @param[in] This The EFI_DRIVER_BINDING_PROTOCOL object > + incorporating this driver (independently of > + any device). > + > + @param[in] DeviceHandle The supported device to drive. > + > + @param[in] RemainingDevicePath Relevant only for bus drivers, ignored. > + > + > + @retval EFI_SUCCESS Driver instance has been created and > + initialized for the virtio-blk PCI device, > it s/virtio-blk PCI/virtio-pci/ > + is now accessibla via EFI_BLOCK_IO_PROTOCOL. s/EFI_BLOCK_IO_PROTOCOL/VIRTIO_DEVICE_PROTOCOL/ > + > + @retval EFI_OUT_OF_RESOURCES Memory allocation failed. > + > + @return Error codes from the OpenProtocol() boot > + service, the PciIo protocol, VirtioBlkInit(), > + or the InstallProtocolInterface() boot > service. s/VirtioBlkInit/VirtioPciInit/ > + > +**/ > +EFI_STATUS > +EFIAPI > +VirtioPciDeviceBindingStart ( > + IN EFI_DRIVER_BINDING_PROTOCOL *This, > + IN EFI_HANDLE DeviceHandle, > + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath > + ) > +{ > + VIRTIO_PCI_DEVICE *Device; > + EFI_STATUS Status; > + > + Device = (VIRTIO_PCI_DEVICE *) AllocateZeroPool (sizeof *Device); > + if (Device == NULL) { > + return EFI_OUT_OF_RESOURCES; > + } > + > + Status = gBS->OpenProtocol (DeviceHandle, &gEfiPciIoProtocolGuid, > + (VOID **)&Device->PciIo, This->DriverBindingHandle, > + DeviceHandle, EFI_OPEN_PROTOCOL_BY_DRIVER); > + if (EFI_ERROR (Status)) { > + goto FreeVirtioPci; > + } > + > + // > + // We must retain and ultimately restore the original PCI attributes of the > + // device. See Driver Writer's Guide for UEFI 2.3.1 v1.01, 18.3 PCI > drivers / > + // 18.3.2 Start() and Stop(). > + // > + // The third parameter ("Attributes", input) is ignored by the Get > operation. > + // The fourth parameter ("Result", output) is ignored by the Enable and Set > + // operations. > + // > + // For virtio-blk we only need IO space access. > + // s/virtio-blk/virtio-pci/ > + Status = Device->PciIo->Attributes (Device->PciIo, > + EfiPciIoAttributeOperationGet, 0, &Device->OriginalPciAttributes); > + if (EFI_ERROR (Status)) { > + goto ClosePciIo; > + } > + > + Status = Device->PciIo->Attributes (Device->PciIo, > + EfiPciIoAttributeOperationEnable, > + EFI_PCI_IO_ATTRIBUTE_IO, NULL); > + if (EFI_ERROR (Status)) { > + goto ClosePciIo; > + } > + > + // > + // PCI IO access granted, configure virtio-blk device. > + // s/configure virtio-blk device/configure protocol instance/ > + > + // Copy protocol template > + CopyMem (&Device->VirtioDevice, &mDeviceProtocolTemplate, > + sizeof (VIRTIO_DEVICE_PROTOCOL)); > + > + Status = VirtioPciInit (Device); > + if (EFI_ERROR (Status)) { > + goto RestorePciAttributes; > + } Can you move the CopyMem() into VirtioPciInit()? I don't insist of course but conceptually it would be cleaner. > + > + // > + // Setup complete, attempt to export the driver instance's BlockIo > interface. > + // s/BlockIo interface/VirtioDevice interface/ > + Device->Signature = VIRTIO_PCI_DEVICE_SIGNATURE; > + Status = gBS->InstallProtocolInterface (&DeviceHandle, > + &gVirtioDeviceProtocolGuid, EFI_NATIVE_INTERFACE, > + &Device->VirtioDevice); > + if (EFI_ERROR (Status)) { > + goto UninitDev; > + } Right; I think this illustrates my point about VirtioPciUninit() well. If InstallProtocolInterface() fails, there's no grounds for any virtio communication whatsoever, given that we haven't done any in VirtioPciInit() either. > + > + return EFI_SUCCESS; > + > +UninitDev: > + VirtioPciUninit (Device); > + > +RestorePciAttributes: > + Device->PciIo->Attributes (Device->PciIo, EfiPciIoAttributeOperationSet, > + Device->OriginalPciAttributes, NULL); > + > +ClosePciIo: > + gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid, > + This->DriverBindingHandle, DeviceHandle); > + > +FreeVirtioPci: > + FreePool (Device); > + > + return Status; > +} OK. > + > +/** > + > + Stop driving the Virtio PCI device > + > + @param[in] This The EFI_DRIVER_BINDING_PROTOCOL object > + incorporating this driver (independently of > any > + device). > + > + @param[in] DeviceHandle Stop driving this device. > + > + @param[in] NumberOfChildren Since this function belongs to a device > driver > + only (as opposed to a bus driver), the caller > + environment sets NumberOfChildren to zero, > and > + we ignore it. > + > + @param[in] ChildHandleBuffer Ignored (corresponding to NumberOfChildren). > + Oops, I forgot to document the retvals in VirtioBlkDriverBindingStop() :) > +**/ > +EFI_STATUS > +EFIAPI > +VirtioPciDeviceBindingStop ( > + IN EFI_DRIVER_BINDING_PROTOCOL *This, > + IN EFI_HANDLE DeviceHandle, > + IN UINTN NumberOfChildren, > + IN EFI_HANDLE *ChildHandleBuffer > + ) > +{ > + EFI_STATUS Status; > + VIRTIO_DEVICE_PROTOCOL *VirtioDevice; > + VIRTIO_PCI_DEVICE *Device; > + > + Status = gBS->OpenProtocol ( > + DeviceHandle, // candidate device > + &gVirtioDeviceProtocolGuid, // retrieve the BlockIo > iface s/BlockIo/VirtioDevice/ in the comment; or just drop BlockIo if the new text wouldn't fit 79 columns. > + (VOID **)&VirtioDevice, // target pointer > + This->DriverBindingHandle, // requestor driver identity > + DeviceHandle, // requesting lookup for > dev. > + EFI_OPEN_PROTOCOL_GET_PROTOCOL // lookup only, no ref. > added > + ); > + if (EFI_ERROR (Status)) { > + return Status; > + } > + > + Device = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (VirtioDevice); > + > + // > + // Handle Stop() requests for in-use driver instances gracefully. > + // > + Status = gBS->UninstallProtocolInterface (DeviceHandle, > + &gVirtioDeviceProtocolGuid, &Device->VirtioDevice); > + if (EFI_ERROR (Status)) { > + return Status; > + } Right. This is where we're catching the dependent block/scsi/net/etc driver (disconnecting it gracefully, or if that fails, getting EFI_ACCESS_DENIED returned here). > + > + VirtioPciUninit (Device); Correct, but as discussed, VirtioPciUninit() should be doing nothing for now. > + > + Device->PciIo->Attributes (Device->PciIo, EfiPciIoAttributeOperationSet, > + Device->OriginalPciAttributes, NULL); > + > + Status = gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid, > + This->DriverBindingHandle, DeviceHandle); > + > + FreePool (Device); > + > + return Status; > +} The retval propagation is in fact more careful here than in VirtioBlkDriverBindingStop(). This is the eternal problem with destructors. If CloseProtocol() succeeds (and it really should -- I should have asserted that in VirtioBlk); then both approaches are fine. What if it fails (which should be impossible)? VirtioBlkDriverBindingStop() leaks a PciIo reference, but convinces the caller (with EFI_SUCCESS) never to call it again. VirtioPciDeviceBindingStop() returns the error instead (which, again, should be impossible here). If the caller tries to call VirtioPciDeviceBindingStop() again, the very first OpenProtocol() invocation will fail -- always. The PciIo reference is leaked the same way. It's basically impossible to recover from partial destruction after some point. But it should never happen. OK. > + > + > +// > +// The static object that groups the Supported() (ie. probe), Start() and > +// Stop() functions of the driver together. Refer to UEFI Spec 2.3.1 + Errata > +// C, 10.1 EFI Driver Binding Protocol. > +// > +STATIC EFI_DRIVER_BINDING_PROTOCOL gDriverBinding = { > + &VirtioPciDeviceBindingSupported, > + &VirtioPciDeviceBindingStart, > + &VirtioPciDeviceBindingStop, > + 0x10, // Version, must be in [0x10 .. 0xFFFFFFEF] for IHV-developed drivers > + NULL, // ImageHandle, to be overwritten by > + // EfiLibInstallDriverBindingComponentName2() in > VirtioPciEntryPoint() > + NULL // DriverBindingHandle, ditto > +}; > + > + > +// > +// The purpose of the following scaffolding (EFI_COMPONENT_NAME_PROTOCOL and > +// EFI_COMPONENT_NAME2_PROTOCOL implementation) is to format the driver's > name > +// in English, for display on standard console devices. This is recommended > for > +// UEFI drivers that follow the UEFI Driver Model. Refer to the Driver > Writer's > +// Guide for UEFI 2.3.1 v1.01, 11 UEFI Driver and Controller Names. > +// > +STATIC > +EFI_UNICODE_STRING_TABLE mDriverNameTable[] = { > + { "eng;en", L"Virtio PCI Driver" }, > + { NULL, NULL } > +}; Seems to work fine: Shell> dh -p SimpleNetwork -d -v 99: UnknownDevice LoadFile PXEBaseCode MTFTPv4ServiceBinding DHCPv4ServiceBinding UDPv4ServiceBinding TCPv4ServiceBinding IPv4ServiceBinding IPv4Config ARPServiceBinding UnknownDevice ManagedNetworkServiceBinding EfiVlanConfigProtocolGuid DevicePath PciRoot(0x0)/Pci(0x3,0x0)/MAC(525400938342,0x1) SimpleNetwork Controller Name : Virtio Network Device Device Path : PciRoot(0x0)/Pci(0x3,0x0)/MAC(525400938342,0x1) Controller Type : BUS Configuration : NO Diagnostics : NO Managed by : Drv[67] : MNP Network Service Driver Drv[68] : VLAN Configuration Driver Drv[6B] : IP4 CONFIG Network Service Driver Parent Controllers : Parent[84] : Virtio Network Device Child Controllers : Child[9B] : MNP (MAC=52-54-00-93-83-42, ProtocolType=0x806, VlanId=0) Child[9C] : MNP (Not started) Child[9E] : MNP (MAC=52-54-00-93-83-42, ProtocolType=0x800, VlanId=0) Child[9A] : PciRoot(0x0)/Pci(0x3,0x0)/MAC(525400938342,0x1)/VenHw(D79DF6B0-EF44-43BD-9797-43E93BCF5FA8) Child[9D] : PciRoot(0x0)/Pci(0x3,0x0)/MAC(525400938342,0x1)/VenHw(D8944553-C4DD-41F4-9B30-E1397CFB267B) Shell> dh 84 -d -v 84: 3E7AE718 UnknownDevice PCIIO DevicePath PciRoot(0x0)/Pci(0x3,0x0) Controller Name : Virtio Network Device Device Path : PciRoot(0x0)/Pci(0x3,0x0) Controller Type : BUS Configuration : NO Diagnostics : NO Managed by : Drv[49] : Virtio PCI Driver ^^^^^^^^^^^^^^^^^ Drv[73] : Virtio Network Driver Parent Controllers : Parent[30] : PciRoot(0x0) Child Controllers : Child[99] : Virtio Network Device > + > +STATIC > +EFI_COMPONENT_NAME_PROTOCOL gComponentName; > + > +EFI_STATUS > +EFIAPI > +VirtioPciGetDriverName ( > + IN EFI_COMPONENT_NAME_PROTOCOL *This, > + IN CHAR8 *Language, > + OUT CHAR16 **DriverName > + ) > +{ > + return LookupUnicodeString2 ( > + Language, > + This->SupportedLanguages, > + mDriverNameTable, > + DriverName, > + (BOOLEAN)(This == &gComponentName) // Iso639Language > + ); > +} > + > +EFI_STATUS > +EFIAPI > +VirtioPciGetDeviceName ( > + IN EFI_COMPONENT_NAME_PROTOCOL *This, > + IN EFI_HANDLE DeviceHandle, > + IN EFI_HANDLE ChildHandle, > + IN CHAR8 *Language, > + OUT CHAR16 **ControllerName > + ) > +{ > + return EFI_UNSUPPORTED; > +} This is optional indeed, and clearly I didn't bother to implement it in VirtioBlk and VirtioScsi. I decided to support it in VirtioNetGetControllerName() [OvmfPkg/VirtioNetDxe/ComponentName.c], if an example can convince you to support it :) But, again, it's optional. > + > +STATIC > +EFI_COMPONENT_NAME_PROTOCOL gComponentName = { > + &VirtioPciGetDriverName, > + &VirtioPciGetDeviceName, > + "eng" // SupportedLanguages, ISO 639-2 language codes > +}; > + > +STATIC > +EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = { > + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &VirtioPciGetDriverName, > + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &VirtioPciGetDeviceName, > + "en" // SupportedLanguages, RFC 4646 language codes > +}; > + > + > +// > +// Entry point of this driver. > +// > +EFI_STATUS > +EFIAPI > +VirtioPciDeviceEntryPoint ( > + IN EFI_HANDLE ImageHandle, > + IN EFI_SYSTEM_TABLE *SystemTable > + ) > +{ > + return EfiLibInstallDriverBindingComponentName2 ( > + ImageHandle, > + SystemTable, > + &gDriverBinding, > + ImageHandle, > + &gComponentName, > + &gComponentName2 > + ); > +} OK. > diff --git a/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.h > b/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.h > new file mode 100644 > index 0000000..da5881f > --- /dev/null > +++ b/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.h > @@ -0,0 +1,267 @@ > +/** @file > + > + Internal definitions for the VirtIo PCI Device driver > + > + 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. > + > +**/ > + > +#ifndef _VIRTIO_PCI_DEVICE_DXE_H_ > +#define _VIRTIO_PCI_DEVICE_DXE_H_ > + > +#include <Protocol/ComponentName.h> > +#include <Protocol/DriverBinding.h> > +#include <Protocol/PciIo.h> > +#include <Protocol/VirtioDevice.h> > + > +#include <IndustryStandard/Virtio.h> > + > +#define OFFSET_OF_VNET(Field) OFFSET_OF (VIRTIO_NET_CONFIG, Field) > +#define SIZE_OF_VNET(Field) (sizeof ((VIRTIO_NET_CONFIG *) > 0)->Field) I think these don't belong here. > +#define VIRTIO_PCI_DEVICE_SIGNATURE SIGNATURE_32 ('V', 'P', 'C', 'I') OK. > + > +#define VIRTIO_CFG_WRITE(Dev, Offset, Size, Value) \ > + VirtioPciIoWrite (Dev, Offset, Size, (Value)) > +#define VIRTIO_CFG_READ(Dev, Offset, Size, Buf) \ > + VirtioPciIoRead (Dev, Offset, Size, Size, Buf) (a) It's a matter of taste, but since you use these macros only in the VirtioPciFunctions.c file, perhaps they should be defined there. (And the underlying functions could be moved too, but it's really tangential.) (b) The original idea behind VIRTIO_CFG_READ() was the following (from "OvmfPkg/VirtioBlkDxe/VirtioBlk.c"): #define VIRTIO_CFG_READ(Dev, Field, Pointer) (VirtioRead ( \ (Dev)->PciIo, \ OFFSET_OF_VBLK (Field), \ SIZE_OF_VBLK (Field), \ sizeof *(Pointer), \ (Pointer) \ )) This relied entirely on the packed structures with the Vhdr* fields. The point was to specify the header field and the recipient integer value, and size matching would be automatic. For example, from VirtioBlkInit(): Status = VIRTIO_CFG_READ (Dev, Generic.VhdrDeviceFeatureBits, &Features); I needed this because I was massaging the fields all the time, directly, and I wanted some safety. However, since you are going to - use VIRTIO_*_OFFSET values and sizeof()s *internally*, and - protect the driver implementor by hiding all the poking behind a protocol, I think it would be simpler to *drop* the FieldSize parameter of VirtioPciIoRead(). It buys you nothing, and it doesn't contribute to the (otherwise great) safety you're providing for the driver implementor. This should remove the mindless argument duplication in VIRTIO_CFG_READ() above. (c) You can even *drop* both of these macros. VIRTIO_CFG_WRITE() doesn't add any value. Nor does VIRTIO_CFG_READ(), after implementing (b). > + > +typedef struct { > + UINT32 Signature; > + VIRTIO_DEVICE_PROTOCOL VirtioDevice; > + EFI_PCI_IO_PROTOCOL *PciIo; > + UINT64 OriginalPciAttributes; > + UINT32 DeviceSpecificConfigurationOffset; > +} VIRTIO_PCI_DEVICE; > + > +#define VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE(Device) \ > + CR (Device, VIRTIO_PCI_DEVICE, VirtioDevice, VIRTIO_PCI_DEVICE_SIGNATURE) > + > + OK. > +/** > + > + Device probe function for this driver. > + > + The DXE core calls this function for any given device in order to see if > the > + driver can drive the device. > + > + @param[in] This The EFI_DRIVER_BINDING_PROTOCOL object > + incorporating this driver (independently of > + any device). > + > + @param[in] DeviceHandle The device to probe. > + > + @param[in] RemainingDevicePath Relevant only for bus drivers, ignored. > + > + > + @retval EFI_SUCCESS The driver supports the device being probed. > + > + @retval EFI_UNSUPPORTED Based on virtio-blk PCI discovery, we do not > support > + the device. > + > + @return Error codes from the OpenProtocol() boot service > or > + the PciIo protocol. > + > +**/ > + > +EFI_STATUS > +EFIAPI > +VirtioPciDeviceBindingSupported ( > + IN EFI_DRIVER_BINDING_PROTOCOL *This, > + IN EFI_HANDLE DeviceHandle, > + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath > + ); I think it's not necessary to declare functions here that you only reference in precisely one .c file. (They could be STATIC even.) I'm aware that OvmfPkg/VirtioBlkDxe/VirtioBlk.h exports them all -- I *vagualy* recall that Jordan requested that, so maybe my point is invalid. In that case, you'll need to update each functions leading comment (that you choose to update, that is) in two places. For me that's been a major headache. [snip] > +EFI_STATUS > +EFIAPI > +VirtioPciIoRead ( > + IN VIRTIO_PCI_DEVICE *Dev, > + IN UINTN FieldOffset, > + IN UINTN FieldSize, > + IN UINTN BufferSize, > + OUT VOID *Buffer > + ); > + > +EFI_STATUS > +EFIAPI > +VirtioPciIoWrite ( > + IN VIRTIO_PCI_DEVICE *Dev, > + IN UINTN FieldOffset, > + IN UINTN FieldSize, > + IN UINT64 Value > + ); > + > +/******************************************** > + * PCI Functions for VIRTIO_DEVICE_PROTOCOL > + *******************************************/ > +EFI_STATUS > +EFIAPI > +VirtioPciDeviceRead ( > + IN VIRTIO_DEVICE_PROTOCOL *This, > + IN UINTN FieldOffset, > + IN UINTN FieldSize, > + IN UINTN BufferSize, > + OUT VOID *Buffer > + ); > + > +EFI_STATUS > +EFIAPI > +VirtioPciDeviceWrite ( > + IN VIRTIO_DEVICE_PROTOCOL *This, > + IN UINTN FieldOffset, > + IN UINTN FieldSize, > + IN UINT64 Value > + ); > + > +UINT32 > +EFIAPI > +VirtioPciGetDeviceFeatures ( > + VIRTIO_DEVICE_PROTOCOL *This > + ); > + > +UINT32 > +EFIAPI > +VirtioPciGetGuestFeatures ( > + VIRTIO_DEVICE_PROTOCOL *This > + ); > + > +UINT32 > +EFIAPI > +VirtioPciGetQueueAddress ( > + VIRTIO_DEVICE_PROTOCOL *This > + ); > + > +UINT16 > +EFIAPI > +VirtioPciGetQueueSize ( > + VIRTIO_DEVICE_PROTOCOL *This > + ); > + > +EFI_STATUS > +EFIAPI > +VirtioPciSetQueueAlignment ( > + VIRTIO_DEVICE_PROTOCOL *This, > + UINT32 Alignment > + ); > + > +UINT8 > +EFIAPI > +VirtioPciGetDeviceStatus ( > + VIRTIO_DEVICE_PROTOCOL *This > + ); > + > +EFI_STATUS > +EFIAPI > +VirtioPciSetGuestFeatures ( > + VIRTIO_DEVICE_PROTOCOL *This, > + UINT32 Features > + ); > + > +EFI_STATUS > +EFIAPI > +VirtioPciSetQueueAddress ( > + VIRTIO_DEVICE_PROTOCOL *This, > + UINT32 Address > + ); > + > +EFI_STATUS > +EFIAPI > +VirtioPciSetQueueSel ( > + VIRTIO_DEVICE_PROTOCOL *This, > + UINT16 Sel > + ); > + > +EFI_STATUS > +EFIAPI > +VirtioPciSetQueueNotify ( > + VIRTIO_DEVICE_PROTOCOL *This, > + UINT16 Index > + ); > + > +VOID > +EFIAPI > +VirtioPciSetQueueSize ( > + VIRTIO_DEVICE_PROTOCOL *This, > + UINT16 Size > + ); > + > +EFI_STATUS > +EFIAPI > +VirtioPciSetDeviceStatus ( > + VIRTIO_DEVICE_PROTOCOL *This, > + UINT8 DeviceStatus > + ); > + > +#endif // _VIRTIO_PCI_DEVICE_DXE_H_ I agree that it's best to keep the leading comment only in the .c file; only one place to update again and again and again... > diff --git a/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDeviceDxe.inf > b/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDeviceDxe.inf > new file mode 100644 > index 0000000..98c5226 > --- /dev/null > +++ b/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDeviceDxe.inf > @@ -0,0 +1,43 @@ > +## @file > +# This driver produces the VirtIo Device Protocol instances for VirtIo PCI > +# Device > +# > +# 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. > +# > +## > + > +[Defines] > + INF_VERSION = 0x00010005 > + BASE_NAME = VirtioPciDeviceDxe > + FILE_GUID = 83dd3b39-7caf-4fac-a542-e050b767e3a7 > + MODULE_TYPE = UEFI_DRIVER > + VERSION_STRING = 1.0 > + ENTRY_POINT = VirtioPciDeviceEntryPoint > + > +[Sources] > + VirtioPciDevice.c > + VirtioPciFunctions.c > + > +[Packages] > + MdePkg/MdePkg.dec > + OvmfPkg/OvmfPkg.dec > + > +[LibraryClasses] > + BaseMemoryLib > + DebugLib > + MemoryAllocationLib > + UefiBootServicesTableLib > + UefiDriverEntryPoint > + UefiLib > + > +[Protocols] > + gEfiPciIoProtocolGuid ## TO_START > + gVirtioDeviceProtocolGuid Maybe add ## BY_START after gVirtioDeviceProtocolGuid? It means that the driver produces protocol instances with that GUID. BY_START is documented under "3.11 [Protocols] Sections" in the INF spec. It's mostly a hint for the human reader I think. > diff --git a/OvmfPkg/VirtioPciDeviceDxe/VirtioPciFunctions.c > b/OvmfPkg/VirtioPciDeviceDxe/VirtioPciFunctions.c > new file mode 100644 > index 0000000..15c4348 > --- /dev/null > +++ b/OvmfPkg/VirtioPciDeviceDxe/VirtioPciFunctions.c > @@ -0,0 +1,288 @@ > +/** @file > + > + This driver produces Virtio Device Protocol instances for Virtio PCI > 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 <Library/BaseMemoryLib.h> > +#include <Library/DebugLib.h> > +#include <Library/MemoryAllocationLib.h> > +#include <Library/UefiBootServicesTableLib.h> > +#include <Library/UefiLib.h> > +#include "VirtioPciDevice.h" > + > +#define VIRTIO_FEATURE_BITS_OFFSET 0 > +#define VIRTIO_GUEST_FEATURE_BITS_OFFSET 4 > +#define VIRTIO_QUEUE_ADDRESS_OFFSET 8 > +#define VIRTIO_QUEUE_SIZE_OFFSET 12 > +#define VIRTIO_QUEUE_SELECT_OFFSET 14 > +#define VIRTIO_QUEUE_NOTIFY_OFFSET 16 > +#define VIRTIO_QUEUE_DEVICE_STATUS_OFFSET 18 > +#define VIRTIO_QUEUE_DEVICE_ISR_OFFSET 19 Seems precise, OK. > + > +/** > + > + Read a word from Region 0 of the device specified by PciIo. > + > + Region 0 must be an iomem region. This is an internal function for the > + driver-specific VIRTIO_CFG_READ() macros. I think this comment should instead explain that the function implements the ReadDevice protocol member. It is not an internal function in the same sense as VirtioPciIoRead() is. Also, it is not called by the macros. > + > + @param[in] PciIo Source PCI device. > + > + @param[in] FieldOffset Source offset. > + > + @param[in] FieldSize Source field size, must be in { 1, 2, 4, 8 }. > + > + @param[in] BufferSize Number of bytes available in the target buffer. > Must > + equal FieldSize. > + > + @param[out] Buffer Target buffer. > + > + > + @return Status code returned by PciIo->Io.Read(). > + > +**/ > +EFI_STATUS > +EFIAPI > +VirtioPciDeviceRead ( > + IN VIRTIO_DEVICE_PROTOCOL *This, > + IN UINTN FieldOffset, > + IN UINTN FieldSize, > + IN UINTN BufferSize, > + OUT VOID *Buffer > + ) > +{ > + VIRTIO_PCI_DEVICE *Dev; > + > + Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This); > + > + return VirtioPciIoRead (Dev, > + Dev->DeviceSpecificConfigurationOffset + FieldOffset, > + FieldSize, BufferSize, Buffer); > +} (d) Seems correct (device-specific region is native-endian). (e) Since this is a public interface (a protocol member), I do think we should keep both FieldSize and BufferSize in the prototype here, and actually *move* the ASSERT() from VirtioPciIoRead() to here. This is the boundary where the driver implementor needs safety. > + > +/** > + > + Write a word into Region 0 of the device specified by PciIo. > + > + @param[in] PciIo Target PCI device. This comment should be updated from PCI to Virtio. > + > + @param[in] FieldOffset Destination offset. > + > + @param[in] FieldSize Destination field size, must be in { 1, 2, 4, 8 }. > + > + @param[in] Value Little endian value to write, converted to UINT64. > + The least significant FieldSize bytes will be used. I think, according to my rant at the top about endiannes: (f) this function (= protocol member impl.) should always take a native endian Value, then... > + > + > + @return Status code returned by PciIo->Io.Write(). > + > +**/ > +EFI_STATUS > +EFIAPI > +VirtioPciDeviceWrite ( > + IN VIRTIO_DEVICE_PROTOCOL *This, > + IN UINTN FieldOffset, > + IN UINTN FieldSize, > + IN UINT64 Value > + ) > +{ > + VIRTIO_PCI_DEVICE *Dev; > + > + Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This); > + > + return VIRTIO_CFG_WRITE (Dev, > + Dev->DeviceSpecificConfigurationOffset + FieldOffset, FieldSize, > Value); > +} ... do the optional byte swapping here. ... Aaaargh. Huge facepalm here. I wanted to propose some code, and I searched the UEFI spec for a "canonical way" for determining endianness. This is what I found: 1.8.1 Data Structure Descriptions Supported processors are "little endian" machines. This distinction means that the low-order byte of a multibyte data item in memory is at the lowest address, while the high-order byte is at the highest address. Some supported 64-bit processors may be configured for both "little endian" and "big endian" operation. All implementations designed to conform to this specification use "little endian" operation. 2.3.5 AArch32 Platforms The core will be configured as follows (common across all processor architecture revisions): Little endian mode 2.3.5.3 Detailed Calling Convention Only little endian operation is supported. 2.3.6 AArch64 Platforms The core will be configured as follows: Little endian mode 2.3.6.4 Detailed Calling Convention Only little endian operation is supported. How embarrassing :) Scratch everything I said about making the code endianness-aware. (Comments, on the other hand, can apparently *never* emphasize little-endian enough! :) ) > + > +UINT32 > +EFIAPI > +VirtioPciGetDeviceFeatures ( > + VIRTIO_DEVICE_PROTOCOL *This > + ) > +{ > + VIRTIO_PCI_DEVICE *Dev; > + UINT32 FeatureBits; > + > + Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This); > + > + VIRTIO_CFG_READ (Dev, VIRTIO_FEATURE_BITS_OFFSET, sizeof (UINT32), > + &FeatureBits); > + > + return FeatureBits; > +} (g) My remark about the getters throwing away the return status applies. (h) In the vein of (c), the macro call should be replaced by a direct function call. > + > +UINT32 > +EFIAPI > +VirtioPciGetGuestFeatures ( > + VIRTIO_DEVICE_PROTOCOL *This > + ) > +{ > + VIRTIO_PCI_DEVICE *Dev; > + UINT32 FeatureBits; > + > + Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This); > + > + VIRTIO_CFG_READ (Dev, VIRTIO_GUEST_FEATURE_BITS_OFFSET, sizeof (UINT32), > + &FeatureBits); > + > + return FeatureBits; > +} As discussed before: this protocol member is undefined on virtio-mmio (write-only), so we should probably drop it. > + > +UINT32 > +EFIAPI > +VirtioPciGetQueueAddress ( > + VIRTIO_DEVICE_PROTOCOL *This > + ) > +{ > + VIRTIO_PCI_DEVICE *Dev; > + UINT32 QueueAddress; > + > + Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This); > + > + VIRTIO_CFG_READ (Dev, VIRTIO_QUEUE_ADDRESS_OFFSET, sizeof (UINT32), > + &QueueAddress); > + > + return QueueAddress; > +} > + > +UINT16 > +EFIAPI > +VirtioPciGetQueueSize ( > + VIRTIO_DEVICE_PROTOCOL *This > + ) > +{ > + VIRTIO_PCI_DEVICE *Dev; > + UINT16 QueueSize; > + > + Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This); > + > + VIRTIO_CFG_READ (Dev, VIRTIO_QUEUE_SIZE_OFFSET, sizeof (UINT16), > &QueueSize); > + return QueueSize; > +} > + > +UINT8 > +EFIAPI > +VirtioPciGetDeviceStatus ( > + VIRTIO_DEVICE_PROTOCOL *This > + ) > +{ > + VIRTIO_PCI_DEVICE *Dev; > + UINT8 DeviceStatus; > + > + Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This); > + > + VIRTIO_CFG_READ (Dev, VIRTIO_QUEUE_DEVICE_STATUS_OFFSET, sizeof (UINT8), > + &DeviceStatus); > + > + return DeviceStatus; > +} > + > +EFI_STATUS > +EFIAPI > +VirtioPciSetGuestFeatures ( > + VIRTIO_DEVICE_PROTOCOL *This, > + UINT32 Features > + ) > +{ > + VIRTIO_PCI_DEVICE *Dev; > + > + Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This); > + > + return VIRTIO_CFG_WRITE (Dev, VIRTIO_GUEST_FEATURE_BITS_OFFSET, > + sizeof (UINT32), Features); > +} > + > +EFI_STATUS > +EFIAPI > +VirtioPciSetQueueAddress ( > + VIRTIO_DEVICE_PROTOCOL *This, > + UINT32 Address > + ) > +{ > + VIRTIO_PCI_DEVICE *Dev; > + > + Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This); > + > + return VIRTIO_CFG_WRITE (Dev, VIRTIO_QUEUE_ADDRESS_OFFSET, sizeof (UINT32), > + Address); > +} > + > +EFI_STATUS > +EFIAPI > +VirtioPciSetQueueSel ( > + VIRTIO_DEVICE_PROTOCOL *This, > + UINT16 Sel > + ) > +{ > + VIRTIO_PCI_DEVICE *Dev; > + > + Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This); > + > + return VIRTIO_CFG_WRITE (Dev, VIRTIO_QUEUE_SELECT_OFFSET, sizeof (UINT16), > + Sel); > +} > + > +EFI_STATUS > +EFIAPI > +VirtioPciSetQueueAlignment ( > + VIRTIO_DEVICE_PROTOCOL *This, > + UINT32 Alignment > + ) > +{ > + return EFI_SUCCESS; > +} > + > +EFI_STATUS > +EFIAPI > +VirtioPciSetQueueNotify ( > + VIRTIO_DEVICE_PROTOCOL *This, > + UINT16 Index > + ) > +{ > + VIRTIO_PCI_DEVICE *Dev; > + > + Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This); > + > + return VIRTIO_CFG_WRITE (Dev, VIRTIO_QUEUE_NOTIFY_OFFSET, sizeof (UINT16), > + Index); > +} > + > +VOID > +EFIAPI > +VirtioPciSetQueueSize ( > + VIRTIO_DEVICE_PROTOCOL *This, > + UINT16 Size > + ) > +{ > + // > + // This function is only applicable in Virtio-MMIO. > + // (The QueueSize field is read-only in Virtio proper (PCI)) > + // > +} Hm. I missed that the return type of this protocol member was VOID. I agree that it can never fail on the current implementations. Not sure if we should promise that on the protocol level. > + > +EFI_STATUS > +EFIAPI > +VirtioPciSetDeviceStatus ( > + VIRTIO_DEVICE_PROTOCOL *This, > + UINT8 DeviceStatus > + ) > +{ > + VIRTIO_PCI_DEVICE *Dev; > + > + Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This); > + > + return VIRTIO_CFG_WRITE (Dev, VIRTIO_QUEUE_DEVICE_STATUS_OFFSET, > + sizeof (UINT8), DeviceStatus); > +} > I usually write up whatever crosses my mind during review. That doesn't mean everything should be addressed. Please decide what you deem important enough to fix -- I'd hate to block this great work forever. I don't think I've said too many important things here; whatever you choose to ignore or postpone I can always try to modify in a followup series. I'm not giving my R-b right now only because the changes I've asked for in patch #3 will affect this patch. Thanks! Laszlo ------------------------------------------------------------------------------ October Webinars: Code for Performance Free Intel webinars can help you accelerate application performance. Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from the latest Intel processors and coprocessors. See abstracts and register > http://pubads.g.doubleclick.net/gampad/clk?id=60133471&iu=/4140/ostg.clktrk _______________________________________________ edk2-devel mailing list edk2-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/edk2-devel