From: Mathias Nyman mathias.nyman@linux.intel.com
mainline inclusion from mainline-v6.4-rc3 commit f8f80be501aa2f10669585c3e328fad079d8cb3a bugzilla: https://gitee.com/src-openeuler/kernel/issues/I9DNQ5 CVE: CVE-2024-26659
--------------------------------
Use soft retry to recover from a USB Transaction Errors that are caused by temporary error conditions. The USB device is not aware that the xHC has halted the endpoint, and will be waiting for another retry
A Soft Retry perform additional retries and recover from an error which has caused the xHC to halt an endpoint.
Soft retry has some limitations: Soft Retry attempts shall not be performed on Isoch endpoints Soft Retry attempts shall not be performed if the device is behind a TT in a HS Hub
Software shall limit the number of unsuccessful Soft Retry attempts to prevent an infinite loop.
For more details on Soft retry see xhci specs 4.6.8.1
Signed-off-by: Mathias Nyman mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org Signed-off-by: Li Huafei lihuafei1@huawei.com --- drivers/usb/host/xhci-ring.c | 19 +++++++++++++++++++ drivers/usb/host/xhci.h | 2 ++ 2 files changed, 21 insertions(+)
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 98b67605d3cf..8658015f84c4 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1159,6 +1159,10 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id, /* Clear our internal halted state */ xhci->devs[slot_id]->eps[ep_index].ep_state &= ~EP_HALTED; } + + /* if this was a soft reset, then restart */ + if ((le32_to_cpu(trb->generic.field[3])) & TRB_TSP) + ring_doorbell_for_active_rings(xhci, slot_id, ep_index); }
static void xhci_handle_cmd_enable_slot(struct xhci_hcd *xhci, int slot_id, @@ -2192,10 +2196,16 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td, union xhci_trb *ep_trb, struct xhci_transfer_event *event, struct xhci_virt_ep *ep, int *status) { + struct xhci_slot_ctx *slot_ctx; struct xhci_ring *ep_ring; u32 trb_comp_code; u32 remaining, requested, ep_trb_len; + unsigned int slot_id; + int ep_index;
+ slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags)); + slot_ctx = xhci_get_slot_ctx(xhci, xhci->devs[slot_id]->out_ctx); + ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1; ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer)); trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len)); remaining = EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)); @@ -2204,6 +2214,7 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
switch (trb_comp_code) { case COMP_SUCCESS: + ep_ring->err_count = 0; /* handle success with untransferred data as short packet */ if (ep_trb != td->last_trb || remaining) { xhci_warn(xhci, "WARN Successful completion on short TX\n"); @@ -2227,6 +2238,14 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td, ep_trb_len = 0; remaining = 0; break; + case COMP_USB_TRANSACTION_ERROR: + if ((ep_ring->err_count++ > MAX_SOFT_RETRY) || + le32_to_cpu(slot_ctx->tt_info) & TT_SLOT) + break; + *status = 0; + xhci_cleanup_halted_endpoint(xhci, slot_id, ep_index, + ep_ring->stream_id, td, EP_SOFT_RESET); + return 0; default: /* do nothing */ break; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 485ba36b566c..7ec0a5164e07 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1513,6 +1513,7 @@ static inline const char *xhci_trb_type_string(u8 type) /* How much data is left before the 64KB boundary? */ #define TRB_BUFF_LEN_UP_TO_BOUNDARY(addr) (TRB_MAX_BUFF_SIZE - \ (addr & (TRB_MAX_BUFF_SIZE - 1))) +#define MAX_SOFT_RETRY 3
struct xhci_segment { union xhci_trb *trbs; @@ -1600,6 +1601,7 @@ struct xhci_ring { * if we own the TRB (if we are the consumer). See section 4.9.1. */ u32 cycle_state; + unsigned int err_count; unsigned int stream_id; unsigned int num_segs; unsigned int num_trbs_free;