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