1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2000,2001 Peter Wemm <peter@FreeBSD.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include <sys/param.h> 33 #include <sys/lock.h> 34 #include <sys/malloc.h> 35 #include <sys/mutex.h> 36 #include <sys/sysctl.h> 37 #include <sys/systm.h> 38 #include <sys/bus.h> 39 40 /* 41 * Access functions for device resources. 42 */ 43 44 static int checkmethod = 1; 45 static int use_kenv; 46 static char *hintp; 47 48 /* 49 * Define kern.hintmode sysctl, which only accept value 2, that cause to 50 * switch from Static KENV mode to Dynamic KENV. So systems that have hints 51 * compiled into kernel will be able to see/modify KENV (and hints too). 52 */ 53 54 static int 55 sysctl_hintmode(SYSCTL_HANDLER_ARGS) 56 { 57 const char *cp; 58 char *line, *eq; 59 int eqidx, error, from_kenv, i, value; 60 61 from_kenv = 0; 62 cp = kern_envp; 63 value = hintmode; 64 65 /* Fetch candidate for new hintmode value */ 66 error = sysctl_handle_int(oidp, &value, 0, req); 67 if (error || req->newptr == NULL) 68 return (error); 69 70 if (value != 2) 71 /* Only accept swithing to hintmode 2 */ 72 return (EINVAL); 73 74 /* Migrate from static to dynamic hints */ 75 switch (hintmode) { 76 case 0: 77 if (dynamic_kenv) { 78 /* 79 * Already here. But assign hintmode to 2, to not 80 * check it in the future. 81 */ 82 hintmode = 2; 83 return (0); 84 } 85 from_kenv = 1; 86 cp = kern_envp; 87 break; 88 case 1: 89 cp = static_hints; 90 break; 91 case 2: 92 /* Nothing to do, hintmode already 2 */ 93 return (0); 94 } 95 96 while (cp) { 97 i = strlen(cp); 98 if (i == 0) 99 break; 100 if (from_kenv) { 101 if (strncmp(cp, "hint.", 5) != 0) 102 /* kenv can have not only hints */ 103 continue; 104 } 105 eq = strchr(cp, '='); 106 if (eq == NULL) 107 /* Bad hint value */ 108 continue; 109 eqidx = eq - cp; 110 111 line = malloc(i+1, M_TEMP, M_WAITOK); 112 strcpy(line, cp); 113 line[eqidx] = '\0'; 114 kern_setenv(line, line + eqidx + 1); 115 free(line, M_TEMP); 116 cp += i + 1; 117 } 118 119 hintmode = value; 120 use_kenv = 1; 121 return (0); 122 } 123 124 SYSCTL_PROC(_kern, OID_AUTO, hintmode, CTLTYPE_INT|CTLFLAG_RW, 125 &hintmode, 0, sysctl_hintmode, "I", "Get/set current hintmode"); 126 127 /* 128 * Evil wildcarding resource string lookup. 129 * This walks the supplied env string table and returns a match. 130 * The start point can be remembered for incremental searches. 131 */ 132 static int 133 res_find(int *line, int *startln, 134 const char *name, int *unit, const char *resname, const char *value, 135 const char **ret_name, int *ret_namelen, int *ret_unit, 136 const char **ret_resname, int *ret_resnamelen, const char **ret_value) 137 { 138 int n = 0, hit, i = 0; 139 char r_name[32]; 140 int r_unit; 141 char r_resname[32]; 142 char r_value[128]; 143 const char *s, *cp; 144 char *p; 145 146 if (checkmethod) { 147 hintp = NULL; 148 149 switch (hintmode) { 150 case 0: /* loader hints in environment only */ 151 break; 152 case 1: /* static hints only */ 153 hintp = static_hints; 154 checkmethod = 0; 155 break; 156 case 2: /* fallback mode */ 157 if (dynamic_kenv) { 158 mtx_lock(&kenv_lock); 159 cp = kenvp[0]; 160 for (i = 0; cp != NULL; cp = kenvp[++i]) { 161 if (!strncmp(cp, "hint.", 5)) { 162 use_kenv = 1; 163 checkmethod = 0; 164 break; 165 } 166 } 167 mtx_unlock(&kenv_lock); 168 } else { 169 cp = kern_envp; 170 while (cp) { 171 if (strncmp(cp, "hint.", 5) == 0) { 172 cp = NULL; 173 hintp = kern_envp; 174 break; 175 } 176 while (*cp != '\0') 177 cp++; 178 cp++; 179 if (*cp == '\0') { 180 cp = NULL; 181 hintp = static_hints; 182 break; 183 } 184 } 185 } 186 break; 187 default: 188 break; 189 } 190 if (hintp == NULL) { 191 if (dynamic_kenv) { 192 use_kenv = 1; 193 checkmethod = 0; 194 } else 195 hintp = kern_envp; 196 } 197 } 198 199 if (use_kenv) { 200 mtx_lock(&kenv_lock); 201 i = 0; 202 cp = kenvp[0]; 203 if (cp == NULL) { 204 mtx_unlock(&kenv_lock); 205 return (ENOENT); 206 } 207 } else 208 cp = hintp; 209 while (cp) { 210 hit = 1; 211 (*line)++; 212 if (strncmp(cp, "hint.", 5) != 0) 213 hit = 0; 214 else 215 n = sscanf(cp, "hint.%32[^.].%d.%32[^=]=%127s", 216 r_name, &r_unit, r_resname, r_value); 217 if (hit && n != 4) { 218 printf("CONFIG: invalid hint '%s'\n", cp); 219 p = strchr(cp, 'h'); 220 *p = 'H'; 221 hit = 0; 222 } 223 if (hit && startln && *startln >= 0 && *line < *startln) 224 hit = 0; 225 if (hit && name && strcmp(name, r_name) != 0) 226 hit = 0; 227 if (hit && unit && *unit != r_unit) 228 hit = 0; 229 if (hit && resname && strcmp(resname, r_resname) != 0) 230 hit = 0; 231 if (hit && value && strcmp(value, r_value) != 0) 232 hit = 0; 233 if (hit) 234 break; 235 if (use_kenv) { 236 cp = kenvp[++i]; 237 if (cp == NULL) 238 break; 239 } else { 240 while (*cp != '\0') 241 cp++; 242 cp++; 243 if (*cp == '\0') { 244 cp = NULL; 245 break; 246 } 247 } 248 } 249 if (use_kenv) 250 mtx_unlock(&kenv_lock); 251 if (cp == NULL) 252 return ENOENT; 253 254 s = cp; 255 /* This is a bit of a hack, but at least is reentrant */ 256 /* Note that it returns some !unterminated! strings. */ 257 s = strchr(s, '.') + 1; /* start of device */ 258 if (ret_name) 259 *ret_name = s; 260 s = strchr(s, '.') + 1; /* start of unit */ 261 if (ret_namelen && ret_name) 262 *ret_namelen = s - *ret_name - 1; /* device length */ 263 if (ret_unit) 264 *ret_unit = r_unit; 265 s = strchr(s, '.') + 1; /* start of resname */ 266 if (ret_resname) 267 *ret_resname = s; 268 s = strchr(s, '=') + 1; /* start of value */ 269 if (ret_resnamelen && ret_resname) 270 *ret_resnamelen = s - *ret_resname - 1; /* value len */ 271 if (ret_value) 272 *ret_value = s; 273 if (startln) /* line number for anchor */ 274 *startln = *line + 1; 275 return 0; 276 } 277 278 /* 279 * Search all the data sources for matches to our query. We look for 280 * dynamic hints first as overrides for static or fallback hints. 281 */ 282 static int 283 resource_find(int *line, int *startln, 284 const char *name, int *unit, const char *resname, const char *value, 285 const char **ret_name, int *ret_namelen, int *ret_unit, 286 const char **ret_resname, int *ret_resnamelen, const char **ret_value) 287 { 288 int i; 289 int un; 290 291 *line = 0; 292 293 /* Search for exact unit matches first */ 294 i = res_find(line, startln, name, unit, resname, value, 295 ret_name, ret_namelen, ret_unit, ret_resname, ret_resnamelen, 296 ret_value); 297 if (i == 0) 298 return 0; 299 if (unit == NULL) 300 return ENOENT; 301 /* If we are still here, search for wildcard matches */ 302 un = -1; 303 i = res_find(line, startln, name, &un, resname, value, 304 ret_name, ret_namelen, ret_unit, ret_resname, ret_resnamelen, 305 ret_value); 306 if (i == 0) 307 return 0; 308 return ENOENT; 309 } 310 311 int 312 resource_int_value(const char *name, int unit, const char *resname, int *result) 313 { 314 int error; 315 const char *str; 316 char *op; 317 unsigned long val; 318 int line; 319 320 line = 0; 321 error = resource_find(&line, NULL, name, &unit, resname, NULL, 322 NULL, NULL, NULL, NULL, NULL, &str); 323 if (error) 324 return error; 325 if (*str == '\0') 326 return EFTYPE; 327 val = strtoul(str, &op, 0); 328 if (*op != '\0') 329 return EFTYPE; 330 *result = val; 331 return 0; 332 } 333 334 int 335 resource_long_value(const char *name, int unit, const char *resname, 336 long *result) 337 { 338 int error; 339 const char *str; 340 char *op; 341 unsigned long val; 342 int line; 343 344 line = 0; 345 error = resource_find(&line, NULL, name, &unit, resname, NULL, 346 NULL, NULL, NULL, NULL, NULL, &str); 347 if (error) 348 return error; 349 if (*str == '\0') 350 return EFTYPE; 351 val = strtoul(str, &op, 0); 352 if (*op != '\0') 353 return EFTYPE; 354 *result = val; 355 return 0; 356 } 357 358 int 359 resource_string_value(const char *name, int unit, const char *resname, 360 const char **result) 361 { 362 int error; 363 const char *str; 364 int line; 365 366 line = 0; 367 error = resource_find(&line, NULL, name, &unit, resname, NULL, 368 NULL, NULL, NULL, NULL, NULL, &str); 369 if (error) 370 return error; 371 *result = str; 372 return 0; 373 } 374 375 /* 376 * This is a bit nasty, but allows us to not modify the env strings. 377 */ 378 static const char * 379 resource_string_copy(const char *s, int len) 380 { 381 static char stringbuf[256]; 382 static int offset = 0; 383 const char *ret; 384 385 if (len == 0) 386 len = strlen(s); 387 if (len > 255) 388 return NULL; 389 if ((offset + len + 1) > 255) 390 offset = 0; 391 bcopy(s, &stringbuf[offset], len); 392 stringbuf[offset + len] = '\0'; 393 ret = &stringbuf[offset]; 394 offset += len + 1; 395 return ret; 396 } 397 398 /* 399 * err = resource_find_match(&anchor, &name, &unit, resname, value) 400 * Iteratively fetch a list of devices wired "at" something 401 * res and value are restrictions. eg: "at", "scbus0". 402 * For practical purposes, res = required, value = optional. 403 * *name and *unit are set. 404 * set *anchor to zero before starting. 405 */ 406 int 407 resource_find_match(int *anchor, const char **name, int *unit, 408 const char *resname, const char *value) 409 { 410 const char *found_name; 411 int found_namelen; 412 int found_unit; 413 int ret; 414 int newln; 415 416 newln = *anchor; 417 ret = resource_find(anchor, &newln, NULL, NULL, resname, value, 418 &found_name, &found_namelen, &found_unit, NULL, NULL, NULL); 419 if (ret == 0) { 420 *name = resource_string_copy(found_name, found_namelen); 421 *unit = found_unit; 422 } 423 *anchor = newln; 424 return ret; 425 } 426 427 428 /* 429 * err = resource_find_dev(&anchor, name, &unit, res, value); 430 * Iterate through a list of devices, returning their unit numbers. 431 * res and value are optional restrictions. eg: "at", "scbus0". 432 * *unit is set to the value. 433 * set *anchor to zero before starting. 434 */ 435 int 436 resource_find_dev(int *anchor, const char *name, int *unit, 437 const char *resname, const char *value) 438 { 439 int found_unit; 440 int newln; 441 int ret; 442 443 newln = *anchor; 444 ret = resource_find(anchor, &newln, name, NULL, resname, value, 445 NULL, NULL, &found_unit, NULL, NULL, NULL); 446 if (ret == 0) { 447 *unit = found_unit; 448 } 449 *anchor = newln; 450 return ret; 451 } 452 453 /* 454 * Check to see if a device is disabled via a disabled hint. 455 */ 456 int 457 resource_disabled(const char *name, int unit) 458 { 459 int error, value; 460 461 error = resource_int_value(name, unit, "disabled", &value); 462 if (error) 463 return (0); 464 return (value); 465 } 466 467 /* 468 * Clear a value associated with a device by removing it from 469 * the kernel environment. This only removes a hint for an 470 * exact unit. 471 */ 472 int 473 resource_unset_value(const char *name, int unit, const char *resname) 474 { 475 char varname[128]; 476 const char *retname, *retvalue; 477 int error, line; 478 size_t len; 479 480 line = 0; 481 error = resource_find(&line, NULL, name, &unit, resname, NULL, 482 &retname, NULL, NULL, NULL, NULL, &retvalue); 483 if (error) 484 return (error); 485 486 retname -= strlen("hint."); 487 len = retvalue - retname - 1; 488 if (len > sizeof(varname) - 1) 489 return (ENAMETOOLONG); 490 memcpy(varname, retname, len); 491 varname[len] = '\0'; 492 return (kern_unsetenv(varname)); 493 } 494