FAILED: patch "[PATCH] usb: xhci: bail out of setup if the controller is" failed to apply to 6.12-stable tree
Subsystems (3)
[email protected]
maintainerThe patch below does not apply to the 6.12-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.12.y
git checkout FETCH_HEAD
git cherry-pick -x 78203d5b54a40f0e36196ebf31c9c7a380fc8811
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026083120-autism-gerbil-efbb@gregkh' --subject-prefix 'PATCH 6.12.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 78203d5b54a40f0e36196ebf31c9c7a380fc8811 Mon Sep 17 00:00:00 2001
From: Breno Leitao <[email protected]>
Date: Thu, 6 Aug 2026 17:21:06 +0300
Subject: [PATCH] usb: xhci: bail out of setup if the controller is
inaccessible
xhci_gen_setup() locates the operational registers using the capability length read from the very first register:
xhci->op_regs = hcd->regs + HC_LENGTH(readl(&xhci->cap_regs->hc_capbase));
If the controller is dead or has dropped off the bus, that read returns ~0, HC_LENGTH() truncates it to 0xff, and op_regs ends up 0xff bytes past the page-aligned MMIO base, i.e. unaligned. The first access through it, xhci_halt() -> xhci_handshake() reading op_regs->status, is then an unaligned readl() on device memory. arm64 faults on unaligned device accesses, so instead of xhci_handshake() catching the all-ones value and returning -ENODEV, setup oopses:
xhci-pci-renesas 0005:08:00.0: Unable to change power state from D3cold to D0, device inaccessible xhci-pci-renesas 0005:08:00.0: xHCI Host Controller
xhci-pci-renesas 0005:08:00.0: new USB bus registered, assigned bus number 1
Unable to handle kernel paging request at virtual address ffff80030a770103
ESR = 0x0000000096000021
FSC = 0x21: alignment fault
Internal error: Oops: 0000000096000021 [#1] SMP
pc : xhci_halt [xhci_hcd]
Call trace:
xhci_halt
xhci_gen_setup
xhci_pci_setup
usb_add_hcd
usb_hcd_pci_probe
xhci_pci_common_probe
xhci_pci_renesas_probeThis was hit with a Renesas uPD720201 that failed to power up ("Unable to change power state from D3cold to D0, device inaccessible") yet still reached the HCD probe path.
Read the capability register once, and if it reads back the all-ones value (as xhci_handshake() and xhci_reset() already test for), abort setup with -ENODEV before op_regs is derived from it. Reading it once also avoids re-reading a register that may change under a concurrent hot-removal.
drivers/usb/host/xhci.c +8 -3
@@ -5434,6 +5434,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)struct device *dev = hcd->self.sysdev;int retval;u32 hcs_params1; +u32 hc_capbase;/* Accept arbitrarily long scatter-gather lists */ hcd->self.sg_tablesize = ~0 ;
@@ -5454,15 +5455,19 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)mutex_init (&xhci->mutex); xhci->main_hcd = hcd; xhci->cap_regs = hcd->regs; - xhci->op_regs = hcd->regs + -HC_LENGTH (readl (&xhci->cap_regs->hc_capbase)); + hc_capbase =readl (&xhci->cap_regs->hc_capbase); +if (hc_capbase ==U32_MAX ) { +xhci_warn (xhci,"Host controller not accessible, removed?\n" ); +return -ENODEV ; + } + xhci->op_regs = hcd->regs +HC_LENGTH (hc_capbase); xhci->run_regs = hcd->regs + (readl (&xhci->cap_regs->run_regs_off) &RTSOFF_MASK );/* Cache read-only capability registers */ hcs_params1 =readl (&xhci->cap_regs->hcs_params1); xhci->hcs_params2 =readl (&xhci->cap_regs->hcs_params2); xhci->hcs_params3 =readl (&xhci->cap_regs->hcs_params3); - xhci->hci_version =HC_VERSION (readl (&xhci->cap_regs->hc_capbase)); + xhci->hci_version =HC_VERSION (hc_capbase); xhci->hcc_params =readl (&xhci->cap_regs->hcc_params);if (xhci->hci_version >0x100 ) xhci->hcc_params2 =readl (&xhci->cap_regs->hcc_params2);
Sasha Levin
maintainerPATCH 1/4 usb: xhci: add USB Port Register Set struct
From: Niklas Neronin <[email protected]>
[ Upstream commit 377a91594e008848363641d07f51d2e48f4bdde5 ]
Introduce a new struct for the Host Controller USB Port Register Set to enhance readability and maintainability.
The Host Controller Operational Registers (struct 'xhci_op_regs') span from offset 0x0 to 0x3FF and consist of fixed fields. Following these fixed fields are the Host Controller USB Port Register Sets, which are dynamic and repeat from 1 to MaxPorts, as defined by HCSPARAMS1.
Currently, the struct 'xhci_op_regs' includes:
__le32 port_status_base; The first PORTSC
__le32 port_power_base; The first PORTPMSC
__le32 port_link_base; The first PORTLI
__le32 reserved5; The first PORTHLPMC, not reserved
__le32 reserved6[NUM_PORT_REGS*254]; Port registers 2 to MaxPorts
Replace this with the simpler:
struct xhci_port_regs port_regs[]; Port registers 1 to MaxPorts
Host Controller USB Port Register Set:
| Offset | Mnemonic | Register Name
--------------------------------------------------------------------------
| 0x0 | PORTSC | Port Status and Control | 0x4 | PORTPMSC | Port Power Management Status and Control | 0x8 | PORTLI | Port Link Info | 0xC | PORTHLPMC | Port Hardware LPM Control
Stable-dep-of: 78203d5b54a4 ("usb: xhci: bail out of setup if the controller is inaccessible")
--- drivers/usb/host/xhci-mem.c | 3 +-- drivers/usb/host/xhci.h | 36 ++++++++++++++++-------------------- 2 files changed, 17 insertions(+), 22 deletions(-)
drivers/usb/host/xhci-mem.c +1 -2
@@ -2161,8 +2161,7 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)return -ENOMEM ;for (i =0 ; i < num_ports; i++) { - xhci->hw_ports[i].addr = &xhci->op_regs->port_status_base + -NUM_PORT_REGS * i; + xhci->hw_ports[i].addr = &xhci->op_regs->port_regs[i].portsc; xhci->hw_ports[i].hw_portnum = i;init_completion (&xhci->hw_ports[i].rexit_done);
drivers/usb/host/xhci.h +16 -20
@@ -66,14 +66,25 @@ struct xhci_cap_regs {/* Reserved up to (CAPLENGTH - 0x1C) */ }; -/* Number of registers per port */ -#define NUM_PORT_REGS 4 -#define PORTSC 0#define PORTPMSC 1#define PORTLI 2#define PORTHLPMC 3 +/* +* struct xhci_port_regs - Host Controller USB Port Register Set. xHCI spec 5.4.8 +* @portsc: Port Status and Control +* @portpmsc: Port Power Management Status and Control +* @portli: Port Link Info +* @porthlmpc: Port Hardware LPM Control +*/ +struct xhci_port_regs { +__le32 portsc; +__le32 portpmsc; +__le32 portli; +__le32 porthlmpc; +}; +/** * struct xhci_op_regs - xHCI Host Controller Operational Registers. * @command: USBCMD - xHC command register
@@ -85,16 +96,7 @@ struct xhci_cap_regs {* @cmd_ring: CRP - 64-bit Command Ring Pointer * @dcbaa_ptr: DCBAAP - 64-bit Device Context Base Address Array Pointer * @config_reg: CONFIG - Configure Register -* @port_status_base: PORTSCn - base address for Port Status and Control -* Each port has a Port Status and Control register, -* followed by a Port Power Management Status and Control -* register, a Port Link Info register, and a reserved -* register. -* @port_power_base: PORTPMSCn - base address for -* Port Power Management Status and Control -* @port_link_base: PORTLIn - base address for Port Link Info (current -* Link PM state and control) for USB 2.1 and USB 3.0 -* devices. +* @port_regs: Port Register Sets, from 1 to MaxPorts (defined by HCSPARAMS1). */ struct xhci_op_regs {__le32 command;
@@ -110,13 +112,7 @@ struct xhci_op_regs {__le32 config_reg;/* rsvd: offset 0x3C-3FF */ __le32 reserved4[241 ]; -/* port 1 registers, which serve as a base address for other ports */ -__le32 port_status_base; -__le32 port_power_base; -__le32 port_link_base; -__le32 reserved5; -/* registers for ports 2-255 */ -__le32 reserved6[NUM_PORT_REGS *254 ]; +struct xhci_port_regs port_regs[]; };/* USBCMD - USB command - command bitmasks */
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/4 usb: xhci: use cached HCSPARAMS1 value
From: Niklas Neronin <[email protected]>
[ Upstream commit 70651cc3f5a4c7cec529f121e36ea3b45ea84778 ]
The Structural Parameters 1 (HCSPARAMS1) register is read and cached in 'xhci->hcs_params1' during host controller initialization. Since this register is read-only and its value remains constant for the lifetime of the controller, re-reading it later is unnecessary.
Replace subsequent register reads with the cached 'xhci->hcs_params1' value to avoid redundant MMIO access.
Stable-dep-of: 78203d5b54a4 ("usb: xhci: bail out of setup if the controller is inaccessible")
--- drivers/usb/host/xhci.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/usb/host/xhci.c +1 -2
@@ -4040,8 +4040,7 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)xhci_err (xhci,"Error while assigning device slot ID: %s\n" ,xhci_trb_comp_code_string (command->status));xhci_err (xhci,"Max number of devices this xHCI host supports is %u.\n" , -HCS_MAX_SLOTS ( -readl (&xhci->cap_regs->hcs_params1))); +HCS_MAX_SLOTS (xhci->hcs_params1));xhci_free_command (xhci, command);return 0 ; }
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/4 usb: xhci: simplify handling of Structural Parameters 1 values
In reply to Sasha LevinFrom: Niklas Neronin <[email protected]>
[ Upstream commit df08973556851b29bd78e79db696d992ed1b43f0 ]
The 32-bit read-only HCSPARAMS1 register contains the following fields:
Bits 7:0 - Number of Device Slots (MaxSlots) Bits 18:8 - Number of Interrupters (MaxIntrs) Bits 23:19 - Reserved Bits 31:24 - Number of Ports (MaxPorts)
Since the register value is constant for the lifetime of the controller, it is cached in 'xhci->hcs_params1'. However, platform drivers may override the number of interrupters through a separate variable, 'xhci->max_interrupters', leaving only the maximum slots and ports values still derived from the cached register.
To simplify the code and improve readability, replace 'xhci->hcs_params1' with two dedicated 'u8' fields: 'xhci->max_slots' and 'xhci->max_ports'. These values are initialized once and used directly instead of calling 'HCS_MAX_SLOTS()' and 'HCS_MAX_PORTS()' macros.
This change reduces code clutter without increasing memory usage.
[ Adjusted for 6.12, which lacks several later xhci cleanups:
- The xhci_init() split into xhci_hcd_page_size(), xhci_enable_max_dev_slots(), xhci_set_cmd_ring_deq(), xhci_set_doorbell_ptr() and xhci_set_dev_notifications() is not present, so the xhci_enable_max_dev_slots() hunk is dropped. The equivalent 6.12 code in xhci_mem_init() reads HCSPARAMS1 straight from cap_regs and never touched xhci->hcs_params1, so it needs no change.
- The port bandwidth debugfs files (xhci_port_bw_show() and friends) do not exist, so that xhci-debugfs.c hunk is dropped.
- struct xhci_port still uses the 'addr' pointer to PORTSC rather than the 'port_reg' pointer to the whole register set, so only the loop bound is converted in xhci_setup_port_arrays().
- 6.12 has one extra user of the removed xhci->hcs_params1 field, xhci_try_enable_msi() in xhci-pci.c, which upstream had already converted to xhci->max_interrupters before this commit. Convert it here as well, otherwise removing the field breaks the build. It is
equivalent: xhci_try_enable_msi() runs from the ->start() callback, i.e. after xhci_gen_setup() has clamped xhci->max_interrupters to HCS_MAX_INTRS(HCSPARAMS1), and nothing in 6.12 presets it. ]
Stable-dep-of: 78203d5b54a4 ("usb: xhci: bail out of setup if the controller is inaccessible")
--- drivers/usb/host/xhci-debugfs.c | 10 +++------- drivers/usb/host/xhci-hub.c | 2 +- drivers/usb/host/xhci-mem.c | 31 +++++++++++++------------------ drivers/usb/host/xhci-pci.c | 9 ++++----- drivers/usb/host/xhci-ring.c | 6 ++---- drivers/usb/host/xhci.c | 15 ++++++++------- drivers/usb/host/xhci.h | 3 ++- 7 files changed, 33 insertions(+), 43 deletions(-)
drivers/usb/host/xhci-debugfs.c +3 -7
@@ -629,20 +629,16 @@ void xhci_debugfs_remove_slot(struct xhci_hcd *xhci, int slot_id)static void xhci_debugfs_create_ports (struct xhci_hcd *xhci,struct dentry *parent) { -unsigned int num_ports;char port_name[8 ];struct xhci_port *port;struct dentry *dir; - num_ports =HCS_MAX_PORTS (xhci->hcs_params1); - parent =debugfs_create_dir ("ports" , parent); -while (num_ports--) { -scnprintf (port_name,sizeof (port_name),"port%02d" , - num_ports +1 ); +for (int i =0 ; i < xhci->max_ports; i++) { +scnprintf (port_name,sizeof (port_name),"port%02d" , i +1 ); dir =debugfs_create_dir (port_name, parent); - port = &xhci->hw_ports[num_ports]; + port = &xhci->hw_ports[i];debugfs_create_file ("portsc" ,0644 , dir, port, &port_fops); } }
drivers/usb/host/xhci-hub.c +1 -1
@@ -700,7 +700,7 @@ static int xhci_enter_test_mode(struct xhci_hcd *xhci,/* Disable all Device Slots */ xhci_dbg (xhci,"Disable all slots\n" );spin_unlock_irqrestore (&xhci->lock, *flags); -for (i =1 ; i <=HCS_MAX_SLOTS (xhci->hcs_params1); i++) { +for (i =1 ; i <= xhci->max_slots; i++) {if (!xhci->devs[i])continue ;
drivers/usb/host/xhci-mem.c +13 -18
@@ -918,7 +918,7 @@ static void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_i/* is this a hub device that added a tt_info to the tts list */ if (tt_info->slot_id == slot_id) {/* are any devices using this tt_info? */ -for (i =1 ; i <HCS_MAX_SLOTS (xhci->hcs_params1); i++) { +for (i =1 ; i < xhci->max_slots; i++) { vdev = xhci->devs[i];if (vdev && (vdev->tt_info == tt_info)) xhci_free_virt_devices_depth_first(
@@ -1864,7 +1864,7 @@ EXPORT_SYMBOL_GPL(xhci_remove_secondary_interrupter);void xhci_mem_cleanup (struct xhci_hcd *xhci) {struct device *dev =xhci_to_hcd (xhci)->self.sysdev; -int i, j, num_ports; +int i, j;cancel_delayed_work_sync (&xhci->cmd_timer);
@@ -1883,8 +1883,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)xhci_dbg_trace (xhci, trace_xhci_dbg_init,"Freed command ring" );xhci_cleanup_command_queue (xhci); - num_ports =HCS_MAX_PORTS (xhci->hcs_params1); -for (i =0 ; i < num_ports && xhci->rh_bw; i++) { +for (i =0 ; i < xhci->max_ports && xhci->rh_bw; i++) {struct xhci_interval_bw_table *bwt = &xhci->rh_bw[i].bw_table;for (j =0 ; j <XHCI_MAX_INTERVAL ; j++) {struct list_head *ep = &bwt->interval_bw[j].endpoints;
@@ -1893,7 +1892,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) } } -for (i =HCS_MAX_SLOTS (xhci->hcs_params1); i >0 ; i--) +for (i = xhci->max_slots; i >0 ; i--)xhci_free_virt_devices_depth_first (xhci, i);dma_pool_destroy (xhci->segment_pool);
@@ -1924,7 +1923,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)if (!xhci->rh_bw) goto no_bw; -for (i =0 ; i < num_ports; i++) { +for (i =0 ; i < xhci->max_ports; i++) {struct xhci_tt_bw_info *tt, *n;list_for_each_entry_safe (tt, n, &xhci->rh_bw[i].tts, tt_list) {list_del (&tt->tt_list);
@@ -2125,7 +2124,7 @@ static void xhci_create_rhub_port_array(struct xhci_hcd *xhci,if (!rhub->ports)return ; -for (i =0 ; i <HCS_MAX_PORTS (xhci->hcs_params1); i++) { +for (i =0 ; i < xhci->max_ports; i++) {if (xhci->hw_ports[i].rhub != rhub || xhci->hw_ports[i].hcd_portnum ==DUPLICATE_ENTRY )continue ;
@@ -2148,19 +2147,17 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) {void __iomem *base;u32 offset; -unsigned int num_ports;int i, j;int cap_count =0 ;u32 cap_start;struct device *dev =xhci_to_hcd (xhci)->self.sysdev; - num_ports =HCS_MAX_PORTS (xhci->hcs_params1); - xhci->hw_ports =kcalloc_node (num_ports,sizeof (*xhci->hw_ports), - flags,dev_to_node (dev)); + xhci->hw_ports =kcalloc_node (xhci->max_ports,sizeof (*xhci->hw_ports), + flags,dev_to_node (dev));if (!xhci->hw_ports)return -ENOMEM ; -for (i =0 ; i < num_ports; i++) { +for (i =0 ; i < xhci->max_ports; i++) { xhci->hw_ports[i].addr = &xhci->op_regs->port_regs[i].portsc; xhci->hw_ports[i].hw_portnum = i;
@@ -2168,11 +2165,10 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)init_completion (&xhci->hw_ports[i].u3exit_done); } - xhci->rh_bw =kcalloc_node (num_ports,sizeof (*xhci->rh_bw), flags, -dev_to_node (dev)); + xhci->rh_bw =kcalloc_node (xhci->max_ports,sizeof (*xhci->rh_bw), flags,dev_to_node (dev));if (!xhci->rh_bw)return -ENOMEM ; -for (i =0 ; i < num_ports; i++) { +for (i =0 ; i < xhci->max_ports; i++) {struct xhci_interval_bw_table *bw_table;INIT_LIST_HEAD (&xhci->rh_bw[i].tts);
@@ -2204,9 +2200,8 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) offset = cap_start;while (offset) { -xhci_add_in_port (xhci, num_ports, base + offset, cap_count); -if (xhci->usb2_rhub.num_ports + xhci->usb3_rhub.num_ports == - num_ports) +xhci_add_in_port (xhci, xhci->max_ports, base + offset, cap_count); +if (xhci->usb2_rhub.num_ports + xhci->usb3_rhub.num_ports == xhci->max_ports)break ; offset =xhci_find_next_ext_cap (base, offset,XHCI_EXT_CAPS_PROTOCOL );
drivers/usb/host/xhci-pci.c +4 -5
@@ -163,13 +163,12 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd)/* * calculate number of MSI-X vectors supported. -* - HCS_MAX_INTRS: the max number of interrupts the host can handle, -* with max number of interrupters based on the xhci HCSPARAMS1. +* - max_interrupters: the max number of interrupters requested, capped +* to the number the host can handle based on the xhci HCSPARAMS1. * - num_online_cpus: maximum MSI-X vectors per CPUs core. * Add additional 1 vector to ensure always available interrupt. */ - xhci->nvecs =min (num_online_cpus () +1 , -HCS_MAX_INTRS (xhci->hcs_params1)); + xhci->nvecs =min (num_online_cpus () +1 , xhci->max_interrupters);/* TODO: Check with MSI Soc for sysdev */ xhci->nvecs = pci_alloc_irq_vectors(pdev, 1, xhci->nvecs,
@@ -898,7 +897,7 @@ static int xhci_pci_poweroff_late(struct usb_hcd *hcd, bool do_wakeup)if (!(xhci->quirks &XHCI_RESET_TO_DEFAULT ))return 0 ; -for (i =0 ; i <HCS_MAX_PORTS (xhci->hcs_params1); i++) { +for (i =0 ; i < xhci->max_ports; i++) { port = &xhci->hw_ports[i]; portsc =readl (port->addr);
drivers/usb/host/xhci-ring.c +2 -4
@@ -1324,7 +1324,7 @@ void xhci_hc_died(struct xhci_hcd *xhci)xhci_cleanup_command_queue (xhci);/* return any pending urbs, remove may be waiting for them */ -for (i =0 ; i <=HCS_MAX_SLOTS (xhci->hcs_params1); i++) { +for (i =0 ; i <= xhci->max_slots; i++) {if (!xhci->devs[i])continue ;for (j =0 ; j <31 ; j++)
@@ -1954,7 +1954,6 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event)struct usb_hcd *hcd;u32 port_id;u32 portsc, cmd_reg; -int max_ports;unsigned int hcd_portnum;struct xhci_bus_state *bus_state;bool bogus_port_status = false;
@@ -1966,9 +1965,8 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event) "WARN: xHC returned failed port status event\n"); port_id =GET_PORT_ID (le32_to_cpu (event->generic.field[0 ])); - max_ports =HCS_MAX_PORTS (xhci->hcs_params1); -if ((port_id <=0 ) || (port_id > max_ports)) { +if ((port_id <=0 ) || (port_id > xhci->max_ports)) {xhci_warn (xhci,"Port change event with invalid port ID %d\n" , port_id);return ;
drivers/usb/host/xhci.c +8 -7
@@ -276,8 +276,7 @@ static void xhci_zero_64b_regs(struct xhci_hcd *xhci)if (upper_32_bits (val))xhci_write_64 (xhci,0 , &xhci->op_regs->cmd_ring); - intrs =min_t (u32,HCS_MAX_INTRS (xhci->hcs_params1), -ARRAY_SIZE (xhci->run_regs->ir_set)); + intrs =min_t (u32, xhci->max_interrupters,ARRAY_SIZE (xhci->run_regs->ir_set));for (i =0 ; i < intrs; i++) {struct xhci_intr_reg __iomem *ir;
@@ -4040,7 +4039,7 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)xhci_err (xhci,"Error while assigning device slot ID: %s\n" ,xhci_trb_comp_code_string (command->status));xhci_err (xhci,"Max number of devices this xHCI host supports is %u.\n" , -HCS_MAX_SLOTS (xhci->hcs_params1)); + xhci->max_slots);xhci_free_command (xhci, command);return 0 ; }
@@ -5225,6 +5224,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)*/ struct device *dev = hcd->self.sysdev;int retval; +u32 hcs_params1;/* Accept arbitrarily long scatter-gather lists */ hcd->self.sg_tablesize = ~0 ;
@@ -5250,7 +5250,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) xhci->run_regs = hcd->regs + (readl (&xhci->cap_regs->run_regs_off) &RTSOFF_MASK );/* Cache read-only capability registers */ - xhci->hcs_params1 =readl (&xhci->cap_regs->hcs_params1); + hcs_params1 =readl (&xhci->cap_regs->hcs_params1); xhci->hcs_params2 =readl (&xhci->cap_regs->hcs_params2); xhci->hcs_params3 =readl (&xhci->cap_regs->hcs_params3); xhci->hci_version =HC_VERSION (readl (&xhci->cap_regs->hc_capbase));
@@ -5259,10 +5259,11 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) xhci->hcc_params2 =readl (&xhci->cap_regs->hcc_params2); xhci->dma_mask_bits =64 ; + xhci->max_slots =HCS_MAX_SLOTS (hcs_params1); + xhci->max_ports =HCS_MAX_PORTS (hcs_params1);/* xhci-plat or xhci-pci might have set max_interrupters already */ -if ((!xhci->max_interrupters) || - xhci->max_interrupters >HCS_MAX_INTRS (xhci->hcs_params1)) - xhci->max_interrupters =HCS_MAX_INTRS (xhci->hcs_params1); +if ((!xhci->max_interrupters) || xhci->max_interrupters >HCS_MAX_INTRS (hcs_params1)) + xhci->max_interrupters =HCS_MAX_INTRS (hcs_params1); xhci->quirks |= quirks;
drivers/usb/host/xhci.h +2 -1
@@ -1492,7 +1492,6 @@ struct xhci_hcd {struct xhci_doorbell_array __iomem *dba;/* Cached register copies of read-only HC data */ -__u32 hcs_params1;__u32 hcs_params2;__u32 hcs_params3;__u32 hcc_params;
@@ -1503,6 +1502,8 @@ struct xhci_hcd {/* packed release number */ u16 hci_version;u16 max_interrupters; +u8 max_slots; +u8 max_ports;/* imod_interval in ns (I * 250ns) */ u32 imod_interval;u32 page_size;
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 4/4 usb: xhci: bail out of setup if the controller is inaccessible
In reply to Sasha LevinFrom: Breno Leitao <[email protected]>
[ Upstream commit 78203d5b54a40f0e36196ebf31c9c7a380fc8811 ]
xhci_gen_setup() locates the operational registers using the capability length read from the very first register:
xhci->op_regs = hcd->regs + HC_LENGTH(readl(&xhci->cap_regs->hc_capbase));
If the controller is dead or has dropped off the bus, that read returns ~0, HC_LENGTH() truncates it to 0xff, and op_regs ends up 0xff bytes past the page-aligned MMIO base, i.e. unaligned. The first access through it, xhci_halt() -> xhci_handshake() reading op_regs->status, is then an unaligned readl() on device memory. arm64 faults on unaligned device accesses, so instead of xhci_handshake() catching the all-ones value and returning -ENODEV, setup oopses:
xhci-pci-renesas 0005:08:00.0: Unable to change power state from D3cold to D0, device inaccessible xhci-pci-renesas 0005:08:00.0: xHCI Host Controller
xhci-pci-renesas 0005:08:00.0: new USB bus registered, assigned bus number 1
Unable to handle kernel paging request at virtual address ffff80030a770103
ESR = 0x0000000096000021
FSC = 0x21: alignment fault
Internal error: Oops: 0000000096000021 [#1] SMP
pc : xhci_halt [xhci_hcd]
Call trace:
xhci_halt
xhci_gen_setup
xhci_pci_setup
usb_add_hcd
usb_hcd_pci_probe
xhci_pci_common_probe
xhci_pci_renesas_probeThis was hit with a Renesas uPD720201 that failed to power up ("Unable to change power state from D3cold to D0, device inaccessible") yet still reached the HCD probe path.
Read the capability register once, and if it reads back the all-ones value (as xhci_handshake() and xhci_reset() already test for), abort setup with -ENODEV before op_regs is derived from it. Reading it once also avoids re-reading a register that may change under a concurrent hot-removal.
--- drivers/usb/host/xhci.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
drivers/usb/host/xhci.c +8 -3
@@ -5225,6 +5225,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)struct device *dev = hcd->self.sysdev;int retval;u32 hcs_params1; +u32 hc_capbase;/* Accept arbitrarily long scatter-gather lists */ hcd->self.sg_tablesize = ~0 ;
@@ -5245,15 +5246,19 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)mutex_init (&xhci->mutex); xhci->main_hcd = hcd; xhci->cap_regs = hcd->regs; - xhci->op_regs = hcd->regs + -HC_LENGTH (readl (&xhci->cap_regs->hc_capbase)); + hc_capbase =readl (&xhci->cap_regs->hc_capbase); +if (hc_capbase ==U32_MAX ) { +xhci_warn (xhci,"Host controller not accessible, removed?\n" ); +return -ENODEV ; + } + xhci->op_regs = hcd->regs +HC_LENGTH (hc_capbase); xhci->run_regs = hcd->regs + (readl (&xhci->cap_regs->run_regs_off) &RTSOFF_MASK );/* Cache read-only capability registers */ hcs_params1 =readl (&xhci->cap_regs->hcs_params1); xhci->hcs_params2 =readl (&xhci->cap_regs->hcs_params2); xhci->hcs_params3 =readl (&xhci->cap_regs->hcs_params3); - xhci->hci_version =HC_VERSION (readl (&xhci->cap_regs->hc_capbase)); + xhci->hci_version =HC_VERSION (hc_capbase); xhci->hcc_params =readl (&xhci->cap_regs->hcc_params);if (xhci->hci_version >0x100 ) xhci->hcc_params2 =readl (&xhci->cap_regs->hcc_params2);
Signature · 1 line
2.53.0