diff options
author | Hans de Goede <hdegoede@redhat.com> | 2012-12-14 14:35:29 +0100 |
---|---|---|
committer | Gerd Hoffmann <kraxel@redhat.com> | 2013-01-07 12:57:23 +0100 |
commit | bbbc39ccacf66ef58261c155f9eed503947c3023 (patch) | |
tree | b0b96db909e383f3311d5f4205e3d5275f8f673c /hw | |
parent | 51e0c5d02951ee5bd653132e46123f6e963ff23f (diff) |
ehci: Verify a queue's ep direction does not change
ehci_fill_queue assumes that there is a one on one relationship between an ep
and a qh, this patch adds a check to ensure this.
Note I don't expect this to ever trigger, this is just something I noticed
the guest might do while working on other stuff. The only way this check can
trigger is if a guest mixes in and out qtd-s in a single qh for a non
control ep.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Diffstat (limited to 'hw')
-rw-r--r-- | hw/usb/hcd-ehci.c | 19 | ||||
-rw-r--r-- | hw/usb/hcd-ehci.h | 1 |
2 files changed, 20 insertions, 0 deletions
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index dae414abb9..5d314a09f1 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -527,6 +527,19 @@ static bool ehci_verify_qtd(EHCIPacket *p, EHCIqtd *qtd) } } +static bool ehci_verify_pid(EHCIQueue *q, EHCIqtd *qtd) +{ + int ep = get_field(q->qh.epchar, QH_EPCHAR_EP); + int pid = ehci_get_pid(qtd); + + /* Note the pid changing is normal for ep 0 (the control ep) */ + if (q->last_pid && ep != 0 && pid != q->last_pid) { + return false; + } else { + return true; + } +} + /* Finish executing and writeback a packet outside of the regular fetchqh -> fetchqtd -> execute -> writeback cycle */ static void ehci_writeback_async_complete_packet(EHCIPacket *p) @@ -634,6 +647,7 @@ static int ehci_reset_queue(EHCIQueue *q) packets = ehci_cancel_queue(q); q->dev = NULL; q->qtdaddr = 0; + q->last_pid = 0; return packets; } @@ -1368,6 +1382,7 @@ static int ehci_execute(EHCIPacket *p, const char *action) } p->pid = ehci_get_pid(&p->qtd); + p->queue->last_pid = p->pid; endp = get_field(p->queue->qh.epchar, QH_EPCHAR_EP); ep = usb_ep_get(p->queue->dev, p->pid, endp); @@ -1883,6 +1898,10 @@ static int ehci_fill_queue(EHCIPacket *p) if (!(qtd.token & QTD_TOKEN_ACTIVE)) { break; } + if (!ehci_verify_pid(q, &qtd)) { + ehci_trace_guest_bug(q->ehci, "guest queued token with wrong pid"); + break; + } p = ehci_alloc_packet(q); p->qtdaddr = qtdaddr; p->qtd = qtd; diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h index e35144d386..14ee3bea5a 100644 --- a/hw/usb/hcd-ehci.h +++ b/hw/usb/hcd-ehci.h @@ -248,6 +248,7 @@ struct EHCIQueue { EHCIqh qh; /* copy of current QH (being worked on) */ uint32_t qhaddr; /* address QH read from */ uint32_t qtdaddr; /* address QTD read from */ + int last_pid; /* pid of last packet executed */ USBDevice *dev; QTAILQ_HEAD(pkts_head, EHCIPacket) packets; }; |