xref: /freebsd/sys/kern/subr_hints.c (revision 70e0bbedef95258a4dadc996d641a9bebd3f107d)
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 			p = strchr(cp, 'h');
137 			*p = 'H';
138 			hit = 0;
139 		}
140 		if (hit && startln && *startln >= 0 && *line < *startln)
141 			hit = 0;
142 		if (hit && name && strcmp(name, r_name) != 0)
143 			hit = 0;
144 		if (hit && unit && *unit != r_unit)
145 			hit = 0;
146 		if (hit && resname && strcmp(resname, r_resname) != 0)
147 			hit = 0;
148 		if (hit && value && strcmp(value, r_value) != 0)
149 			hit = 0;
150 		if (hit)
151 			break;
152 		if (use_kenv) {
153 			cp = kenvp[++i];
154 			if (cp == NULL)
155 				break;
156 		} else {
157 			while (*cp != '\0')
158 				cp++;
159 			cp++;
160 			if (*cp == '\0') {
161 				cp = NULL;
162 				break;
163 			}
164 		}
165 	}
166 	if (use_kenv)
167 		mtx_unlock(&kenv_lock);
168 	if (cp == NULL)
169 		return ENOENT;
170 
171 	s = cp;
172 	/* This is a bit of a hack, but at least is reentrant */
173 	/* Note that it returns some !unterminated! strings. */
174 	s = strchr(s, '.') + 1;		/* start of device */
175 	if (ret_name)
176 		*ret_name = s;
177 	s = strchr(s, '.') + 1;		/* start of unit */
178 	if (ret_namelen && ret_name)
179 		*ret_namelen = s - *ret_name - 1; /* device length */
180 	if (ret_unit)
181 		*ret_unit = r_unit;
182 	s = strchr(s, '.') + 1;		/* start of resname */
183 	if (ret_resname)
184 		*ret_resname = s;
185 	s = strchr(s, '=') + 1;		/* start of value */
186 	if (ret_resnamelen && ret_resname)
187 		*ret_resnamelen = s - *ret_resname - 1; /* value len */
188 	if (ret_value)
189 		*ret_value = s;
190 	if (startln)			/* line number for anchor */
191 		*startln = *line + 1;
192 	return 0;
193 }
194 
195 /*
196  * Search all the data sources for matches to our query.  We look for
197  * dynamic hints first as overrides for static or fallback hints.
198  */
199 static int
200 resource_find(int *line, int *startln,
201     const char *name, int *unit, const char *resname, const char *value,
202     const char **ret_name, int *ret_namelen, int *ret_unit,
203     const char **ret_resname, int *ret_resnamelen, const char **ret_value)
204 {
205 	int i;
206 	int un;
207 
208 	*line = 0;
209 
210 	/* Search for exact unit matches first */
211 	i = res_find(line, startln, name, unit, resname, value,
212 	    ret_name, ret_namelen, ret_unit, ret_resname, ret_resnamelen,
213 	    ret_value);
214 	if (i == 0)
215 		return 0;
216 	if (unit == NULL)
217 		return ENOENT;
218 	/* If we are still here, search for wildcard matches */
219 	un = -1;
220 	i = res_find(line, startln, name, &un, resname, value,
221 	    ret_name, ret_namelen, ret_unit, ret_resname, ret_resnamelen,
222 	    ret_value);
223 	if (i == 0)
224 		return 0;
225 	return ENOENT;
226 }
227 
228 int
229 resource_int_value(const char *name, int unit, const char *resname, int *result)
230 {
231 	int error;
232 	const char *str;
233 	char *op;
234 	unsigned long val;
235 	int line;
236 
237 	line = 0;
238 	error = resource_find(&line, NULL, name, &unit, resname, NULL,
239 	    NULL, NULL, NULL, NULL, NULL, &str);
240 	if (error)
241 		return error;
242 	if (*str == '\0')
243 		return EFTYPE;
244 	val = strtoul(str, &op, 0);
245 	if (*op != '\0')
246 		return EFTYPE;
247 	*result = val;
248 	return 0;
249 }
250 
251 int
252 resource_long_value(const char *name, int unit, const char *resname,
253     long *result)
254 {
255 	int error;
256 	const char *str;
257 	char *op;
258 	unsigned long val;
259 	int line;
260 
261 	line = 0;
262 	error = resource_find(&line, NULL, name, &unit, resname, NULL,
263 	    NULL, NULL, NULL, NULL, NULL, &str);
264 	if (error)
265 		return error;
266 	if (*str == '\0')
267 		return EFTYPE;
268 	val = strtoul(str, &op, 0);
269 	if (*op != '\0')
270 		return EFTYPE;
271 	*result = val;
272 	return 0;
273 }
274 
275 int
276 resource_string_value(const char *name, int unit, const char *resname,
277     const char **result)
278 {
279 	int error;
280 	const char *str;
281 	int line;
282 
283 	line = 0;
284 	error = resource_find(&line, NULL, name, &unit, resname, NULL,
285 	    NULL, NULL, NULL, NULL, NULL, &str);
286 	if (error)
287 		return error;
288 	*result = str;
289 	return 0;
290 }
291 
292 /*
293  * This is a bit nasty, but allows us to not modify the env strings.
294  */
295 static const char *
296 resource_string_copy(const char *s, int len)
297 {
298 	static char stringbuf[256];
299 	static int offset = 0;
300 	const char *ret;
301 
302 	if (len == 0)
303 		len = strlen(s);
304 	if (len > 255)
305 		return NULL;
306 	if ((offset + len + 1) > 255)
307 		offset = 0;
308 	bcopy(s, &stringbuf[offset], len);
309 	stringbuf[offset + len] = '\0';
310 	ret = &stringbuf[offset];
311 	offset += len + 1;
312 	return ret;
313 }
314 
315 /*
316  * err = resource_find_match(&anchor, &name, &unit, resname, value)
317  * Iteratively fetch a list of devices wired "at" something
318  * res and value are restrictions.  eg: "at", "scbus0".
319  * For practical purposes, res = required, value = optional.
320  * *name and *unit are set.
321  * set *anchor to zero before starting.
322  */
323 int
324 resource_find_match(int *anchor, const char **name, int *unit,
325     const char *resname, const char *value)
326 {
327 	const char *found_name;
328 	int found_namelen;
329 	int found_unit;
330 	int ret;
331 	int newln;
332 
333 	newln = *anchor;
334 	ret = resource_find(anchor, &newln, NULL, NULL, resname, value,
335 	    &found_name, &found_namelen, &found_unit, NULL, NULL, NULL);
336 	if (ret == 0) {
337 		*name = resource_string_copy(found_name, found_namelen);
338 		*unit = found_unit;
339 	}
340 	*anchor = newln;
341 	return ret;
342 }
343 
344 
345 /*
346  * err = resource_find_dev(&anchor, name, &unit, res, value);
347  * Iterate through a list of devices, returning their unit numbers.
348  * res and value are optional restrictions.  eg: "at", "scbus0".
349  * *unit is set to the value.
350  * set *anchor to zero before starting.
351  */
352 int
353 resource_find_dev(int *anchor, const char *name, int *unit,
354     const char *resname, const char *value)
355 {
356 	int found_unit;
357 	int newln;
358 	int ret;
359 
360 	newln = *anchor;
361 	ret = resource_find(anchor, &newln, name, NULL, resname, value,
362 	    NULL, NULL, &found_unit, NULL, NULL, NULL);
363 	if (ret == 0) {
364 		*unit = found_unit;
365 	}
366 	*anchor = newln;
367 	return ret;
368 }
369 
370 /*
371  * Check to see if a device is disabled via a disabled hint.
372  */
373 int
374 resource_disabled(const char *name, int unit)
375 {
376 	int error, value;
377 
378 	error = resource_int_value(name, unit, "disabled", &value);
379 	if (error)
380 	       return (0);
381 	return (value);
382 }
383