qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] USB2.0 disk format failure in windows guest


From: Alan Stern
Subject: Re: [Qemu-devel] USB2.0 disk format failure in windows guest
Date: Tue, 16 Apr 2013 11:20:02 -0400 (EDT)

On Tue, 16 Apr 2013, Gonglei (Arei) wrote:

> > > Yes, this disk was using EHCI, since guest QEMU and Linux kernel both
> > > prints matching EHCI logs, such as transfer types and transfer sizes.
> > > There are many buck-out URBs whose sizes are 31 or 4064 that are not
> > > multiples of 512. Since URB size 31 does occur without guest format
> > > USB 2.0 disk sceneiro, did you mean that buck-out size 4064 should
> > > not occur? /* EHCI spec version 1.0 Section 4.10.6 */
> > 
> > That's right; it should not occur.
> > 
> > It's okay to have an URB size that isn't a multiple of 512 if that URB
> > is the last one in a transfer.  For example, the 31-byte URBs were the
> > only URBs in their transfers, so they were okay.  But the 4064-byte
> > URBs occurred at the start and in the middle of their transfers, so
> > they were wrong.
> > 
> 
> Alan, the code which get the virtual machine USB packet in the Qemu is as 
> follows:
> # /qemu-1.4.0/hw/usb/hcd-ehci.c
> static int ehci_init_transfer(EHCIPacket *p)
> {
>     uint32_t cpage, offset, bytes, plen;
>     dma_addr_t page;
> 
>     cpage  = get_field(p->qtd.token, QTD_TOKEN_CPAGE);
>     bytes  = get_field(p->qtd.token, QTD_TOKEN_TBYTES);
>     offset = p->qtd.bufptr[0] & ~QTD_BUFPTR_MASK;
>     pci_dma_sglist_init(&p->sgl, &p->queue->ehci->dev, 5);
> 
>     while (bytes > 0) {
>         if (cpage > 4) {
>             fprintf(stderr, "cpage out of range (%d)\n", cpage);
>             return USB_RET_PROCERR;
>         }
> 
>         page  = p->qtd.bufptr[cpage] & QTD_BUFPTR_MASK;
>         page += offset;
>         plen  = bytes;
>         if (plen > 4096 - offset) {
>             plen = 4096 - offset;
>             offset = 0;
>             cpage++;
>         }
> 
>         qemu_sglist_add(&p->sgl, page, plen);
>         bytes -= plen;
>     }
>     return 0;
> }

I see.

> After our repeated verification and debug, we found that windows
> virtual machine USB disk format will send a 64Kbits's URB packet
> which was divided into four QTD (Size: 19968,16384,16384,12800):

That's okay.

> The first QTD offset is 32, split into five URB
> 4064/4096/4096/4096/3616 bits ( we don't understand why the offset is
> always 32, but the linux virtual machine is always 0)

I guess Windows uses strange offsets.  Are the pages that make up the
qTD adjacent in the host's virtual memory?

> The second QTD offset 3616, split into five URB
> 480/4096/4096/4096/3616 bits
> Third QTD offset is 3616, split into five URB 480/4096/4096/4096/3616 bits
> Fourth QTD offset is 3616, split into five URB 480/4096/4096/4096/32 bits

Fixing this will require qemu to copy the beginning and ending parts of
these non-aligned qTDs into separate bounce buffers so that the URB
length can be divisible by 512.  For example, with the first qTD above,
qemu could send one URB of length 3584 at offset 32.  Then qemu would
copy the last 480 bytes from that page and the first 32 bytes from the
next page into a bounce buffer, and send a 512-byte URB for that
buffer.  Then qemu would send a 3584-byte URB starting at offset 480 in
the second page, and so on.

Of course, this can be optimized in the case where the pages happen to
be adjacent in the host's memory.  It's okay to let an URB span a page
boundary.  But if the pages aren't adjacent, you will have to use a
bounce buffer.

By the way, this approach has to be used for control and interrupt
transfers as well as bulk transfers.  If a guest's qTD has be to split
up, all the pieces except the last must be a multiple of the
wMaxPacketSize value.

Alan Stern




reply via email to

[Prev in Thread] Current Thread [Next in Thread]