qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Hans de Goede <hdegoede@redhat.com>
To: Gerd Hoffmann <kraxel@redhat.com>
Cc: qemu-devel@nongnu.org
Subject: Re: [Qemu-devel] [PATCH] ehci: handle dma errors
Date: Thu, 15 Nov 2012 14:13:48 +0100	[thread overview]
Message-ID: <50A4EA8C.4000507@redhat.com> (raw)
In-Reply-To: <1352982136-30761-1-git-send-email-kraxel@redhat.com>

Looks good, ack.


On 11/15/2012 01:22 PM, Gerd Hoffmann wrote:
> Starting with commit 1c380f9460522f32c8dd2577b2a53d518ec91c6d dma
> transfers can actually fail.  This patch makes ehci keep track
> of the busmaster bit in pci config space, by setting/clearing the
> dma_context pointer.  Attempts to dma without context will result
> in raising HSE (Host System Error) interrupt and stopping the host
> controller.
>
> This patch fixes WinXP not booting with a usb stick attached to ehci.
> Root cause is seabios activating ehci so you can boot from the stick,
> and WinXP clearing the busmaster bit before resetting the host
> controller, leading to ehci actually trying dma while it is disabled.
>
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> ---
>   hw/usb/hcd-ehci-pci.c |   17 +++++++++++++
>   hw/usb/hcd-ehci.c     |   63 ++++++++++++++++++++++++++++++++++--------------
>   trace-events          |    1 +
>   3 files changed, 62 insertions(+), 19 deletions(-)
>
> diff --git a/hw/usb/hcd-ehci-pci.c b/hw/usb/hcd-ehci-pci.c
> index fe45a1f..5887eab 100644
> --- a/hw/usb/hcd-ehci-pci.c
> +++ b/hw/usb/hcd-ehci-pci.c
> @@ -17,6 +17,7 @@
>
>   #include "hw/usb/hcd-ehci.h"
>   #include "hw/pci.h"
> +#include "range.h"
>
>   typedef struct EHCIPCIState {
>       PCIDevice pcidev;
> @@ -79,6 +80,21 @@ static int usb_ehci_pci_initfn(PCIDevice *dev)
>       return 0;
>   }
>
> +static void usb_ehci_pci_write_config(PCIDevice *dev, uint32_t addr,
> +                                      uint32_t val, int l)
> +{
> +    EHCIPCIState *i = DO_UPCAST(EHCIPCIState, pcidev, dev);
> +    bool busmaster;
> +
> +    pci_default_write_config(dev, addr, val, l);
> +
> +    if (!range_covers_byte(addr, l, PCI_COMMAND)) {
> +        return;
> +    }
> +    busmaster = pci_get_word(dev->config + PCI_COMMAND) & PCI_COMMAND_MASTER;
> +    i->ehci.dma = busmaster ? pci_dma_context(dev) : NULL;
> +}
> +
>   static Property ehci_pci_properties[] = {
>       DEFINE_PROP_UINT32("maxframes", EHCIPCIState, ehci.maxframes, 128),
>       DEFINE_PROP_END_OF_LIST(),
> @@ -106,6 +122,7 @@ static void ehci_class_init(ObjectClass *klass, void *data)
>       k->device_id = i->device_id;
>       k->revision = i->revision;
>       k->class_id = PCI_CLASS_SERIAL_USB;
> +    k->config_write = usb_ehci_pci_write_config;
>       dc->vmsd = &vmstate_ehci_pci;
>       dc->props = ehci_pci_properties;
>   }
> diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
> index 57da76c..df3aaed 100644
> --- a/hw/usb/hcd-ehci.c
> +++ b/hw/usb/hcd-ehci.c
> @@ -1000,21 +1000,25 @@ static void ehci_opreg_write(void *ptr, hwaddr addr,
>                                   *mmio, old);
>   }
>
> -
> -// TODO : Put in common header file, duplication from usb-ohci.c
> -
>   /* Get an array of dwords from main memory */
>   static inline int get_dwords(EHCIState *ehci, uint32_t addr,
>                                uint32_t *buf, int num)
>   {
>       int i;
>
> +    if (!ehci->dma) {
> +        ehci_raise_irq(ehci, USBSTS_HSE);
> +        ehci->usbcmd &= ~USBCMD_RUNSTOP;
> +        trace_usb_ehci_dma_error();
> +        return -1;
> +    }
> +
>       for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
>           dma_memory_read(ehci->dma, addr, buf, sizeof(*buf));
>           *buf = le32_to_cpu(*buf);
>       }
>
> -    return 1;
> +    return num;
>   }
>
>   /* Put an array of dwords in to main memory */
> @@ -1023,12 +1027,19 @@ static inline int put_dwords(EHCIState *ehci, uint32_t addr,
>   {
>       int i;
>
> +    if (!ehci->dma) {
> +        ehci_raise_irq(ehci, USBSTS_HSE);
> +        ehci->usbcmd &= ~USBCMD_RUNSTOP;
> +        trace_usb_ehci_dma_error();
> +        return -1;
> +    }
> +
>       for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
>           uint32_t tmp = cpu_to_le32(*buf);
>           dma_memory_write(ehci->dma, addr, &tmp, sizeof(tmp));
>       }
>
> -    return 1;
> +    return num;
>   }
>
>   /*
> @@ -1440,8 +1451,10 @@ static int ehci_state_waitlisthead(EHCIState *ehci,  int async)
>
>       /*  Find the head of the list (4.9.1.1) */
>       for(i = 0; i < MAX_QH; i++) {
> -        get_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &qh,
> -                   sizeof(EHCIqh) >> 2);
> +        if (get_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &qh,
> +                       sizeof(EHCIqh) >> 2) < 0) {
> +            return 0;
> +        }
>           ehci_trace_qh(NULL, NLPTR_GET(entry), &qh);
>
>           if (qh.epchar & QH_EPCHAR_H) {
> @@ -1538,8 +1551,11 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
>           goto out;
>       }
>
> -    get_dwords(ehci, NLPTR_GET(q->qhaddr),
> -               (uint32_t *) &qh, sizeof(EHCIqh) >> 2);
> +    if (get_dwords(ehci, NLPTR_GET(q->qhaddr),
> +                   (uint32_t *) &qh, sizeof(EHCIqh) >> 2) < 0) {
> +        q = NULL;
> +        goto out;
> +    }
>       ehci_trace_qh(q, NLPTR_GET(q->qhaddr), &qh);
>
>       /*
> @@ -1626,8 +1642,10 @@ static int ehci_state_fetchitd(EHCIState *ehci, int async)
>       assert(!async);
>       entry = ehci_get_fetch_addr(ehci, async);
>
> -    get_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &itd,
> -               sizeof(EHCIitd) >> 2);
> +    if (get_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &itd,
> +                   sizeof(EHCIitd) >> 2) < 0) {
> +        return -1;
> +    }
>       ehci_trace_itd(ehci, entry, &itd);
>
>       if (ehci_process_itd(ehci, &itd, entry) != 0) {
> @@ -1650,8 +1668,10 @@ static int ehci_state_fetchsitd(EHCIState *ehci, int async)
>       assert(!async);
>       entry = ehci_get_fetch_addr(ehci, async);
>
> -    get_dwords(ehci, NLPTR_GET(entry), (uint32_t *)&sitd,
> -               sizeof(EHCIsitd) >> 2);
> +    if (get_dwords(ehci, NLPTR_GET(entry), (uint32_t *)&sitd,
> +                   sizeof(EHCIsitd) >> 2) < 0) {
> +        return 0;
> +    }
>       ehci_trace_sitd(ehci, entry, &sitd);
>
>       if (!(sitd.results & SITD_RESULTS_ACTIVE)) {
> @@ -1712,8 +1732,10 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
>       EHCIPacket *p;
>       int again = 1;
>
> -    get_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &qtd,
> -               sizeof(EHCIqtd) >> 2);
> +    if (get_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &qtd,
> +                   sizeof(EHCIqtd) >> 2) < 0) {
> +        return 0;
> +    }
>       ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), &qtd);
>
>       p = QTAILQ_FIRST(&q->packets);
> @@ -1806,8 +1828,10 @@ static int ehci_fill_queue(EHCIPacket *p)
>                   goto leave;
>               }
>           }
> -        get_dwords(q->ehci, NLPTR_GET(qtdaddr),
> -                   (uint32_t *) &qtd, sizeof(EHCIqtd) >> 2);
> +        if (get_dwords(q->ehci, NLPTR_GET(qtdaddr),
> +                       (uint32_t *) &qtd, sizeof(EHCIqtd) >> 2) < 0) {
> +            return -1;
> +        }
>           ehci_trace_qtd(q, NLPTR_GET(qtdaddr), &qtd);
>           if (!(qtd.token & QTD_TOKEN_ACTIVE)) {
>               break;
> @@ -2106,8 +2130,9 @@ static void ehci_advance_periodic_state(EHCIState *ehci)
>           }
>           list |= ((ehci->frindex & 0x1ff8) >> 1);
>
> -        dma_memory_read(ehci->dma, list, &entry, sizeof entry);
> -        entry = le32_to_cpu(entry);
> +        if (get_dwords(ehci, list, &entry, 1) < 0) {
> +            break;
> +        }
>
>           DPRINTF("PERIODIC state adv fr=%d.  [%08X] -> %08X\n",
>                   ehci->frindex / 8, list, entry);
> diff --git a/trace-events b/trace-events
> index 10813d5..913e00b 100644
> --- a/trace-events
> +++ b/trace-events
> @@ -286,6 +286,7 @@ usb_ehci_irq(uint32_t level, uint32_t frindex, uint32_t sts, uint32_t mask) "lev
>   usb_ehci_guest_bug(const char *reason) "%s"
>   usb_ehci_doorbell_ring(void) ""
>   usb_ehci_doorbell_ack(void) ""
> +usb_ehci_dma_error(void) ""
>
>   # hw/usb/hcd-uhci.c
>   usb_uhci_reset(void) "=== RESET ==="
>

      reply	other threads:[~2012-11-15 13:11 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-11-15 12:22 [Qemu-devel] [PATCH] ehci: handle dma errors Gerd Hoffmann
2012-11-15 13:13 ` Hans de Goede [this message]

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=50A4EA8C.4000507@redhat.com \
    --to=hdegoede@redhat.com \
    --cc=kraxel@redhat.com \
    --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).