qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Laszlo Ersek <lersek@redhat.com>
To: Peter Maydell <peter.maydell@linaro.org>
Cc: Alexander Graf <agraf@suse.de>, Gerd Hoffmann <kraxel@redhat.com>,
	qemu-devel@nongnu.org
Subject: Re: [Qemu-devel] [PULL 1/1] xhci: generate a Transfer Event for each Transfer TRB with the IOC bit set
Date: Wed, 04 Mar 2015 10:03:32 +0100	[thread overview]
Message-ID: <54F6CA64.7030801@redhat.com> (raw)
In-Reply-To: <1425370469-15755-2-git-send-email-kraxel@redhat.com>

Hi Peter,

sorry about poking you -- I'm aware you've been on vacation, but in
<http://lists.nongnu.org/archive/html/qemu-devel/2015-03/msg00174.html>
you said you still inteded to pull stuff.

So, can you please pull this? :) I'd like to enable the XHCI driver in
both OVMF and AAVMF, and I'd like to reference the qemu commit in the
edk2 commit messages.

Thanks!
Laszlo

On 03/03/15 09:14, Gerd Hoffmann wrote:
> From: Laszlo Ersek <lersek@redhat.com>
> 
> At the moment, when the XHCI driver in edk2
> (MdeModulePkg/Bus/Pci/XhciDxe/XhciDxe.inf) runs on QEMU, with the options
> 
>   -device nec-usb-xhci -device usb-kbd
> 
> it crashes with:
> 
>   ASSERT MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c(1759):
>   TrsRing != ((void*) 0)
> 
> The crash hits in the following edk2 call sequence (all files under
> MdeModulePkg/Bus/):
> 
> UsbEnumerateNewDev()                         [Usb/UsbBusDxe/UsbEnumer.c]
>   UsbBuildDescTable()                        [Usb/UsbBusDxe/UsbDesc.c]
>     UsbGetDevDesc()                          [Usb/UsbBusDxe/UsbDesc.c]
>       UsbCtrlGetDesc(USB_REQ_GET_DESCRIPTOR) [Usb/UsbBusDxe/UsbDesc.c]
>         UsbCtrlRequest()                     [Usb/UsbBusDxe/UsbDesc.c]
>           UsbHcControlTransfer()             [Usb/UsbBusDxe/UsbUtility.c]
>             XhcControlTransfer()             [Pci/XhciDxe/Xhci.c]
>               XhcCreateUrb()                 [Pci/XhciDxe/XhciSched.c]
>                 XhcCreateTransferTrb()       [Pci/XhciDxe/XhciSched.c]
>               XhcExecTransfer()              [Pci/XhciDxe/XhciSched.c]
>                 XhcCheckUrbResult()          [Pci/XhciDxe/XhciSched.c]
>                   //
>                   // look for TRB_TYPE_DATA_STAGE event [1]
>                   //
>               //
>               // Store a copy of the device descriptor, as the hub device
>               // needs this info to configure endpoint. [2]
>               //
>   UsbSetConfig()                             [Usb/UsbBusDxe/UsbDesc.c]
>     UsbCtrlRequest(USB_REQ_SET_CONFIG)       [Usb/UsbBusDxe/UsbDesc.c]
>       UsbHcControlTransfer()                 [Usb/UsbBusDxe/UsbUtility.c]
>         XhcControlTransfer()                 [Pci/XhciDxe/Xhci.c]
>           XhcSetConfigCmd()                  [Pci/XhciDxe/XhciSched.c]
>             XhcInitializeEndpointContext()   [Pci/XhciDxe/XhciSched.c]
>               //
>               // allocate transfer ring for the endpoint [3]
>               //
> 
> USBKeyboardDriverBindingStart()              [Usb/UsbKbDxe/EfiKey.c]
>   UsbIoAsyncInterruptTransfer()              [Usb/UsbBusDxe/UsbBus.c]
>     UsbHcAsyncInterruptTransfer()            [Usb/UsbBusDxe/UsbUtility.c]
>       XhcAsyncInterruptTransfer()            [Pci/XhciDxe/Xhci.c]
>         XhcCreateUrb()                       [Pci/XhciDxe/Xhci.c]
>           XhcCreateTransferTrb()             [Pci/XhciDxe/XhciSched.c]
>             XhcSyncTrsRing()                 [Pci/XhciDxe/XhciSched.c]
>               ASSERT (TrsRing != NULL) [4]
> 
> UsbEnumerateNewDev() in the USB bus driver issues a GET_DESCRIPTOR
> request, in order to determine the number of configurations that the
> endpoint supports. The requests consists of three stages (three TRBs),
> setup, data, and status. The length of the response is determined in [1],
> namely from the transfer event that the host controller generates in
> response to the request's middle stage (ie. the data stage).
> 
> If the length of the answer is correct (a full GET_DESCRIPTOR request
> takes 18 bytes), then the XHCI driver that underlies the USB bus driver
> "snoops" (caches) the descriptor data for later [2].
> 
> Later, the USB bus driver sends a SET_CONFIG request. The underlying XHCI
> driver allocates a transfer ring for the endpoint, relying on the data
> snooped and cached in step [2].
> 
> Finally, the USB keyboard driver submits an asynchronous interrupt
> transfer to manage the keyboard. As part of this it asserts [4] that the
> ring has been allocated in step [3].
> 
> And this ASSERT() fires. The root cause can be found in the way QEMU
> handles the initial GET_DESCRIPTOR request.
> 
> Again, that request consists of three stages (TRBs, Transfer Request
> Blocks), "setup", "data", and "status". The XhcCreateTransferTrb()
> function sets the IOC ("Interrupt on Completion") flag in each of these
> TRBs.
> 
> According to the XHCI specification, the host controller shall generate a
> Transfer Event in response to *each* individual TRB of the request that
> had the IOC flag set. This means that QEMU should queue three events:
> setup, data, and status, for edk2's XHCI driver.
> 
> However, QEMU only generates two events:
> - one for the setup (ie. 1st) stage,
> - another for the status (ie. 3rd) stage.
> 
> No event is generated for the middle (ie. data) stage. The loop in QEMU's
> xhci_xfer_report() function runs three times, but due to the "reported"
> variable, only the first and the last TRBs elicit events, the middle (data
> stage) results in no event queued.
> 
> As a consequence:
> - When handling the GET_DESCRIPTOR request, XhcCheckUrbResult() in [1]
>   does not update the response length from zero.
> 
> - XhcControlTransfer() thinks that the response is invalid (it has zero
>   length payload instead of 18 bytes), hence [2] is not reached; the
>   device descriptor is not stashed for later, and the number of possible
>   configurations is left at zero.
> 
> - When handling the SET_CONFIG request, (NumConfigurations == 0) from
>   above prevents the allocation of the endpoint's transfer ring.
> 
> - When the keyboard driver tries to use the endpoint, the ASSERT() blows
>   up.
> 
> The solution is to correct the emulation in QEMU, and to generate a
> transfer event whenever IOC is set in a TRB.
> 
> The patch replaces
> 
>   !reported && (IOC || foo)    == !reported && IOC ||
>                                   !reported && foo
> 
> with
> 
>   IOC || (!reported && foo)    == IOC ||
>                                   !reported && foo
> 
> which only changes how
> 
>   reported && IOC
> 
> is handled. (Namely, it now generates an event.)
> 
> Tested with edk2 built for "qemu-system-aarch64 -M virt" (ie.
> "ArmVirtualizationQemu.dsc", aka "AAVMF"), and guest Linux.
> 
> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> ---
>  hw/usb/hcd-xhci.c | 15 ++++++++++++---
>  1 file changed, 12 insertions(+), 3 deletions(-)
> 
> diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
> index 776699b..828c2a7 100644
> --- a/hw/usb/hcd-xhci.c
> +++ b/hw/usb/hcd-xhci.c
> @@ -1767,9 +1767,18 @@ static void xhci_xfer_report(XHCITransfer *xfer)
>              break;
>          }
>  
> -        if (!reported && ((trb->control & TRB_TR_IOC) ||
> -                          (shortpkt && (trb->control & TRB_TR_ISP)) ||
> -                          (xfer->status != CC_SUCCESS && left == 0))) {
> +        /*
> +         * XHCI 1.1, 4.11.3.1 Transfer Event TRB -- "each Transfer TRB
> +         * encountered with its IOC flag set to '1' shall generate a Transfer
> +         * Event."
> +         *
> +         * Otherwise, longer transfers can have multiple data TRBs (for scatter
> +         * gather). Short transfers and errors should be reported once per
> +         * transfer only.
> +         */
> +        if ((trb->control & TRB_TR_IOC) ||
> +            (!reported && ((shortpkt && (trb->control & TRB_TR_ISP)) ||
> +                           (xfer->status != CC_SUCCESS && left == 0)))) {
>              event.slotid = xfer->slotid;
>              event.epid = xfer->epid;
>              event.length = (trb->status & 0x1ffff) - chunk;
> 

  reply	other threads:[~2015-03-04  9:03 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-03-03  8:14 [Qemu-devel] [PULL 0/1] xhci: generate a Transfer Event for each Transfer TRB with the IOC bit set Gerd Hoffmann
2015-03-03  8:14 ` [Qemu-devel] [PULL 1/1] " Gerd Hoffmann
2015-03-04  9:03   ` Laszlo Ersek [this message]
2015-03-04 11:02     ` Gerd Hoffmann
2015-03-04 14:10     ` Peter Maydell
2015-03-04 15:51       ` Laszlo Ersek
2015-03-08  8:36 ` [Qemu-devel] [PULL 0/1] " Peter Maydell

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=54F6CA64.7030801@redhat.com \
    --to=lersek@redhat.com \
    --cc=agraf@suse.de \
    --cc=kraxel@redhat.com \
    --cc=peter.maydell@linaro.org \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).