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/Include/Library/VirtioMmioDeviceLib.h | 48 ++++ > .../Library/VirtioMmioDeviceLib/VirtioMmioDevice.c | 201 +++++++++++++ > .../Library/VirtioMmioDeviceLib/VirtioMmioDevice.h | 160 +++++++++++ > .../VirtioMmioDeviceFunctions.c | 297 > ++++++++++++++++++++ > .../VirtioMmioDeviceLib/VirtioMmioDeviceLib.inf | 43 +++ > 5 files changed, 749 insertions(+), 0 deletions(-) > create mode 100644 OvmfPkg/Include/Library/VirtioMmioDeviceLib.h > create mode 100644 OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDevice.c > create mode 100644 OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDevice.h > create mode 100644 > OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDeviceFunctions.c > create mode 100644 > OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDeviceLib.inf > > diff --git a/OvmfPkg/Include/Library/VirtioMmioDeviceLib.h > b/OvmfPkg/Include/Library/VirtioMmioDeviceLib.h > new file mode 100644 > index 0000000..2168ed7 > --- /dev/null > +++ b/OvmfPkg/Include/Library/VirtioMmioDeviceLib.h > @@ -0,0 +1,48 @@ > +/** @file > + > + Definitions for the VirtIo MMIO Device Library > + > + 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_MMIO_DEVICE_LIB_H_ > +#define _VIRTIO_MMIO_DEVICE_LIB_H_ > + > +#include <Protocol/VirtioDevice.h> > + > +#define VIRTIO_MMIO_DEVICE_SIGNATURE SIGNATURE_32 ('V', 'M', 'I', 'O') > + > +typedef struct { > + UINT32 Signature; > + VIRTIO_DEVICE_PROTOCOL VirtioDevice; > + PHYSICAL_ADDRESS BaseAddress; > + EFI_HANDLE Handle; > +} VIRTIO_MMIO_DEVICE; > + > +#define VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE(Device) \ > + CR (Device, VIRTIO_MMIO_DEVICE, VirtioDevice, > VIRTIO_MMIO_DEVICE_SIGNATURE) > +#define VIRTIO_MMIO_DEVICE_FROM_HANDLE(Device) \ > + CR (Device, VIRTIO_MMIO_DEVICE, Handle, VIRTIO_MMIO_DEVICE_SIGNATURE)
The VIRTIO_MMIO_DEVICE.Handle field, and the VIRTIO_MMIO_DEVICE_FROM_HANDLE() macro, are only used in VirtioMmioUninstallDevice(). VirtioMmioUninstallDevice() is never called in this patchset. What is the intended use case? Are the Protocol Handler Services insufficient for this purpose? I mean, if the client code knows about Handle (and it can), then gVirtioDeviceProtocolGuid can be located on it, and uninstalled form it. Actually, I think VirtioMmioUninstallDevice() is incorrect (quoting it out of order): > +EFI_STATUS > +VirtioMmioUninstallDevice ( > + IN EFI_HANDLE Handle > + ) > +{ > + VIRTIO_MMIO_DEVICE *Device; > + EFI_STATUS Status; > + > + // Get the device from handle > + Device = VIRTIO_MMIO_DEVICE_FROM_HANDLE (Handle); The VIRTIO_MMIO_DEVICE_FROM_HANDLE() macro does the following: - Take a pointer into a VIRTIO_MMIO_DEVICE structure, let's denote it with MmioDev, - this pointer is interpreted as the *address* of the MmioDev.Handle field, - Subtract the relevant offset of the pointer so that we get the beginning of the structure, - assert that the structure begins with the correct signature. The value passed to VIRTIO_MMIO_DEVICE_FROM_HANDLE() is not the *address* of any Handle field embedded into a VIRTIO_MMIO_DEVICE structure. For that the function would have to take an (EFI_HANDLE*), not an EFI_HANDLE. The compiler doesn't catch this, because EFI_HANDLE is itself a pointer (a (VOID*)), and the CR() underlying VIRTIO_MMIO_DEVICE_FROM_HANDLE() immediately casts it. In practice (with debugging enabled), the assertion on the signature would fire (we're poking into the data that is pointed-to by the VOID* that the handle is), but the patchset never calls the function, so it's not exposed. > + > + // Uninstall the protocol interface > + Status = gBS->UninstallProtocolInterface (Handle, > + &gVirtioDeviceProtocolGuid, &Device->VirtioDevice > + ); > + if (EFI_ERROR (Status)) { > + return Status; > + } > + > + // Uninitialize the VirtIo Device > + VirtioMmioUninit (Device); > + > + return EFI_SUCCESS; > +} I propose that: - the VIRTIO_MMIO_DEVICE.Handle field be dropped, - same for the dependent VIRTIO_MMIO_DEVICE_FROM_HANDLE() macro, - the VirtioMmioUninstallDevice() function should precisely imitate the DriverBindingStop() API: - As input, take the handle that the protocol has been installed on. Normally, this is a controller (device) handle, but under the use pattern visible in patch #10, this is the empty driver's image handle. Good, that can be used; the caller always has access to it. - The VirtioMmioUninstallDevice() should look up the Virtio Device protocol on that handle, using the GUID and the OpenProtocol() / GET_PROTOCOL protocol service. - The function should then use VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE() to get back to the mmio-specific container structure (checking the signature as well), and proceed exactly as DriverBindingStop() would. > + > +// Initialize VirtIo Device and Install VIRTIO_DEVICE_PROTOCOL protocol > +EFI_STATUS > +VirtioMmioInstallDevice ( > + IN PHYSICAL_ADDRESS BaseAddress, > + IN EFI_HANDLE Handle > + ); > + > +EFI_STATUS > +VirtioMmioUninstallDevice ( > + IN EFI_HANDLE Handle > + ); > + > +#endif // _VIRTIO_MMIO_DEVICE_LIB_H_ Still for this header file: since this header declares the library's API, I think internals shouldn't be exposed. That is, all of the following should be moved to "OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDevice.h": - VIRTIO_MMIO_DEVICE_SIGNATURE - VIRTIO_MMIO_DEVICE - VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE Only the declaration of public functions that are useful for client code should remain. > diff --git a/OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDevice.h > b/OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDevice.h > new file mode 100644 > index 0000000..66b3f76 > --- /dev/null > +++ b/OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDevice.h > @@ -0,0 +1,160 @@ > +/** @file > + > + Internal definitions for the VirtIo MMIO 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_MMIO_DEVICE_INTERNAL_H_ > +#define _VIRTIO_MMIO_DEVICE_INTERNAL_H_ > + > +#include <Protocol/VirtioDevice.h> > + > +#include <IndustryStandard/Virtio.h> > + > +#include <Library/DebugLib.h> > +#include <Library/IoLib.h> > +#include <Library/UefiLib.h> > +#include <Library/VirtioMmioDeviceLib.h> > + > +#define VIRTIO_MMIO_OFFSET_MAGIC 0x00 > +#define VIRTIO_MMIO_OFFSET_VERSION 0x04 > +#define VIRTIO_MMIO_OFFSET_DEVICE_ID 0x08 > +#define VIRTIO_MMIO_OFFSET_VENDOR_ID 0x0C > +#define VIRTIO_MMIO_OFFSET_HOST_FEATURES 0x10 > +#define VIRTIO_MMIO_OFFSET_HOST_FEATURES_SEL 0x14 > +#define VIRTIO_MMIO_OFFSET_GUEST_FEATURES 0x20 > +#define VIRTIO_MMIO_OFFSET_GUEST_FEATURES_SEL 0x24 > +#define VIRTIO_MMIO_OFFSET_GUEST_PAGE_SIZE 0x28 > +#define VIRTIO_MMIO_OFFSET_QUEUE_SEL 0x30 > +#define VIRTIO_MMIO_OFFSET_QUEUE_NUM_MAX 0x34 > +#define VIRTIO_MMIO_OFFSET_QUEUE_NUM 0x38 > +#define VIRTIO_MMIO_OFFSET_QUEUE_ALIGN 0x3C > +#define VIRTIO_MMIO_OFFSET_QUEUE_PAGE_OFF 0x40 (maybe call this one VIRTIO_MMIO_OFFSET_QUEUE_PFN?) > +#define VIRTIO_MMIO_OFFSET_QUEUE_NOTIFY 0x50 > +#define VIRTIO_MMIO_OFFSET_INTERRUPT_STATUS 0x60 > +#define VIRTIO_MMIO_OFFSET_INTERRUPT_ACK 0x64 > +#define VIRTIO_MMIO_OFFSET_STATUS 0x70 > +#define VIRTIO_MMIO_OFFSET_DEVICE_SPECIFIC_CONFIG 0x100 These offset constants belong into "OvmfPkg/Include/IndustryStandard/Virtio.h", near VIRTIO_HDR (which is going to be removed in patch #7). > + > +#define VIRTIO_MMIO_MAGIC 0x74726976 // "virt" Should go under IndustryStandard as well. > +#define VIRTIO_MMIO_VENDOR_ID 0x1AF4 Same. And then, I think it should be renamed to VIRTIO_VENDOR_ID, and reused by the PCI implementation. > + > +#define VIRTIO_CFG_WRITE(Device, Offset, Val) \ > + (MmioWrite32 (Device->BaseAddress + Offset, Val)) > +#define VIRTIO_CFG_READ(Device, Offset) \ > + (MmioRead32 (Device->BaseAddress + Offset)) Correct, these are library-specific. Perhaps Offset should be parenthesized in the replacement text, so that eg. the ternary operator ?: can be passed in ("+" binds more strongly). > + > +EFI_STATUS > +EFIAPI > +VirtioMmioDeviceRead ( > + IN VIRTIO_DEVICE_PROTOCOL *This, > + IN UINTN FieldOFfset, > + IN UINTN FieldSize, > + IN UINTN BufferSize, > + OUT VOID* Buffer > + ); > + > +EFI_STATUS > +EFIAPI > +VirtioMmioDeviceWrite ( > + IN VIRTIO_DEVICE_PROTOCOL *This, > + IN UINTN FieldOffset, > + IN UINTN FieldSize, > + IN UINT64 Value > + ); These seem to be exposed in the protocol. OK. > + > +UINT32 > +EFIAPI > +VirtioMmioGetDeviceFeatures ( > + VIRTIO_DEVICE_PROTOCOL *This > + ); My comment about getters suppressing return status applies. The virtio-mmio implementation should always report EFI_SUCCESS, probably. > + > +UINT32 > +EFIAPI > +VirtioMmioGetGuestFeatures ( > + VIRTIO_DEVICE_PROTOCOL *This > + ); My comment about the GuestFeatures register (at VIRTIO_MMIO_OFFSET_GUEST_FEATURES) being write-only applies; this protocol member should be dropped altogether (seems to be quite useless in general). > + > +UINT32 > +EFIAPI > +VirtioMmioGetQueueAddress ( > + VIRTIO_DEVICE_PROTOCOL *This > + ); > + > +UINT32 > +EFIAPI > +VirtioMmioGetQueueSel ( > + VIRTIO_DEVICE_PROTOCOL *This > + ); > + > +UINT16 > +EFIAPI > +VirtioMmioGetQueueSize ( > + VIRTIO_DEVICE_PROTOCOL *This > + ); > + > +UINT8 > +EFIAPI > +VirtioMmioGetDeviceStatus ( > + VIRTIO_DEVICE_PROTOCOL *This > + ); > + > +VOID > +EFIAPI > +VirtioMmioSetQueueSize ( > + VIRTIO_DEVICE_PROTOCOL *This, > + UINT16 QueueSize > + ); Just repeating my earlier comment: this setter should return a status too. > + > +EFI_STATUS > +EFIAPI > +VirtioMmioSetDeviceStatus ( > + VIRTIO_DEVICE_PROTOCOL *This, > + UINT8 DeviceStatus > + ); > + > +EFI_STATUS > +EFIAPI > +VirtioMmioSetQueueNotify ( > + VIRTIO_DEVICE_PROTOCOL *This, > + UINT16 QueueNotify > + ); > + > +EFI_STATUS > +EFIAPI > +VirtioMmioSetQueueSel ( > + VIRTIO_DEVICE_PROTOCOL *This, > + UINT16 Sel > + ); > + > +EFI_STATUS > +VirtioMmioSetQueueAddress ( > + VIRTIO_DEVICE_PROTOCOL *This, > + UINT32 Address > + ); > + > +EFI_STATUS > +EFIAPI > +VirtioMmioSetQueueAlignment ( > + VIRTIO_DEVICE_PROTOCOL *This, > + UINT32 Alignment > + ); > + > +EFI_STATUS > +EFIAPI > +VirtioMmioSetGuestFeatures ( > + VIRTIO_DEVICE_PROTOCOL *This, > + UINT32 Features > + ); > + > +#endif // _VIRTIO_MMIO_DEVICE_INTERNAL_H_ OK. > diff --git a/OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDevice.c > b/OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDevice.c > new file mode 100644 > index 0000000..945da57 > --- /dev/null > +++ b/OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDevice.c > @@ -0,0 +1,201 @@ > +/** @file > + > + This driver produces Virtio Device Protocol instances for Virtio Mmio > devices. > + > + 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/MemoryAllocationLib.h> > +#include <Library/UefiBootServicesTableLib.h> > + > +#include "VirtioMmioDevice.h" > + > +static VIRTIO_DEVICE_PROTOCOL mMmioDeviceProtocolTemplate = { > + 0, // SubSystemDeviceId > + VirtioMmioGetDeviceFeatures, // GetDeviceFeatures > + VirtioMmioGetGuestFeatures, // GetGuestFeatures > + VirtioMmioSetGuestFeatures, // SetGuestFeatures > + VirtioMmioGetQueueAddress, // GetQueueAddress > + VirtioMmioSetQueueAddress, // SetQueueAddress > + VirtioMmioSetQueueSel, // SetQueueSel > + VirtioMmioSetQueueNotify, // SetQueueNotify > + VirtioMmioSetQueueAlignment, // SetQueueAlign > + VirtioMmioGetQueueSize, // GetQueueNumMax > + VirtioMmioSetQueueSize, // SetQueueNum > + VirtioMmioGetDeviceStatus, // GetDeviceStatus > + VirtioMmioSetDeviceStatus, // SetDeviceStatus > + VirtioMmioDeviceWrite, // WriteDevice > + VirtioMmioDeviceRead // ReadDevice > +}; > + > +/** > + > + Initialize the VirtIo PCI Device s/PCI/MMIO/ > + > + @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 caller is responsible for ensuring that Device->BaseAddress has been initialized. - [in,out] (again, apologies for that!) - The parameter is called "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. The description of the retval should be updated (magic check etc.) > + > + @return Error codes from VirtioRingInit() or > + VIRTIO_CFG_READ() / VIRTIO_CFG_WRITE(). VirtioRingInit() is the business of the dependent device driver, we shouldn't mention it here. > + > +**/ > +STATIC > +EFI_STATUS > +EFIAPI > +VirtioMmioInit ( > + IN OUT VIRTIO_MMIO_DEVICE *Device > + ) > +{ > + // Set Mmio Page Size > + > + UINT8 NextDevStat; > + UINT32 MagicValue; > + UINT32 Version; > + > + // > + // Execute virtio-0.9.5, 2.2.1 Device Initialization Sequence. > + // > + NextDevStat = 0; // step 1 -- reset device > + VIRTIO_CFG_WRITE (Device, VIRTIO_MMIO_OFFSET_STATUS, NextDevStat); I agree that within this protocol implementation, we can ignore the retval check. I tried to follow what MmioWrite32() does -- it has a bunch of platfom-dependent implementations, but the value it is the value it takes as input; hence it should always succeed. The implementation in "IntelFrameworkPkg/Library/DxeIoLibCpuIo/IoLib.c" (which is I guess never used on ARM, but I'm only verifying the interface contract) calls MmioWriteWorker() in the same file. That function *does* call mCpuIo->Mem.Write(), which returns EFI_STATUS, but MmioWriteWorker() asserts that it won't fail. Good. > + > + NextDevStat |= VSTAT_ACK; // step 2 -- acknowledge device presence > + VIRTIO_CFG_WRITE (Device, VIRTIO_MMIO_OFFSET_STATUS, NextDevStat); > + > + NextDevStat |= VSTAT_DRIVER; // step 3 -- we know how to drive it > + VIRTIO_CFG_WRITE (Device, VIRTIO_MMIO_OFFSET_STATUS, NextDevStat); > + > + // Double-check MMIO-specific values > + MagicValue = VIRTIO_CFG_READ (Device, VIRTIO_MMIO_OFFSET_MAGIC); > + if (MagicValue != VIRTIO_MMIO_MAGIC) { > + return EFI_UNSUPPORTED; > + } > + > + Version = VIRTIO_CFG_READ (Device, VIRTIO_MMIO_OFFSET_VERSION); > + if (Version != 1) { > + return EFI_UNSUPPORTED; > + } > + > + // Set page size (for later queue allocations) > + VIRTIO_CFG_WRITE (Device, VIRTIO_MMIO_OFFSET_GUEST_PAGE_SIZE, > EFI_PAGE_SIZE); > + > + return EFI_SUCCESS; > +} I'm not familiar with virtio-mmio (and have been looking at that part of the spec only in the last few days), so, questions/remarks: (1) I think we shouldn't write anything before checking for the magic value (r/o operation) and the version number (r/o operation). We should first write once we're past these sanity checks. Otherwise, in case of a misconfigured Address (which is coming from the library client), we could be trampling over a random mmio register. (2) Do we need to bring up the device at all (ie. massage its status register)? I think the Virtio Subsystem Device ID (the register at 0x008) is available immediately. I believe logic even *dictates* that it be available first -- without knowing the subsystem ID, a driver is not expected to decide whether it can drive the device or not. The fact that the four read-only registers: MagicValue, Version, DeviceID, VendorID are the very first ones in the MMIO space suggest that we should be looking at them right off the bat. (3) The VendorID register is not checked here, against VIRTIO_MMIO_VENDOR_ID (0x1AF4), and it is also not exposed in the protocol interface (which only saves the DeviceID). In effect I believe the VendorID value is never checked on virtio-mmio. For virtio-pci, the VirtioPciDeviceBindingSupported() function covers the VendorID check, so I think it should be done here as well (and it doesn't have to be exposed in the protocol). (4) Regarding the guest page size, quoting the spec: Device driver must write the guest page size in bytes to the register during initialization, before any queues are used. [...] The device initialization is performed as described in 2.2.1 with one exception: the Guest must notify the Host about its page size, writing the size in bytes to GuestPageSize register before the initialization is finished. Looking at the seven steps in 2.2.1 Device Initialization Sequence, I agree that the relative order around setting the guest page size should be: - set VSTAT_DRIVER, - [...] - set guest page size, - [...] - allocate virtqueues, - [...] - set VSTAT_DRIVER_OK The function above performs that correctly (it does the the first two steps, and the rest is left to the device driver). However: the virtio block/scsi/net drivers all *reset* the device as part of their initialization. This is true before the series, and patch #6 keeps it intact as well (correctly). See the three occurrences of the comment // step 1 -- reset device in patch #6 (VirtioNet even resets it twice, see VirtioNetGetFeatures()). Resetting the device gives the host free hand to forget *all* configuration about it. So, we must reconfigure the page size each time we're going through the above sequence. We can solve this in two ways: (4a) turn the page-size-setting into a new member of the protocol. All VSTAT_DRIVER...VSTAT_DRIVER_OK sequences must be located in the current drivers, and extended with a call to the new member. (4b) internally to virtio-mmio, piggy-back this setting on another operation, preferably SetQueueAddress(). The patchset has already chosen (4a) for the virtio-mmio-only SetQueueAlignment() and SetQueueNum() members, so we should probably follow suit here too. The new member should do the following: backend take page size return EFI_UNSUPPORTED configure parameter if the value is not host side EFI_PAGE_SIZE ----------- -------------- ---------------------- --------- virtio-pci yes yes no virtio-mmio yes yes yes EFI_PAGE_SIZE is hardcoded in VirtioRingInit() [OvmfPkg/Library/VirtioLib/VirtioLib.c], so any relaxation to the above EFI_UNSUPPORTED case would have to be synched with VirtioRingInit(). Regardless of the specific choice between (4a) and (4b): VirtioMmioInit() can (and should) be implemented without *any* writes to the device. (See point (2) again.) > + > + > +/** > + > + Uninitialize the internals of a virtio-blk device that has been > successfully > + set up with VirtioMmioInit(). s/virtio-blk/virtio-mmio/ > + > + @param[in out] Dev The device to clean up. > + > +**/ [in,out], and Device. > + > +STATIC > +VOID > +EFIAPI > +VirtioMmioUninit ( > + IN VIRTIO_MMIO_DEVICE *Device > + ) > +{ > + // > + // Reset the virtual device -- see virtio-0.9.5, 2.2.2.1 Device Status. > When > + // VIRTIO_CFG_WRITE() returns, the host will have learned to stay away from > + // the old comms area. > + // > + VIRTIO_CFG_WRITE (Device, VIRTIO_MMIO_OFFSET_STATUS, 0); > +} Same as for virtio-pci, and according to my point (2) above, this function should exist, but do nothing. > + > +EFI_STATUS > +VirtioMmioInstallDevice ( > + IN PHYSICAL_ADDRESS BaseAddress, > + IN EFI_HANDLE Handle > + ) The function lacks the leading comment -- it should have (a short) one, considering this is the main function of the library. > +{ > + EFI_STATUS Status; > + VIRTIO_MMIO_DEVICE *VirtIo; > + > + if (!BaseAddress) { > + return EFI_INVALID_PARAMETER; > + } > + if (Handle == NULL) { > + return EFI_INVALID_PARAMETER; > + } > + > + // Allocate VIRTIO_MMIO_DEVICE > + VirtIo = AllocateZeroPool (sizeof (VIRTIO_MMIO_DEVICE)); > + if (VirtIo == NULL) { > + return EFI_OUT_OF_RESOURCES; > + } > + VirtIo->BaseAddress = BaseAddress; > + VirtIo->Signature = VIRTIO_MMIO_DEVICE_SIGNATURE; > + VirtIo->Handle = Handle; The Handle assignment should be dropped (see at the top). Plus, I'm tempted to say that the BaseAddress assignment should happen in VirtioMmioInit(). The signature check is OK here, that's tied directly to the protocol stuff. > + > + // Initialize VirtIo Mmio Device > + CopyMem (&VirtIo->VirtioDevice, &mMmioDeviceProtocolTemplate, > + sizeof (VIRTIO_DEVICE_PROTOCOL)); This should happen in VirtioMmioInit(). > + VirtIo->VirtioDevice.SubSystemDeviceId = > + MmioRead32 (BaseAddress + VIRTIO_MMIO_OFFSET_DEVICE_ID); This too, probably after the (currently missing) VendorID check there. > + Status = VirtioMmioInit (VirtIo); > + if (EFI_ERROR (Status)) { > + goto FreeVirtioMem; > + } > + > + // Install VIRTIO_DEVICE_PROTOCOL to Handle > + Status = gBS->InstallProtocolInterface (&Handle, > + &gVirtioDeviceProtocolGuid, EFI_NATIVE_INTERFACE, > + &VirtIo->VirtioDevice); > + if (EFI_ERROR (Status)) { > + goto UninstallVirtio; > + } I recommend to rename the label to UninitVirtio. > + > + return EFI_SUCCESS; > + > +UninstallVirtio: > + VirtioMmioUninit (VirtIo); > + > +FreeVirtioMem: > + FreePool (VirtIo); > + return Status; > +} > + OK. I will continue the review of this patch from here. I very much hope that I haven't come through as snarky or whatever -- that's certainly not my intent. My points are just food for thought. This is huge work and I realize reworking such a series is big pain. I'll have to see the rest, but right now I feel that I'd be OK with a plan where - you'd fix up the smaller stuff for the initial commit, and - either of us would address the bigger stuff in a followup series. (I'd be willing to do that; for me it's easier to write code than to review code, and in that case you and Jordan would have to do the review :) ) It is 100% your decision of course. Thanks! Laszlo > diff --git a/OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDeviceFunctions.c > b/OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDeviceFunctions.c > new file mode 100644 > index 0000000..284c25a > --- /dev/null > +++ b/OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDeviceFunctions.c > @@ -0,0 +1,297 @@ > +/** @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" > + > +UINT32 > +EFIAPI > +VirtioMmioGetDeviceFeatures ( > + VIRTIO_DEVICE_PROTOCOL *This > + ) > +{ > + VIRTIO_MMIO_DEVICE *Device; > + > + Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This); > + > + return VIRTIO_CFG_READ (Device, VIRTIO_MMIO_OFFSET_HOST_FEATURES); > +} > + > +UINT32 > +EFIAPI > +VirtioMmioGetGuestFeatures ( > + VIRTIO_DEVICE_PROTOCOL *This > + ) > +{ > + VIRTIO_MMIO_DEVICE *Device; > + > + Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This); > + > + return VIRTIO_CFG_READ (Device, VIRTIO_MMIO_OFFSET_GUEST_FEATURES); > +} > + > +UINT32 > +EFIAPI > +VirtioMmioGetQueueAddress ( > + VIRTIO_DEVICE_PROTOCOL *This > + ) > +{ > + VIRTIO_MMIO_DEVICE *Device; > + > + Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This); > + > + return VIRTIO_CFG_READ (Device, VIRTIO_MMIO_OFFSET_QUEUE_PAGE_OFF); > +} > + > +UINT32 > +EFIAPI > +VirtioMmioGetQueueSel ( > + VIRTIO_DEVICE_PROTOCOL *This > + ) > +{ > + VIRTIO_MMIO_DEVICE *Device; > + > + Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This); > + > + return VIRTIO_CFG_READ (Device, VIRTIO_MMIO_OFFSET_QUEUE_SEL); > +} > + > +UINT16 > +EFIAPI > +VirtioMmioGetQueueSize ( > + VIRTIO_DEVICE_PROTOCOL *This > + ) > +{ > + VIRTIO_MMIO_DEVICE *Device; > + > + Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This); > + > + return VIRTIO_CFG_READ (Device, VIRTIO_MMIO_OFFSET_QUEUE_NUM_MAX) & 0xFFFF; > +} > + > +UINT8 > +EFIAPI > +VirtioMmioGetDeviceStatus ( > + VIRTIO_DEVICE_PROTOCOL *This > + ) > +{ > + VIRTIO_MMIO_DEVICE *Device; > + > + Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This); > + > + return VIRTIO_CFG_READ (Device, VIRTIO_MMIO_OFFSET_STATUS) & 0xFF; > +} > + > + > +VOID > +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); > +} > + > +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 > +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_PAGE_OFF, 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; > + UINT8 *DstByteAddress; > + UINT8 SrcByte; > + UINTN Index; > + VIRTIO_MMIO_DEVICE *Device; > + > + Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This); > + > + // Double-check fieldsize > + if (FieldSize > 8) { > + return EFI_INVALID_PARAMETER; > + } > + > + if ((FieldSize % 2) && FieldSize != 1) { > + return EFI_INVALID_PARAMETER; > + } > + > + // Compute base address > + DstBaseAddress = Device->BaseAddress + > + VIRTIO_MMIO_OFFSET_DEVICE_SPECIFIC_CONFIG + FieldOffset; > + > + // > + // The device-specific memory area of Virtio-MMIO can only be written in > + // byte accesses. This is not currently in the Virtio spec. > + // > + for (Index = 0; Index < FieldSize; Index++) { > + DstByteAddress = ((UINT8 *)DstBaseAddress) + Index; > + SrcByte = *(((UINT8 *)&Value) + Index); > + MmioWrite8 ((UINTN) DstByteAddress, SrcByte); > + } > + > + 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; > + UINT8 *SrcByteAddress; > + UINTN Index; > + VIRTIO_MMIO_DEVICE *Device; > + > + Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This); > + > + // Parameter validation > + if (FieldSize != BufferSize) { > + return EFI_INVALID_PARAMETER; > + } > + > + // Double-check fieldsize > + if (FieldSize > 8) { > + return EFI_INVALID_PARAMETER; > + } > + > + if ((FieldSize % 2) && FieldSize != 1) { > + return EFI_INVALID_PARAMETER; > + } > + > + // Compute base address > + SrcBaseAddress = Device->BaseAddress + > + VIRTIO_MMIO_OFFSET_DEVICE_SPECIFIC_CONFIG + FieldOffset; > + > + // > + // The device-specific memory area of Virtio-MMIO can only be read in > + // byte reads. This is not currently in the Virtio spec. > + // > + for (Index = 0; Index < FieldSize; Index++) { > + SrcByteAddress = ((UINT8 *)SrcBaseAddress) + Index; > + *(UINT8 *)(Buffer + Index) = MmioRead8 ((UINTN) SrcByteAddress); > + } > + > + return EFI_SUCCESS; > +} > diff --git a/OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDeviceLib.inf > b/OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDeviceLib.inf > new file mode 100644 > index 0000000..e6deccb > --- /dev/null > +++ b/OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDeviceLib.inf > @@ -0,0 +1,43 @@ > +## @file > +# This driver produces the VirtIo Device Protocol instances for VirtIo Mmio > +# 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 = VirtioMmioDeviceLib > + FILE_GUID = 3b6ed966-b5d1-46a8-965b-867ff22d9c89 > + MODULE_TYPE = UEFI_DRIVER > + VERSION_STRING = 1.0 > + LIBRARY_CLASS = VirtioMmioDeviceLib > + > +[Sources] > + VirtioMmioDevice.c > + VirtioMmioDeviceFunctions.c > + > +[Packages] > + MdePkg/MdePkg.dec > + OvmfPkg/OvmfPkg.dec > + > +[LibraryClasses] > + BaseMemoryLib > + DebugLib > + IoLib > + MemoryAllocationLib > + UefiBootServicesTableLib > + UefiDriverEntryPoint > + UefiLib > + > +[Protocols] > + gVirtioDeviceProtocolGuid > ------------------------------------------------------------------------------ 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