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