On 09/23/13 15:37, Olivier Martin wrote:

> +/**
> +
> +  Write a word to the device-specific I/O region of the Virtio Header.
> +
> +  @param[in] This         This instance of VIRTIO_DEVICE_PROTOCOL
> +
> +  @param[in] FieldOffset  Destination offset.
> +
> +  @param[in] FieldSize    Destination field size in bytes,
> +                          must be in {1, 2, 4, 8}.
> +
> +  @param[out] Value       Value to write.
> +
> +  @retval EFI_SUCCESS           The data was written successfully.
> +  @retval EFI_UNSUPPORTED       The underlying IO device doesn't support the
> +                                provided address offset and read size.

The explanation for EFI_UNSUPPORTED should say "write size", but it's
not a show-stopper of course -- fix it only in case a v4 is needed.

> +  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a
> +                                lack of resources.
> +  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
> +
> +**/
> +typedef
> +EFI_STATUS
> +(EFIAPI  *VIRTIO_DEVICE_WRITE) (
> +  IN VIRTIO_DEVICE_PROTOCOL *This,
> +  IN UINTN                  FieldOffset,
> +  IN UINTN                  FieldSize,
> +  IN UINT64                 Value
> +  );

I wonder about the byte order in Value...

The virtio spec says that device-specific fields take values in
guest-native endianness. So the Value parameter is transparent here. Can
edk2 be built for big-endian ARM? I'll have to remember to check that in
the implementations of this interface. (For big endian we'll have to
copy out stuff from the far end of Value.)

> +
> +/**
> +  Read the device features field from the Virtio Header.
> +
> +  @param[in] This         This instance of VIRTIO_DEVICE_PROTOCOL
> +
> +  @return                 The 32-bit device features field.
> +
> +**/
> +typedef
> +UINT32
> +(EFIAPI  *VIRTIO_GET_DEVICE_FEATURES) (
> +  IN VIRTIO_DEVICE_PROTOCOL *This
> +  );

The underlying field is always little endian, so we might have to
byte-swap in the implementations. (VirtioLib used to have it easy in
this regard, because it targeted x64 / ia32 only; everything was little
endian.)

(1) Also, regarding the function prototype. For virtio-mmio I guess this
is a non-issue, but (in theory at least) the PciIo.Io.Read() function
can return many kinds of errors, and this interface doesn't seem to
propagate that.

This holds for all of the getters below.

I'll have to see the PCI implementation.

... Hm, yes. For example, VirtioPciGetDeviceStatus() in patch #4 ignores
the return value of VIRTIO_CFG_READ() (which wraps VirtioPciIoRead(),
which in turn wraps PciIo->Io.Read()).

As a consequence, patch #6 has hunks like:

