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