subr_hints.c revision 95592
1/*-
2 * Copyright (c) 2000,2001 Peter Wemm <peter@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: head/sys/kern/subr_hints.c 95592 2002-04-27 22:25:13Z peter $
27 */
28
29#include <sys/param.h>
30#include <sys/lock.h>
31#include <sys/sx.h>
32#include <sys/systm.h>
33#include <sys/bus.h>
34
35/*
36 * Access functions for device resources.
37 */
38
39static int checkmethod = 1;
40static char *hintp;
41
42/*
43 * Evil wildcarding resource string lookup.
44 * This walks the supplied env string table and returns a match.
45 * The start point can be remembered for incremental searches.
46 */
47static int
48res_find(int *line, int *startln,
49    const char *name, int *unit, const char *resname, const char *value,
50    const char **ret_name, int *ret_namelen, int *ret_unit,
51    const char **ret_resname, int *ret_resnamelen, const char **ret_value)
52{
53	int n = 0, hit, use_kenv, i = 0;
54	char r_name[32];
55	int r_unit;
56	char r_resname[32];
57	char r_value[128];
58	const char *s, *cp;
59	char *p;
60
61	use_kenv = 0;
62	if (checkmethod) {
63		switch (hintmode) {
64		case 0:		/* config supplied nothing */
65			break;
66		case 1:		/* static hints only */
67			hintp = static_hints;
68			checkmethod = 0;
69			break;
70		case 2:		/* fallback mode */
71			if (dynamic_kenv) {
72				sx_slock(&kenv_lock);
73				cp = kenvp[0];
74				for (i = 0; cp != NULL; cp = kenvp[++i]) {
75					if (!strncmp(cp, "hint.", 5)) {
76						use_kenv = 1;
77						checkmethod = 0;
78						break;
79					}
80				}
81				sx_sunlock(&kenv_lock);
82			} else {
83				cp = kern_envp;
84				while (cp) {
85					if (strncmp(cp, "hint.", 5) == 0) {
86						cp = NULL;
87						hintp = kern_envp;
88						break;
89					}
90					while (*cp != '\0')
91						cp++;
92					cp++;
93					if (*cp == '\0') {
94						cp = NULL;
95						hintp = static_hints;
96						break;
97					}
98				}
99			}
100			break;
101		default:
102			break;
103		}
104		if (hintp == NULL) {
105			if (dynamic_kenv) {
106				use_kenv = 1;
107				checkmethod = 0;
108			} else
109				hintp = kern_envp;
110		}
111	}
112
113	if (use_kenv) {
114		sx_slock(&kenv_lock);
115		i = 0;
116		cp = kenvp[0];
117		if (cp == NULL) {
118			sx_sunlock(&kenv_lock);
119			return (ENOENT);
120		}
121	} else {
122		cp = hintp;
123	}
124	while (cp) {
125		hit = 1;
126		(*line)++;
127		if (strncmp(cp, "hint.", 5) != 0)
128			hit = 0;
129		else
130			n = sscanf(cp, "hint.%32[^.].%d.%32[^=]=%128s",
131			    r_name, &r_unit, r_resname, r_value);
132		if (hit && n != 4) {
133			printf("CONFIG: invalid hint '%s'\n", cp);
134			/* XXX: abuse bogus index() declaration */
135			p = index(cp, 'h');
136			*p = 'H';
137			hit = 0;
138		}
139		if (hit && startln && *startln >= 0 && *line < *startln)
140			hit = 0;
141		if (hit && name && strcmp(name, r_name) != 0)
142			hit = 0;
143		if (hit && unit && *unit != r_unit)
144			hit = 0;
145		if (hit && resname && strcmp(resname, r_resname) != 0)
146			hit = 0;
147		if (hit && value && strcmp(value, r_value) != 0)
148			hit = 0;
149		if (hit)
150			break;
151		if (use_kenv)
152			cp = kenvp[++i];
153		else {
154			while (*cp != '\0')
155				cp++;
156			cp++;
157		}
158		if (*cp == '\0') {
159			cp = NULL;
160			break;
161		}
162	}
163	if (use_kenv)
164		sx_sunlock(&kenv_lock);
165	if (cp == NULL)
166		return ENOENT;
167
168	s = cp;
169	/* This is a bit of a hack, but at least is reentrant */
170	/* Note that it returns some !unterminated! strings. */
171	s = index(s, '.') + 1;		/* start of device */
172	if (ret_name)
173		*ret_name = s;
174	s = index(s, '.') + 1;		/* start of unit */
175	if (ret_namelen)
176		*ret_namelen = s - *ret_name - 1; /* device length */
177	if (ret_unit)
178		*ret_unit = r_unit;
179	s = index(s, '.') + 1;		/* start of resname */
180	if (ret_resname)
181		*ret_resname = s;
182	s = index(s, '=') + 1;		/* start of value */
183	if (ret_resnamelen)
184		*ret_resnamelen = s - *ret_resname - 1; /* value len */
185	if (ret_value)
186		*ret_value = s;
187	if (startln)			/* line number for anchor */
188		*startln = *line + 1;
189	return 0;
190}
191
192/*
193 * Search all the data sources for matches to our query.  We look for
194 * dynamic hints first as overrides for static or fallback hints.
195 */
196static int
197resource_find(int *line, int *startln,
198    const char *name, int *unit, const char *resname, const char *value,
199    const char **ret_name, int *ret_namelen, int *ret_unit,
200    const char **ret_resname, int *ret_resnamelen, const char **ret_value)
201{
202	int i;
203	int un;
204
205	*line = 0;
206
207	/* Search for exact unit matches first */
208	i = res_find(line, startln, name, unit, resname, value,
209	    ret_name, ret_namelen, ret_unit, ret_resname, ret_resnamelen,
210	    ret_value);
211	if (i == 0)
212		return 0;
213	if (unit == NULL)
214		return ENOENT;
215	/* If we are still here, search for wildcard matches */
216	un = -1;
217	i = res_find(line, startln, name, &un, resname, value,
218	    ret_name, ret_namelen, ret_unit, ret_resname, ret_resnamelen,
219	    ret_value);
220	if (i == 0)
221		return 0;
222	return ENOENT;
223}
224
225int
226resource_int_value(const char *name, int unit, const char *resname, int *result)
227{
228	int error;
229	const char *str;
230	char *op;
231	unsigned long val;
232	int line;
233
234	line = 0;
235	error = resource_find(&line, NULL, name, &unit, resname, NULL,
236	    NULL, NULL, NULL, NULL, NULL, &str);
237	if (error)
238		return error;
239	if (*str == '\0')
240		return EFTYPE;
241	val = strtoul(str, &op, 0);
242	if (*op != '\0')
243		return EFTYPE;
244	*result = val;
245	return 0;
246}
247
248int
249resource_long_value(const char *name, int unit, const char *resname,
250    long *result)
251{
252	int error;
253	const char *str;
254	char *op;
255	unsigned long val;
256	int line;
257
258	line = 0;
259	error = resource_find(&line, NULL, name, &unit, resname, NULL,
260	    NULL, NULL, NULL, NULL, NULL, &str);
261	if (error)
262		return error;
263	if (*str == '\0')
264		return EFTYPE;
265	val = strtoul(str, &op, 0);
266	if (*op != '\0')
267		return EFTYPE;
268	*result = val;
269	return 0;
270}
271
272int
273resource_string_value(const char *name, int unit, const char *resname,
274    const char **result)
275{
276	int error;
277	const char *str;
278	int line;
279
280	line = 0;
281	error = resource_find(&line, NULL, name, &unit, resname, NULL,
282	    NULL, NULL, NULL, NULL, NULL, &str);
283	if (error)
284		return error;
285	*result = str;
286	return 0;
287}
288
289/*
290 * This is a bit nasty, but allows us to not modify the env strings.
291 */
292static const char *
293resource_string_copy(const char *s, int len)
294{
295	static char stringbuf[256];
296	static int offset = 0;
297	const char *ret;
298
299	if (len == 0)
300		len = strlen(s);
301	if (len > 255)
302		return NULL;
303	if ((offset + len + 1) > 255)
304		offset = 0;
305	bcopy(s, &stringbuf[offset], len);
306	stringbuf[offset + len] = '\0';
307	ret = &stringbuf[offset];
308	offset += len + 1;
309	return ret;
310}
311
312/*
313 * err = resource_find_at(&anchor, &name, &unit, resname, value)
314 * Iteratively fetch a list of devices wired "at" something
315 * res and value are restrictions.  eg: "at", "scbus0".
316 * For practical purposes, res = required, value = optional.
317 * *name and *unit are set.
318 * set *anchor to zero before starting.
319 */
320int
321resource_find_match(int *anchor, const char **name, int *unit,
322    const char *resname, const char *value)
323{
324	const char *found_name;
325	int found_namelen;
326	int found_unit;
327	int ret;
328	int newln;
329
330	newln = *anchor;
331	ret = resource_find(anchor, &newln, NULL, NULL, resname, value,
332	    &found_name, &found_namelen, &found_unit, NULL, NULL, NULL);
333	if (ret == 0) {
334		*name = resource_string_copy(found_name, found_namelen);
335		*unit = found_unit;
336	}
337	*anchor = newln;
338	return ret;
339}
340
341
342/*
343 * err = resource_find_dev(&anchor, name, &unit, res, value);
344 * Iterate through a list of devices, returning their unit numbers.
345 * res and value are optional restrictions.  eg: "at", "scbus0".
346 * *unit is set to the value.
347 * set *anchor to zero before starting.
348 */
349int
350resource_find_dev(int *anchor, const char *name, int *unit,
351    const char *resname, const char *value)
352{
353	int found_unit;
354	int newln;
355	int ret;
356
357	newln = *anchor;
358	ret = resource_find(anchor, &newln, name, NULL, resname, value,
359	    NULL, NULL, &found_unit, NULL, NULL, NULL);
360	if (ret == 0) {
361		*unit = found_unit;
362	}
363	*anchor = newln;
364	return ret;
365}
366