aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvi Kivity <avi@redhat.com>2012-09-06 11:31:27 +0300
committerAvi Kivity <avi@redhat.com>2012-09-06 11:31:27 +0300
commit4c3e02beed9878a5f760eeceb6cd42c475cf0127 (patch)
treeb99d39a241a5db9981d1c89dd87d2512a16e4630
parent1cc707b10567b0ebf2670e39f4a3c01da664fd6a (diff)
parent8db972cfa469b4e4afd9c65e54e796b83b5ce3a2 (diff)
Merge tag 'v1.2.0'qemu-kvm-1.2.0
Tag for 1.2.0 * tag 'v1.2.0': Update version for 1.2.0 console: bounds check whenever changing the cursor due to an escape code qemu-timer: properly arm alarm timer for timers set by device initialization target-xtensa: return ENOSYS for unimplemented simcalls Update version to 1.2.0-rc3 console: Fix warning from clang (and potential crash) uas: move transfer kickoff ehci: Fix interrupt endpoints no longer working ehci: handle TD deactivation of inflight packets ehci: add ehci_cancel_queue() ehci: simplify ehci_state_executing ehci: Remove unnecessary ehci_flush_qh call ehci: Schedule async-bh when IAAD bit gets set ehci: Fix NULL ptr deref when unplugging an USB dev with an iso stream active usb: unique packet ids usb: Halt ep queue en cancel pending packets on a packet error fix info qtree indention qemu-iotests: add backing file smaller than image test case stream: complete early if end of backing file is reached qed: refuse unaligned zero writes with a backing file Signed-off-by: Avi Kivity <avi@redhat.com>
-rw-r--r--VERSION2
-rw-r--r--block/qed.c11
-rw-r--r--block/stream.c6
-rw-r--r--console.c59
-rw-r--r--hw/qdev-monitor.c2
-rw-r--r--hw/usb.h4
-rw-r--r--hw/usb/core.c38
-rw-r--r--hw/usb/dev-uas.c3
-rw-r--r--hw/usb/hcd-ehci.c122
-rw-r--r--hw/usb/hcd-musb.c3
-rw-r--r--hw/usb/hcd-ohci.c4
-rw-r--r--hw/usb/hcd-uhci.c20
-rw-r--r--hw/usb/hcd-xhci.c2
-rw-r--r--qemu-timer.c3
-rw-r--r--target-xtensa/xtensa-semi.c2
-rwxr-xr-xtests/qemu-iotests/03033
-rw-r--r--tests/qemu-iotests/030.out4
17 files changed, 222 insertions, 96 deletions
diff --git a/VERSION b/VERSION
index 99f6a00b1..26aaba0e8 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.1.92
+1.2.0
diff --git a/block/qed.c b/block/qed.c
index a02dbfd72..21cb23987 100644
--- a/block/qed.c
+++ b/block/qed.c
@@ -1363,10 +1363,21 @@ static int coroutine_fn bdrv_qed_co_write_zeroes(BlockDriverState *bs,
int nb_sectors)
{
BlockDriverAIOCB *blockacb;
+ BDRVQEDState *s = bs->opaque;
QEDWriteZeroesCB cb = { .done = false };
QEMUIOVector qiov;
struct iovec iov;
+ /* Refuse if there are untouched backing file sectors */
+ if (bs->backing_hd) {
+ if (qed_offset_into_cluster(s, sector_num * BDRV_SECTOR_SIZE) != 0) {
+ return -ENOTSUP;
+ }
+ if (qed_offset_into_cluster(s, nb_sectors * BDRV_SECTOR_SIZE) != 0) {
+ return -ENOTSUP;
+ }
+ }
+
/* Zero writes start without an I/O buffer. If a buffer becomes necessary
* then it will be allocated during request processing.
*/
diff --git a/block/stream.c b/block/stream.c
index 37c46525d..c4f87dd5b 100644
--- a/block/stream.c
+++ b/block/stream.c
@@ -122,6 +122,12 @@ wait:
* known-unallocated area [sector_num, sector_num+n). */
ret = bdrv_co_is_allocated_above(bs->backing_hd, base,
sector_num, n, &n);
+
+ /* Finish early if end of backing file has been reached */
+ if (ret == 0 && n == 0) {
+ n = end - sector_num;
+ }
+
copy = (ret == 1);
}
trace_stream_one_iteration(s, sector_num, n, ret);
diff --git a/console.c b/console.c
index 4525cc70b..3b5cabba3 100644
--- a/console.c
+++ b/console.c
@@ -850,6 +850,26 @@ static void console_clear_xy(TextConsole *s, int x, int y)
update_xy(s, x, y);
}
+/* set cursor, checking bounds */
+static void set_cursor(TextConsole *s, int x, int y)
+{
+ if (x < 0) {
+ x = 0;
+ }
+ if (y < 0) {
+ y = 0;
+ }
+ if (y >= s->height) {
+ y = s->height - 1;
+ }
+ if (x >= s->width) {
+ x = s->width - 1;
+ }
+
+ s->x = x;
+ s->y = y;
+}
+
static void console_putchar(TextConsole *s, int ch)
{
TextCell *c;
@@ -921,7 +941,8 @@ static void console_putchar(TextConsole *s, int ch)
s->esc_params[s->nb_esc_params] * 10 + ch - '0';
}
} else {
- s->nb_esc_params++;
+ if (s->nb_esc_params < MAX_ESC_PARAMS)
+ s->nb_esc_params++;
if (ch == ';')
break;
#ifdef DEBUG_CONSOLE
@@ -935,59 +956,37 @@ static void console_putchar(TextConsole *s, int ch)
if (s->esc_params[0] == 0) {
s->esc_params[0] = 1;
}
- s->y -= s->esc_params[0];
- if (s->y < 0) {
- s->y = 0;
- }
+ set_cursor(s, s->x, s->y - s->esc_params[0]);
break;
case 'B':
/* move cursor down */
if (s->esc_params[0] == 0) {
s->esc_params[0] = 1;
}
- s->y += s->esc_params[0];
- if (s->y >= s->height) {
- s->y = s->height - 1;
- }
+ set_cursor(s, s->x, s->y + s->esc_params[0]);
break;
case 'C':
/* move cursor right */
if (s->esc_params[0] == 0) {
s->esc_params[0] = 1;
}
- s->x += s->esc_params[0];
- if (s->x >= s->width) {
- s->x = s->width - 1;
- }
+ set_cursor(s, s->x + s->esc_params[0], s->y);
break;
case 'D':
/* move cursor left */
if (s->esc_params[0] == 0) {
s->esc_params[0] = 1;
}
- s->x -= s->esc_params[0];
- if (s->x < 0) {
- s->x = 0;
- }
+ set_cursor(s, s->x - s->esc_params[0], s->y);
break;
case 'G':
/* move cursor to column */
- s->x = s->esc_params[0] - 1;
- if (s->x < 0) {
- s->x = 0;
- }
+ set_cursor(s, s->esc_params[0] - 1, s->y);
break;
case 'f':
case 'H':
/* move cursor to row, column */
- s->x = s->esc_params[1] - 1;
- if (s->x < 0) {
- s->x = 0;
- }
- s->y = s->esc_params[0] - 1;
- if (s->y < 0) {
- s->y = 0;
- }
+ set_cursor(s, s->esc_params[1] - 1, s->esc_params[0] - 1);
break;
case 'J':
switch (s->esc_params[0]) {
@@ -1087,7 +1086,7 @@ void console_select(unsigned int index)
if (s) {
DisplayState *ds = s->ds;
- if (active_console->cursor_timer) {
+ if (active_console && active_console->cursor_timer) {
qemu_del_timer(active_console->cursor_timer);
}
active_console = s;
diff --git a/hw/qdev-monitor.c b/hw/qdev-monitor.c
index 018b38678..33b7f79a9 100644
--- a/hw/qdev-monitor.c
+++ b/hw/qdev-monitor.c
@@ -543,7 +543,7 @@ static void qdev_print(Monitor *mon, DeviceState *dev, int indent)
qdev_print_props(mon, dev, DEVICE_CLASS(class)->props, indent);
class = object_class_get_parent(class);
} while (class != object_class_by_name(TYPE_DEVICE));
- bus_print_dev(dev->parent_bus, mon, dev, indent + 2);
+ bus_print_dev(dev->parent_bus, mon, dev, indent);
QLIST_FOREACH(child, &dev->child_bus, sibling) {
qbus_print(mon, child, indent);
}
diff --git a/hw/usb.h b/hw/usb.h
index 432ccae57..b8fceec89 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -179,6 +179,7 @@ struct USBEndpoint {
uint8_t ifnum;
int max_packet_size;
bool pipeline;
+ bool halted;
USBDevice *dev;
QTAILQ_HEAD(, USBPacket) queue;
};
@@ -331,6 +332,7 @@ typedef enum USBPacketState {
struct USBPacket {
/* Data fields for use by the driver. */
int pid;
+ uint64_t id;
USBEndpoint *ep;
QEMUIOVector iov;
uint64_t parameter; /* control transfers */
@@ -343,7 +345,7 @@ struct USBPacket {
void usb_packet_init(USBPacket *p);
void usb_packet_set_state(USBPacket *p, USBPacketState state);
void usb_packet_check_state(USBPacket *p, USBPacketState expected);
-void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep);
+void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id);
void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len);
int usb_packet_map(USBPacket *p, QEMUSGList *sgl);
void usb_packet_unmap(USBPacket *p, QEMUSGList *sgl);
diff --git a/hw/usb/core.c b/hw/usb/core.c
index c7e5bc047..2da38e7fd 100644
--- a/hw/usb/core.c
+++ b/hw/usb/core.c
@@ -382,12 +382,23 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p)
usb_packet_check_state(p, USB_PACKET_SETUP);
assert(p->ep != NULL);
+ /* Submitting a new packet clears halt */
+ if (p->ep->halted) {
+ assert(QTAILQ_EMPTY(&p->ep->queue));
+ p->ep->halted = false;
+ }
+
if (QTAILQ_EMPTY(&p->ep->queue) || p->ep->pipeline) {
ret = usb_process_one(p);
if (ret == USB_RET_ASYNC) {
usb_packet_set_state(p, USB_PACKET_ASYNC);
QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
} else {
+ /*
+ * When pipelining is enabled usb-devices must always return async,
+ * otherwise packets can complete out of order!
+ */
+ assert(!p->ep->pipeline);
p->result = ret;
usb_packet_set_state(p, USB_PACKET_COMPLETE);
}
@@ -399,6 +410,20 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p)
return ret;
}
+static void __usb_packet_complete(USBDevice *dev, USBPacket *p)
+{
+ USBEndpoint *ep = p->ep;
+
+ assert(p->result != USB_RET_ASYNC && p->result != USB_RET_NAK);
+
+ if (p->result < 0) {
+ ep->halted = true;
+ }
+ usb_packet_set_state(p, USB_PACKET_COMPLETE);
+ QTAILQ_REMOVE(&ep->queue, p, queue);
+ dev->port->ops->complete(dev->port, p);
+}
+
/* Notify the controller that an async packet is complete. This should only
be called for packets previously deferred by returning USB_RET_ASYNC from
handle_packet. */
@@ -409,11 +434,9 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p)
usb_packet_check_state(p, USB_PACKET_ASYNC);
assert(QTAILQ_FIRST(&ep->queue) == p);
- usb_packet_set_state(p, USB_PACKET_COMPLETE);
- QTAILQ_REMOVE(&ep->queue, p, queue);
- dev->port->ops->complete(dev->port, p);
+ __usb_packet_complete(dev, p);
- while (!QTAILQ_EMPTY(&ep->queue)) {
+ while (!ep->halted && !QTAILQ_EMPTY(&ep->queue)) {
p = QTAILQ_FIRST(&ep->queue);
if (p->state == USB_PACKET_ASYNC) {
break;
@@ -425,9 +448,7 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p)
break;
}
p->result = ret;
- usb_packet_set_state(p, USB_PACKET_COMPLETE);
- QTAILQ_REMOVE(&ep->queue, p, queue);
- dev->port->ops->complete(dev->port, p);
+ __usb_packet_complete(ep->dev, p);
}
}
@@ -499,10 +520,11 @@ void usb_packet_set_state(USBPacket *p, USBPacketState state)
p->state = state;
}
-void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep)
+void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id)
{
assert(!usb_packet_is_inflight(p));
assert(p->iov.iov != NULL);
+ p->id = id;
p->pid = pid;
p->ep = ep;
p->result = 0;
diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c
index b13eeba56..5a0057a36 100644
--- a/hw/usb/dev-uas.c
+++ b/hw/usb/dev-uas.c
@@ -424,6 +424,7 @@ static void usb_uas_scsi_free_request(SCSIBus *bus, void *priv)
}
QTAILQ_REMOVE(&uas->requests, req, next);
g_free(req);
+ usb_uas_start_next_transfer(uas);
}
static UASRequest *usb_uas_find_request(UASDevice *uas, uint16_t tag)
@@ -456,7 +457,6 @@ static void usb_uas_scsi_command_complete(SCSIRequest *r,
uint32_t status, size_t resid)
{
UASRequest *req = r->hba_private;
- UASDevice *uas = req->uas;
trace_usb_uas_scsi_complete(req->uas->dev.addr, req->tag, status, resid);
req->complete = true;
@@ -465,7 +465,6 @@ static void usb_uas_scsi_command_complete(SCSIRequest *r,
}
usb_uas_queue_sense(req, status);
scsi_req_unref(req->req);
- usb_uas_start_next_transfer(uas);
}
static void usb_uas_scsi_request_cancelled(SCSIRequest *r)
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index 8b94b1772..017342b56 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -766,15 +766,27 @@ static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, uint32_t addr, int async)
return q;
}
+static void ehci_cancel_queue(EHCIQueue *q)
+{
+ EHCIPacket *p;
+
+ p = QTAILQ_FIRST(&q->packets);
+ if (p == NULL) {
+ return;
+ }
+
+ trace_usb_ehci_queue_action(q, "cancel");
+ do {
+ ehci_free_packet(p);
+ } while ((p = QTAILQ_FIRST(&q->packets)) != NULL);
+}
+
static void ehci_free_queue(EHCIQueue *q)
{
EHCIQueueHead *head = q->async ? &q->ehci->aqueues : &q->ehci->pqueues;
- EHCIPacket *p;
trace_usb_ehci_queue_action(q, "free");
- while ((p = QTAILQ_FIRST(&q->packets)) != NULL) {
- ehci_free_packet(p);
- }
+ ehci_cancel_queue(q);
QTAILQ_REMOVE(head, q, next);
g_free(q);
}
@@ -1194,6 +1206,15 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
val &= ~USBCMD_FLS;
}
+ if (val & USBCMD_IAAD) {
+ /*
+ * Process IAAD immediately, otherwise the Linux IAAD watchdog may
+ * trigger and re-use a qh without us seeing the unlink.
+ */
+ s->async_stepdown = 0;
+ qemu_bh_schedule(s->async_bh);
+ }
+
if (((USBCMD_RUNSTOP | USBCMD_PSE | USBCMD_ASE) & val) !=
((USBCMD_RUNSTOP | USBCMD_PSE | USBCMD_ASE) & s->usbcmd)) {
if (s->pstate == EST_INACTIVE) {
@@ -1530,7 +1551,7 @@ static int ehci_execute(EHCIPacket *p, const char *action)
endp = get_field(p->queue->qh.epchar, QH_EPCHAR_EP);
ep = usb_ep_get(p->queue->dev, p->pid, endp);
- usb_packet_setup(&p->packet, p->pid, ep);
+ usb_packet_setup(&p->packet, p->pid, ep, p->qtdaddr);
usb_packet_map(&p->packet, &p->sgl);
trace_usb_ehci_packet_action(p->queue, p, action);
@@ -1552,7 +1573,8 @@ static int ehci_execute(EHCIPacket *p, const char *action)
*/
static int ehci_process_itd(EHCIState *ehci,
- EHCIitd *itd)
+ EHCIitd *itd,
+ uint32_t addr)
{
USBDevice *dev;
USBEndpoint *ep;
@@ -1597,8 +1619,8 @@ static int ehci_process_itd(EHCIState *ehci,
dev = ehci_find_device(ehci, devaddr);
ep = usb_ep_get(dev, pid, endp);
- if (ep->type == USB_ENDPOINT_XFER_ISOC) {
- usb_packet_setup(&ehci->ipacket, pid, ep);
+ if (ep && ep->type == USB_ENDPOINT_XFER_ISOC) {
+ usb_packet_setup(&ehci->ipacket, pid, ep, addr);
usb_packet_map(&ehci->ipacket, &ehci->isgl);
ret = usb_handle_packet(dev, &ehci->ipacket);
assert(ret != USB_RET_ASYNC);
@@ -1786,9 +1808,7 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
if (q->dev != NULL && q->dev->addr != devaddr) {
if (!QTAILQ_EMPTY(&q->packets)) {
/* should not happen (guest bug) */
- while ((p = QTAILQ_FIRST(&q->packets)) != NULL) {
- ehci_free_packet(p);
- }
+ ehci_cancel_queue(q);
}
q->dev = NULL;
}
@@ -1796,11 +1816,6 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
q->dev = ehci_find_device(q->ehci, devaddr);
}
- if (p && p->async == EHCI_ASYNC_INFLIGHT) {
- /* I/O still in progress -- skip queue */
- ehci_set_state(ehci, async, EST_HORIZONTALQH);
- goto out;
- }
if (p && p->async == EHCI_ASYNC_FINISHED) {
/* I/O finished -- continue processing queue */
trace_usb_ehci_packet_action(p->queue, p, "complete");
@@ -1862,7 +1877,7 @@ static int ehci_state_fetchitd(EHCIState *ehci, int async)
sizeof(EHCIitd) >> 2);
ehci_trace_itd(ehci, entry, &itd);
- if (ehci_process_itd(ehci, &itd) != 0) {
+ if (ehci_process_itd(ehci, &itd, entry) != 0) {
return -1;
}
@@ -1949,29 +1964,50 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), &qtd);
p = QTAILQ_FIRST(&q->packets);
- while (p != NULL && p->qtdaddr != q->qtdaddr) {
- /* should not happen (guest bug) */
- ehci_free_packet(p);
- p = QTAILQ_FIRST(&q->packets);
- }
if (p != NULL) {
- ehci_qh_do_overlay(q);
- ehci_flush_qh(q);
- if (p->async == EHCI_ASYNC_INFLIGHT) {
- ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
+ if (p->qtdaddr != q->qtdaddr ||
+ (!NLPTR_TBIT(p->qtd.next) && (p->qtd.next != qtd.next)) ||
+ (!NLPTR_TBIT(p->qtd.altnext) && (p->qtd.altnext != qtd.altnext)) ||
+ p->qtd.bufptr[0] != qtd.bufptr[0]) {
+ /* guest bug: guest updated active QH or qTD underneath us */
+ ehci_cancel_queue(q);
+ p = NULL;
} else {
+ p->qtd = qtd;
+ ehci_qh_do_overlay(q);
+ }
+ }
+
+ if (!(qtd.token & QTD_TOKEN_ACTIVE)) {
+ if (p != NULL) {
+ /* transfer canceled by guest (clear active) */
+ ehci_cancel_queue(q);
+ p = NULL;
+ }
+ ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
+ again = 1;
+ } else if (p != NULL) {
+ switch (p->async) {
+ case EHCI_ASYNC_NONE:
+ /* Previously nacked packet (likely interrupt ep) */
+ ehci_set_state(q->ehci, q->async, EST_EXECUTE);
+ break;
+ case EHCI_ASYNC_INFLIGHT:
+ /* Unfinyshed async handled packet, go horizontal */
+ ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
+ break;
+ case EHCI_ASYNC_FINISHED:
+ /* Should never happen, as this case is caught by fetchqh */
ehci_set_state(q->ehci, q->async, EST_EXECUTING);
+ break;
}
again = 1;
- } else if (qtd.token & QTD_TOKEN_ACTIVE) {
+ } else {
p = ehci_alloc_packet(q);
p->qtdaddr = q->qtdaddr;
p->qtd = qtd;
ehci_set_state(q->ehci, q->async, EST_EXECUTE);
again = 1;
- } else {
- ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
- again = 1;
}
return again;
@@ -2075,19 +2111,11 @@ out:
static int ehci_state_executing(EHCIQueue *q)
{
EHCIPacket *p = QTAILQ_FIRST(&q->packets);
- int again = 0;
assert(p != NULL);
assert(p->qtdaddr == q->qtdaddr);
ehci_execute_complete(q);
- if (p->usb_status == USB_RET_ASYNC) {
- goto out;
- }
- if (p->usb_status == USB_RET_PROCERR) {
- again = -1;
- goto out;
- }
// 4.10.3
if (!q->async) {
@@ -2105,11 +2133,8 @@ static int ehci_state_executing(EHCIQueue *q)
ehci_set_state(q->ehci, q->async, EST_WRITEBACK);
}
- again = 1;
-
-out:
ehci_flush_qh(q);
- return again;
+ return 1;
}
@@ -2138,6 +2163,19 @@ static int ehci_state_writeback(EHCIQueue *q)
* bit is clear.
*/
if (q->qh.token & QTD_TOKEN_HALT) {
+ /*
+ * We should not do any further processing on a halted queue!
+ * This is esp. important for bulk endpoints with pipelining enabled
+ * (redirection to a real USB device), where we must cancel all the
+ * transfers after this one so that:
+ * 1) If they've completed already, they are not processed further
+ * causing more stalls, originating from the same failed transfer
+ * 2) If still in flight, they are cancelled before the guest does
+ * a clear stall, otherwise the guest and device can loose sync!
+ */
+ while ((p = QTAILQ_FIRST(&q->packets)) != NULL) {
+ ehci_free_packet(p);
+ }
ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
again = 1;
} else {
diff --git a/hw/usb/hcd-musb.c b/hw/usb/hcd-musb.c
index fa9385ee4..0bb5c7b19 100644
--- a/hw/usb/hcd-musb.c
+++ b/hw/usb/hcd-musb.c
@@ -626,7 +626,8 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep,
/* A wild guess on the FADDR semantics... */
dev = usb_find_device(&s->port, ep->faddr[idx]);
uep = usb_ep_get(dev, pid, ep->type[idx] & 0xf);
- usb_packet_setup(&ep->packey[dir].p, pid, uep);
+ usb_packet_setup(&ep->packey[dir].p, pid, uep,
+ (dev->addr << 16) | (uep->nr << 8) | pid);
usb_packet_addbuf(&ep->packey[dir].p, ep->buf[idx], len);
ep->packey[dir].ep = ep;
ep->packey[dir].dir = dir;
diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c
index 844e7ed16..c36184ae4 100644
--- a/hw/usb/hcd-ohci.c
+++ b/hw/usb/hcd-ohci.c
@@ -812,7 +812,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
} else {
dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN));
- usb_packet_setup(&ohci->usb_packet, pid, ep);
+ usb_packet_setup(&ohci->usb_packet, pid, ep, addr);
usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len);
ret = usb_handle_packet(dev, &ohci->usb_packet);
if (ret == USB_RET_ASYNC) {
@@ -1011,7 +1011,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
}
dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN));
- usb_packet_setup(&ohci->usb_packet, pid, ep);
+ usb_packet_setup(&ohci->usb_packet, pid, ep, addr);
usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, pktlen);
ret = usb_handle_packet(dev, &ohci->usb_packet);
#ifdef DEBUG_PACKET
diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c
index 1ace2a41d..b0db92145 100644
--- a/hw/usb/hcd-uhci.c
+++ b/hw/usb/hcd-uhci.c
@@ -748,6 +748,22 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_
return TD_RESULT_COMPLETE;
out:
+ /*
+ * We should not do any further processing on a queue with errors!
+ * This is esp. important for bulk endpoints with pipelining enabled
+ * (redirection to a real USB device), where we must cancel all the
+ * transfers after this one so that:
+ * 1) If they've completed already, they are not processed further
+ * causing more stalls, originating from the same failed transfer
+ * 2) If still in flight, they are cancelled before the guest does
+ * a clear stall, otherwise the guest and device can loose sync!
+ */
+ while (!QTAILQ_EMPTY(&async->queue->asyncs)) {
+ UHCIAsync *as = QTAILQ_FIRST(&async->queue->asyncs);
+ uhci_async_unlink(as);
+ uhci_async_cancel(as);
+ }
+
switch(ret) {
case USB_RET_STALL:
td->ctrl |= TD_CTRL_STALL;
@@ -843,14 +859,14 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td,
* for initial isochronous requests
*/
async->queue->valid = 32;
- async->isoc = td->ctrl & TD_CTRL_IOS;
+ async->isoc = td->ctrl & TD_CTRL_IOS;
max_len = ((td->token >> 21) + 1) & 0x7ff;
pid = td->token & 0xff;
dev = uhci_find_device(s, (td->token >> 8) & 0x7f);
ep = usb_ep_get(dev, pid, (td->token >> 15) & 0xf);
- usb_packet_setup(&async->packet, pid, ep);
+ usb_packet_setup(&async->packet, pid, ep, addr);
qemu_sglist_add(&async->sgl, td->buffer, max_len);
usb_packet_map(&async->packet, &async->sgl);
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index 6c2ff024e..3eb27fadb 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -1392,7 +1392,7 @@ static int xhci_setup_packet(XHCITransfer *xfer, USBDevice *dev)
dir = xfer->in_xfer ? USB_TOKEN_IN : USB_TOKEN_OUT;
ep = usb_ep_get(dev, dir, xfer->epid >> 1);
- usb_packet_setup(&xfer->packet, dir, ep);
+ usb_packet_setup(&xfer->packet, dir, ep, xfer->trbs[0].addr);
usb_packet_addbuf(&xfer->packet, xfer->data, xfer->data_length);
DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n",
xfer->packet.pid, dev->addr, ep->nr);
diff --git a/qemu-timer.c b/qemu-timer.c
index 5aea94e8e..c7a1551a3 100644
--- a/qemu-timer.c
+++ b/qemu-timer.c
@@ -759,11 +759,8 @@ int init_timer_alarm(void)
goto fail;
}
- /* first event is at time 0 */
atexit(quit_timers);
- t->pending = true;
alarm_timer = t;
-
return 0;
fail:
diff --git a/target-xtensa/xtensa-semi.c b/target-xtensa/xtensa-semi.c
index 1c8a19ed5..6d001c2c1 100644
--- a/target-xtensa/xtensa-semi.c
+++ b/target-xtensa/xtensa-semi.c
@@ -218,6 +218,8 @@ void HELPER(simcall)(CPUXtensaState *env)
default:
qemu_log("%s(%d): not implemented\n", __func__, regs[2]);
+ regs[2] = -1;
+ regs[3] = ENOSYS;
break;
}
}
diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030
index f71ab8dd1..55b16f81d 100755
--- a/tests/qemu-iotests/030
+++ b/tests/qemu-iotests/030
@@ -125,6 +125,39 @@ class TestSingleDrive(ImageStreamingTestCase):
result = self.vm.qmp('block-stream', device='nonexistent')
self.assert_qmp(result, 'error/class', 'DeviceNotFound')
+
+class TestSmallerBackingFile(ImageStreamingTestCase):
+ backing_len = 1 * 1024 * 1024 # MB
+ image_len = 2 * backing_len
+
+ def setUp(self):
+ self.create_image(backing_img, self.backing_len)
+ qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img, str(self.image_len))
+ self.vm = iotests.VM().add_drive(test_img)
+ self.vm.launch()
+
+ # If this hangs, then you are missing a fix to complete streaming when the
+ # end of the backing file is reached.
+ def test_stream(self):
+ self.assert_no_active_streams()
+
+ result = self.vm.qmp('block-stream', device='drive0')
+ self.assert_qmp(result, 'return', {})
+
+ completed = False
+ while not completed:
+ for event in self.vm.get_qmp_events(wait=True):
+ if event['event'] == 'BLOCK_JOB_COMPLETED':
+ self.assert_qmp(event, 'data/type', 'stream')
+ self.assert_qmp(event, 'data/device', 'drive0')
+ self.assert_qmp(event, 'data/offset', self.image_len)
+ self.assert_qmp(event, 'data/len', self.image_len)
+ completed = True
+
+ self.assert_no_active_streams()
+ self.vm.shutdown()
+
+
class TestStreamStop(ImageStreamingTestCase):
image_len = 8 * 1024 * 1024 * 1024 # GB
diff --git a/tests/qemu-iotests/030.out b/tests/qemu-iotests/030.out
index 3f8a935a0..2f7d3902f 100644
--- a/tests/qemu-iotests/030.out
+++ b/tests/qemu-iotests/030.out
@@ -1,5 +1,5 @@
-......
+.......
----------------------------------------------------------------------
-Ran 6 tests
+Ran 7 tests
OK