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,31 ****
--- 19,36 ----
   * 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,753 ****
                      "running in simulated polled mode");
  
                  (void) thread_create(NULL, 0, ehci_poll_intr, ehcip, 0, &p0,
                      TS_RUN, maxclsyspri);
  
!                 goto skip_intr;
          }
  
  #if defined(__x86)
          /*
           * Make sure that the interrupt pin is connected to the
--- 748,758 ----
                      "running in simulated polled mode");
  
                  (void) thread_create(NULL, 0, ehci_poll_intr, ehcip, 0, &p0,
                      TS_RUN, maxclsyspri);
  
!                 return (DDI_SUCCESS);
          }
  
  #if defined(__x86)
          /*
           * Make sure that the interrupt pin is connected to the
*** 817,831 ****
  
                  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);
  }
  
  
  /*
--- 822,831 ----
*** 832,843 ****
   * ehci_add_intrs:
   *
   * Register FIXED or MSI interrupts.
   */
  static int
! 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,
--- 832,842 ----
   * ehci_add_intrs:
   *
   * Register FIXED or MSI interrupts.
   */
  static int
! 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,1241 ****
          }
  
          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)
  {
          USB_DPRINTF_L4(PRINT_MASK_ATTA, ehcip->ehci_log_hdl, "ehci_init_ctlr:");
  
          if (init_type == EHCI_NORMAL_INITIALIZATION) {
  
--- 1171,1187 ----
          }
  
          return (DDI_SUCCESS);
  }
  
  /*
   * ehci_init_ctlr:
   *
   * Initialize the Host Controller (HC).
   */
  int
! 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,1352 ****
              (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);
  }
--- 1268,1286 ----
*** 1404,1413 ****
--- 1338,1360 ----
  
                  /* 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,1425 ****
--- 1363,1373 ----
                  }
  
                  /* 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,1941 ****
          }
  
          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);
  
--- 1877,1886 ----
*** 2974,2984 ****
   * 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) {
          uint_t  lattice_leaf;
          uint_t  height = ehci_lattice_height(interval);
          uint_t  pnode;
          int     i;
  
--- 2919,2930 ----
   * 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)
! {
          uint_t  lattice_leaf;
          uint_t  height = ehci_lattice_height(interval);
          uint_t  pnode;
          int     i;