lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20130109201506.959312835@linuxfoundation.org>
Date:	Wed,  9 Jan 2013 12:35:04 -0800
From:	Greg Kroah-Hartman <gregkh@...uxfoundation.org>
To:	linux-kernel@...r.kernel.org, stable@...r.kernel.org
Cc:	Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
	alan@...rguk.ukuu.org.uk,
	Sarah Sharp <sarah.a.sharp@...ux.intel.com>,
	Chintan Mehta <chintan.mehta@...ridgetech.com>,
	Shimmer Huang <shimmering.h@...il.com>,
	Bhavik Kothari <bhavik.kothari@...ridgetech.com>
Subject: [ 065/123] xHCI: Fix TD Size calculation on 1.0 hosts.

3.7-stable review patch.  If anyone has any objections, please let me know.

------------------

From: Sarah Sharp <sarah.a.sharp@...ux.intel.com>

commit 4525c0a10dff7ad3669763c28016c7daffc3900e upstream.

The xHCI 1.0 specification made a change to the TD Size field in TRBs.
The value is now the number of packets that remain to be sent in the TD,
not including this TRB.  The TD Size value for the last TRB in a TD must
always be zero.

The xHCI function xhci_v1_0_td_remainder() attempts to calculate this,
but it gets it wrong.  First, it erroneously reuses the old
xhci_td_remainder function, which will right shift the value by 10.  The
xHCI 1.0 spec as of June 2011 says nothing about right shifting by 10.
Second, it does not set the TD size for the last TRB in a TD to zero.

Third, it uses roundup instead of DIV_ROUND_UP.  The total packet count
is supposed to be the total number of bytes in this TD, divided by the
max packet size, rounded up.  DIV_ROUND_UP is the right function to use
in that case.

With the old code, a TD on an endpoint with max packet size 1024 would
be set up like so:
TRB 1, TRB length = 600 bytes, TD size = 0
TRB 1, TRB length = 200 bytes, TD size = 0
TRB 1, TRB length = 100 bytes, TD size = 0

With the new code, the TD would be set up like this:
TRB 1, TRB length = 600 bytes, TD size = 1
TRB 1, TRB length = 200 bytes, TD size = 1
TRB 1, TRB length = 100 bytes, TD size = 0

This commit should be backported to kernels as old as 3.0, that contain
the commit 4da6e6f247a2601ab9f1e63424e4d944ed4124f3 "xhci 1.0: Update TD
size field format."

Signed-off-by: Sarah Sharp <sarah.a.sharp@...ux.intel.com>
Reported-by: Chintan Mehta <chintan.mehta@...ridgetech.com>
Reported-by: Shimmer Huang <shimmering.h@...il.com>
Tested-by: Bhavik Kothari <bhavik.kothari@...ridgetech.com>
Tested-by: Shimmer Huang <shimmering.h@...il.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@...uxfoundation.org>

---
 drivers/usb/host/xhci-ring.c |   32 +++++++++++++++++++-------------
 1 file changed, 19 insertions(+), 13 deletions(-)

--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -3071,11 +3071,11 @@ static u32 xhci_td_remainder(unsigned in
 }
 
 /*
- * For xHCI 1.0 host controllers, TD size is the number of packets remaining in
- * the TD (*not* including this TRB).
+ * For xHCI 1.0 host controllers, TD size is the number of max packet sized
+ * packets remaining in the TD (*not* including this TRB).
  *
  * Total TD packet count = total_packet_count =
- *     roundup(TD size in bytes / wMaxPacketSize)
+ *     DIV_ROUND_UP(TD size in bytes / wMaxPacketSize)
  *
  * Packets transferred up to and including this TRB = packets_transferred =
  *     rounddown(total bytes transferred including this TRB / wMaxPacketSize)
@@ -3083,15 +3083,16 @@ static u32 xhci_td_remainder(unsigned in
  * TD size = total_packet_count - packets_transferred
  *
  * It must fit in bits 21:17, so it can't be bigger than 31.
+ * The last TRB in a TD must have the TD size set to zero.
  */
