Merge remote-tracking branch 'kraxel/usb.83' into staging
# By Gerd Hoffmann (5) and others # Via Gerd Hoffmann * kraxel/usb.83: xhci: add live migration support xhci: add xhci_init_epctx xhci: add xhci_alloc_epctx xhci: add XHCISlot->addressed pci: add VMSTATE_MSIX host-libusb: Correct test for USB packet state Fix usage of USB_DEV_FLAG_IS_HOST flag. Message-id: 1370253951-12323-1-git-send-email-kraxel@redhat.com Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
commit
40459a0312
|
@ -569,3 +569,36 @@ void msix_unset_vector_notifiers(PCIDevice *dev)
|
||||||
dev->msix_vector_release_notifier = NULL;
|
dev->msix_vector_release_notifier = NULL;
|
||||||
dev->msix_vector_poll_notifier = NULL;
|
dev->msix_vector_poll_notifier = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void put_msix_state(QEMUFile *f, void *pv, size_t size)
|
||||||
|
{
|
||||||
|
msix_save(pv, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_msix_state(QEMUFile *f, void *pv, size_t size)
|
||||||
|
{
|
||||||
|
msix_load(pv, f);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static VMStateInfo vmstate_info_msix = {
|
||||||
|
.name = "msix state",
|
||||||
|
.get = get_msix_state,
|
||||||
|
.put = put_msix_state,
|
||||||
|
};
|
||||||
|
|
||||||
|
const VMStateDescription vmstate_msix = {
|
||||||
|
.name = "msix",
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
{
|
||||||
|
.name = "msix",
|
||||||
|
.version_id = 0,
|
||||||
|
.field_exists = NULL,
|
||||||
|
.size = 0, /* ouch */
|
||||||
|
.info = &vmstate_info_msix,
|
||||||
|
.flags = VMS_SINGLE,
|
||||||
|
.offset = 0,
|
||||||
|
},
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -410,7 +410,7 @@ void usb_handle_packet(USBDevice *dev, USBPacket *p)
|
||||||
assert(p->ep->type != USB_ENDPOINT_XFER_ISOC);
|
assert(p->ep->type != USB_ENDPOINT_XFER_ISOC);
|
||||||
/* using async for interrupt packets breaks migration */
|
/* using async for interrupt packets breaks migration */
|
||||||
assert(p->ep->type != USB_ENDPOINT_XFER_INT ||
|
assert(p->ep->type != USB_ENDPOINT_XFER_INT ||
|
||||||
(dev->flags & USB_DEV_FLAG_IS_HOST));
|
(dev->flags & (1 << USB_DEV_FLAG_IS_HOST)));
|
||||||
usb_packet_set_state(p, USB_PACKET_ASYNC);
|
usb_packet_set_state(p, USB_PACKET_ASYNC);
|
||||||
QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
|
QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
|
||||||
} else if (p->status == USB_RET_ADD_TO_QUEUE) {
|
} else if (p->status == USB_RET_ADD_TO_QUEUE) {
|
||||||
|
|
|
@ -405,6 +405,7 @@ struct XHCIEPContext {
|
||||||
|
|
||||||
typedef struct XHCISlot {
|
typedef struct XHCISlot {
|
||||||
bool enabled;
|
bool enabled;
|
||||||
|
bool addressed;
|
||||||
dma_addr_t ctx;
|
dma_addr_t ctx;
|
||||||
USBPort *uport;
|
USBPort *uport;
|
||||||
XHCIEPContext * eps[31];
|
XHCIEPContext * eps[31];
|
||||||
|
@ -1197,31 +1198,30 @@ static void xhci_ep_kick_timer(void *opaque)
|
||||||
xhci_kick_ep(epctx->xhci, epctx->slotid, epctx->epid, 0);
|
xhci_kick_ep(epctx->xhci, epctx->slotid, epctx->epid, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid,
|
static XHCIEPContext *xhci_alloc_epctx(XHCIState *xhci,
|
||||||
unsigned int epid, dma_addr_t pctx,
|
unsigned int slotid,
|
||||||
uint32_t *ctx)
|
unsigned int epid)
|
||||||
{
|
{
|
||||||
XHCISlot *slot;
|
|
||||||
XHCIEPContext *epctx;
|
XHCIEPContext *epctx;
|
||||||
dma_addr_t dequeue;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
trace_usb_xhci_ep_enable(slotid, epid);
|
epctx = g_new0(XHCIEPContext, 1);
|
||||||
assert(slotid >= 1 && slotid <= xhci->numslots);
|
|
||||||
assert(epid >= 1 && epid <= 31);
|
|
||||||
|
|
||||||
slot = &xhci->slots[slotid-1];
|
|
||||||
if (slot->eps[epid-1]) {
|
|
||||||
xhci_disable_ep(xhci, slotid, epid);
|
|
||||||
}
|
|
||||||
|
|
||||||
epctx = g_malloc(sizeof(XHCIEPContext));
|
|
||||||
memset(epctx, 0, sizeof(XHCIEPContext));
|
|
||||||
epctx->xhci = xhci;
|
epctx->xhci = xhci;
|
||||||
epctx->slotid = slotid;
|
epctx->slotid = slotid;
|
||||||
epctx->epid = epid;
|
epctx->epid = epid;
|
||||||
|
|
||||||
slot->eps[epid-1] = epctx;
|
for (i = 0; i < ARRAY_SIZE(epctx->transfers); i++) {
|
||||||
|
usb_packet_init(&epctx->transfers[i].packet);
|
||||||
|
}
|
||||||
|
epctx->kick_timer = qemu_new_timer_ns(vm_clock, xhci_ep_kick_timer, epctx);
|
||||||
|
|
||||||
|
return epctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xhci_init_epctx(XHCIEPContext *epctx,
|
||||||
|
dma_addr_t pctx, uint32_t *ctx)
|
||||||
|
{
|
||||||
|
dma_addr_t dequeue;
|
||||||
|
|
||||||
dequeue = xhci_addr64(ctx[2] & ~0xf, ctx[3]);
|
dequeue = xhci_addr64(ctx[2] & ~0xf, ctx[3]);
|
||||||
|
|
||||||
|
@ -1237,16 +1237,34 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid,
|
||||||
if (epctx->max_pstreams) {
|
if (epctx->max_pstreams) {
|
||||||
xhci_alloc_streams(epctx, dequeue);
|
xhci_alloc_streams(epctx, dequeue);
|
||||||
} else {
|
} else {
|
||||||
xhci_ring_init(xhci, &epctx->ring, dequeue);
|
xhci_ring_init(epctx->xhci, &epctx->ring, dequeue);
|
||||||
epctx->ring.ccs = ctx[2] & 1;
|
epctx->ring.ccs = ctx[2] & 1;
|
||||||
}
|
}
|
||||||
for (i = 0; i < ARRAY_SIZE(epctx->transfers); i++) {
|
|
||||||
usb_packet_init(&epctx->transfers[i].packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
epctx->interval = 1 << (ctx[0] >> 16) & 0xff;
|
epctx->interval = 1 << (ctx[0] >> 16) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid,
|
||||||
|
unsigned int epid, dma_addr_t pctx,
|
||||||
|
uint32_t *ctx)
|
||||||
|
{
|
||||||
|
XHCISlot *slot;
|
||||||
|
XHCIEPContext *epctx;
|
||||||
|
|
||||||
|
trace_usb_xhci_ep_enable(slotid, epid);
|
||||||
|
assert(slotid >= 1 && slotid <= xhci->numslots);
|
||||||
|
assert(epid >= 1 && epid <= 31);
|
||||||
|
|
||||||
|
slot = &xhci->slots[slotid-1];
|
||||||
|
if (slot->eps[epid-1]) {
|
||||||
|
xhci_disable_ep(xhci, slotid, epid);
|
||||||
|
}
|
||||||
|
|
||||||
|
epctx = xhci_alloc_epctx(xhci, slotid, epid);
|
||||||
|
slot->eps[epid-1] = epctx;
|
||||||
|
xhci_init_epctx(epctx, pctx, ctx);
|
||||||
|
|
||||||
epctx->mfindex_last = 0;
|
epctx->mfindex_last = 0;
|
||||||
epctx->kick_timer = qemu_new_timer_ns(vm_clock, xhci_ep_kick_timer, epctx);
|
|
||||||
|
|
||||||
epctx->state = EP_RUNNING;
|
epctx->state = EP_RUNNING;
|
||||||
ctx[0] &= ~EP_STATE_MASK;
|
ctx[0] &= ~EP_STATE_MASK;
|
||||||
|
@ -2041,6 +2059,7 @@ static TRBCCode xhci_disable_slot(XHCIState *xhci, unsigned int slotid)
|
||||||
}
|
}
|
||||||
|
|
||||||
xhci->slots[slotid-1].enabled = 0;
|
xhci->slots[slotid-1].enabled = 0;
|
||||||
|
xhci->slots[slotid-1].addressed = 0;
|
||||||
return CC_SUCCESS;
|
return CC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2167,6 +2186,7 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
|
||||||
xhci_dma_write_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx));
|
xhci_dma_write_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx));
|
||||||
xhci_dma_write_u32s(xhci, octx+32, ep0_ctx, sizeof(ep0_ctx));
|
xhci_dma_write_u32s(xhci, octx+32, ep0_ctx, sizeof(ep0_ctx));
|
||||||
|
|
||||||
|
xhci->slots[slotid-1].addressed = 1;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3366,9 +3386,171 @@ static int usb_xhci_initfn(struct PCIDevice *dev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int usb_xhci_post_load(void *opaque, int version_id)
|
||||||
|
{
|
||||||
|
XHCIState *xhci = opaque;
|
||||||
|
XHCISlot *slot;
|
||||||
|
XHCIEPContext *epctx;
|
||||||
|
dma_addr_t dcbaap, pctx;
|
||||||
|
uint32_t slot_ctx[4];
|
||||||
|
uint32_t ep_ctx[5];
|
||||||
|
int slotid, epid, state, intr;
|
||||||
|
|
||||||
|
dcbaap = xhci_addr64(xhci->dcbaap_low, xhci->dcbaap_high);
|
||||||
|
|
||||||
|
for (slotid = 1; slotid <= xhci->numslots; slotid++) {
|
||||||
|
slot = &xhci->slots[slotid-1];
|
||||||
|
if (!slot->addressed) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
slot->ctx =
|
||||||
|
xhci_mask64(ldq_le_pci_dma(&xhci->pci_dev, dcbaap + 8*slotid));
|
||||||
|
xhci_dma_read_u32s(xhci, slot->ctx, slot_ctx, sizeof(slot_ctx));
|
||||||
|
slot->uport = xhci_lookup_uport(xhci, slot_ctx);
|
||||||
|
assert(slot->uport && slot->uport->dev);
|
||||||
|
|
||||||
|
for (epid = 1; epid <= 32; epid++) {
|
||||||
|
pctx = slot->ctx + 32 * epid;
|
||||||
|
xhci_dma_read_u32s(xhci, pctx, ep_ctx, sizeof(ep_ctx));
|
||||||
|
state = ep_ctx[0] & EP_STATE_MASK;
|
||||||
|
if (state == EP_DISABLED) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
epctx = xhci_alloc_epctx(xhci, slotid, epid);
|
||||||
|
slot->eps[epid-1] = epctx;
|
||||||
|
xhci_init_epctx(epctx, pctx, ep_ctx);
|
||||||
|
epctx->state = state;
|
||||||
|
if (state == EP_RUNNING) {
|
||||||
|
/* kick endpoint after vmload is finished */
|
||||||
|
qemu_mod_timer(epctx->kick_timer, qemu_get_clock_ns(vm_clock));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (intr = 0; intr < xhci->numintrs; intr++) {
|
||||||
|
if (xhci->intr[intr].msix_used) {
|
||||||
|
msix_vector_use(&xhci->pci_dev, intr);
|
||||||
|
} else {
|
||||||
|
msix_vector_unuse(&xhci->pci_dev, intr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_xhci_ring = {
|
||||||
|
.name = "xhci-ring",
|
||||||
|
.version_id = 1,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_UINT64(dequeue, XHCIRing),
|
||||||
|
VMSTATE_BOOL(ccs, XHCIRing),
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_xhci_port = {
|
||||||
|
.name = "xhci-port",
|
||||||
|
.version_id = 1,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_UINT32(portsc, XHCIPort),
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_xhci_slot = {
|
||||||
|
.name = "xhci-slot",
|
||||||
|
.version_id = 1,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_BOOL(enabled, XHCISlot),
|
||||||
|
VMSTATE_BOOL(addressed, XHCISlot),
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_xhci_event = {
|
||||||
|
.name = "xhci-event",
|
||||||
|
.version_id = 1,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_UINT32(type, XHCIEvent),
|
||||||
|
VMSTATE_UINT32(ccode, XHCIEvent),
|
||||||
|
VMSTATE_UINT64(ptr, XHCIEvent),
|
||||||
|
VMSTATE_UINT32(length, XHCIEvent),
|
||||||
|
VMSTATE_UINT32(flags, XHCIEvent),
|
||||||
|
VMSTATE_UINT8(slotid, XHCIEvent),
|
||||||
|
VMSTATE_UINT8(epid, XHCIEvent),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool xhci_er_full(void *opaque, int version_id)
|
||||||
|
{
|
||||||
|
struct XHCIInterrupter *intr = opaque;
|
||||||
|
return intr->er_full;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_xhci_intr = {
|
||||||
|
.name = "xhci-intr",
|
||||||
|
.version_id = 1,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
/* registers */
|
||||||
|
VMSTATE_UINT32(iman, XHCIInterrupter),
|
||||||
|
VMSTATE_UINT32(imod, XHCIInterrupter),
|
||||||
|
VMSTATE_UINT32(erstsz, XHCIInterrupter),
|
||||||
|
VMSTATE_UINT32(erstba_low, XHCIInterrupter),
|
||||||
|
VMSTATE_UINT32(erstba_high, XHCIInterrupter),
|
||||||
|
VMSTATE_UINT32(erdp_low, XHCIInterrupter),
|
||||||
|
VMSTATE_UINT32(erdp_high, XHCIInterrupter),
|
||||||
|
|
||||||
|
/* state */
|
||||||
|
VMSTATE_BOOL(msix_used, XHCIInterrupter),
|
||||||
|
VMSTATE_BOOL(er_pcs, XHCIInterrupter),
|
||||||
|
VMSTATE_UINT64(er_start, XHCIInterrupter),
|
||||||
|
VMSTATE_UINT32(er_size, XHCIInterrupter),
|
||||||
|
VMSTATE_UINT32(er_ep_idx, XHCIInterrupter),
|
||||||
|
|
||||||
|
/* event queue (used if ring is full) */
|
||||||
|
VMSTATE_BOOL(er_full, XHCIInterrupter),
|
||||||
|
VMSTATE_UINT32_TEST(ev_buffer_put, XHCIInterrupter, xhci_er_full),
|
||||||
|
VMSTATE_UINT32_TEST(ev_buffer_get, XHCIInterrupter, xhci_er_full),
|
||||||
|
VMSTATE_STRUCT_ARRAY_TEST(ev_buffer, XHCIInterrupter, EV_QUEUE,
|
||||||
|
xhci_er_full, 1,
|
||||||
|
vmstate_xhci_event, XHCIEvent),
|
||||||
|
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static const VMStateDescription vmstate_xhci = {
|
static const VMStateDescription vmstate_xhci = {
|
||||||
.name = "xhci",
|
.name = "xhci",
|
||||||
.unmigratable = 1,
|
.version_id = 1,
|
||||||
|
.post_load = usb_xhci_post_load,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_PCIE_DEVICE(pci_dev, XHCIState),
|
||||||
|
VMSTATE_MSIX(pci_dev, XHCIState),
|
||||||
|
|
||||||
|
VMSTATE_STRUCT_VARRAY_UINT32(ports, XHCIState, numports, 1,
|
||||||
|
vmstate_xhci_port, XHCIPort),
|
||||||
|
VMSTATE_STRUCT_VARRAY_UINT32(slots, XHCIState, numslots, 1,
|
||||||
|
vmstate_xhci_slot, XHCISlot),
|
||||||
|
VMSTATE_STRUCT_VARRAY_UINT32(intr, XHCIState, numintrs, 1,
|
||||||
|
vmstate_xhci_intr, XHCIInterrupter),
|
||||||
|
|
||||||
|
/* Operational Registers */
|
||||||
|
VMSTATE_UINT32(usbcmd, XHCIState),
|
||||||
|
VMSTATE_UINT32(usbsts, XHCIState),
|
||||||
|
VMSTATE_UINT32(dnctrl, XHCIState),
|
||||||
|
VMSTATE_UINT32(crcr_low, XHCIState),
|
||||||
|
VMSTATE_UINT32(crcr_high, XHCIState),
|
||||||
|
VMSTATE_UINT32(dcbaap_low, XHCIState),
|
||||||
|
VMSTATE_UINT32(dcbaap_high, XHCIState),
|
||||||
|
VMSTATE_UINT32(config, XHCIState),
|
||||||
|
|
||||||
|
/* Runtime Registers & state */
|
||||||
|
VMSTATE_INT64(mfindex_start, XHCIState),
|
||||||
|
VMSTATE_TIMER(mfwrap_timer, XHCIState),
|
||||||
|
VMSTATE_STRUCT(cmd_ring, XHCIState, 1, vmstate_xhci_ring, XHCIRing),
|
||||||
|
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static Property xhci_properties[] = {
|
static Property xhci_properties[] = {
|
||||||
|
|
|
@ -385,7 +385,7 @@ out:
|
||||||
static void usb_host_req_abort(USBHostRequest *r)
|
static void usb_host_req_abort(USBHostRequest *r)
|
||||||
{
|
{
|
||||||
USBHostDevice *s = r->host;
|
USBHostDevice *s = r->host;
|
||||||
bool inflight = (r->p && r->p->state == USB_RET_ASYNC);
|
bool inflight = (r->p && r->p->state == USB_PACKET_ASYNC);
|
||||||
|
|
||||||
if (inflight) {
|
if (inflight) {
|
||||||
r->p->status = USB_RET_NODEV;
|
r->p->status = USB_RET_NODEV;
|
||||||
|
|
|
@ -43,4 +43,15 @@ int msix_set_vector_notifiers(PCIDevice *dev,
|
||||||
MSIVectorReleaseNotifier release_notifier,
|
MSIVectorReleaseNotifier release_notifier,
|
||||||
MSIVectorPollNotifier poll_notifier);
|
MSIVectorPollNotifier poll_notifier);
|
||||||
void msix_unset_vector_notifiers(PCIDevice *dev);
|
void msix_unset_vector_notifiers(PCIDevice *dev);
|
||||||
|
|
||||||
|
extern const VMStateDescription vmstate_msix;
|
||||||
|
|
||||||
|
#define VMSTATE_MSIX(_field, _state) { \
|
||||||
|
.name = (stringify(_field)), \
|
||||||
|
.size = sizeof(PCIDevice), \
|
||||||
|
.vmsd = &vmstate_msix, \
|
||||||
|
.flags = VMS_STRUCT, \
|
||||||
|
.offset = vmstate_offset_value(_state, _field, PCIDevice), \
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue