Print this page
MFV: illumos-gate@9a48f6c443e5968307491ba7cc134bbdd0328801
9806 ehci_take_control() can infinite loop due to PCI invalid reads
Reviewed by: Dan McDonald <danmcd@joyent.com>
Reviewed by: Jason King <jason.king@joyent.com>
Reviewed by: Patrick Mooney <patrick.mooney@joyent.com>
Reviewed by: Toomas Soome <tsoome@me.com>
Reviewed by: Garrett D'Amore <garrett@damore.org>
Approved by: Richard Lowe <richlowe@richlowe.net>
Author: Robert Mustacchi <rm@joyent.com>
NEX-16600 "No SOF interrupts have been received" on HPE ProLiant DL380 Gen10, leading to non-working USB EHCI controller
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>

@@ -19,13 +19,18 @@
  * CDDL HEADER END
  */
 
 /*
  * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, Joyent, Inc.
  */
 
 /*
+ * Copyright 2018 Nexenta Systems, Inc.
+ */
+
+/*
  * EHCI Host Controller Driver (EHCI)
  *
  * The EHCI driver is a software driver which interfaces to the Universal
  * Serial Bus layer (USBA) and the Host Controller (HC). The interface to
  * the Host Controller is defined by the EHCI Host Controller Interface.

@@ -743,11 +748,11 @@
                     "running in simulated polled mode");
 
                 (void) thread_create(NULL, 0, ehci_poll_intr, ehcip, 0, &p0,
                     TS_RUN, maxclsyspri);
 
-                goto skip_intr;
+                return (DDI_SUCCESS);
         }
 
 #if defined(__x86)
         /*
          * Make sure that the interrupt pin is connected to the

@@ -817,15 +822,10 @@
 
                 ehcip->ehci_intr_type = DDI_INTR_TYPE_FIXED;
                 ehcip->ehci_flags |= EHCI_INTR;
         }
 
-skip_intr:
-        /* Create prototype for advance on async schedule */
-        cv_init(&ehcip->ehci_async_schedule_advance_cv,
-            NULL, CV_DRIVER, NULL);
-
         return (DDI_SUCCESS);
 }
 
 
 /*

@@ -832,12 +832,11 @@
  * ehci_add_intrs:
  *
  * Register FIXED or MSI interrupts.
  */
 static int
-ehci_add_intrs(ehci_state_t     *ehcip,
-                int             intr_type)
+ehci_add_intrs(ehci_state_t *ehcip, int intr_type)
 {
         int     actual, avail, intr_size, count = 0;
         int     i, flag, ret;
 
         USB_DPRINTF_L4(PRINT_MASK_ATTA, ehcip->ehci_log_hdl,

@@ -1172,70 +1171,17 @@
         }
 
         return (DDI_SUCCESS);
 }
 
-
 /*
- * ehci_init_check_status
- *
- * Check if EHCI host controller is running
- */
-int
-ehci_init_check_status(ehci_state_t     *ehcip)
-{
-        clock_t                 sof_time_wait;
-
-        /*
-         * Get the number of clock ticks to wait.
-         * This is based on the maximum time it takes for a frame list rollover
-         * and maximum time wait for SOFs to begin.
-         */
-        sof_time_wait = drv_usectohz((EHCI_NUM_PERIODIC_FRAME_LISTS * 1000) +
-            EHCI_SOF_TIMEWAIT);
-
-        /* Tell the ISR to broadcast ehci_async_schedule_advance_cv */
-        ehcip->ehci_flags |= EHCI_CV_INTR;
-
-        /* We need to add a delay to allow the chip time to start running */
-        (void) cv_reltimedwait(&ehcip->ehci_async_schedule_advance_cv,
-            &ehcip->ehci_int_mutex, sof_time_wait, TR_CLOCK_TICK);
-
-        /*
-         * Check EHCI host controller is running, otherwise return failure.
-         */
-        if ((ehcip->ehci_flags & EHCI_CV_INTR) ||
-            (Get_OpReg(ehci_status) & EHCI_STS_HOST_CTRL_HALTED)) {
-
-                USB_DPRINTF_L0(PRINT_MASK_ATTA, ehcip->ehci_log_hdl,
-                    "No SOF interrupts have been received, this USB EHCI host"
-                    "controller is unusable");
-
-                /*
-                 * Route all Root hub ports to Classic host
-                 * controller, in case this is an unusable ALI M5273
-                 * EHCI controller.
-                 */
-                if (ehcip->ehci_vendor_id == PCI_VENDOR_ALI) {
-                        Set_OpReg(ehci_config_flag, EHCI_CONFIG_FLAG_CLASSIC);
-                }
-
-                return (DDI_FAILURE);
-        }
-
-        return (DDI_SUCCESS);
-}
-
-
-/*
  * ehci_init_ctlr:
  *
  * Initialize the Host Controller (HC).
  */
 int
-ehci_init_ctlr(ehci_state_t     *ehcip,
-                int             init_type)
+ehci_init_ctlr(ehci_state_t *ehcip, int init_type)
 {
         USB_DPRINTF_L4(PRINT_MASK_ATTA, ehcip->ehci_log_hdl, "ehci_init_ctlr:");
 
         if (init_type == EHCI_NORMAL_INITIALIZATION) {
 

@@ -1322,31 +1268,19 @@
             (EHCI_CMD_01_INTR | EHCI_CMD_HOST_CTRL_RUN)));
 
         ASSERT(Get_OpReg(ehci_command) & EHCI_CMD_HOST_CTRL_RUN);
 
         if (init_type == EHCI_NORMAL_INITIALIZATION) {
-
                 if (ehci_init_workaround(ehcip) != DDI_SUCCESS) {
 
                         /* Set host controller soft state to error */
                         ehcip->ehci_hc_soft_state = EHCI_CTLR_ERROR_STATE;
 
                         return (DDI_FAILURE);
                 }
-
-                if (ehci_init_check_status(ehcip) != DDI_SUCCESS) {
-
-                        /* Set host controller soft state to error */
-                        ehcip->ehci_hc_soft_state = EHCI_CTLR_ERROR_STATE;
-
-                        return (DDI_FAILURE);
                 }
 
-                USB_DPRINTF_L4(PRINT_MASK_ATTA, ehcip->ehci_log_hdl,
-                    "ehci_init_ctlr: SOF's have started");
-        }
-
         /* Route all Root hub ports to EHCI host controller */
         Set_OpReg(ehci_config_flag, EHCI_CONFIG_FLAG_EHCI);
 
         return (DDI_SUCCESS);
 }

@@ -1404,10 +1338,23 @@
 
                 /* Get the extended capability value. */
                 extended_cap = pci_config_get32(ehcip->ehci_config_handle,
                     extended_cap_offset);
 
+                /*
+                 * It's possible that we'll receive an invalid PCI read here due
+                 * to something going wrong due to platform firmware. This has
+                 * been observed in the wild depending on the version of ACPI in
+                 * use. If this happens, we'll assume that the capability does
+                 * not exist and that we do not need to take control from the
+                 * BIOS.
+                 */
+                if (extended_cap == PCI_EINVAL32) {
+                        extended_cap_id = EHCI_EX_CAP_ID_RESERVED;
+                        break;
+                }
+
                 /* Get the capability ID */
                 extended_cap_id = (extended_cap & EHCI_EX_CAP_ID) >>
                     EHCI_EX_CAP_ID_SHIFT;
 
                 /* Check if the card support legacy */

@@ -1416,10 +1363,11 @@
                 }
 
                 /* Get the offset of the next capability */
                 extended_cap_offset = (extended_cap & EHCI_EX_CAP_NEXT_PTR) >>
                     EHCI_EX_CAP_NEXT_PTR_SHIFT;
+
         }
 
         /*
          * Unable to find legacy support in hardware's extended capability list.
          * This means we don't need to worry about BIOS handoff.

@@ -1929,13 +1877,10 @@
         }
 
         if (flags & EHCI_INTR) {
                 /* Destroy the mutex */
                 mutex_destroy(&ehcip->ehci_int_mutex);
-
-                /* Destroy the async schedule advance condition variable */
-                cv_destroy(&ehcip->ehci_async_schedule_advance_cv);
         }
 
         /* clean up kstat structs */
         ehci_destroy_stats(ehcip);
 

@@ -2974,11 +2919,12 @@
  * ehci_find_periodic_node:
  *
  * Based on the "real" array leaf node and interval, get the periodic node.
  */
 static uint_t
-ehci_find_periodic_node(uint_t leaf, int interval) {
+ehci_find_periodic_node(uint_t leaf, int interval)
+{
         uint_t  lattice_leaf;
         uint_t  height = ehci_lattice_height(interval);
         uint_t  pnode;
         int     i;