178135Speter/*- 278135Speter * Copyright (c) 2000,2001 Peter Wemm <peter@FreeBSD.org> 378135Speter * All rights reserved. 478135Speter * 578135Speter * Redistribution and use in source and binary forms, with or without 678135Speter * modification, are permitted provided that the following conditions 778135Speter * are met: 878135Speter * 1. Redistributions of source code must retain the above copyright 978135Speter * notice, this list of conditions and the following disclaimer. 1078135Speter * 2. Redistributions in binary form must reproduce the above copyright 1178135Speter * notice, this list of conditions and the following disclaimer in the 1278135Speter * documentation and/or other materials provided with the distribution. 1378135Speter * 1478135Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1578135Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1678135Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1778135Speter * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1878135Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1978135Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2078135Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2178135Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2278135Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2378135Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2478135Speter * SUCH DAMAGE. 2578135Speter */ 2678135Speter 27116182Sobrien#include <sys/cdefs.h> 28116182Sobrien__FBSDID("$FreeBSD$"); 29116182Sobrien 3078135Speter#include <sys/param.h> 3194936Smux#include <sys/lock.h> 32240067Sray#include <sys/malloc.h> 33160217Sscottl#include <sys/mutex.h> 34240119Sray#include <sys/sysctl.h> 3578135Speter#include <sys/systm.h> 3678135Speter#include <sys/bus.h> 3778135Speter 3878135Speter/* 3978135Speter * Access functions for device resources. 4078135Speter */ 4178135Speter 4294936Smuxstatic int checkmethod = 1; 4395593Speterstatic int use_kenv; 4479696Speterstatic char *hintp; 4578135Speter 4678135Speter/* 47240067Sray * Define kern.hintmode sysctl, which only accept value 2, that cause to 48240067Sray * switch from Static KENV mode to Dynamic KENV. So systems that have hints 49240067Sray * compiled into kernel will be able to see/modify KENV (and hints too). 50240067Sray */ 51240067Sray 52240067Sraystatic int 53240067Sraysysctl_hintmode(SYSCTL_HANDLER_ARGS) 54240067Sray{ 55240067Sray const char *cp; 56240067Sray char *line, *eq; 57240119Sray int eqidx, error, from_kenv, i, value; 58240067Sray 59240067Sray from_kenv = 0; 60240067Sray cp = kern_envp; 61240067Sray value = hintmode; 62240067Sray 63240067Sray /* Fetch candidate for new hintmode value */ 64240067Sray error = sysctl_handle_int(oidp, &value, 0, req); 65240119Sray if (error || req->newptr == NULL) 66240067Sray return (error); 67240067Sray 68240067Sray if (value != 2) 69240067Sray /* Only accept swithing to hintmode 2 */ 70240067Sray return (EINVAL); 71240067Sray 72240067Sray /* Migrate from static to dynamic hints */ 73240067Sray switch (hintmode) { 74240067Sray case 0: 75240070Sray if (dynamic_kenv) { 76240119Sray /* 77240119Sray * Already here. But assign hintmode to 2, to not 78240119Sray * check it in the future. 79240119Sray */ 80240119Sray hintmode = 2; 81240067Sray return (0); 82240070Sray } 83240067Sray from_kenv = 1; 84240067Sray cp = kern_envp; 85240067Sray break; 86240067Sray case 1: 87240067Sray cp = static_hints; 88240067Sray break; 89240067Sray case 2: 90240067Sray /* Nothing to do, hintmode already 2 */ 91240067Sray return (0); 92240067Sray } 93240067Sray 94240067Sray while (cp) { 95240067Sray i = strlen(cp); 96240067Sray if (i == 0) 97240067Sray break; 98240067Sray if (from_kenv) { 99240067Sray if (strncmp(cp, "hint.", 5) != 0) 100240067Sray /* kenv can have not only hints */ 101240067Sray continue; 102240067Sray } 103240067Sray eq = strchr(cp, '='); 104240119Sray if (eq == NULL) 105240067Sray /* Bad hint value */ 106240067Sray continue; 107240067Sray eqidx = eq - cp; 108240067Sray 109240067Sray line = malloc(i+1, M_TEMP, M_WAITOK); 110240067Sray strcpy(line, cp); 111240067Sray line[eqidx] = '\0'; 112240067Sray setenv(line, line + eqidx + 1); 113240067Sray free(line, M_TEMP); 114240067Sray cp += i + 1; 115240067Sray } 116240067Sray 117240067Sray hintmode = value; 118240067Sray use_kenv = 1; 119240067Sray return (0); 120240067Sray} 121240067Sray 122240067SraySYSCTL_PROC(_kern, OID_AUTO, hintmode, CTLTYPE_INT|CTLFLAG_RW, 123240067Sray &hintmode, 0, sysctl_hintmode, "I", "Get/set current hintmode"); 124240067Sray 125240067Sray/* 12678135Speter * Evil wildcarding resource string lookup. 12778135Speter * This walks the supplied env string table and returns a match. 12878135Speter * The start point can be remembered for incremental searches. 12978135Speter */ 13078135Speterstatic int 13179696Speterres_find(int *line, int *startln, 13278135Speter const char *name, int *unit, const char *resname, const char *value, 13378135Speter const char **ret_name, int *ret_namelen, int *ret_unit, 13478135Speter const char **ret_resname, int *ret_resnamelen, const char **ret_value) 13578135Speter{ 13695593Speter int n = 0, hit, i = 0; 13778135Speter char r_name[32]; 13878135Speter int r_unit; 13978135Speter char r_resname[32]; 14078135Speter char r_value[128]; 14179696Speter const char *s, *cp; 14278135Speter char *p; 14378135Speter 14494936Smux if (checkmethod) { 145148586Snetchild hintp = NULL; 146148586Snetchild 14779696Speter switch (hintmode) { 14895839Speter case 0: /* loader hints in environment only */ 14979696Speter break; 15079696Speter case 1: /* static hints only */ 15179696Speter hintp = static_hints; 15294936Smux checkmethod = 0; 15379696Speter break; 15479696Speter case 2: /* fallback mode */ 15594936Smux if (dynamic_kenv) { 156160217Sscottl mtx_lock(&kenv_lock); 15794936Smux cp = kenvp[0]; 15894936Smux for (i = 0; cp != NULL; cp = kenvp[++i]) { 15994936Smux if (!strncmp(cp, "hint.", 5)) { 16094936Smux use_kenv = 1; 16194936Smux checkmethod = 0; 16294936Smux break; 16394936Smux } 16479696Speter } 165160217Sscottl mtx_unlock(&kenv_lock); 16694936Smux } else { 16794936Smux cp = kern_envp; 16894936Smux while (cp) { 16994936Smux if (strncmp(cp, "hint.", 5) == 0) { 17094936Smux cp = NULL; 17194936Smux hintp = kern_envp; 17294936Smux break; 17394936Smux } 17494936Smux while (*cp != '\0') 17594936Smux cp++; 17679696Speter cp++; 17794936Smux if (*cp == '\0') { 17894936Smux cp = NULL; 17994936Smux hintp = static_hints; 18094936Smux break; 18194936Smux } 18279696Speter } 18379696Speter } 18479696Speter break; 18579696Speter default: 18679696Speter break; 18779696Speter } 18894936Smux if (hintp == NULL) { 18994936Smux if (dynamic_kenv) { 19094936Smux use_kenv = 1; 19194936Smux checkmethod = 0; 19294936Smux } else 19394936Smux hintp = kern_envp; 19494936Smux } 19579696Speter } 19679696Speter 19794936Smux if (use_kenv) { 198160217Sscottl mtx_lock(&kenv_lock); 19994936Smux i = 0; 20094936Smux cp = kenvp[0]; 20194936Smux if (cp == NULL) { 202160217Sscottl mtx_unlock(&kenv_lock); 20394936Smux return (ENOENT); 20494936Smux } 20595839Speter } else 20694936Smux cp = hintp; 20778135Speter while (cp) { 20878135Speter hit = 1; 20978135Speter (*line)++; 21078135Speter if (strncmp(cp, "hint.", 5) != 0) 21178135Speter hit = 0; 21278135Speter else 213267902Spluknet n = sscanf(cp, "hint.%32[^.].%d.%32[^=]=%127s", 21478135Speter r_name, &r_unit, r_resname, r_value); 21578135Speter if (hit && n != 4) { 21678135Speter printf("CONFIG: invalid hint '%s'\n", cp); 217229272Sed p = strchr(cp, 'h'); 21878135Speter *p = 'H'; 21978135Speter hit = 0; 22078135Speter } 22178135Speter if (hit && startln && *startln >= 0 && *line < *startln) 22278135Speter hit = 0; 22378135Speter if (hit && name && strcmp(name, r_name) != 0) 22478135Speter hit = 0; 22578135Speter if (hit && unit && *unit != r_unit) 22678135Speter hit = 0; 22778135Speter if (hit && resname && strcmp(resname, r_resname) != 0) 22878135Speter hit = 0; 22978135Speter if (hit && value && strcmp(value, r_value) != 0) 23078135Speter hit = 0; 23178135Speter if (hit) 23278135Speter break; 23395593Speter if (use_kenv) { 23494936Smux cp = kenvp[++i]; 23595593Speter if (cp == NULL) 23695593Speter break; 23795593Speter } else { 23895592Speter while (*cp != '\0') 23995592Speter cp++; 24078135Speter cp++; 24195593Speter if (*cp == '\0') { 24295593Speter cp = NULL; 24395593Speter break; 24495593Speter } 24595592Speter } 24678135Speter } 24794936Smux if (use_kenv) 248160217Sscottl mtx_unlock(&kenv_lock); 24978135Speter if (cp == NULL) 25078135Speter return ENOENT; 25178135Speter 25278135Speter s = cp; 25378135Speter /* This is a bit of a hack, but at least is reentrant */ 25478135Speter /* Note that it returns some !unterminated! strings. */ 255229272Sed s = strchr(s, '.') + 1; /* start of device */ 25678135Speter if (ret_name) 25778135Speter *ret_name = s; 258229272Sed s = strchr(s, '.') + 1; /* start of unit */ 259144077Sjhb if (ret_namelen && ret_name) 26078135Speter *ret_namelen = s - *ret_name - 1; /* device length */ 26178135Speter if (ret_unit) 26278135Speter *ret_unit = r_unit; 263229272Sed s = strchr(s, '.') + 1; /* start of resname */ 26478135Speter if (ret_resname) 26578135Speter *ret_resname = s; 266229272Sed s = strchr(s, '=') + 1; /* start of value */ 267144077Sjhb if (ret_resnamelen && ret_resname) 26878135Speter *ret_resnamelen = s - *ret_resname - 1; /* value len */ 26978135Speter if (ret_value) 27078135Speter *ret_value = s; 27178135Speter if (startln) /* line number for anchor */ 27278135Speter *startln = *line + 1; 27378135Speter return 0; 27478135Speter} 27578135Speter 27678135Speter/* 27778135Speter * Search all the data sources for matches to our query. We look for 27878135Speter * dynamic hints first as overrides for static or fallback hints. 27978135Speter */ 28078135Speterstatic int 28178135Speterresource_find(int *line, int *startln, 28278135Speter const char *name, int *unit, const char *resname, const char *value, 28378135Speter const char **ret_name, int *ret_namelen, int *ret_unit, 28478135Speter const char **ret_resname, int *ret_resnamelen, const char **ret_value) 28578135Speter{ 28678135Speter int i; 28778135Speter int un; 28878135Speter 28978135Speter *line = 0; 29078135Speter 29178135Speter /* Search for exact unit matches first */ 29279696Speter i = res_find(line, startln, name, unit, resname, value, 29378135Speter ret_name, ret_namelen, ret_unit, ret_resname, ret_resnamelen, 29478135Speter ret_value); 29578135Speter if (i == 0) 29678135Speter return 0; 29778135Speter if (unit == NULL) 29878135Speter return ENOENT; 29978135Speter /* If we are still here, search for wildcard matches */ 30078135Speter un = -1; 30179696Speter i = res_find(line, startln, name, &un, resname, value, 30278135Speter ret_name, ret_namelen, ret_unit, ret_resname, ret_resnamelen, 30378135Speter ret_value); 30478135Speter if (i == 0) 30578135Speter return 0; 30678135Speter return ENOENT; 30778135Speter} 30878135Speter 30978135Speterint 31078135Speterresource_int_value(const char *name, int unit, const char *resname, int *result) 31178135Speter{ 31278135Speter int error; 31378135Speter const char *str; 31478135Speter char *op; 31578135Speter unsigned long val; 31678135Speter int line; 31778135Speter 31878135Speter line = 0; 31978135Speter error = resource_find(&line, NULL, name, &unit, resname, NULL, 32078135Speter NULL, NULL, NULL, NULL, NULL, &str); 32178135Speter if (error) 32278135Speter return error; 32378135Speter if (*str == '\0') 32478135Speter return EFTYPE; 32578135Speter val = strtoul(str, &op, 0); 32678135Speter if (*op != '\0') 32778135Speter return EFTYPE; 32878135Speter *result = val; 32978135Speter return 0; 33078135Speter} 33178135Speter 33278135Speterint 33378135Speterresource_long_value(const char *name, int unit, const char *resname, 33478135Speter long *result) 33578135Speter{ 33678135Speter int error; 33778135Speter const char *str; 33878135Speter char *op; 33978135Speter unsigned long val; 34078135Speter int line; 34178135Speter 34278135Speter line = 0; 34378135Speter error = resource_find(&line, NULL, name, &unit, resname, NULL, 34478135Speter NULL, NULL, NULL, NULL, NULL, &str); 34578135Speter if (error) 34678135Speter return error; 34778135Speter if (*str == '\0') 34878135Speter return EFTYPE; 34978135Speter val = strtoul(str, &op, 0); 35078135Speter if (*op != '\0') 35178135Speter return EFTYPE; 35278135Speter *result = val; 35378135Speter return 0; 35478135Speter} 35578135Speter 35678135Speterint 35778135Speterresource_string_value(const char *name, int unit, const char *resname, 35878135Speter const char **result) 35978135Speter{ 36078135Speter int error; 36178135Speter const char *str; 36278135Speter int line; 36378135Speter 36478135Speter line = 0; 36578135Speter error = resource_find(&line, NULL, name, &unit, resname, NULL, 36678135Speter NULL, NULL, NULL, NULL, NULL, &str); 36778135Speter if (error) 36878135Speter return error; 36978135Speter *result = str; 37078135Speter return 0; 37178135Speter} 37278135Speter 37378135Speter/* 37478135Speter * This is a bit nasty, but allows us to not modify the env strings. 37578135Speter */ 37678135Speterstatic const char * 37778135Speterresource_string_copy(const char *s, int len) 37878135Speter{ 37978135Speter static char stringbuf[256]; 38078135Speter static int offset = 0; 38178135Speter const char *ret; 38278135Speter 38378135Speter if (len == 0) 38478135Speter len = strlen(s); 38578135Speter if (len > 255) 38678135Speter return NULL; 38778135Speter if ((offset + len + 1) > 255) 38878135Speter offset = 0; 38978135Speter bcopy(s, &stringbuf[offset], len); 39078135Speter stringbuf[offset + len] = '\0'; 39178135Speter ret = &stringbuf[offset]; 39278135Speter offset += len + 1; 39378135Speter return ret; 39478135Speter} 39578135Speter 39678135Speter/* 397143386Sjmg * err = resource_find_match(&anchor, &name, &unit, resname, value) 39878135Speter * Iteratively fetch a list of devices wired "at" something 39978135Speter * res and value are restrictions. eg: "at", "scbus0". 40078135Speter * For practical purposes, res = required, value = optional. 40178135Speter * *name and *unit are set. 40278135Speter * set *anchor to zero before starting. 40378135Speter */ 40478135Speterint 40578135Speterresource_find_match(int *anchor, const char **name, int *unit, 40678135Speter const char *resname, const char *value) 40778135Speter{ 40878135Speter const char *found_name; 40978135Speter int found_namelen; 41078135Speter int found_unit; 41178135Speter int ret; 41278135Speter int newln; 41378135Speter 41478135Speter newln = *anchor; 41578135Speter ret = resource_find(anchor, &newln, NULL, NULL, resname, value, 41678135Speter &found_name, &found_namelen, &found_unit, NULL, NULL, NULL); 41778135Speter if (ret == 0) { 41878135Speter *name = resource_string_copy(found_name, found_namelen); 41978135Speter *unit = found_unit; 42078135Speter } 42178135Speter *anchor = newln; 42278135Speter return ret; 42378135Speter} 42478135Speter 42578135Speter 42678135Speter/* 42778135Speter * err = resource_find_dev(&anchor, name, &unit, res, value); 42878135Speter * Iterate through a list of devices, returning their unit numbers. 42978135Speter * res and value are optional restrictions. eg: "at", "scbus0". 43078135Speter * *unit is set to the value. 43178135Speter * set *anchor to zero before starting. 43278135Speter */ 43378135Speterint 43478135Speterresource_find_dev(int *anchor, const char *name, int *unit, 43578135Speter const char *resname, const char *value) 43678135Speter{ 43778135Speter int found_unit; 43878135Speter int newln; 43978135Speter int ret; 44078135Speter 44178135Speter newln = *anchor; 44278135Speter ret = resource_find(anchor, &newln, name, NULL, resname, value, 44378135Speter NULL, NULL, &found_unit, NULL, NULL, NULL); 44478135Speter if (ret == 0) { 44578135Speter *unit = found_unit; 44678135Speter } 44778135Speter *anchor = newln; 44878135Speter return ret; 44978135Speter} 450117166Sjhb 451117166Sjhb/* 452117166Sjhb * Check to see if a device is disabled via a disabled hint. 453117166Sjhb */ 454117166Sjhbint 455117166Sjhbresource_disabled(const char *name, int unit) 456117166Sjhb{ 457117166Sjhb int error, value; 458117166Sjhb 459117166Sjhb error = resource_int_value(name, unit, "disabled", &value); 460117166Sjhb if (error) 461117166Sjhb return (0); 462117166Sjhb return (value); 463117166Sjhb} 464