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