mirror of https://github.com/xemu-project/xemu.git
ehci: Validate qh is not changed unexpectedly by the guest
-combine the qh check with the check for devaddr changes -also ensure that p gets set to NULL when the queue gets cancelled on devaddr change, which was not done properly before this patch Signed-off-by: Hans de Goede <hdegoede@redhat.com>
This commit is contained in:
parent
66f092d256
commit
dafe31fc2a
|
@ -780,6 +780,14 @@ static void ehci_cancel_queue(EHCIQueue *q)
|
||||||
} while ((p = QTAILQ_FIRST(&q->packets)) != NULL);
|
} while ((p = QTAILQ_FIRST(&q->packets)) != NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ehci_reset_queue(EHCIQueue *q)
|
||||||
|
{
|
||||||
|
trace_usb_ehci_queue_action(q, "reset");
|
||||||
|
ehci_cancel_queue(q);
|
||||||
|
q->dev = NULL;
|
||||||
|
q->qtdaddr = 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void ehci_free_queue(EHCIQueue *q)
|
static void ehci_free_queue(EHCIQueue *q)
|
||||||
{
|
{
|
||||||
EHCIQueueHead *head = q->async ? &q->ehci->aqueues : &q->ehci->pqueues;
|
EHCIQueueHead *head = q->async ? &q->ehci->aqueues : &q->ehci->pqueues;
|
||||||
|
@ -1755,8 +1763,9 @@ out:
|
||||||
static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
|
static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
|
||||||
{
|
{
|
||||||
EHCIPacket *p;
|
EHCIPacket *p;
|
||||||
uint32_t entry, devaddr;
|
uint32_t entry, devaddr, endp;
|
||||||
EHCIQueue *q;
|
EHCIQueue *q;
|
||||||
|
EHCIqh qh;
|
||||||
|
|
||||||
entry = ehci_get_fetch_addr(ehci, async);
|
entry = ehci_get_fetch_addr(ehci, async);
|
||||||
q = ehci_find_queue_by_qh(ehci, entry, async);
|
q = ehci_find_queue_by_qh(ehci, entry, async);
|
||||||
|
@ -1774,17 +1783,25 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
|
||||||
}
|
}
|
||||||
|
|
||||||
get_dwords(ehci, NLPTR_GET(q->qhaddr),
|
get_dwords(ehci, NLPTR_GET(q->qhaddr),
|
||||||
(uint32_t *) &q->qh, sizeof(EHCIqh) >> 2);
|
(uint32_t *) &qh, sizeof(EHCIqh) >> 2);
|
||||||
ehci_trace_qh(q, NLPTR_GET(q->qhaddr), &q->qh);
|
ehci_trace_qh(q, NLPTR_GET(q->qhaddr), &qh);
|
||||||
|
|
||||||
devaddr = get_field(q->qh.epchar, QH_EPCHAR_DEVADDR);
|
/*
|
||||||
if (q->dev != NULL && q->dev->addr != devaddr) {
|
* The overlay area of the qh should never be changed by the guest,
|
||||||
if (!QTAILQ_EMPTY(&q->packets)) {
|
* except when idle, in which case the reset is a nop.
|
||||||
/* should not happen (guest bug) */
|
*/
|
||||||
ehci_cancel_queue(q);
|
devaddr = get_field(qh.epchar, QH_EPCHAR_DEVADDR);
|
||||||
}
|
endp = get_field(qh.epchar, QH_EPCHAR_EP);
|
||||||
q->dev = NULL;
|
if ((devaddr != get_field(q->qh.epchar, QH_EPCHAR_DEVADDR)) ||
|
||||||
|
(endp != get_field(q->qh.epchar, QH_EPCHAR_EP)) ||
|
||||||
|
(memcmp(&qh.current_qtd, &q->qh.current_qtd,
|
||||||
|
9 * sizeof(uint32_t)) != 0) ||
|
||||||
|
(q->dev != NULL && q->dev->addr != devaddr)) {
|
||||||
|
ehci_reset_queue(q);
|
||||||
|
p = NULL;
|
||||||
}
|
}
|
||||||
|
q->qh = qh;
|
||||||
|
|
||||||
if (q->dev == NULL) {
|
if (q->dev == NULL) {
|
||||||
q->dev = ehci_find_device(q->ehci, devaddr);
|
q->dev = ehci_find_device(q->ehci, devaddr);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue