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$"); 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 kern_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[^=]=%127s", 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 } 464 465 /* 466 * Clear a value associated with a device by removing it from 467 * the kernel environment. This only removes a hint for an 468 * exact unit. 469 */ 470 int 471 resource_unset_value(const char *name, int unit, const char *resname) 472 { 473 char varname[128]; 474 const char *retname, *retvalue; 475 int error, line; 476 size_t len; 477 478 line = 0; 479 error = resource_find(&line, NULL, name, &unit, resname, NULL, 480 &retname, NULL, NULL, NULL, NULL, &retvalue); 481 if (error) 482 return (error); 483 484 retname -= strlen("hint."); 485 len = retvalue - retname - 1; 486 if (len > sizeof(varname) - 1) 487 return (ENAMETOOLONG); 488 memcpy(varname, retname, len); 489 varname[len] = '\0'; 490 return (kern_unsetenv(varname)); 491 } 492