> @@ -646,11 +620,9 @@ VirtioBlkInit (
>    //
>    // step 4a -- retrieve and validate features
>    //
> -  Status = VIRTIO_CFG_READ (Dev, Generic.VhdrDeviceFeatureBits, &Features);
> -  if (EFI_ERROR (Status)) {
> -    goto Failed;
> -  }
> -  Status = VIRTIO_CFG_READ (Dev, VhdrCapacity, &NumSectors);
> +  Features = Dev->VirtIo->GetDeviceFeatures (Dev->VirtIo);

which actively throws away the preexistent error check.

I think this is not a good thing. The retval checks should remain in
place, and the virtio-mmio implementation of the protocol should always
return EFI_SUCCESS for reads.

... Unless, of course, you can convince me that such PciIo->Io.Read()
calls will *always* succeed on a properly configured PciIo
device/protocol. (That may be the case, I'm no PCI expert by any means,
I just wanted to cover all possibilities listed in the UEFI spec.)

In this case though, please add the appropriate ASSERT_EFI_ERROR() call
right after PciIo->Io.Read(), in VirtioPciIoRead().


> +
> +/**
> +  Read the guest features field from the Virtio Header.
> +
> +  @param[in] This         This instance of VIRTIO_DEVICE_PROTOCOL
> +
> +  @return                 The 32-bit guest features field.
> +
> +**/
> +typedef
> +UINT32
> +(EFIAPI *VIRTIO_GET_GUEST_FEATURES) (
> +  IN VIRTIO_DEVICE_PROTOCOL *This
> +  );

(same)

(2) Plus: I think this field is write-only for virtio-mmio. Can we drop
this from the protocol? I doubt PCI would use it for anything.

> +
> +/**
> +  Write the guest features field in the Virtio Header.
> +
> +  @param[in] This         This instance of VIRTIO_DEVICE_PROTOCOL
> +
> +  @param[in] Features     The 32-bit guest guest features field
> +
> +**/
> +typedef
> +EFI_STATUS
> +(EFIAPI *VIRTIO_SET_GUEST_FEATURES) (
> +  IN VIRTIO_DEVICE_PROTOCOL  *This,
> +  IN UINT32                   Features
> +  );

A note about endianness would be nice (maybe a leading comment in the
file regarding any internal conversion?)

> +
> +/**
> +  Read the queue address field from the Virtio Header.
> +
> +  @param[in] This         This instance of VIRTIO_DEVICE_PROTOCOL
> +
> +  @return                 The 32-bit queue address field.
> +
> +**/
> +typedef
> +UINT32
> +(EFIAPI *VIRTIO_GET_QUEUE_ADDRESS) (
> +  IN VIRTIO_DEVICE_PROTOCOL *This
> +  );
> +
> +/**
> +  Write the queue address field in the Virtio Header.
> +
> +  @param[in] This         This instance of VIRTIO_DEVICE_PROTOCOL
> +
> +  @param[in] Address      The 32-bit Queue Address field
> +
> +**/
> +typedef
> +EFI_STATUS
> +(EFIAPI *VIRTIO_SET_QUEUE_ADDRESS) (
> +  IN VIRTIO_DEVICE_PROTOCOL  *This,
> +  IN UINT32                   Address
> +  );

Perhaps a note about the divisor to be used here (= page size of the
guest)?

> +
> +/**
> +
> +  Write the queue select field in the Virtio Header.
> +
> +  Writing to the queue select field sets the index of the queue to which
> +  operations such as SetQueueAlign and GetQueueNumMax apply.
> +
> +  @param[in] This         This instance of VIRTIO_DEVICE_PROTOCOL
> +
> +  @param[in] Index        The index of the queue to select
> +
> +**/
> +typedef
> +EFI_STATUS
> +(EFIAPI *VIRTIO_SET_QUEUE_SEL) (
> +  IN VIRTIO_DEVICE_PROTOCOL  *This,
> +  IN UINT16                   Index
> +  );

A VIRTIO_GET_QUEUE_SEL member / typedef could have been appropriate for
PCI, but I agree that virtio-mmio allows write-only here.

> +
> +/**
> +
> +  Write the queue notify field in the Virtio Header.
> +
> +  @param[in] This         This instance of VIRTIO_DEVICE_PROTOCOL
> +
> +  @param[in] Address      The 32-bit Queue Notify field
> +
> +**/
> +typedef
> +EFI_STATUS
> +(EFIAPI *VIRTIO_SET_QUEUE_NOTIFY) (
> +  IN VIRTIO_DEVICE_PROTOCOL  *This,
> +  IN UINT16                   Index
> +  );

(same)

> +
> +/**
> +  Write the queue alignment field in the Virtio Header.
> +
> +  The queue to which the alignment applies is selected by the Queue Select
> +  field.
> +
> +  @param[in] This         This instance of VIRTIO_DEVICE_PROTOCOL
> +
> +  @param[in] Alignment    The alignment boundary of the Used Ring in bytes.
> +                          Must be a power of 2.
> +
> +**/
> +typedef
> +EFI_STATUS
> +(EFIAPI *VIRTIO_SET_QUEUE_ALIGN) (
> +  IN VIRTIO_DEVICE_PROTOCOL  *This,
> +  IN UINT32                   Alignment
> +  );

Seems to be virtio-mmio only. Probably a NOP for PCI. (Maybe add a
comment about it?)

Plus, indeed write-only. OK.

> +
> +/**
> +
> +  Get the size of the virtqueue selected by the queue select field.
> +
> +  See Virtio spec Section 2.3
> +
> +  @param[in] This         This instance of VIRTIO_DEVICE_PROTOCOL
> +
> +  @return                 The size of the virtqueue in bytes.
> +                          Always a power of 2.
> +
> +**/
> +typedef
> +UINT16
> +(EFIAPI *VIRTIO_GET_QUEUE_NUM_MAX) (
> +  IN VIRTIO_DEVICE_PROTOCOL  *This
> +  );
> +
> +/**
> +
> +  Write to the QueueNum field in the Virtio Header.
> +
> +  This function only applies to Virtio-MMIO and may be a stub for other
> +  implementations. See Virtio Spec appendix X.
> +
> +  @param[in] This         This instance of VIRTIO_DEVICE_PROTOCOL
> +
> +  @param[in] QueueSize    The number of elements in the queue.
> +
> +**/
> +typedef
> +VOID
> +(EFIAPI *VIRTIO_SET_QUEUE_NUM) (
> +  IN VIRTIO_DEVICE_PROTOCOL  *This,
> +  IN UINT16                   QueueSize
> +  );
> +
> +/**
> +
> +  Write the QueueNum field in the Virtio Header.
> +
> +  @param[in] This         This instance of VIRTIO_DEVICE_PROTOCOL
> +
> +**/
> +typedef
> +UINT8
> +(EFIAPI *VIRTIO_GET_DEVICE_STATUS) (
> +  IN VIRTIO_DEVICE_PROTOCOL  *This
> +  );

The leading comment of this member is stale (copy/paste error from
QueueNum above).

> +
> +/**
> +
> +  Write the DeviceStatus field in the Virtio Header.
> +
> +  @param[in] This         This instance of VIRTIO_DEVICE_PROTOCOL
> +
> +  @param[in] DeviceStatus The 8-bit value for the Device status field
> +**/
> +typedef
> +EFI_STATUS
> +(EFIAPI *VIRTIO_SET_DEVICE_STATUS) (
> +  IN VIRTIO_DEVICE_PROTOCOL  *This,
> +  IN UINT8                   DeviceStatus
> +  );
> +
> +
> +///
> +///  This protocol provides an abstraction over the VirtIo transport layer
> +///
> +struct _VIRTIO_DEVICE_PROTOCOL {
> +  /// From the Virtio Spec
> +  INT32                       SubSystemDeviceId;
> +
> +  VIRTIO_GET_DEVICE_FEATURES  GetDeviceFeatures;
> +  VIRTIO_GET_GUEST_FEATURES   GetGuestFeatures;
> +  VIRTIO_SET_GUEST_FEATURES   SetGuestFeatures;
> +
> +  VIRTIO_GET_QUEUE_ADDRESS    GetQueueAddress;
> +  VIRTIO_SET_QUEUE_ADDRESS    SetQueueAddress;
> +
> +  VIRTIO_SET_QUEUE_SEL        SetQueueSel;
> +
> +  VIRTIO_SET_QUEUE_NOTIFY     SetQueueNotify;
> +
> +  VIRTIO_SET_QUEUE_ALIGN      SetQueueAlign;
> +
> +  VIRTIO_GET_QUEUE_NUM_MAX    GetQueueNumMax;
> +  VIRTIO_SET_QUEUE_NUM        SetQueueNum;
> +
> +  VIRTIO_GET_DEVICE_STATUS    GetDeviceStatus;
> +  VIRTIO_SET_DEVICE_STATUS    SetDeviceStatus;
> +
> +  // Functions to read/write Device Specific headers
> +  VIRTIO_DEVICE_WRITE         WriteDevice;
> +  VIRTIO_DEVICE_READ          ReadDevice;
> +};

(3) I wonder if the structure should start with a revision field. Using
a revision field, we can later extend the protocol in a backward
compatible manner, and keep the same GUID. Even if we don't have any
ideas for extensions now, I think we should keep that door open. (If we
add no revision field now, then any later extensions, even if backward
compatible, will require a new GUID -- there won't be any way for a
client to figure out if the protocol is basic or extended.)

> +
> +extern EFI_GUID gVirtioDeviceProtocolGuid;
> +
> +#endif
> diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
> index d874f0c..7ccc025 100644
> --- a/OvmfPkg/OvmfPkg.dec
> +++ b/OvmfPkg/OvmfPkg.dec
> @@ -44,6 +44,7 @@
>    gEfiXenInfoGuid                 = {0xd3b46f3b, 0xd441, 0x1244, {0x9a, 
> 0x12, 0x0, 0x12, 0x27, 0x3f, 0xc1, 0x4d}}
>
>  [Protocols]
> +  gVirtioDeviceProtocolGuid       = {0xfa920010, 0x6785, 0x4941, {0xb6, 
> 0xec, 0x49, 0x8c, 0x57, 0x9f, 0x16, 0x0a}}
>    gBlockMmioProtocolGuid          = {0x6b558ce3, 0x69e5, 0x4c67, {0xa6, 
> 0x34, 0xf7, 0xfe, 0x72, 0xad, 0xbe, 0x84}}
>
>  [PcdsFixedAtBuild]
>

I think points (1) to (3) should be addressed:

(1) error propagation and explicit error handling for all the getters,
*or* a deep ASSERT_EFI_ERROR() for PciIo.Io.Read(),

(2) please investigate and drop VIRTIO_GET_GUEST_FEATURES (write-only
for virtio-mmio, and r/w but likely useless/unused on virtio-pci &
current drivers),

(3) Please add a UINT64 revision field to the beginning of
_VIRTIO_DEVICE_PROTOCOL. (At this point I don't think we need to define
any macros or values for it, just set it to zero.)
_EFI_BLOCK_IO_PROTOCOL is a good example I think.

The rest is just "nice to have, maybe".


I apologize for the number of turns I'm taking to find stuff that I
could have found in v1 as well. My time is very fragmented,
unfortunately. (Just like everyone else's, I guess...)

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

Reply via email to