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;