]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
USB: xhci: Fix TRB physical to virtual address translation.
authorSarah Sharp <sarah.a.sharp@linux.intel.com>
Wed, 4 Nov 2009 06:02:24 +0000 (22:02 -0800)
committerGreg Kroah-Hartman <gregkh@suse.de>
Tue, 8 Dec 2009 18:21:30 +0000 (10:21 -0800)
commit 2fa88daa6f299bfb83672c3b525d786ad03b4735 upstream.

The trb_in_td() function in the xHCI driver is supposed to translate a
physical transfer buffer request (TRB) into a virtual pointer to the ring
segment that TRB is in.

Unfortunately, a mistake in this function may cause endless loops as the
driver searches through the linked list of ring segments over and over
again.  Fix a couple bugs that may lead to loops or bad output:

1. Bail out if we get a NULL pointer when translating the segment's
private structure and the starting DMA address of the segment chunk.  If
this happens, we've been handed a starting TRB pointer from a different
ring.

2. Make sure the function works when there's multiple segments in the
ring.  In the while loop to search through the ring segments, use the
current segment variable (cur_seg), rather than the starting segment
variable (start_seg) that is passed in.

3. Stop searching the ring if we've run through all the segments in the
ring.

Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/host/xhci-ring.c

index ff5e6bc2299d560d2a1340151015a5e910c7b955..9874d9a600803485d5b4f40c6e405ce77357e3ff 100644 (file)
@@ -822,9 +822,11 @@ static struct xhci_segment *trb_in_td(
        cur_seg = start_seg;
 
        do {
+               if (start_dma == 0)
+                       return 0;
                /* We may get an event for a Link TRB in the middle of a TD */
                end_seg_dma = xhci_trb_virt_to_dma(cur_seg,
-                               &start_seg->trbs[TRBS_PER_SEGMENT - 1]);
+                               &cur_seg->trbs[TRBS_PER_SEGMENT - 1]);
                /* If the end TRB isn't in this segment, this is set to 0 */
                end_trb_dma = xhci_trb_virt_to_dma(cur_seg, end_trb);
 
@@ -851,8 +853,9 @@ static struct xhci_segment *trb_in_td(
                }
                cur_seg = cur_seg->next;
                start_dma = xhci_trb_virt_to_dma(cur_seg, &cur_seg->trbs[0]);
-       } while (1);
+       } while (cur_seg != start_seg);
 
+       return 0;
 }
 
 /*