xref: /freebsd/sys/kern/subr_hints.c (revision 74bf4e164ba5851606a27d4feff27717452583e5)
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/sx.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 		switch (hintmode) {
65 		case 0:		/* loader hints in environment only */
66 			break;
67 		case 1:		/* static hints only */
68 			hintp = static_hints;
69 			checkmethod = 0;
70 			break;
71 		case 2:		/* fallback mode */
72 			if (dynamic_kenv) {
73 				sx_slock(&kenv_lock);
74 				cp = kenvp[0];
75 				for (i = 0; cp != NULL; cp = kenvp[++i]) {
76 					if (!strncmp(cp, "hint.", 5)) {
77 						use_kenv = 1;
78 						checkmethod = 0;
79 						break;
80 					}
81 				}
82 				sx_sunlock(&kenv_lock);
83 			} else {
84 				cp = kern_envp;
85 				while (cp) {
86 					if (strncmp(cp, "hint.", 5) == 0) {
87 						cp = NULL;
88 						hintp = kern_envp;
89 						break;
90 					}
91 					while (*cp != '\0')
92 						cp++;
93 					cp++;
94 					if (*cp == '\0') {
95 						cp = NULL;
96 						hintp = static_hints;
97 						break;
98 					}
99 				}
100 			}
101 			break;
102 		default:
103 			break;
104 		}
105 		if (hintp == NULL) {
106 			if (dynamic_kenv) {
107 				use_kenv = 1;
108 				checkmethod = 0;
109 			} else
110 				hintp = kern_envp;
111 		}
112 	}
113 
114 	if (use_kenv) {
115 		sx_slock(&kenv_lock);
116 		i = 0;
117 		cp = kenvp[0];
118 		if (cp == NULL) {
119 			sx_sunlock(&kenv_lock);
120 			return (ENOENT);
121 		}
122 	} else
123 		cp = hintp;
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 			if (cp == NULL)
154 				break;
155 		} else {
156 			while (*cp != '\0')
157 				cp++;
158 			cp++;
159 			if (*cp == '\0') {
160 				cp = NULL;
161 				break;
162 			}
163 		}
164 	}
165 	if (use_kenv)
166 		sx_sunlock(&kenv_lock);
167 	if (cp == NULL)
168 		return ENOENT;
169 
170 	s = cp;
171 	/* This is a bit of a hack, but at least is reentrant */
172 	/* Note that it returns some !unterminated! strings. */
173 	s = index(s, '.') + 1;		/* start of device */
174 	if (ret_name)
175 		*ret_name = s;
176 	s = index(s, '.') + 1;		/* start of unit */
177 	if (ret_namelen)
178 		*ret_namelen = s - *ret_name - 1; /* device length */
179 	if (ret_unit)
180 		*ret_unit = r_unit;
181 	s = index(s, '.') + 1;		/* start of resname */
182 	if (ret_resname)
183 		*ret_resname = s;
184 	s = index(s, '=') + 1;		/* start of value */
185 	if (ret_resnamelen)
186 		*ret_resnamelen = s - *ret_resname - 1; /* value len */
187 	if (ret_value)
188 		*ret_value = s;
189 	if (startln)			/* line number for anchor */
190 		*startln = *line + 1;
191 	return 0;
192 }
193 
194 /*
195  * Search all the data sources for matches to our query.  We look for
196  * dynamic hints first as overrides for static or fallback hints.
197  */
198 static int
199 resource_find(int *line, int *startln,
200     const char *name, int *unit, const char *resname, const char *value,
201     const char **ret_name, int *ret_namelen, int *ret_unit,
202     const char **ret_resname, int *ret_resnamelen, const char **ret_value)
203 {
204 	int i;
205 	int un;
206 
207 	*line = 0;
208 
209 	/* Search for exact unit matches first */
210 	i = res_find(line, startln, name, unit, resname, value,
211 	    ret_name, ret_namelen, ret_unit, ret_resname, ret_resnamelen,
212 	    ret_value);
213 	if (i == 0)
214 		return 0;
215 	if (unit == NULL)
216 		return ENOENT;
217 	/* If we are still here, search for wildcard matches */
218 	un = -1;
219 	i = res_find(line, startln, name, &un, resname, value,
220 	    ret_name, ret_namelen, ret_unit, ret_resname, ret_resnamelen,
221 	    ret_value);
222 	if (i == 0)
223 		return 0;
224 	return ENOENT;
225 }
226 
227 int
228 resource_int_value(const char *name, int unit, const char *resname, int *result)
229 {
230 	int error;
231 	const char *str;
232 	char *op;
233 	unsigned long val;
234 	int line;
235 
236 	line = 0;
237 	error = resource_find(&line, NULL, name, &unit, resname, NULL,
238 	    NULL, NULL, NULL, NULL, NULL, &str);
239 	if (error)
240 		return error;
241 	if (*str == '\0')
242 		return EFTYPE;
243 	val = strtoul(str, &op, 0);
244 	if (*op != '\0')
245 		return EFTYPE;
246 	*result = val;
247 	return 0;
248 }
249 
250 int
251 resource_long_value(const char *name, int unit, const char *resname,
252     long *result)
253 {
254 	int error;
255 	const char *str;
256 	char *op;
257 	unsigned long val;
258 	int line;
259 
260 	line = 0;
261 	error = resource_find(&line, NULL, name, &unit, resname, NULL,
262 	    NULL, NULL, NULL, NULL, NULL, &str);
263 	if (error)
264 		return error;
265 	if (*str == '\0')
266 		return EFTYPE;
267 	val = strtoul(str, &op, 0);
268 	if (*op != '\0')
269 		return EFTYPE;
270 	*result = val;
271 	return 0;
272 }
273 
274 int
275 resource_string_value(const char *name, int unit, const char *resname,
276     const char **result)
277 {
278 	int error;
279 	const char *str;
280 	int line;
281 
282 	line = 0;
283 	error = resource_find(&line, NULL, name, &unit, resname, NULL,
284 	    NULL, NULL, NULL, NULL, NULL, &str);
285 	if (error)
286 		return error;
287 	*result = str;
288 	return 0;
289 }
290 
291 /*
292  * This is a bit nasty, but allows us to not modify the env strings.
293  */
294 static const char *
295 resource_string_copy(const char *s, int len)
296 {
297 	static char stringbuf[256];
298 	static int offset = 0;
299 	const char *ret;
300 
301 	if (len == 0)
302 		len = strlen(s);
303 	if (len > 255)
304 		return NULL;
305 	if ((offset + len + 1) > 255)
306 		offset = 0;
307 	bcopy(s, &stringbuf[offset], len);
308 	stringbuf[offset + len] = '\0';
309 	ret = &stringbuf[offset];
310 	offset += len + 1;
311 	return ret;
312 }
313 
314 /*
315  * err = resource_find_at(&anchor, &name, &unit, resname, value)
316  * Iteratively fetch a list of devices wired "at" something
317  * res and value are restrictions.  eg: "at", "scbus0".
318  * For practical purposes, res = required, value = optional.
319  * *name and *unit are set.
320  * set *anchor to zero before starting.
321  */
322 int
323 resource_find_match(int *anchor, const char **name, int *unit,
324     const char *resname, const char *value)
325 {
326 	const char *found_name;
327 	int found_namelen;
328 	int found_unit;
329 	int ret;
330 	int newln;
331 
332 	newln = *anchor;
333 	ret = resource_find(anchor, &newln, NULL, NULL, resname, value,
334 	    &found_name, &found_namelen, &found_unit, NULL, NULL, NULL);
335 	if (ret == 0) {
336 		*name = resource_string_copy(found_name, found_namelen);
337 		*unit = found_unit;
338 	}
339 	*anchor = newln;
340 	return ret;
341 }
342 
343 
344 /*
345  * err = resource_find_dev(&anchor, name, &unit, res, value);
346  * Iterate through a list of devices, returning their unit numbers.
347  * res and value are optional restrictions.  eg: "at", "scbus0".
348  * *unit is set to the value.
349  * set *anchor to zero before starting.
350  */
351 int
352 resource_find_dev(int *anchor, const char *name, int *unit,
353     const char *resname, const char *value)
354 {
355 	int found_unit;
356 	int newln;
357 	int ret;
358 
359 	newln = *anchor;
360 	ret = resource_find(anchor, &newln, name, NULL, resname, value,
361 	    NULL, NULL, &found_unit, NULL, NULL, NULL);
362 	if (ret == 0) {
363 		*unit = found_unit;
364 	}
365 	*anchor = newln;
366 	return ret;
367 }
368 
369 /*
370  * Check to see if a device is disabled via a disabled hint.
371  */
372 int
373 resource_disabled(const char *name, int unit)
374 {
375 	int error, value;
376 
377 	error = resource_int_value(name, unit, "disabled", &value);
378 	if (error)
379 	       return (0);
380 	return (value);
381 }
382