aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host
diff options
context:
space:
mode:
authorSarah Sharp <sarah.a.sharp@linux.intel.com>2009-11-09 16:35:23 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2009-12-11 14:55:22 -0500
commit6648f29d3be2972a74ef8e29aa5d425ab4f1fc48 (patch)
tree019ca7fd4b73772952f570ef2a5dec67c5eabeaa /drivers/usb/host
parent7f4e985448e82fe4a1cc0ae4fcecaf7e0301c07b (diff)
USB: xhci: Add tests for TRB address translation.
It's not surprising that the transfer request buffer (TRB) physical to virtual address translation function has bugs in it, since I wrote most of it at 4am last October. Add a test suite to check the TRB math. This runs at memory initialization time, and causes the driver to fail to load if the TRB math fails. Please excuse the excessively long lines in the test vectors; they can't really be made shorter and still be readable. Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host')
-rw-r--r--drivers/usb/host/xhci-mem.c159
-rw-r--r--drivers/usb/host/xhci-ring.c3
-rw-r--r--drivers/usb/host/xhci.h3
3 files changed, 163 insertions, 2 deletions
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index fd05247b7bb1..711dc554e716 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -859,6 +859,163 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
859 xhci->page_shift = 0; 859 xhci->page_shift = 0;
860} 860}
861 861
862static int xhci_test_trb_in_td(struct xhci_hcd *xhci,
863 struct xhci_segment *input_seg,
864 union xhci_trb *start_trb,
865 union xhci_trb *end_trb,
866 dma_addr_t input_dma,
867 struct xhci_segment *result_seg,
868 char *test_name, int test_number)
869{
870 unsigned long long start_dma;
871 unsigned long long end_dma;
872 struct xhci_segment *seg;
873
874 start_dma = xhci_trb_virt_to_dma(input_seg, start_trb);
875 end_dma = xhci_trb_virt_to_dma(input_seg, end_trb);
876
877 seg = trb_in_td(input_seg, start_trb, end_trb, input_dma);
878 if (seg != result_seg) {
879 xhci_warn(xhci, "WARN: %s TRB math test %d failed!\n",
880 test_name, test_number);
881 xhci_warn(xhci, "Tested TRB math w/ seg %p and "
882 "input DMA 0x%llx\n",
883 input_seg,
884 (unsigned long long) input_dma);
885 xhci_warn(xhci, "starting TRB %p (0x%llx DMA), "
886 "ending TRB %p (0x%llx DMA)\n",
887 start_trb, start_dma,
888 end_trb, end_dma);
889 xhci_warn(xhci, "Expected seg %p, got seg %p\n",
890 result_seg, seg);
891 return -1;
892 }
893 return 0;
894}
895
896/* TRB math checks for xhci_trb_in_td(), using the command and event rings. */
897static int xhci_check_trb_in_td_math(struct xhci_hcd *xhci, gfp_t mem_flags)
898{
899 struct {
900 dma_addr_t input_dma;
901 struct xhci_segment *result_seg;
902 } simple_test_vector [] = {
903 /* A zeroed DMA field should fail */
904 { 0, NULL },
905 /* One TRB before the ring start should fail */
906 { xhci->event_ring->first_seg->dma - 16, NULL },
907 /* One byte before the ring start should fail */
908 { xhci->event_ring->first_seg->dma - 1, NULL },
909 /* Starting TRB should succeed */
910 { xhci->event_ring->first_seg->dma, xhci->event_ring->first_seg },
911 /* Ending TRB should succeed */
912 { xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT - 1)*16,
913 xhci->event_ring->first_seg },
914 /* One byte after the ring end should fail */
915 { xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT - 1)*16 + 1, NULL },
916 /* One TRB after the ring end should fail */
917 { xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT)*16, NULL },
918 /* An address of all ones should fail */
919 { (dma_addr_t) (~0), NULL },
920 };
921 struct {
922 struct xhci_segment *input_seg;
923 union xhci_trb *start_trb;
924 union xhci_trb *end_trb;
925 dma_addr_t input_dma;
926 struct xhci_segment *result_seg;
927 } complex_test_vector [] = {
928 /* Test feeding a valid DMA address from a different ring */
929 { .input_seg = xhci->event_ring->first_seg,
930 .start_trb = xhci->event_ring->first_seg->trbs,
931 .end_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1],
932 .input_dma = xhci->cmd_ring->first_seg->dma,
933 .result_seg = NULL,
934 },
935 /* Test feeding a valid end TRB from a different ring */
936 { .input_seg = xhci->event_ring->first_seg,
937 .start_trb = xhci->event_ring->first_seg->trbs,
938 .end_trb = &xhci->cmd_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1],
939 .input_dma = xhci->cmd_ring->first_seg->dma,
940 .result_seg = NULL,
941 },
942 /* Test feeding a valid start and end TRB from a different ring */
943 { .input_seg = xhci->event_ring->first_seg,
944 .start_trb = xhci->cmd_ring->first_seg->trbs,
945 .end_trb = &xhci->cmd_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1],
946 .input_dma = xhci->cmd_ring->first_seg->dma,
947 .result_seg = NULL,
948 },
949 /* TRB in this ring, but after this TD */
950 { .input_seg = xhci->event_ring->first_seg,
951 .start_trb = &xhci->event_ring->first_seg->trbs[0],
952 .end_trb = &xhci->event_ring->first_seg->trbs[3],
953 .input_dma = xhci->event_ring->first_seg->dma + 4*16,
954 .result_seg = NULL,
955 },
956 /* TRB in this ring, but before this TD */
957 { .input_seg = xhci->event_ring->first_seg,
958 .start_trb = &xhci->event_ring->first_seg->trbs[3],
959 .end_trb = &xhci->event_ring->first_seg->trbs[6],
960 .input_dma = xhci->event_ring->first_seg->dma + 2*16,
961 .result_seg = NULL,
962 },
963 /* TRB in this ring, but after this wrapped TD */
964 { .input_seg = xhci->event_ring->first_seg,
965 .start_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 3],
966 .end_trb = &xhci->event_ring->first_seg->trbs[1],
967 .input_dma = xhci->event_ring->first_seg->dma + 2*16,
968 .result_seg = NULL,
969 },
970 /* TRB in this ring, but before this wrapped TD */
971 { .input_seg = xhci->event_ring->first_seg,
972 .start_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 3],
973 .end_trb = &xhci->event_ring->first_seg->trbs[1],
974 .input_dma = xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT - 4)*16,
975 .result_seg = NULL,
976 },
977 /* TRB not in this ring, and we have a wrapped TD */
978 { .input_seg = xhci->event_ring->first_seg,
979 .start_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 3],
980 .end_trb = &xhci->event_ring->first_seg->trbs[1],
981 .input_dma = xhci->cmd_ring->first_seg->dma + 2*16,
982 .result_seg = NULL,
983 },
984 };
985
986 unsigned int num_tests;
987 int i, ret;
988
989 num_tests = sizeof(simple_test_vector) / sizeof(simple_test_vector[0]);
990 for (i = 0; i < num_tests; i++) {
991 ret = xhci_test_trb_in_td(xhci,
992 xhci->event_ring->first_seg,
993 xhci->event_ring->first_seg->trbs,
994 &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1],
995 simple_test_vector[i].input_dma,
996 simple_test_vector[i].result_seg,
997 "Simple", i);
998 if (ret < 0)
999 return ret;
1000 }
1001
1002 num_tests = sizeof(complex_test_vector) / sizeof(complex_test_vector[0]);
1003 for (i = 0; i < num_tests; i++) {
1004 ret = xhci_test_trb_in_td(xhci,
1005 complex_test_vector[i].input_seg,
1006 complex_test_vector[i].start_trb,
1007 complex_test_vector[i].end_trb,
1008 complex_test_vector[i].input_dma,
1009 complex_test_vector[i].result_seg,
1010 "Complex", i);
1011 if (ret < 0)
1012 return ret;
1013 }
1014 xhci_dbg(xhci, "TRB math tests passed.\n");
1015 return 0;
1016}
1017
1018
862int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) 1019int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
863{ 1020{
864 dma_addr_t dma; 1021 dma_addr_t dma;
@@ -962,6 +1119,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
962 xhci->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, false, flags); 1119 xhci->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, false, flags);
963 if (!xhci->event_ring) 1120 if (!xhci->event_ring)
964 goto fail; 1121 goto fail;
1122 if (xhci_check_trb_in_td_math(xhci, flags) < 0)
1123 goto fail;
965 1124
966 xhci->erst.entries = pci_alloc_consistent(to_pci_dev(dev), 1125 xhci->erst.entries = pci_alloc_consistent(to_pci_dev(dev),
967 sizeof(struct xhci_erst_entry)*ERST_NUM_SEGS, &dma); 1126 sizeof(struct xhci_erst_entry)*ERST_NUM_SEGS, &dma);
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index aaed076bae37..371ba4297d9c 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -987,8 +987,7 @@ static void handle_port_status(struct xhci_hcd *xhci,
987 * TRB in this TD, this function returns that TRB's segment. Otherwise it 987 * TRB in this TD, this function returns that TRB's segment. Otherwise it
988 * returns 0. 988 * returns 0.
989 */ 989 */
990static struct xhci_segment *trb_in_td( 990struct xhci_segment *trb_in_td(struct xhci_segment *start_seg,
991 struct xhci_segment *start_seg,
992 union xhci_trb *start_trb, 991 union xhci_trb *start_trb,
993 union xhci_trb *end_trb, 992 union xhci_trb *end_trb,
994 dma_addr_t suspect_dma) 993 dma_addr_t suspect_dma)
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index c92f84154fb5..3989aadcf670 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1268,6 +1268,9 @@ void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev);
1268 1268
1269/* xHCI ring, segment, TRB, and TD functions */ 1269/* xHCI ring, segment, TRB, and TD functions */
1270dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, union xhci_trb *trb); 1270dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, union xhci_trb *trb);
1271struct xhci_segment *trb_in_td(struct xhci_segment *start_seg,
1272 union xhci_trb *start_trb, union xhci_trb *end_trb,
1273 dma_addr_t suspect_dma);
1271void xhci_ring_cmd_db(struct xhci_hcd *xhci); 1274void xhci_ring_cmd_db(struct xhci_hcd *xhci);
1272void *xhci_setup_one_noop(struct xhci_hcd *xhci); 1275void *xhci_setup_one_noop(struct xhci_hcd *xhci);
1273void xhci_handle_event(struct xhci_hcd *xhci); 1276void xhci_handle_event(struct xhci_hcd *xhci);