Commit 3d091a6f authored by Andiry Xu's avatar Andiry Xu Committed by Greg Kroah-Hartman

USB: EHCI: AMD periodic frame list table quirk

On AMD SB700/SB800/Hudson-2/3 platforms, USB EHCI controller may read/write
to memory space not allocated to USB controller if there is longer than
normal latency on DMA read encountered. In this condition the exposure will
be encountered only if the driver has following format of Periodic Frame
List link pointer structure:

For any idle periodic schedule, the Frame List link pointers that have the
T-bit set to 1 intending to terminate the use of frame list link pointer
as a physical memory pointer.

Idle periodic schedule Frame List Link pointer shoule be in the following
format to avoid the issue:

Frame list link pointer should be always contains a valid pointer to a
inactive QHead with T-bit set to 0.
Signed-off-by: default avatarAndiry Xu <andiry.xu@amd.com>
Acked-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 3d965875
...@@ -141,6 +141,10 @@ static void ehci_mem_cleanup (struct ehci_hcd *ehci) ...@@ -141,6 +141,10 @@ static void ehci_mem_cleanup (struct ehci_hcd *ehci)
qh_put (ehci->async); qh_put (ehci->async);
ehci->async = NULL; ehci->async = NULL;
if (ehci->dummy)
qh_put(ehci->dummy);
ehci->dummy = NULL;
/* DMA consistent memory and pools */ /* DMA consistent memory and pools */
if (ehci->qtd_pool) if (ehci->qtd_pool)
dma_pool_destroy (ehci->qtd_pool); dma_pool_destroy (ehci->qtd_pool);
...@@ -227,8 +231,26 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags) ...@@ -227,8 +231,26 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags)
if (ehci->periodic == NULL) { if (ehci->periodic == NULL) {
goto fail; goto fail;
} }
if (ehci->use_dummy_qh) {
struct ehci_qh_hw *hw;
ehci->dummy = ehci_qh_alloc(ehci, flags);
if (!ehci->dummy)
goto fail;
hw = ehci->dummy->hw;
hw->hw_next = EHCI_LIST_END(ehci);
hw->hw_qtd_next = EHCI_LIST_END(ehci);
hw->hw_alt_next = EHCI_LIST_END(ehci);
hw->hw_token &= ~QTD_STS_ACTIVE;
ehci->dummy->hw = hw;
for (i = 0; i < ehci->periodic_size; i++)
ehci->periodic[i] = ehci->dummy->qh_dma;
} else {
for (i = 0; i < ehci->periodic_size; i++) for (i = 0; i < ehci->periodic_size; i++)
ehci->periodic [i] = EHCI_LIST_END(ehci); ehci->periodic[i] = EHCI_LIST_END(ehci);
}
/* software shadow of hardware table */ /* software shadow of hardware table */
ehci->pshadow = kcalloc(ehci->periodic_size, sizeof(void *), flags); ehci->pshadow = kcalloc(ehci->periodic_size, sizeof(void *), flags);
......
...@@ -103,6 +103,19 @@ static int ehci_pci_setup(struct usb_hcd *hcd) ...@@ -103,6 +103,19 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
if (retval) if (retval)
return retval; return retval;
if ((pdev->vendor == PCI_VENDOR_ID_AMD && pdev->device == 0x7808) ||
(pdev->vendor == PCI_VENDOR_ID_ATI && pdev->device == 0x4396)) {
/* EHCI controller on AMD SB700/SB800/Hudson-2/3 platforms may
* read/write memory space which does not belong to it when
* there is NULL pointer with T-bit set to 1 in the frame list
* table. To avoid the issue, the frame list link pointer
* should always contain a valid pointer to a inactive qh.
*/
ehci->use_dummy_qh = 1;
ehci_info(ehci, "applying AMD SB700/SB800/Hudson-2/3 EHCI "
"dummy qh workaround\n");
}
/* data structure init */ /* data structure init */
retval = ehci_init(hcd); retval = ehci_init(hcd);
if (retval) if (retval)
......
...@@ -98,7 +98,14 @@ static void periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr) ...@@ -98,7 +98,14 @@ static void periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr)
*/ */
*prev_p = *periodic_next_shadow(ehci, &here, *prev_p = *periodic_next_shadow(ehci, &here,
Q_NEXT_TYPE(ehci, *hw_p)); Q_NEXT_TYPE(ehci, *hw_p));
*hw_p = *shadow_next_periodic(ehci, &here, Q_NEXT_TYPE(ehci, *hw_p));
if (!ehci->use_dummy_qh ||
*shadow_next_periodic(ehci, &here, Q_NEXT_TYPE(ehci, *hw_p))
!= EHCI_LIST_END(ehci))
*hw_p = *shadow_next_periodic(ehci, &here,
Q_NEXT_TYPE(ehci, *hw_p));
else
*hw_p = ehci->dummy->qh_dma;
} }
/* how many of the uframe's 125 usecs are allocated? */ /* how many of the uframe's 125 usecs are allocated? */
...@@ -2335,7 +2342,11 @@ scan_periodic (struct ehci_hcd *ehci) ...@@ -2335,7 +2342,11 @@ scan_periodic (struct ehci_hcd *ehci)
* pointer for much longer, if at all. * pointer for much longer, if at all.
*/ */
*q_p = q.itd->itd_next; *q_p = q.itd->itd_next;
if (!ehci->use_dummy_qh ||
q.itd->hw_next != EHCI_LIST_END(ehci))
*hw_p = q.itd->hw_next; *hw_p = q.itd->hw_next;
else
*hw_p = ehci->dummy->qh_dma;
type = Q_NEXT_TYPE(ehci, q.itd->hw_next); type = Q_NEXT_TYPE(ehci, q.itd->hw_next);
wmb(); wmb();
modified = itd_complete (ehci, q.itd); modified = itd_complete (ehci, q.itd);
...@@ -2368,7 +2379,11 @@ scan_periodic (struct ehci_hcd *ehci) ...@@ -2368,7 +2379,11 @@ scan_periodic (struct ehci_hcd *ehci)
* URB completion. * URB completion.
*/ */
*q_p = q.sitd->sitd_next; *q_p = q.sitd->sitd_next;
if (!ehci->use_dummy_qh ||
q.sitd->hw_next != EHCI_LIST_END(ehci))
*hw_p = q.sitd->hw_next; *hw_p = q.sitd->hw_next;
else
*hw_p = ehci->dummy->qh_dma;
type = Q_NEXT_TYPE(ehci, q.sitd->hw_next); type = Q_NEXT_TYPE(ehci, q.sitd->hw_next);
wmb(); wmb();
modified = sitd_complete (ehci, q.sitd); modified = sitd_complete (ehci, q.sitd);
......
...@@ -73,6 +73,7 @@ struct ehci_hcd { /* one per controller */ ...@@ -73,6 +73,7 @@ struct ehci_hcd { /* one per controller */
/* async schedule support */ /* async schedule support */
struct ehci_qh *async; struct ehci_qh *async;
struct ehci_qh *dummy; /* For AMD quirk use */
struct ehci_qh *reclaim; struct ehci_qh *reclaim;
unsigned scanning : 1; unsigned scanning : 1;
...@@ -131,6 +132,7 @@ struct ehci_hcd { /* one per controller */ ...@@ -131,6 +132,7 @@ struct ehci_hcd { /* one per controller */
unsigned need_io_watchdog:1; unsigned need_io_watchdog:1;
unsigned broken_periodic:1; unsigned broken_periodic:1;
unsigned fs_i_thresh:1; /* Intel iso scheduling */ unsigned fs_i_thresh:1; /* Intel iso scheduling */
unsigned use_dummy_qh:1; /* AMD Frame List table quirk*/
/* required for usb32 quirk */ /* required for usb32 quirk */
#define OHCI_CTRL_HCFS (3 << 6) #define OHCI_CTRL_HCFS (3 << 6)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment