diff options
author | Gerd Hoffmann <kraxel@redhat.com> | 2013-03-21 10:55:35 +0100 |
---|---|---|
committer | Gerd Hoffmann <kraxel@redhat.com> | 2013-06-03 11:38:03 +0200 |
commit | 37352df30fbc38d1de464db8927536d5e36cf52a (patch) | |
tree | f35c996bfa249642cb80231712daaf365c3947cf /hw/usb | |
parent | 003e15a180373048f0c1f4df0bfe303746eb2676 (diff) |
xhci: add live migration support
With all preparing pieces in place we can finally drop in
the vmstate structs and the postload function.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Diffstat (limited to 'hw/usb')
-rw-r--r-- | hw/usb/hcd-xhci.c | 164 |
1 files changed, 163 insertions, 1 deletions
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 9b90067d0c..91633edbc6 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -3386,9 +3386,171 @@ static int usb_xhci_initfn(struct PCIDevice *dev) 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 = { .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[] = { |