-
 static u32 xhci_v1_0_td_remainder(int running_total, int trb_buff_len,
-		unsigned int total_packet_count, struct urb *urb)
+		unsigned int total_packet_count, struct urb *urb,
+		unsigned int num_trbs_left)
 {
 	int packets_transferred;
 
 	/* One TRB with a zero-length data packet. */
-	if (running_total == 0 && trb_buff_len == 0)
+	if (num_trbs_left == 0 || (running_total == 0 && trb_buff_len == 0))
 		return 0;
 
 	/* All the TRB queueing functions don't count the current TRB in
@@ -3100,7 +3101,9 @@ static u32 xhci_v1_0_td_remainder(int ru
 	packets_transferred = (running_total + trb_buff_len) /
 		usb_endpoint_maxp(&urb->ep->desc);
 
-	return xhci_td_remainder(total_packet_count - packets_transferred);
+	if ((total_packet_count - packets_transferred) > 31)
+		return 31 << 17;
+	return (total_packet_count - packets_transferred) << 17;
 }
 
 static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
@@ -3127,7 +3130,7 @@ static int queue_bulk_sg_tx(struct xhci_
 
 	num_trbs = count_sg_trbs_needed(xhci, urb);
 	num_sgs = urb->num_mapped_sgs;
-	total_packet_count = roundup(urb->transfer_buffer_length,
+	total_packet_count = DIV_ROUND_UP(urb->transfer_buffer_length,
 			usb_endpoint_maxp(&urb->ep->desc));
 
 	trb_buff_len = prepare_transfer(xhci, xhci->devs[slot_id],
@@ -3210,7 +3213,8 @@ static int queue_bulk_sg_tx(struct xhci_
 					running_total);
 		} else {
 			remainder = xhci_v1_0_td_remainder(running_total,
-					trb_buff_len, total_packet_count, urb);
+					trb_buff_len, total_packet_count, urb,
+					num_trbs - 1);
 		}
 		length_field = TRB_LEN(trb_buff_len) |
 			remainder |
@@ -3318,7 +3322,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *
 	start_cycle = ep_ring->cycle_state;
 
 	running_total = 0;
-	total_packet_count = roundup(urb->transfer_buffer_length,
+	total_packet_count = DIV_ROUND_UP(urb->transfer_buffer_length,
 			usb_endpoint_maxp(&urb->ep->desc));
 	/* How much data is in the first TRB? */
 	addr = (u64) urb->transfer_dma;
@@ -3364,7 +3368,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *
 					running_total);
 		} else {
 			remainder = xhci_v1_0_td_remainder(running_total,
-					trb_buff_len, total_packet_count, urb);
+					trb_buff_len, total_packet_count, urb,
+					num_trbs - 1);
 		}
 		length_field = TRB_LEN(trb_buff_len) |
 			remainder |
@@ -3627,7 +3632,7 @@ static int xhci_queue_isoc_tx(struct xhc
 		addr = start_addr + urb->iso_frame_desc[i].offset;
 		td_len = urb->iso_frame_desc[i].length;
 		td_remain_len = td_len;
-		total_packet_count = roundup(td_len,
+		total_packet_count = DIV_ROUND_UP(td_len,
 				usb_endpoint_maxp(&urb->ep->desc));
 		/* A zero-length transfer still involves at least one packet. */
 		if (total_packet_count == 0)
@@ -3706,7 +3711,8 @@ static int xhci_queue_isoc_tx(struct xhc
 			} else {
 				remainder = xhci_v1_0_td_remainder(
 						running_total, trb_buff_len,
-						total_packet_count, urb);
+						total_packet_count, urb,
+						(trbs_per_td - j - 1));
 			}
 			length_field = TRB_LEN(trb_buff_len) |
 				remainder |


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