154359Sroberto/*- 254359Sroberto * SPDX-License-Identifier: BSD-3-Clause 354359Sroberto * 454359Sroberto * Copyright (c) 1992, 1993 582498Sroberto * The Regents of the University of California. All rights reserved. 654359Sroberto * 754359Sroberto * This software was developed by the Computer Systems Engineering group 854359Sroberto * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 954359Sroberto * contributed to Berkeley. 1054359Sroberto * 1154359Sroberto * Redistribution and use in source and binary forms, with or without 1254359Sroberto * modification, are permitted provided that the following conditions 1354359Sroberto * are met: 1454359Sroberto * 1. Redistributions of source code must retain the above copyright 1554359Sroberto * notice, this list of conditions and the following disclaimer. 1654359Sroberto * 2. Redistributions in binary form must reproduce the above copyright 1754359Sroberto * notice, this list of conditions and the following disclaimer in the 1854359Sroberto * documentation and/or other materials provided with the distribution. 1954359Sroberto * 3. Neither the name of the University nor the names of its contributors 2054359Sroberto * may be used to endorse or promote products derived from this software 2154359Sroberto * without specific prior written permission. 2254359Sroberto * 2354359Sroberto * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2454359Sroberto * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2554359Sroberto * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2654359Sroberto * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2754359Sroberto * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2854359Sroberto * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2954359Sroberto * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3054359Sroberto * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3154359Sroberto * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3254359Sroberto * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3354359Sroberto * SUCH DAMAGE. 3454359Sroberto * 3554359Sroberto */ 3654359Sroberto 3754359Sroberto#include <sys/cdefs.h> 3854359Sroberto#include "opt_ddb.h" 3954359Sroberto 4054359Sroberto#include <sys/param.h> 4154359Sroberto#include <sys/kernel.h> 4254359Sroberto#include <sys/linker.h> 4354359Sroberto#include <sys/lock.h> 4454359Sroberto#include <sys/malloc.h> 4554359Sroberto#include <sys/mutex.h> 4654359Sroberto#include <sys/systm.h> 4754359Sroberto 4854359Sroberto/* 4954359Sroberto * Autoconfiguration subroutines. 5054359Sroberto */ 5154359Sroberto 5254359Sroberto/* 53285612Sdelphij * "Interrupt driven config" functions. 54285612Sdelphij */ 55285612Sdelphijstatic STAILQ_HEAD(, intr_config_hook) intr_config_hook_list = 56285612Sdelphij STAILQ_HEAD_INITIALIZER(intr_config_hook_list); 57285612Sdelphijstatic struct intr_config_hook *next_to_notify; 58285612Sdelphijstatic struct mtx intr_config_hook_lock; 59285612SdelphijMTX_SYSINIT(intr_config_hook, &intr_config_hook_lock, "intr config", MTX_DEF); 60285612Sdelphij 61285612Sdelphij/* ARGSUSED */ 62285612Sdelphijstatic void run_interrupt_driven_config_hooks(void); 63285612Sdelphij 64285612Sdelphij/* 65285612Sdelphij * Private data and a shim function for implementing config_interhook_oneshot(). 66285612Sdelphij */ 67285612Sdelphijstruct oneshot_config_hook { 6854359Sroberto struct intr_config_hook 6954359Sroberto och_hook; /* Must be first */ 7054359Sroberto ich_func_t och_func; 71285612Sdelphij void *och_arg; 7254359Sroberto}; 7354359Sroberto 74285612Sdelphijstatic void 75200576Srobertoconfig_intrhook_oneshot_func(void *arg) 76200576Sroberto{ 77200576Sroberto struct oneshot_config_hook *ohook; 78200576Sroberto 79200576Sroberto ohook = arg; 80132451Sroberto ohook->och_func(ohook->och_arg); 81132451Sroberto config_intrhook_disestablish(&ohook->och_hook); 8254359Sroberto free(ohook, M_DEVBUF); 8354359Sroberto} 8454359Sroberto 8554359Sroberto/* 86285612Sdelphij * If we wait too long for an interrupt-driven config hook to return, print 87285612Sdelphij * a diagnostic. 88285612Sdelphij */ 89285612Sdelphij#define WARNING_INTERVAL_SECS 60 90285612Sdelphijstatic void 9154359Srobertorun_interrupt_driven_config_hooks_warning(int warned) 9254359Sroberto{ 9354359Sroberto struct intr_config_hook *hook_entry; 9454359Sroberto char namebuf[64]; 9554359Sroberto long offset; 9654359Sroberto 9754359Sroberto if (warned < 6) { 9854359Sroberto printf("run_interrupt_driven_hooks: still waiting after %d " 9954359Sroberto "seconds for", warned * WARNING_INTERVAL_SECS); 10054359Sroberto STAILQ_FOREACH(hook_entry, &intr_config_hook_list, ich_links) { 10154359Sroberto if (linker_search_symbol_name( 10254359Sroberto (caddr_t)hook_entry->ich_func, namebuf, 10354359Sroberto sizeof(namebuf), &offset) == 0) 10454359Sroberto printf(" %s", namebuf); 10554359Sroberto else 106330141Sdelphij printf(" %p", hook_entry->ich_func); 10754359Sroberto } 108132451Sroberto printf("\n"); 109132451Sroberto } 110132451Sroberto KASSERT(warned < 6, 111132451Sroberto ("run_interrupt_driven_config_hooks: waited too long")); 112132451Sroberto} 113132451Sroberto 114285612Sdelphijstatic void 115285612Sdelphijrun_interrupt_driven_config_hooks(void) 116285612Sdelphij{ 117132451Sroberto static int running; 118132451Sroberto struct intr_config_hook *hook_entry; 119132451Sroberto 120132451Sroberto TSENTER(); 121285612Sdelphij mtx_lock(&intr_config_hook_lock); 122285612Sdelphij 123285612Sdelphij /* 124285612Sdelphij * If hook processing is already active, any newly 125285612Sdelphij * registered hooks will eventually be notified. 12654359Sroberto * Let the currently running session issue these 127285612Sdelphij * notifications. 128285612Sdelphij */ 129285612Sdelphij if (running != 0) { 130285612Sdelphij mtx_unlock(&intr_config_hook_lock); 131285612Sdelphij return; 132285612Sdelphij } 133285612Sdelphij running = 1; 134285612Sdelphij 135285612Sdelphij while (next_to_notify != NULL) { 136285612Sdelphij hook_entry = next_to_notify; 137285612Sdelphij next_to_notify = STAILQ_NEXT(hook_entry, ich_links); 138285612Sdelphij hook_entry->ich_state = ICHS_RUNNING; 139285612Sdelphij mtx_unlock(&intr_config_hook_lock); 140285612Sdelphij (*hook_entry->ich_func)(hook_entry->ich_arg); 141285612Sdelphij mtx_lock(&intr_config_hook_lock); 142285612Sdelphij } 143285612Sdelphij 144285612Sdelphij running = 0; 145285612Sdelphij mtx_unlock(&intr_config_hook_lock); 146285612Sdelphij TSEXIT(); 147285612Sdelphij} 148285612Sdelphij 149285612Sdelphijstatic void 150285612Sdelphijboot_run_interrupt_driven_config_hooks(void *dummy) 151285612Sdelphij{ 152285612Sdelphij int warned; 153285612Sdelphij 154285612Sdelphij run_interrupt_driven_config_hooks(); 155285612Sdelphij 156285612Sdelphij /* Block boot processing until all hooks are disestablished. */ 157285612Sdelphij TSWAIT("config hooks"); 158285612Sdelphij mtx_lock(&intr_config_hook_lock); 159285612Sdelphij warned = 0; 160285612Sdelphij while (!STAILQ_EMPTY(&intr_config_hook_list)) { 161285612Sdelphij if (msleep(&intr_config_hook_list, &intr_config_hook_lock, 162285612Sdelphij 0, "conifhk", WARNING_INTERVAL_SECS * hz) == 163285612Sdelphij EWOULDBLOCK) { 164285612Sdelphij mtx_unlock(&intr_config_hook_lock); 165285612Sdelphij warned++; 166285612Sdelphij run_interrupt_driven_config_hooks_warning(warned); 167285612Sdelphij mtx_lock(&intr_config_hook_lock); 168285612Sdelphij } 169285612Sdelphij } 170285612Sdelphij mtx_unlock(&intr_config_hook_lock); 171285612Sdelphij TSUNWAIT("config hooks"); 172285612Sdelphij} 173285612Sdelphij 174285612SdelphijSYSINIT(intr_config_hooks, SI_SUB_INT_CONFIG_HOOKS, SI_ORDER_FIRST, 175285612Sdelphij boot_run_interrupt_driven_config_hooks, NULL); 176285612Sdelphij 177285612Sdelphij/* 178285612Sdelphij * Register a hook that will be called after "cold" 179285612Sdelphij * autoconfiguration is complete and interrupts can 180285612Sdelphij * be used to complete initialization. 181285612Sdelphij */ 182285612Sdelphijint 183285612Sdelphijconfig_intrhook_establish(struct intr_config_hook *hook) 184285612Sdelphij{ 185285612Sdelphij struct intr_config_hook *hook_entry; 186285612Sdelphij 187285612Sdelphij TSHOLD("config hooks"); 188285612Sdelphij mtx_lock(&intr_config_hook_lock); 189285612Sdelphij STAILQ_FOREACH(hook_entry, &intr_config_hook_list, ich_links) 190285612Sdelphij if (hook_entry == hook) 191285612Sdelphij break; 192285612Sdelphij if (hook_entry != NULL) { 193285612Sdelphij mtx_unlock(&intr_config_hook_lock); 194285612Sdelphij printf("config_intrhook_establish: establishing an " 195285612Sdelphij "already established hook.\n"); 196285612Sdelphij return (1); 197285612Sdelphij } 198285612Sdelphij STAILQ_INSERT_TAIL(&intr_config_hook_list, hook, ich_links); 199285612Sdelphij if (next_to_notify == NULL) 200285612Sdelphij next_to_notify = hook; 201285612Sdelphij hook->ich_state = ICHS_QUEUED; 202285612Sdelphij mtx_unlock(&intr_config_hook_lock); 203285612Sdelphij if (cold == 0) 204285612Sdelphij /* 205285612Sdelphij * XXX Call from a task since not all drivers expect 206285612Sdelphij * to be re-entered at the time a hook is established. 207285612Sdelphij */ 208285612Sdelphij /* XXX Sufficient for modules loaded after initial config??? */ 209285612Sdelphij run_interrupt_driven_config_hooks(); 210285612Sdelphij return (0); 211285612Sdelphij} 212285612Sdelphij 213285612Sdelphij/* 214285612Sdelphij * Register a hook function that is automatically unregistered after it runs. 215285612Sdelphij */ 216285612Sdelphijvoid 217289997Sglebiusconfig_intrhook_oneshot(ich_func_t func, void *arg) 218285612Sdelphij{ 219285612Sdelphij struct oneshot_config_hook *ohook; 220285612Sdelphij 221285612Sdelphij ohook = malloc(sizeof(*ohook), M_DEVBUF, M_WAITOK); 222285612Sdelphij ohook->och_func = func; 223285612Sdelphij ohook->och_arg = arg; 224285612Sdelphij ohook->och_hook.ich_func = config_intrhook_oneshot_func; 225330141Sdelphij ohook->och_hook.ich_arg = ohook; 226285612Sdelphij config_intrhook_establish(&ohook->och_hook); 227285612Sdelphij} 228285612Sdelphij 229285612Sdelphijstatic void 230285612Sdelphijconfig_intrhook_disestablish_locked(struct intr_config_hook *hook) 231285612Sdelphij{ 232285612Sdelphij struct intr_config_hook *hook_entry; 233285612Sdelphij 234285612Sdelphij STAILQ_FOREACH(hook_entry, &intr_config_hook_list, ich_links) 235285612Sdelphij if (hook_entry == hook) 236285612Sdelphij break; 237285612Sdelphij if (hook_entry == NULL) 238285612Sdelphij panic("config_intrhook_disestablish: disestablishing an " 239285612Sdelphij "unestablished hook"); 240285612Sdelphij 241285612Sdelphij if (next_to_notify == hook) 242285612Sdelphij next_to_notify = STAILQ_NEXT(hook, ich_links); 243285612Sdelphij STAILQ_REMOVE(&intr_config_hook_list, hook, intr_config_hook, ich_links); 244285612Sdelphij TSRELEASE("config hooks"); 245289997Sglebius 246285612Sdelphij /* Wakeup anyone watching the list */ 247285612Sdelphij hook->ich_state = ICHS_DONE; 248285612Sdelphij wakeup(&intr_config_hook_list); 249285612Sdelphij} 250285612Sdelphij 251285612Sdelphijvoid 252285612Sdelphijconfig_intrhook_disestablish(struct intr_config_hook *hook) 253285612Sdelphij{ 254285612Sdelphij mtx_lock(&intr_config_hook_lock); 255285612Sdelphij config_intrhook_disestablish_locked(hook); 256285612Sdelphij mtx_unlock(&intr_config_hook_lock); 257285612Sdelphij} 25854359Sroberto 25954359Srobertoint 26054359Srobertoconfig_intrhook_drain(struct intr_config_hook *hook) 26154359Sroberto{ 26254359Sroberto mtx_lock(&intr_config_hook_lock); 26354359Sroberto 26454359Sroberto /* 26554359Sroberto * The config hook has completed, so just return. 26654359Sroberto */ 26754359Sroberto if (hook->ich_state == ICHS_DONE) { 26854359Sroberto mtx_unlock(&intr_config_hook_lock); 26954359Sroberto return (ICHS_DONE); 270285612Sdelphij } 27154359Sroberto 272285612Sdelphij /* 273285612Sdelphij * The config hook hasn't started running, just call disestablish. 27454359Sroberto */ 27554359Sroberto if (hook->ich_state == ICHS_QUEUED) { 27654359Sroberto config_intrhook_disestablish_locked(hook); 27754359Sroberto mtx_unlock(&intr_config_hook_lock); 27854359Sroberto return (ICHS_QUEUED); 27954359Sroberto } 28054359Sroberto 28154359Sroberto /* 28254359Sroberto * The config hook is running, so wait for it to complete and return. 28354359Sroberto */ 28454359Sroberto while (hook->ich_state != ICHS_DONE) { 28554359Sroberto if (msleep(&intr_config_hook_list, &intr_config_hook_lock, 28654359Sroberto 0, "confhd", hz) == EWOULDBLOCK) { 28754359Sroberto // XXX do I whine? 288285612Sdelphij } 289285612Sdelphij } 29054359Sroberto mtx_unlock(&intr_config_hook_lock); 29154359Sroberto return (ICHS_RUNNING); 292285612Sdelphij} 29354359Sroberto 294285612Sdelphij#ifdef DDB 295285612Sdelphij#include <ddb/ddb.h> 296285612Sdelphij 29754359SrobertoDB_SHOW_COMMAND_FLAGS(conifhk, db_show_conifhk, DB_CMD_MEMSAFE) 298285612Sdelphij{ 299285612Sdelphij struct intr_config_hook *hook_entry; 30054359Sroberto char namebuf[64]; 30154359Sroberto long offset; 30254359Sroberto 30354359Sroberto STAILQ_FOREACH(hook_entry, &intr_config_hook_list, ich_links) { 304285612Sdelphij if (linker_ddb_search_symbol_name( 30554359Sroberto (caddr_t)hook_entry->ich_func, namebuf, sizeof(namebuf), 306132451Sroberto &offset) == 0) { 307132451Sroberto db_printf("hook: %p at %s+%#lx arg: %p\n", 308285612Sdelphij hook_entry->ich_func, namebuf, offset, 309285612Sdelphij hook_entry->ich_arg); 310285612Sdelphij } else { 311285612Sdelphij db_printf("hook: %p at ??+?? arg %p\n", 312285612Sdelphij hook_entry->ich_func, hook_entry->ich_arg); 313285612Sdelphij } 314285612Sdelphij } 315285612Sdelphij} 316285612Sdelphij#endif /* DDB */ 317285612Sdelphij