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