Nicholas Piggin <npig...@gmail.com> writes:

> Add tests which init the host controller registers to the point
> where command and event rings, irqs are operational. Enumerate
> ports and set up an attached device context that enables device
> transfer ring to be set up and tested.
>
> This test does a bunch of things at once and is yet well
> librified, but it allows testing basic mechanisms and gives a
> starting point for further work.

Please give it a pass through checkpatch when you get the chance.

>
> Signed-off-by: Nicholas Piggin <npig...@gmail.com>
> ---
>  tests/qtest/usb-hcd-xhci-test.h | 232 +++++++++++++++
>  tests/qtest/usb-hcd-xhci-test.c | 506 +++++++++++++++++++++++++++++++-
>  2 files changed, 732 insertions(+), 6 deletions(-)
>  create mode 100644 tests/qtest/usb-hcd-xhci-test.h
>

<snip>

> +static void pci_xhci_stress_rings(void)
> +{
> +    XHCIQState *s;
> +    uint32_t value;
> +    uint64_t input_context;
> +    XHCIEvRingSeg ev_seg;
> +    XHCITRB trb;
> +    uint32_t hcsparams1;
> +    uint32_t slotid;
> +    void *mem;
> +    int i;
> +
> +    mem = g_malloc(0x1000);

This is leaking.

> +    memset(mem, 0, 0x1000);
> +
> +    s = xhci_boot("-M q35 "
> +            "-device nec-usb-xhci,id=xhci,bus=pcie.0,addr=1d.0 "
> +            "-device usb-storage,bus=xhci.0,drive=drive0 "
> +            "-drive id=drive0,if=none,file=null-co://,"
> +                "file.read-zeroes=on,format=raw "
> +            );
> +//            "-d trace:*xhci*,trace:*usb*,trace:*msi*");
> +
> +    hcsparams1 = xhci_cap_readl(s, 0x4); /* HCSPARAMS1 */
> +    s->maxports = (hcsparams1 >> 24) & 0xff;
> +    s->maxintrs = (hcsparams1 >> 8) & 0x3ff;
> +    s->maxslots = hcsparams1 & 0xff;
> +
> +    s->dboff = xhci_cap_readl(s, 0x14); /* DBOFF */
> +    s->rtoff = xhci_cap_readl(s, 0x18); /* RTOFF */
> +
> +    s->dc_base_array = xhci_guest_zalloc(s, 0x800);
> +    s->command_ring = xhci_guest_zalloc(s, 0x1000);
> +    s->event_ring = xhci_guest_zalloc(s, 0x1000);
> +    s->event_ring_seg = xhci_guest_zalloc(s, 0x100);
> +
> +    /* Arbitrary small sizes so we can make them wrap */
> +    s->cr_trb_entries = 0x20;
> +    s->cr_trb_c = 1;
> +    s->er_trb_entries = 0x10;
> +    s->er_trb_c = 1;
> +
> +    ev_seg.addr_low = cpu_to_le32(s->event_ring & 0xffffffff);
> +    ev_seg.addr_high = cpu_to_le32(s->event_ring >> 32);
> +    ev_seg.size = cpu_to_le32(0x10);
> +    ev_seg.rsvd = 0;
> +    qtest_memwrite(s->parent->qts, s->event_ring_seg, &ev_seg, 
> sizeof(ev_seg));
> +
> +    xhci_op_writel(s, 0x0, USBCMD_HCRST); /* USBCMD */
> +    do {
> +        value = xhci_op_readl(s, 0x4); /* USBSTS */
> +    } while (value & (1 << 11)); /* CNR */
> +
> +    xhci_op_writel(s, 0x38, s->maxslots); /* CONFIG */
> +
> +    /* DCBAAP */
> +    xhci_op_writel(s, 0x30, s->dc_base_array & 0xffffffff);
> +    xhci_op_writel(s, 0x34, s->dc_base_array >> 32);
> +
> +    /* CRCR */
> +    xhci_op_writel(s, 0x18, (s->command_ring & 0xffffffff) | s->cr_trb_c);
> +    xhci_op_writel(s, 0x1c, s->command_ring >> 32);
> +
> +    xhci_rt_writel(s, 0x28, 1); /* ERSTSZ */
> +
> +    /* ERSTBA */
> +    xhci_rt_writel(s, 0x30, s->event_ring_seg & 0xffffffff);
> +    xhci_rt_writel(s, 0x34, s->event_ring_seg >> 32);
> +
> +    /* ERDP */
> +    xhci_rt_writel(s, 0x38, s->event_ring & 0xffffffff);
> +    xhci_rt_writel(s, 0x3c, s->event_ring >> 32);
> +
> +    qpci_msix_enable(s->dev);
> +    xhci_op_writel(s, 0x0, USBCMD_RS | USBCMD_INTE); /* RUN + INTE */
> +
> +    /* Enable interrupts on ER IMAN */
> +    xhci_rt_writel(s, 0x20, IMAN_IE);
> +
> +    assert(!qpci_msix_pending(s->dev, 0));
> +
> +    /* Wrap the command and event rings with no-ops a few times */
> +    for (i = 0; i < 100; i++) {
> +        /* Issue a command ring no-op */
> +        memset(&trb, 0, sizeof(trb));
> +        trb.control |= CR_NOOP << TRB_TYPE_SHIFT;
> +        trb.control |= TRB_TR_IOC;
> +        submit_cr_trb(s, &trb);
> +        wait_event_trb(s, &trb);
> +    }
> +
> +    /* Query ports */
> +    for (i = 0; i < s->maxports; i++) {
> +        value = xhci_port_readl(s, i, 0); /* PORTSC */
> +
> +        /* Only first port should be attached and enabled */
> +        if (i == 0) {
> +            g_assert(value & PORTSC_CCS);
> +            g_assert(value & PORTSC_PED);
> +         /* Port Speed must be identified */
> +         g_assert(((value >> PORTSC_SPEED_SHIFT) & PORTSC_SPEED_MASK) != 0);
> +        } else {
> +            g_assert(!(value & PORTSC_CCS));
> +            g_assert(!(value & PORTSC_PED));
> +            g_assert(((value >> PORTSC_PLS_SHIFT) & PORTSC_PLS_MASK) == 5);
> +        }
> +    }
> +
> +    /* Issue a command ring enable slot */
> +    memset(&trb, 0, sizeof(trb));
> +    trb.control |= CR_ENABLE_SLOT << TRB_TYPE_SHIFT;
> +    trb.control |= TRB_TR_IOC;
> +    submit_cr_trb(s, &trb);
> +    wait_event_trb(s, &trb);
> +    slotid = (trb.control >> TRB_CR_SLOTID_SHIFT) & 0xff;
> +
> +    s->slots[slotid].transfer_ring = xhci_guest_zalloc(s, 0x1000);
> +    s->slots[slotid].tr_trb_entries = 0x10;
> +    s->slots[slotid].tr_trb_c = 1;
> +
> +    /* 32-byte input context size, should check HCCPARAMS1 for 64-byte size 
> */
> +    input_context = xhci_guest_zalloc(s, 0x420);
> +
> +    /* Set input control context */
> +    ((uint32_t *)mem)[1] = cpu_to_le32(0x3); /* Add device contexts 0 and 1 
> */
> +    ((uint32_t *)mem)[8] = cpu_to_le32(1 << 27); /* 1 context entry */
> +    ((uint32_t *)mem)[9] = cpu_to_le32(1 << 16); /* 1 port number */
> +
> +    /* Set endpoint 0 context */
> +    ((uint32_t *)mem)[16] = 0;
> +    ((uint32_t *)mem)[17] = cpu_to_le32((ET_CONTROL << EP_TYPE_SHIFT) |
> +                                        (0x200 << 16)); /* max packet sz 
> XXX? */
> +    ((uint32_t *)mem)[18] = cpu_to_le32((s->slots[slotid].transfer_ring & 
> 0xffffffff) | 1); /* DCS=1 */
> +    ((uint32_t *)mem)[19] = cpu_to_le32(s->slots[slotid].transfer_ring >> 
> 32);
> +    ((uint32_t *)mem)[20] = cpu_to_le32(0x200); /* Average TRB length */
> +    qtest_memwrite(s->parent->qts, input_context, mem, 0x420);
> +
> +    s->slots[slotid].device_context = xhci_guest_zalloc(s, 0x400);
> +
> +    ((uint64_t *)mem)[0] = cpu_to_le64(s->slots[slotid].device_context);
> +    qtest_memwrite(s->parent->qts, s->dc_base_array + 8*slotid, mem, 8);
> +
> +    /* Issue a command ring address device */
> +    memset(&trb, 0, sizeof(trb));
> +    trb.parameter = input_context;
> +    trb.control |= CR_ADDRESS_DEVICE << TRB_TYPE_SHIFT;
> +    trb.control |= slotid << TRB_CR_SLOTID_SHIFT;
> +    submit_cr_trb(s, &trb);
> +    wait_event_trb(s, &trb);
> +
> +    /* XXX: Check EP state is running? */
> +
> +    /* Shut it down */
> +    qpci_msix_disable(s->dev);
> +
> +    guest_free(&s->parent->alloc, s->slots[slotid].device_context);
> +    guest_free(&s->parent->alloc, s->slots[slotid].transfer_ring);
> +    guest_free(&s->parent->alloc, input_context);
> +    guest_free(&s->parent->alloc, s->event_ring);
> +    guest_free(&s->parent->alloc, s->event_ring_seg);
> +    guest_free(&s->parent->alloc, s->command_ring);
> +    guest_free(&s->parent->alloc, s->dc_base_array);
> +
> +    xhci_shutdown(s);
> +}
> +
> +/* tests */
>  int main(int argc, char **argv)
>  {
>      int ret;
> +    const char *arch;
>  
>      g_test_init(&argc, &argv, NULL);
>  
> +    /* Check architecture */
> +    arch = qtest_get_arch();
> +    if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) {
> +        g_test_message("Skipping test for non-x86");
> +        return 0;
> +    }
> +
> +    if (!qtest_has_device("nec-usb-xhci")) {
> +        return 0;
> +    }
> +
>      qtest_add_func("/xhci/pci/hotplug", test_xhci_hotplug);
>      if (qtest_has_device("usb-uas")) {
>          qtest_add_func("/xhci/pci/hotplug/usb-uas", test_usb_uas_hotplug);
> @@ -56,11 +549,12 @@ int main(int argc, char **argv)
>      if (qtest_has_device("usb-ccid")) {
>          qtest_add_func("/xhci/pci/hotplug/usb-ccid", test_usb_ccid_hotplug);
>      }
> +    if (qtest_has_device("usb-storage")) {
> +        qtest_add_func("/xhci/pci/xhci-stress-rings", pci_xhci_stress_rings);
> +    }
>  
> -    qtest_start("-device nec-usb-xhci,id=xhci"
> -                " -drive id=drive0,if=none,file=null-co://,"
> -                "file.read-zeroes=on,format=raw");
>      ret = g_test_run();
> +
>      qtest_end();
>  
>      return ret;

Reply via email to