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;