aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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);