xref: /freebsd/sys/kern/subr_hints.c (revision d786139c76ad8d7cd25f962f7e67d4af98685418)
12398f0cdSPeter Wemm /*-
22398f0cdSPeter Wemm  * Copyright (c) 2000,2001 Peter Wemm <peter@FreeBSD.org>
32398f0cdSPeter Wemm  * All rights reserved.
42398f0cdSPeter Wemm  *
52398f0cdSPeter Wemm  * Redistribution and use in source and binary forms, with or without
62398f0cdSPeter Wemm  * modification, are permitted provided that the following conditions
72398f0cdSPeter Wemm  * are met:
82398f0cdSPeter Wemm  * 1. Redistributions of source code must retain the above copyright
92398f0cdSPeter Wemm  *    notice, this list of conditions and the following disclaimer.
102398f0cdSPeter Wemm  * 2. Redistributions in binary form must reproduce the above copyright
112398f0cdSPeter Wemm  *    notice, this list of conditions and the following disclaimer in the
122398f0cdSPeter Wemm  *    documentation and/or other materials provided with the distribution.
132398f0cdSPeter Wemm  *
142398f0cdSPeter Wemm  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
152398f0cdSPeter Wemm  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
162398f0cdSPeter Wemm  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
172398f0cdSPeter Wemm  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
182398f0cdSPeter Wemm  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
192398f0cdSPeter Wemm  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
202398f0cdSPeter Wemm  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
212398f0cdSPeter Wemm  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
222398f0cdSPeter Wemm  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
232398f0cdSPeter Wemm  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
242398f0cdSPeter Wemm  * SUCH DAMAGE.
252398f0cdSPeter Wemm  *
262398f0cdSPeter Wemm  * $FreeBSD$
272398f0cdSPeter Wemm  */
282398f0cdSPeter Wemm 
292398f0cdSPeter Wemm #include <sys/param.h>
30d786139cSMaxime Henrion #include <sys/lock.h>
31d786139cSMaxime Henrion #include <sys/sx.h>
322398f0cdSPeter Wemm #include <sys/systm.h>
332398f0cdSPeter Wemm #include <sys/bus.h>
342398f0cdSPeter Wemm 
352398f0cdSPeter Wemm /*
362398f0cdSPeter Wemm  * Access functions for device resources.
372398f0cdSPeter Wemm  */
382398f0cdSPeter Wemm 
39d786139cSMaxime Henrion static int checkmethod = 1;
409516fbd6SPeter Wemm static char *hintp;
412398f0cdSPeter Wemm 
422398f0cdSPeter Wemm /*
432398f0cdSPeter Wemm  * Evil wildcarding resource string lookup.
442398f0cdSPeter Wemm  * This walks the supplied env string table and returns a match.
452398f0cdSPeter Wemm  * The start point can be remembered for incremental searches.
462398f0cdSPeter Wemm  */
472398f0cdSPeter Wemm static int
489516fbd6SPeter Wemm res_find(int *line, int *startln,
492398f0cdSPeter Wemm     const char *name, int *unit, const char *resname, const char *value,
502398f0cdSPeter Wemm     const char **ret_name, int *ret_namelen, int *ret_unit,
512398f0cdSPeter Wemm     const char **ret_resname, int *ret_resnamelen, const char **ret_value)
522398f0cdSPeter Wemm {
53d786139cSMaxime Henrion 	int n = 0, hit, use_kenv, i = 0;
542398f0cdSPeter Wemm 	char r_name[32];
552398f0cdSPeter Wemm 	int r_unit;
562398f0cdSPeter Wemm 	char r_resname[32];
572398f0cdSPeter Wemm 	char r_value[128];
589516fbd6SPeter Wemm 	const char *s, *cp;
592398f0cdSPeter Wemm 	char *p;
602398f0cdSPeter Wemm 
61d786139cSMaxime Henrion 	use_kenv = 0;
62d786139cSMaxime Henrion 	if (checkmethod) {
639516fbd6SPeter Wemm 		switch (hintmode) {
649516fbd6SPeter Wemm 		case 0:		/* config supplied nothing */
659516fbd6SPeter Wemm 			break;
669516fbd6SPeter Wemm 		case 1:		/* static hints only */
679516fbd6SPeter Wemm 			hintp = static_hints;
68d786139cSMaxime Henrion 			checkmethod = 0;
699516fbd6SPeter Wemm 			break;
709516fbd6SPeter Wemm 		case 2:		/* fallback mode */
71d786139cSMaxime Henrion 			if (dynamic_kenv) {
72d786139cSMaxime Henrion 				sx_slock(&kenv_lock);
73d786139cSMaxime Henrion 				cp = kenvp[0];
74d786139cSMaxime Henrion 				for (i = 0; cp != NULL; cp = kenvp[++i]) {
75d786139cSMaxime Henrion 					if (!strncmp(cp, "hint.", 5)) {
76d786139cSMaxime Henrion 						use_kenv = 1;
77d786139cSMaxime Henrion 						checkmethod = 0;
78d786139cSMaxime Henrion 						break;
79d786139cSMaxime Henrion 					}
80d786139cSMaxime Henrion 				}
81d786139cSMaxime Henrion 				sx_sunlock(&kenv_lock);
82d786139cSMaxime Henrion 			} else {
839516fbd6SPeter Wemm 				cp = kern_envp;
849516fbd6SPeter Wemm 				while (cp) {
859516fbd6SPeter Wemm 					if (strncmp(cp, "hint.", 5) == 0) {
869516fbd6SPeter Wemm 						cp = NULL;
879516fbd6SPeter Wemm 						hintp = kern_envp;
889516fbd6SPeter Wemm 						break;
899516fbd6SPeter Wemm 					}
909516fbd6SPeter Wemm 					while (*cp != '\0')
919516fbd6SPeter Wemm 						cp++;
929516fbd6SPeter Wemm 					cp++;
939516fbd6SPeter Wemm 					if (*cp == '\0') {
949516fbd6SPeter Wemm 						cp = NULL;
959516fbd6SPeter Wemm 						hintp = static_hints;
969516fbd6SPeter Wemm 						break;
979516fbd6SPeter Wemm 					}
989516fbd6SPeter Wemm 				}
99d786139cSMaxime Henrion 			}
1009516fbd6SPeter Wemm 			break;
1019516fbd6SPeter Wemm 		default:
1029516fbd6SPeter Wemm 			break;
1039516fbd6SPeter Wemm 		}
104d786139cSMaxime Henrion 		if (hintp == NULL) {
105d786139cSMaxime Henrion 			if (dynamic_kenv) {
106d786139cSMaxime Henrion 				use_kenv = 1;
107d786139cSMaxime Henrion 				checkmethod = 0;
108d786139cSMaxime Henrion 			} else
1099516fbd6SPeter Wemm 				hintp = kern_envp;
1109516fbd6SPeter Wemm 		}
111d786139cSMaxime Henrion 	}
1129516fbd6SPeter Wemm 
113d786139cSMaxime Henrion 	if (use_kenv) {
114d786139cSMaxime Henrion 		sx_slock(&kenv_lock);
115d786139cSMaxime Henrion 		i = 0;
116d786139cSMaxime Henrion 		cp = kenvp[0];
117d786139cSMaxime Henrion 		if (cp == NULL) {
118d786139cSMaxime Henrion 			sx_sunlock(&kenv_lock);
119d786139cSMaxime Henrion 			return (ENOENT);
120d786139cSMaxime Henrion 		}
121d786139cSMaxime Henrion 	} else {
1229516fbd6SPeter Wemm 		cp = hintp;
123d786139cSMaxime Henrion 	}
1242398f0cdSPeter Wemm 	while (cp) {
1252398f0cdSPeter Wemm 		hit = 1;
1262398f0cdSPeter Wemm 		(*line)++;
1272398f0cdSPeter Wemm 		if (strncmp(cp, "hint.", 5) != 0)
1282398f0cdSPeter Wemm 			hit = 0;
1292398f0cdSPeter Wemm 		else
1302398f0cdSPeter Wemm 			n = sscanf(cp, "hint.%32[^.].%d.%32[^=]=%128s",
1312398f0cdSPeter Wemm 			    r_name, &r_unit, r_resname, r_value);
1322398f0cdSPeter Wemm 		if (hit && n != 4) {
1332398f0cdSPeter Wemm 			printf("CONFIG: invalid hint '%s'\n", cp);
1342398f0cdSPeter Wemm 			/* XXX: abuse bogus index() declaration */
1352398f0cdSPeter Wemm 			p = index(cp, 'h');
1362398f0cdSPeter Wemm 			*p = 'H';
1372398f0cdSPeter Wemm 			hit = 0;
1382398f0cdSPeter Wemm 		}
1392398f0cdSPeter Wemm 		if (hit && startln && *startln >= 0 && *line < *startln)
1402398f0cdSPeter Wemm 			hit = 0;
1412398f0cdSPeter Wemm 		if (hit && name && strcmp(name, r_name) != 0)
1422398f0cdSPeter Wemm 			hit = 0;
1432398f0cdSPeter Wemm 		if (hit && unit && *unit != r_unit)
1442398f0cdSPeter Wemm 			hit = 0;
1452398f0cdSPeter Wemm 		if (hit && resname && strcmp(resname, r_resname) != 0)
1462398f0cdSPeter Wemm 			hit = 0;
1472398f0cdSPeter Wemm 		if (hit && value && strcmp(value, r_value) != 0)
1482398f0cdSPeter Wemm 			hit = 0;
1492398f0cdSPeter Wemm 		if (hit)
1502398f0cdSPeter Wemm 			break;
151d786139cSMaxime Henrion 		if (use_kenv)
152d786139cSMaxime Henrion 			cp = kenvp[++i];
1532398f0cdSPeter Wemm 		while (*cp != '\0')
1542398f0cdSPeter Wemm 			cp++;
1552398f0cdSPeter Wemm 		cp++;
1562398f0cdSPeter Wemm 		if (*cp == '\0') {
1572398f0cdSPeter Wemm 			cp = NULL;
1582398f0cdSPeter Wemm 			break;
1592398f0cdSPeter Wemm 		}
1602398f0cdSPeter Wemm 	}
161d786139cSMaxime Henrion 	if (use_kenv)
162d786139cSMaxime Henrion 		sx_sunlock(&kenv_lock);
1632398f0cdSPeter Wemm 	if (cp == NULL)
1642398f0cdSPeter Wemm 		return ENOENT;
1652398f0cdSPeter Wemm 
1662398f0cdSPeter Wemm 	s = cp;
1672398f0cdSPeter Wemm 	/* This is a bit of a hack, but at least is reentrant */
1682398f0cdSPeter Wemm 	/* Note that it returns some !unterminated! strings. */
1692398f0cdSPeter Wemm 	s = index(s, '.') + 1;		/* start of device */
1702398f0cdSPeter Wemm 	if (ret_name)
1712398f0cdSPeter Wemm 		*ret_name = s;
1722398f0cdSPeter Wemm 	s = index(s, '.') + 1;		/* start of unit */
1732398f0cdSPeter Wemm 	if (ret_namelen)
1742398f0cdSPeter Wemm 		*ret_namelen = s - *ret_name - 1; /* device length */
1752398f0cdSPeter Wemm 	if (ret_unit)
1762398f0cdSPeter Wemm 		*ret_unit = r_unit;
1772398f0cdSPeter Wemm 	s = index(s, '.') + 1;		/* start of resname */
1782398f0cdSPeter Wemm 	if (ret_resname)
1792398f0cdSPeter Wemm 		*ret_resname = s;
1802398f0cdSPeter Wemm 	s = index(s, '=') + 1;		/* start of value */
1812398f0cdSPeter Wemm 	if (ret_resnamelen)
1822398f0cdSPeter Wemm 		*ret_resnamelen = s - *ret_resname - 1; /* value len */
1832398f0cdSPeter Wemm 	if (ret_value)
1842398f0cdSPeter Wemm 		*ret_value = s;
1852398f0cdSPeter Wemm 	if (startln)			/* line number for anchor */
1862398f0cdSPeter Wemm 		*startln = *line + 1;
1872398f0cdSPeter Wemm 	return 0;
1882398f0cdSPeter Wemm }
1892398f0cdSPeter Wemm 
1902398f0cdSPeter Wemm /*
1912398f0cdSPeter Wemm  * Search all the data sources for matches to our query.  We look for
1922398f0cdSPeter Wemm  * dynamic hints first as overrides for static or fallback hints.
1932398f0cdSPeter Wemm  */
1942398f0cdSPeter Wemm static int
1952398f0cdSPeter Wemm resource_find(int *line, int *startln,
1962398f0cdSPeter Wemm     const char *name, int *unit, const char *resname, const char *value,
1972398f0cdSPeter Wemm     const char **ret_name, int *ret_namelen, int *ret_unit,
1982398f0cdSPeter Wemm     const char **ret_resname, int *ret_resnamelen, const char **ret_value)
1992398f0cdSPeter Wemm {
2002398f0cdSPeter Wemm 	int i;
2012398f0cdSPeter Wemm 	int un;
2022398f0cdSPeter Wemm 
2032398f0cdSPeter Wemm 	*line = 0;
2042398f0cdSPeter Wemm 
2052398f0cdSPeter Wemm 	/* Search for exact unit matches first */
2069516fbd6SPeter Wemm 	i = res_find(line, startln, name, unit, resname, value,
2072398f0cdSPeter Wemm 	    ret_name, ret_namelen, ret_unit, ret_resname, ret_resnamelen,
2082398f0cdSPeter Wemm 	    ret_value);
2092398f0cdSPeter Wemm 	if (i == 0)
2102398f0cdSPeter Wemm 		return 0;
2112398f0cdSPeter Wemm 	if (unit == NULL)
2122398f0cdSPeter Wemm 		return ENOENT;
2132398f0cdSPeter Wemm 	/* If we are still here, search for wildcard matches */
2142398f0cdSPeter Wemm 	un = -1;
2159516fbd6SPeter Wemm 	i = res_find(line, startln, name, &un, resname, value,
2162398f0cdSPeter Wemm 	    ret_name, ret_namelen, ret_unit, ret_resname, ret_resnamelen,
2172398f0cdSPeter Wemm 	    ret_value);
2182398f0cdSPeter Wemm 	if (i == 0)
2192398f0cdSPeter Wemm 		return 0;
2202398f0cdSPeter Wemm 	return ENOENT;
2212398f0cdSPeter Wemm }
2222398f0cdSPeter Wemm 
2232398f0cdSPeter Wemm int
2242398f0cdSPeter Wemm resource_int_value(const char *name, int unit, const char *resname, int *result)
2252398f0cdSPeter Wemm {
2262398f0cdSPeter Wemm 	int error;
2272398f0cdSPeter Wemm 	const char *str;
2282398f0cdSPeter Wemm 	char *op;
2292398f0cdSPeter Wemm 	unsigned long val;
2302398f0cdSPeter Wemm 	int line;
2312398f0cdSPeter Wemm 
2322398f0cdSPeter Wemm 	line = 0;
2332398f0cdSPeter Wemm 	error = resource_find(&line, NULL, name, &unit, resname, NULL,
2342398f0cdSPeter Wemm 	    NULL, NULL, NULL, NULL, NULL, &str);
2352398f0cdSPeter Wemm 	if (error)
2362398f0cdSPeter Wemm 		return error;
2372398f0cdSPeter Wemm 	if (*str == '\0')
2382398f0cdSPeter Wemm 		return EFTYPE;
2392398f0cdSPeter Wemm 	val = strtoul(str, &op, 0);
2402398f0cdSPeter Wemm 	if (*op != '\0')
2412398f0cdSPeter Wemm 		return EFTYPE;
2422398f0cdSPeter Wemm 	*result = val;
2432398f0cdSPeter Wemm 	return 0;
2442398f0cdSPeter Wemm }
2452398f0cdSPeter Wemm 
2462398f0cdSPeter Wemm int
2472398f0cdSPeter Wemm resource_long_value(const char *name, int unit, const char *resname,
2482398f0cdSPeter Wemm     long *result)
2492398f0cdSPeter Wemm {
2502398f0cdSPeter Wemm 	int error;
2512398f0cdSPeter Wemm 	const char *str;
2522398f0cdSPeter Wemm 	char *op;
2532398f0cdSPeter Wemm 	unsigned long val;
2542398f0cdSPeter Wemm 	int line;
2552398f0cdSPeter Wemm 
2562398f0cdSPeter Wemm 	line = 0;
2572398f0cdSPeter Wemm 	error = resource_find(&line, NULL, name, &unit, resname, NULL,
2582398f0cdSPeter Wemm 	    NULL, NULL, NULL, NULL, NULL, &str);
2592398f0cdSPeter Wemm 	if (error)
2602398f0cdSPeter Wemm 		return error;
2612398f0cdSPeter Wemm 	if (*str == '\0')
2622398f0cdSPeter Wemm 		return EFTYPE;
2632398f0cdSPeter Wemm 	val = strtoul(str, &op, 0);
2642398f0cdSPeter Wemm 	if (*op != '\0')
2652398f0cdSPeter Wemm 		return EFTYPE;
2662398f0cdSPeter Wemm 	*result = val;
2672398f0cdSPeter Wemm 	return 0;
2682398f0cdSPeter Wemm }
2692398f0cdSPeter Wemm 
2702398f0cdSPeter Wemm int
2712398f0cdSPeter Wemm resource_string_value(const char *name, int unit, const char *resname,
2722398f0cdSPeter Wemm     const char **result)
2732398f0cdSPeter Wemm {
2742398f0cdSPeter Wemm 	int error;
2752398f0cdSPeter Wemm 	const char *str;
2762398f0cdSPeter Wemm 	int line;
2772398f0cdSPeter Wemm 
2782398f0cdSPeter Wemm 	line = 0;
2792398f0cdSPeter Wemm 	error = resource_find(&line, NULL, name, &unit, resname, NULL,
2802398f0cdSPeter Wemm 	    NULL, NULL, NULL, NULL, NULL, &str);
2812398f0cdSPeter Wemm 	if (error)
2822398f0cdSPeter Wemm 		return error;
2832398f0cdSPeter Wemm 	*result = str;
2842398f0cdSPeter Wemm 	return 0;
2852398f0cdSPeter Wemm }
2862398f0cdSPeter Wemm 
2872398f0cdSPeter Wemm /*
2882398f0cdSPeter Wemm  * This is a bit nasty, but allows us to not modify the env strings.
2892398f0cdSPeter Wemm  */
2902398f0cdSPeter Wemm static const char *
2912398f0cdSPeter Wemm resource_string_copy(const char *s, int len)
2922398f0cdSPeter Wemm {
2932398f0cdSPeter Wemm 	static char stringbuf[256];
2942398f0cdSPeter Wemm 	static int offset = 0;
2952398f0cdSPeter Wemm 	const char *ret;
2962398f0cdSPeter Wemm 
2972398f0cdSPeter Wemm 	if (len == 0)
2982398f0cdSPeter Wemm 		len = strlen(s);
2992398f0cdSPeter Wemm 	if (len > 255)
3002398f0cdSPeter Wemm 		return NULL;
3012398f0cdSPeter Wemm 	if ((offset + len + 1) > 255)
3022398f0cdSPeter Wemm 		offset = 0;
3032398f0cdSPeter Wemm 	bcopy(s, &stringbuf[offset], len);
3042398f0cdSPeter Wemm 	stringbuf[offset + len] = '\0';
3052398f0cdSPeter Wemm 	ret = &stringbuf[offset];
3062398f0cdSPeter Wemm 	offset += len + 1;
3072398f0cdSPeter Wemm 	return ret;
3082398f0cdSPeter Wemm }
3092398f0cdSPeter Wemm 
3102398f0cdSPeter Wemm /*
3112398f0cdSPeter Wemm  * err = resource_find_at(&anchor, &name, &unit, resname, value)
3122398f0cdSPeter Wemm  * Iteratively fetch a list of devices wired "at" something
3132398f0cdSPeter Wemm  * res and value are restrictions.  eg: "at", "scbus0".
3142398f0cdSPeter Wemm  * For practical purposes, res = required, value = optional.
3152398f0cdSPeter Wemm  * *name and *unit are set.
3162398f0cdSPeter Wemm  * set *anchor to zero before starting.
3172398f0cdSPeter Wemm  */
3182398f0cdSPeter Wemm int
3192398f0cdSPeter Wemm resource_find_match(int *anchor, const char **name, int *unit,
3202398f0cdSPeter Wemm     const char *resname, const char *value)
3212398f0cdSPeter Wemm {
3222398f0cdSPeter Wemm 	const char *found_name;
3232398f0cdSPeter Wemm 	int found_namelen;
3242398f0cdSPeter Wemm 	int found_unit;
3252398f0cdSPeter Wemm 	int ret;
3262398f0cdSPeter Wemm 	int newln;
3272398f0cdSPeter Wemm 
3282398f0cdSPeter Wemm 	newln = *anchor;
3292398f0cdSPeter Wemm 	ret = resource_find(anchor, &newln, NULL, NULL, resname, value,
3302398f0cdSPeter Wemm 	    &found_name, &found_namelen, &found_unit, NULL, NULL, NULL);
3312398f0cdSPeter Wemm 	if (ret == 0) {
3322398f0cdSPeter Wemm 		*name = resource_string_copy(found_name, found_namelen);
3332398f0cdSPeter Wemm 		*unit = found_unit;
3342398f0cdSPeter Wemm 	}
3352398f0cdSPeter Wemm 	*anchor = newln;
3362398f0cdSPeter Wemm 	return ret;
3372398f0cdSPeter Wemm }
3382398f0cdSPeter Wemm 
3392398f0cdSPeter Wemm 
3402398f0cdSPeter Wemm /*
3412398f0cdSPeter Wemm  * err = resource_find_dev(&anchor, name, &unit, res, value);
3422398f0cdSPeter Wemm  * Iterate through a list of devices, returning their unit numbers.
3432398f0cdSPeter Wemm  * res and value are optional restrictions.  eg: "at", "scbus0".
3442398f0cdSPeter Wemm  * *unit is set to the value.
3452398f0cdSPeter Wemm  * set *anchor to zero before starting.
3462398f0cdSPeter Wemm  */
3472398f0cdSPeter Wemm int
3482398f0cdSPeter Wemm resource_find_dev(int *anchor, const char *name, int *unit,
3492398f0cdSPeter Wemm     const char *resname, const char *value)
3502398f0cdSPeter Wemm {
3512398f0cdSPeter Wemm 	int found_unit;
3522398f0cdSPeter Wemm 	int newln;
3532398f0cdSPeter Wemm 	int ret;
3542398f0cdSPeter Wemm 
3552398f0cdSPeter Wemm 	newln = *anchor;
3562398f0cdSPeter Wemm 	ret = resource_find(anchor, &newln, name, NULL, resname, value,
3572398f0cdSPeter Wemm 	    NULL, NULL, &found_unit, NULL, NULL, NULL);
3582398f0cdSPeter Wemm 	if (ret == 0) {
3592398f0cdSPeter Wemm 		*unit = found_unit;
3602398f0cdSPeter Wemm 	}
3612398f0cdSPeter Wemm 	*anchor = newln;
3622398f0cdSPeter Wemm 	return ret;
3632398f0cdSPeter Wemm }
364