1139804Simp/*-
21541Srgrimes * Copyright (c) 1992, 1993
31541Srgrimes *	The Regents of the University of California.  All rights reserved.
41541Srgrimes *
51541Srgrimes * This software was developed by the Computer Systems Engineering group
61541Srgrimes * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
71541Srgrimes * contributed to Berkeley.
81541Srgrimes *
91541Srgrimes * Redistribution and use in source and binary forms, with or without
101541Srgrimes * modification, are permitted provided that the following conditions
111541Srgrimes * are met:
121541Srgrimes * 1. Redistributions of source code must retain the above copyright
131541Srgrimes *    notice, this list of conditions and the following disclaimer.
141541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
151541Srgrimes *    notice, this list of conditions and the following disclaimer in the
161541Srgrimes *    documentation and/or other materials provided with the distribution.
171541Srgrimes * 4. Neither the name of the University nor the names of its contributors
181541Srgrimes *    may be used to endorse or promote products derived from this software
191541Srgrimes *    without specific prior written permission.
201541Srgrimes *
211541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241541Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311541Srgrimes * SUCH DAMAGE.
321541Srgrimes *
331541Srgrimes *	@(#)subr_autoconf.c	8.1 (Berkeley) 6/10/93
341541Srgrimes *
351541Srgrimes */
361541Srgrimes
37116182Sobrien#include <sys/cdefs.h>
38116182Sobrien__FBSDID("$FreeBSD: stable/11/sys/kern/subr_autoconf.c 323464 2017-09-11 21:32:35Z ian $");
39116182Sobrien
40180610Srwatson#include "opt_ddb.h"
41180610Srwatson
421541Srgrimes#include <sys/param.h>
4329680Sgibbs#include <sys/kernel.h>
44180616Srwatson#include <sys/linker.h>
45160509Sjhb#include <sys/lock.h>
46323464Sian#include <sys/malloc.h>
47160509Sjhb#include <sys/mutex.h>
4829680Sgibbs#include <sys/systm.h>
491541Srgrimes
501541Srgrimes/*
511541Srgrimes * Autoconfiguration subroutines.
521541Srgrimes */
531541Srgrimes
541541Srgrimes/*
5529680Sgibbs * "Interrupt driven config" functions.
5629680Sgibbs */
5760938Sjakestatic TAILQ_HEAD(, intr_config_hook) intr_config_hook_list =
5829680Sgibbs	TAILQ_HEAD_INITIALIZER(intr_config_hook_list);
59211236Sgibbsstatic struct intr_config_hook *next_to_notify;
60160509Sjhbstatic struct mtx intr_config_hook_lock;
61160509SjhbMTX_SYSINIT(intr_config_hook, &intr_config_hook_lock, "intr config", MTX_DEF);
6229680Sgibbs
6329680Sgibbs/* ARGSUSED */
64211236Sgibbsstatic void run_interrupt_driven_config_hooks(void);
65160509Sjhb
66180616Srwatson/*
67323464Sian * Private data and a shim function for implementing config_interhook_oneshot().
68323464Sian */
69323464Sianstruct oneshot_config_hook {
70323464Sian	struct intr_config_hook
71323464Sian			och_hook;		/* Must be first */
72323464Sian	ich_func_t	och_func;
73323464Sian	void		*och_arg;
74323464Sian};
75323464Sian
76323464Sianstatic void
77323464Sianconfig_intrhook_oneshot_func(void *arg)
78323464Sian{
79323464Sian	struct oneshot_config_hook *ohook;
80323464Sian
81323464Sian	ohook = arg;
82323464Sian	ohook->och_func(ohook->och_arg);
83323464Sian	config_intrhook_disestablish(&ohook->och_hook);
84323464Sian	free(ohook, M_DEVBUF);
85323464Sian}
86323464Sian
87323464Sian/*
88180616Srwatson * If we wait too long for an interrupt-driven config hook to return, print
89180616Srwatson * a diagnostic.
90180616Srwatson */
91180616Srwatson#define	WARNING_INTERVAL_SECS	60
9229680Sgibbsstatic void
93180616Srwatsonrun_interrupt_driven_config_hooks_warning(int warned)
94180616Srwatson{
95180616Srwatson	struct intr_config_hook *hook_entry;
96180616Srwatson	char namebuf[64];
97180616Srwatson	long offset;
98180616Srwatson
99180673Srwatson	if (warned < 6) {
100180673Srwatson		printf("run_interrupt_driven_hooks: still waiting after %d "
101180673Srwatson		    "seconds for", warned * WARNING_INTERVAL_SECS);
102180673Srwatson		TAILQ_FOREACH(hook_entry, &intr_config_hook_list, ich_links) {
103180673Srwatson			if (linker_search_symbol_name(
104180673Srwatson			    (caddr_t)hook_entry->ich_func, namebuf,
105180673Srwatson			    sizeof(namebuf), &offset) == 0)
106180673Srwatson				printf(" %s", namebuf);
107180673Srwatson			else
108180673Srwatson				printf(" %p", hook_entry->ich_func);
109180673Srwatson		}
110180673Srwatson		printf("\n");
111180616Srwatson	}
112180673Srwatson	KASSERT(warned < 6,
113180673Srwatson	    ("run_interrupt_driven_config_hooks: waited too long"));
114180616Srwatson}
115180616Srwatson
116180616Srwatsonstatic void
117211236Sgibbsrun_interrupt_driven_config_hooks()
11829680Sgibbs{
119211236Sgibbs	static int running;
120211236Sgibbs	struct intr_config_hook *hook_entry;
12129680Sgibbs
122160509Sjhb	mtx_lock(&intr_config_hook_lock);
123211236Sgibbs
124211236Sgibbs	/*
125211236Sgibbs	 * If hook processing is already active, any newly
126211236Sgibbs	 * registered hooks will eventually be notified.
127211236Sgibbs	 * Let the currently running session issue these
128211236Sgibbs	 * notifications.
129211236Sgibbs	 */
130211236Sgibbs	if (running != 0) {
131160509Sjhb		mtx_unlock(&intr_config_hook_lock);
132211236Sgibbs		return;
133211236Sgibbs	}
134211236Sgibbs	running = 1;
135211236Sgibbs
136211236Sgibbs	while (next_to_notify != NULL) {
137211236Sgibbs		hook_entry = next_to_notify;
138211236Sgibbs		next_to_notify = TAILQ_NEXT(hook_entry, ich_links);
139211236Sgibbs		mtx_unlock(&intr_config_hook_lock);
14051684Sn_hibma		(*hook_entry->ich_func)(hook_entry->ich_arg);
141160509Sjhb		mtx_lock(&intr_config_hook_lock);
14229680Sgibbs	}
14329680Sgibbs
144211236Sgibbs	running = 0;
145211236Sgibbs	mtx_unlock(&intr_config_hook_lock);
146211236Sgibbs}
147211236Sgibbs
148211236Sgibbsstatic void
149211236Sgibbsboot_run_interrupt_driven_config_hooks(void *dummy)
150211236Sgibbs{
151211236Sgibbs	int warned;
152211236Sgibbs
153211236Sgibbs	run_interrupt_driven_config_hooks();
154211236Sgibbs
155211236Sgibbs	/* Block boot processing until all hooks are disestablished. */
156211236Sgibbs	mtx_lock(&intr_config_hook_lock);
157180616Srwatson	warned = 0;
15851684Sn_hibma	while (!TAILQ_EMPTY(&intr_config_hook_list)) {
159180616Srwatson		if (msleep(&intr_config_hook_list, &intr_config_hook_lock,
160217075Sjhb		    0, "conifhk", WARNING_INTERVAL_SECS * hz) ==
161180673Srwatson		    EWOULDBLOCK) {
162180616Srwatson			mtx_unlock(&intr_config_hook_lock);
163180616Srwatson			warned++;
164180616Srwatson			run_interrupt_driven_config_hooks_warning(warned);
165180616Srwatson			mtx_lock(&intr_config_hook_lock);
166180616Srwatson		}
16729680Sgibbs	}
168160509Sjhb	mtx_unlock(&intr_config_hook_lock);
16929680Sgibbs}
170211236Sgibbs
17129680SgibbsSYSINIT(intr_config_hooks, SI_SUB_INT_CONFIG_HOOKS, SI_ORDER_FIRST,
172211236Sgibbs	boot_run_interrupt_driven_config_hooks, NULL);
17329680Sgibbs
17429680Sgibbs/*
17529680Sgibbs * Register a hook that will be called after "cold"
17629680Sgibbs * autoconfiguration is complete and interrupts can
17729680Sgibbs * be used to complete initialization.
17829680Sgibbs */
17929680Sgibbsint
180188059Simpconfig_intrhook_establish(struct intr_config_hook *hook)
18129680Sgibbs{
18229680Sgibbs	struct intr_config_hook *hook_entry;
18329680Sgibbs
184160509Sjhb	mtx_lock(&intr_config_hook_lock);
185160509Sjhb	TAILQ_FOREACH(hook_entry, &intr_config_hook_list, ich_links)
18629680Sgibbs		if (hook_entry == hook)
18729680Sgibbs			break;
18829680Sgibbs	if (hook_entry != NULL) {
189160509Sjhb		mtx_unlock(&intr_config_hook_lock);
19029680Sgibbs		printf("config_intrhook_establish: establishing an "
19129680Sgibbs		       "already established hook.\n");
19229680Sgibbs		return (1);
19329680Sgibbs	}
19429680Sgibbs	TAILQ_INSERT_TAIL(&intr_config_hook_list, hook, ich_links);
195211236Sgibbs	if (next_to_notify == NULL)
196211236Sgibbs		next_to_notify = hook;
197160509Sjhb	mtx_unlock(&intr_config_hook_lock);
19829680Sgibbs	if (cold == 0)
199211236Sgibbs		/*
200211236Sgibbs		 * XXX Call from a task since not all drivers expect
201211236Sgibbs		 *     to be re-entered at the time a hook is established.
202211236Sgibbs		 */
20345739Speter		/* XXX Sufficient for modules loaded after initial config??? */
204211236Sgibbs		run_interrupt_driven_config_hooks();
20529680Sgibbs	return (0);
20629680Sgibbs}
20729680Sgibbs
208323464Sian/*
209323464Sian * Register a hook function that is automatically unregistered after it runs.
210323464Sian */
21129680Sgibbsvoid
212323464Sianconfig_intrhook_oneshot(ich_func_t func, void *arg)
213323464Sian{
214323464Sian	struct oneshot_config_hook *ohook;
215323464Sian
216323464Sian	ohook = malloc(sizeof(*ohook), M_DEVBUF, M_WAITOK);
217323464Sian	ohook->och_func = func;
218323464Sian	ohook->och_arg  = arg;
219323464Sian	ohook->och_hook.ich_func = config_intrhook_oneshot_func;
220323464Sian	ohook->och_hook.ich_arg  = ohook;
221323464Sian	config_intrhook_establish(&ohook->och_hook);
222323464Sian}
223323464Sian
224323464Sianvoid
225188059Simpconfig_intrhook_disestablish(struct intr_config_hook *hook)
22629680Sgibbs{
22729680Sgibbs	struct intr_config_hook *hook_entry;
22829680Sgibbs
229160509Sjhb	mtx_lock(&intr_config_hook_lock);
230160509Sjhb	TAILQ_FOREACH(hook_entry, &intr_config_hook_list, ich_links)
23129680Sgibbs		if (hook_entry == hook)
23229680Sgibbs			break;
23329680Sgibbs	if (hook_entry == NULL)
23429680Sgibbs		panic("config_intrhook_disestablish: disestablishing an "
23529680Sgibbs		      "unestablished hook");
23629680Sgibbs
237211236Sgibbs	if (next_to_notify == hook)
238211236Sgibbs		next_to_notify = TAILQ_NEXT(hook, ich_links);
23929680Sgibbs	TAILQ_REMOVE(&intr_config_hook_list, hook, ich_links);
240160509Sjhb
24129680Sgibbs	/* Wakeup anyone watching the list */
24229680Sgibbs	wakeup(&intr_config_hook_list);
243160509Sjhb	mtx_unlock(&intr_config_hook_lock);
24429680Sgibbs}
245180610Srwatson
246180610Srwatson#ifdef DDB
247180610Srwatson#include <ddb/ddb.h>
248180610Srwatson
249180610SrwatsonDB_SHOW_COMMAND(conifhk, db_show_conifhk)
250180610Srwatson{
251180610Srwatson	struct intr_config_hook *hook_entry;
252180610Srwatson	char namebuf[64];
253180610Srwatson	long offset;
254180610Srwatson
255180610Srwatson	TAILQ_FOREACH(hook_entry, &intr_config_hook_list, ich_links) {
256180610Srwatson		if (linker_ddb_search_symbol_name(
257180610Srwatson		    (caddr_t)hook_entry->ich_func, namebuf, sizeof(namebuf),
258180610Srwatson		    &offset) == 0) {
259180610Srwatson			db_printf("hook: %p at %s+%#lx arg: %p\n",
260180610Srwatson			    hook_entry->ich_func, namebuf, offset,
261180610Srwatson			    hook_entry->ich_arg);
262180610Srwatson		} else {
263180610Srwatson			db_printf("hook: %p at ??+?? arg %p\n",
264180610Srwatson			    hook_entry->ich_func, hook_entry->ich_arg);
265180610Srwatson		}
266180610Srwatson	}
267180610Srwatson}
268180610Srwatson#endif /* DDB */
269