diff options
-rw-r--r-- | drivers/usb/host/xhci-mem.c | 159 | ||||
-rw-r--r-- | drivers/usb/host/xhci-ring.c | 3 | ||||
-rw-r--r-- | drivers/usb/host/xhci.h | 3 |
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 | ||
862 | static 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. */ | ||
897 | static 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 | |||
862 | int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) | 1019 | int 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 | */ |
990 | static struct xhci_segment *trb_in_td( | 990 | struct 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 */ |
1270 | dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, union xhci_trb *trb); | 1270 | dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, union xhci_trb *trb); |
1271 | struct 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); | ||
1271 | void xhci_ring_cmd_db(struct xhci_hcd *xhci); | 1274 | void xhci_ring_cmd_db(struct xhci_hcd *xhci); |
1272 | void *xhci_setup_one_noop(struct xhci_hcd *xhci); | 1275 | void *xhci_setup_one_noop(struct xhci_hcd *xhci); |
1273 | void xhci_handle_event(struct xhci_hcd *xhci); | 1276 | void xhci_handle_event(struct xhci_hcd *xhci); |