FreeBSD/Linux Kernel Cross Reference
sys/kern/subr_hints.c
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: releng/10.0/sys/kern/subr_hints.c 240119 2012-09-04 23:16:55Z ray $");
29
30 #include <sys/param.h>
31 #include <sys/lock.h>
32 #include <sys/malloc.h>
33 #include <sys/mutex.h>
34 #include <sys/sysctl.h>
35 #include <sys/systm.h>
36 #include <sys/bus.h>
37
38 /*
39 * Access functions for device resources.
40 */
41
42 static int checkmethod = 1;
43 static int use_kenv;
44 static char *hintp;
45
46 /*
47 * Define kern.hintmode sysctl, which only accept value 2, that cause to
48 * switch from Static KENV mode to Dynamic KENV. So systems that have hints
49 * compiled into kernel will be able to see/modify KENV (and hints too).
50 */
51
52 static int
53 sysctl_hintmode(SYSCTL_HANDLER_ARGS)
54 {
55 const char *cp;
56 char *line, *eq;
57 int eqidx, error, from_kenv, i, value;
58
59 from_kenv = 0;
60 cp = kern_envp;
61 value = hintmode;
62
63 /* Fetch candidate for new hintmode value */
64 error = sysctl_handle_int(oidp, &value, 0, req);
65 if (error || req->newptr == NULL)
66 return (error);
67
68 if (value != 2)
69 /* Only accept swithing to hintmode 2 */
70 return (EINVAL);
71
72 /* Migrate from static to dynamic hints */
73 switch (hintmode) {
74 case 0:
75 if (dynamic_kenv) {
76 /*
77 * Already here. But assign hintmode to 2, to not
78 * check it in the future.
79 */
80 hintmode = 2;
81 return (0);
82 }
83 from_kenv = 1;
84 cp = kern_envp;
85 break;
86 case 1:
87 cp = static_hints;
88 break;
89 case 2:
90 /* Nothing to do, hintmode already 2 */
91 return (0);
92 }
93
94 while (cp) {
95 i = strlen(cp);
96 if (i == 0)
97 break;
98 if (from_kenv) {
99 if (strncmp(cp, "hint.", 5) != 0)
100 /* kenv can have not only hints */
101 continue;
102 }
103 eq = strchr(cp, '=');
104 if (eq == NULL)
105 /* Bad hint value */
106 continue;
107 eqidx = eq - cp;
108
109 line = malloc(i+1, M_TEMP, M_WAITOK);
110 strcpy(line, cp);
111 line[eqidx] = '\0';
112 setenv(line, line + eqidx + 1);
113 free(line, M_TEMP);
114 cp += i + 1;
115 }
116
117 hintmode = value;
118 use_kenv = 1;
119 return (0);
120 }
121
122 SYSCTL_PROC(_kern, OID_AUTO, hintmode, CTLTYPE_INT|CTLFLAG_RW,
123 &hintmode, 0, sysctl_hintmode, "I", "Get/set current hintmode");
124
125 /*
126 * Evil wildcarding resource string lookup.
127 * This walks the supplied env string table and returns a match.
128 * The start point can be remembered for incremental searches.
129 */
130 static int
131 res_find(int *line, int *startln,
132 const char *name, int *unit, const char *resname, const char *value,
133 const char **ret_name, int *ret_namelen, int *ret_unit,
134 const char **ret_resname, int *ret_resnamelen, const char **ret_value)
135 {
136 int n = 0, hit, i = 0;
137 char r_name[32];
138 int r_unit;
139 char r_resname[32];
140 char r_value[128];
141 const char *s, *cp;
142 char *p;
143
144 if (checkmethod) {
145 hintp = NULL;
146
147 switch (hintmode) {
148 case 0: /* loader hints in environment only */
149 break;
150 case 1: /* static hints only */
151 hintp = static_hints;
152 checkmethod = 0;
153 break;
154 case 2: /* fallback mode */
155 if (dynamic_kenv) {
156 mtx_lock(&kenv_lock);
157 cp = kenvp[0];
158 for (i = 0; cp != NULL; cp = kenvp[++i]) {
159 if (!strncmp(cp, "hint.", 5)) {
160 use_kenv = 1;
161 checkmethod = 0;
162 break;
163 }
164 }
165 mtx_unlock(&kenv_lock);
166 } else {
167 cp = kern_envp;
168 while (cp) {
169 if (strncmp(cp, "hint.", 5) == 0) {
170 cp = NULL;
171 hintp = kern_envp;
172 break;
173 }
174 while (*cp != '\0')
175 cp++;
176 cp++;
177 if (*cp == '\0') {
178 cp = NULL;
179 hintp = static_hints;
180 break;
181 }
182 }
183 }
184 break;
185 default:
186 break;
187 }
188 if (hintp == NULL) {
189 if (dynamic_kenv) {
190 use_kenv = 1;
191 checkmethod = 0;
192 } else
193 hintp = kern_envp;
194 }
195 }
196
197 if (use_kenv) {
198 mtx_lock(&kenv_lock);
199 i = 0;
200 cp = kenvp[0];
201 if (cp == NULL) {
202 mtx_unlock(&kenv_lock);
203 return (ENOENT);
204 }
205 } else
206 cp = hintp;
207 while (cp) {
208 hit = 1;
209 (*line)++;
210 if (strncmp(cp, "hint.", 5) != 0)
211 hit = 0;
212 else
213 n = sscanf(cp, "hint.%32[^.].%d.%32[^=]=%128s",
214 r_name, &r_unit, r_resname, r_value);
215 if (hit && n != 4) {
216 printf("CONFIG: invalid hint '%s'\n", cp);
217 p = strchr(cp, 'h');
218 *p = 'H';
219 hit = 0;
220 }
221 if (hit && startln && *startln >= 0 && *line < *startln)
222 hit = 0;
223 if (hit && name && strcmp(name, r_name) != 0)
224 hit = 0;
225 if (hit && unit && *unit != r_unit)
226 hit = 0;
227 if (hit && resname && strcmp(resname, r_resname) != 0)
228 hit = 0;
229 if (hit && value && strcmp(value, r_value) != 0)
230 hit = 0;
231 if (hit)
232 break;
233 if (use_kenv) {
234 cp = kenvp[++i];
235 if (cp == NULL)
236 break;
237 } else {
238 while (*cp != '\0')
239 cp++;
240 cp++;
241 if (*cp == '\0') {
242 cp = NULL;
243 break;
244 }
245 }
246 }
247 if (use_kenv)
248 mtx_unlock(&kenv_lock);
249 if (cp == NULL)
250 return ENOENT;
251
252 s = cp;
253 /* This is a bit of a hack, but at least is reentrant */
254 /* Note that it returns some !unterminated! strings. */
255 s = strchr(s, '.') + 1; /* start of device */
256 if (ret_name)
257 *ret_name = s;
258 s = strchr(s, '.') + 1; /* start of unit */
259 if (ret_namelen && ret_name)
260 *ret_namelen = s - *ret_name - 1; /* device length */
261 if (ret_unit)
262 *ret_unit = r_unit;
263 s = strchr(s, '.') + 1; /* start of resname */
264 if (ret_resname)
265 *ret_resname = s;
266 s = strchr(s, '=') + 1; /* start of value */
267 if (ret_resnamelen && ret_resname)
268 *ret_resnamelen = s - *ret_resname - 1; /* value len */
269 if (ret_value)
270 *ret_value = s;
271 if (startln) /* line number for anchor */
272 *startln = *line + 1;
273 return 0;
274 }
275
276 /*
277 * Search all the data sources for matches to our query. We look for
278 * dynamic hints first as overrides for static or fallback hints.
279 */
280 static int
281 resource_find(int *line, int *startln,
282 const char *name, int *unit, const char *resname, const char *value,
283 const char **ret_name, int *ret_namelen, int *ret_unit,
284 const char **ret_resname, int *ret_resnamelen, const char **ret_value)
285 {
286 int i;
287 int un;
288
289 *line = 0;
290
291 /* Search for exact unit matches first */
292 i = res_find(line, startln, name, unit, resname, value,
293 ret_name, ret_namelen, ret_unit, ret_resname, ret_resnamelen,
294 ret_value);
295 if (i == 0)
296 return 0;
297 if (unit == NULL)
298 return ENOENT;
299 /* If we are still here, search for wildcard matches */
300 un = -1;
301 i = res_find(line, startln, name, &un, resname, value,
302 ret_name, ret_namelen, ret_unit, ret_resname, ret_resnamelen,
303 ret_value);
304 if (i == 0)
305 return 0;
306 return ENOENT;
307 }
308
309 int
310 resource_int_value(const char *name, int unit, const char *resname, int *result)
311 {
312 int error;
313 const char *str;
314 char *op;
315 unsigned long val;
316 int line;
317
318 line = 0;
319 error = resource_find(&line, NULL, name, &unit, resname, NULL,
320 NULL, NULL, NULL, NULL, NULL, &str);
321 if (error)
322 return error;
323 if (*str == '\0')
324 return EFTYPE;
325 val = strtoul(str, &op, 0);
326 if (*op != '\0')
327 return EFTYPE;
328 *result = val;
329 return 0;
330 }
331
332 int
333 resource_long_value(const char *name, int unit, const char *resname,
334 long *result)
335 {
336 int error;
337 const char *str;
338 char *op;
339 unsigned long val;
340 int line;
341
342 line = 0;
343 error = resource_find(&line, NULL, name, &unit, resname, NULL,
344 NULL, NULL, NULL, NULL, NULL, &str);
345 if (error)
346 return error;
347 if (*str == '\0')
348 return EFTYPE;
349 val = strtoul(str, &op, 0);
350 if (*op != '\0')
351 return EFTYPE;
352 *result = val;
353 return 0;
354 }
355
356 int
357 resource_string_value(const char *name, int unit, const char *resname,
358 const char **result)
359 {
360 int error;
361 const char *str;
362 int line;
363
364 line = 0;
365 error = resource_find(&line, NULL, name, &unit, resname, NULL,
366 NULL, NULL, NULL, NULL, NULL, &str);
367 if (error)
368 return error;
369 *result = str;
370 return 0;
371 }
372
373 /*
374 * This is a bit nasty, but allows us to not modify the env strings.
375 */
376 static const char *
377 resource_string_copy(const char *s, int len)
378 {
379 static char stringbuf[256];
380 static int offset = 0;
381 const char *ret;
382
383 if (len == 0)
384 len = strlen(s);
385 if (len > 255)
386 return NULL;
387 if ((offset + len + 1) > 255)
388 offset = 0;
389 bcopy(s, &stringbuf[offset], len);
390 stringbuf[offset + len] = '\0';
391 ret = &stringbuf[offset];
392 offset += len + 1;
393 return ret;
394 }
395
396 /*
397 * err = resource_find_match(&anchor, &name, &unit, resname, value)
398 * Iteratively fetch a list of devices wired "at" something
399 * res and value are restrictions. eg: "at", "scbus0".
400 * For practical purposes, res = required, value = optional.
401 * *name and *unit are set.
402 * set *anchor to zero before starting.
403 */
404 int
405 resource_find_match(int *anchor, const char **name, int *unit,
406 const char *resname, const char *value)
407 {
408 const char *found_name;
409 int found_namelen;
410 int found_unit;
411 int ret;
412 int newln;
413
414 newln = *anchor;
415 ret = resource_find(anchor, &newln, NULL, NULL, resname, value,
416 &found_name, &found_namelen, &found_unit, NULL, NULL, NULL);
417 if (ret == 0) {
418 *name = resource_string_copy(found_name, found_namelen);
419 *unit = found_unit;
420 }
421 *anchor = newln;
422 return ret;
423 }
424
425
426 /*
427 * err = resource_find_dev(&anchor, name, &unit, res, value);
428 * Iterate through a list of devices, returning their unit numbers.
429 * res and value are optional restrictions. eg: "at", "scbus0".
430 * *unit is set to the value.
431 * set *anchor to zero before starting.
432 */
433 int
434 resource_find_dev(int *anchor, const char *name, int *unit,
435 const char *resname, const char *value)
436 {
437 int found_unit;
438 int newln;
439 int ret;
440
441 newln = *anchor;
442 ret = resource_find(anchor, &newln, name, NULL, resname, value,
443 NULL, NULL, &found_unit, NULL, NULL, NULL);
444 if (ret == 0) {
445 *unit = found_unit;
446 }
447 *anchor = newln;
448 return ret;
449 }
450
451 /*
452 * Check to see if a device is disabled via a disabled hint.
453 */
454 int
455 resource_disabled(const char *name, int unit)
456 {
457 int error, value;
458
459 error = resource_int_value(name, unit, "disabled", &value);
460 if (error)
461 return (0);
462 return (value);
463 }
Cache object: f74f25678bd6a3f68eeccf5cfa179117
|