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