xref: /freebsd/sys/kern/subr_hints.c (revision 0fddbf874719b9bd50cf66ac26d1140bb3f2be69)
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$
27  */
28 
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/bus.h>
32 
33 /*
34  * Access functions for device resources.
35  */
36 
37 extern char static_hints[];		/* by config for now */
38 extern int hintmode;			/* 0 = off. 1 = config, 2 = fallback */
39 static char *hintp;
40 
41 /*
42  * Evil wildcarding resource string lookup.
43  * This walks the supplied env string table and returns a match.
44  * The start point can be remembered for incremental searches.
45  */
46 static int
47 res_find(int *line, int *startln,
48     const char *name, int *unit, const char *resname, const char *value,
49     const char **ret_name, int *ret_namelen, int *ret_unit,
50     const char **ret_resname, int *ret_resnamelen, const char **ret_value)
51 {
52 	int n = 0, hit;
53 	char r_name[32];
54 	int r_unit;
55 	char r_resname[32];
56 	char r_value[128];
57 	const char *s, *cp;
58 	char *p;
59 
60 	if (hintp == NULL) {
61 		switch (hintmode) {
62 		case 0:		/* config supplied nothing */
63 			hintp = kern_envp;
64 			break;
65 		case 1:		/* static hints only */
66 			hintp = static_hints;
67 			break;
68 		case 2:		/* fallback mode */
69 			cp = kern_envp;
70 			while (cp) {
71 				if (strncmp(cp, "hint.", 5) == 0) {
72 					cp = NULL;
73 					hintp = kern_envp;
74 					break;
75 				}
76 				while (*cp != '\0')
77 					cp++;
78 				cp++;
79 				if (*cp == '\0') {
80 					cp = NULL;
81 					hintp = static_hints;
82 					break;
83 				}
84 			}
85 			break;
86 		default:
87 			break;
88 		}
89 		if (hintp == NULL)
90 			hintp = kern_envp;
91 	}
92 
93 	cp = hintp;
94 	while (cp) {
95 		hit = 1;
96 		(*line)++;
97 		if (strncmp(cp, "hint.", 5) != 0)
98 			hit = 0;
99 		else
100 			n = sscanf(cp, "hint.%32[^.].%d.%32[^=]=%128s",
101 			    r_name, &r_unit, r_resname, r_value);
102 		if (hit && n != 4) {
103 			printf("CONFIG: invalid hint '%s'\n", cp);
104 			/* XXX: abuse bogus index() declaration */
105 			p = index(cp, 'h');
106 			*p = 'H';
107 			hit = 0;
108 		}
109 		if (hit && startln && *startln >= 0 && *line < *startln)
110 			hit = 0;
111 		if (hit && name && strcmp(name, r_name) != 0)
112 			hit = 0;
113 		if (hit && unit && *unit != r_unit)
114 			hit = 0;
115 		if (hit && resname && strcmp(resname, r_resname) != 0)
116 			hit = 0;
117 		if (hit && value && strcmp(value, r_value) != 0)
118 			hit = 0;
119 		if (hit)
120 			break;
121 		while (*cp != '\0')
122 			cp++;
123 		cp++;
124 		if (*cp == '\0') {
125 			cp = NULL;
126 			break;
127 		}
128 	}
129 	if (cp == NULL)
130 		return ENOENT;
131 
132 	s = cp;
133 	/* This is a bit of a hack, but at least is reentrant */
134 	/* Note that it returns some !unterminated! strings. */
135 	s = index(s, '.') + 1;		/* start of device */
136 	if (ret_name)
137 		*ret_name = s;
138 	s = index(s, '.') + 1;		/* start of unit */
139 	if (ret_namelen)
140 		*ret_namelen = s - *ret_name - 1; /* device length */
141 	if (ret_unit)
142 		*ret_unit = r_unit;
143 	s = index(s, '.') + 1;		/* start of resname */
144 	if (ret_resname)
145 		*ret_resname = s;
146 	s = index(s, '=') + 1;		/* start of value */
147 	if (ret_resnamelen)
148 		*ret_resnamelen = s - *ret_resname - 1; /* value len */
149 	if (ret_value)
150 		*ret_value = s;
151 	if (startln)			/* line number for anchor */
152 		*startln = *line + 1;
153 	return 0;
154 }
155 
156 /*
157  * Search all the data sources for matches to our query.  We look for
158  * dynamic hints first as overrides for static or fallback hints.
159  */
160 static int
161 resource_find(int *line, int *startln,
162     const char *name, int *unit, const char *resname, const char *value,
163     const char **ret_name, int *ret_namelen, int *ret_unit,
164     const char **ret_resname, int *ret_resnamelen, const char **ret_value)
165 {
166 	int i;
167 	int un;
168 
169 	*line = 0;
170 
171 	/* Search for exact unit matches first */
172 	i = res_find(line, startln, name, unit, resname, value,
173 	    ret_name, ret_namelen, ret_unit, ret_resname, ret_resnamelen,
174 	    ret_value);
175 	if (i == 0)
176 		return 0;
177 	if (unit == NULL)
178 		return ENOENT;
179 	/* If we are still here, search for wildcard matches */
180 	un = -1;
181 	i = res_find(line, startln, name, &un, resname, value,
182 	    ret_name, ret_namelen, ret_unit, ret_resname, ret_resnamelen,
183 	    ret_value);
184 	if (i == 0)
185 		return 0;
186 	return ENOENT;
187 }
188 
189 int
190 resource_int_value(const char *name, int unit, const char *resname, int *result)
191 {
192 	int error;
193 	const char *str;
194 	char *op;
195 	unsigned long val;
196 	int line;
197 
198 	line = 0;
199 	error = resource_find(&line, NULL, name, &unit, resname, NULL,
200 	    NULL, NULL, NULL, NULL, NULL, &str);
201 	if (error)
202 		return error;
203 	if (*str == '\0')
204 		return EFTYPE;
205 	val = strtoul(str, &op, 0);
206 	if (*op != '\0')
207 		return EFTYPE;
208 	*result = val;
209 	return 0;
210 }
211 
212 int
213 resource_long_value(const char *name, int unit, const char *resname,
214     long *result)
215 {
216 	int error;
217 	const char *str;
218 	char *op;
219 	unsigned long val;
220 	int line;
221 
222 	line = 0;
223 	error = resource_find(&line, NULL, name, &unit, resname, NULL,
224 	    NULL, NULL, NULL, NULL, NULL, &str);
225 	if (error)
226 		return error;
227 	if (*str == '\0')
228 		return EFTYPE;
229 	val = strtoul(str, &op, 0);
230 	if (*op != '\0')
231 		return EFTYPE;
232 	*result = val;
233 	return 0;
234 }
235 
236 int
237 resource_string_value(const char *name, int unit, const char *resname,
238     const char **result)
239 {
240 	int error;
241 	const char *str;
242 	int line;
243 
244 	line = 0;
245 	error = resource_find(&line, NULL, name, &unit, resname, NULL,
246 	    NULL, NULL, NULL, NULL, NULL, &str);
247 	if (error)
248 		return error;
249 	*result = str;
250 	return 0;
251 }
252 
253 /*
254  * This is a bit nasty, but allows us to not modify the env strings.
255  */
256 static const char *
257 resource_string_copy(const char *s, int len)
258 {
259 	static char stringbuf[256];
260 	static int offset = 0;
261 	const char *ret;
262 
263 	if (len == 0)
264 		len = strlen(s);
265 	if (len > 255)
266 		return NULL;
267 	if ((offset + len + 1) > 255)
268 		offset = 0;
269 	bcopy(s, &stringbuf[offset], len);
270 	stringbuf[offset + len] = '\0';
271 	ret = &stringbuf[offset];
272 	offset += len + 1;
273 	return ret;
274 }
275 
276 /*
277  * err = resource_find_at(&anchor, &name, &unit, resname, value)
278  * Iteratively fetch a list of devices wired "at" something
279  * res and value are restrictions.  eg: "at", "scbus0".
280  * For practical purposes, res = required, value = optional.
281  * *name and *unit are set.
282  * set *anchor to zero before starting.
283  */
284 int
285 resource_find_match(int *anchor, const char **name, int *unit,
286     const char *resname, const char *value)
287 {
288 	const char *found_name;
289 	int found_namelen;
290 	int found_unit;
291 	int ret;
292 	int newln;
293 
294 	newln = *anchor;
295 	ret = resource_find(anchor, &newln, NULL, NULL, resname, value,
296 	    &found_name, &found_namelen, &found_unit, NULL, NULL, NULL);
297 	if (ret == 0) {
298 		*name = resource_string_copy(found_name, found_namelen);
299 		*unit = found_unit;
300 	}
301 	*anchor = newln;
302 	return ret;
303 }
304 
305 
306 /*
307  * err = resource_find_dev(&anchor, name, &unit, res, value);
308  * Iterate through a list of devices, returning their unit numbers.
309  * res and value are optional restrictions.  eg: "at", "scbus0".
310  * *unit is set to the value.
311  * set *anchor to zero before starting.
312  */
313 int
314 resource_find_dev(int *anchor, const char *name, int *unit,
315     const char *resname, const char *value)
316 {
317 	int found_unit;
318 	int newln;
319 	int ret;
320 
321 	newln = *anchor;
322 	ret = resource_find(anchor, &newln, name, NULL, resname, value,
323 	    NULL, NULL, &found_unit, NULL, NULL, NULL);
324 	if (ret == 0) {
325 		*unit = found_unit;
326 	}
327 	*anchor = newln;
328 	return ret;
329 }
330