1 /*- 2 * Copyright (c) 1998 Michael Smith <msmith@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 <stand.h> 32 #include <string.h> 33 34 #include "bootstrap.h" 35 /* 36 * Core console support 37 */ 38 39 static int cons_set(struct env_var *ev, int flags, const void *value); 40 static int cons_find(const char *name); 41 static int cons_check(const char *string); 42 static int cons_change(const char *string); 43 static int twiddle_set(struct env_var *ev, int flags, const void *value); 44 45 #ifndef MODULE_VERBOSE 46 # define MODULE_VERBOSE MODULE_VERBOSE_TWIDDLE 47 #endif 48 int module_verbose = MODULE_VERBOSE; 49 50 static int 51 module_verbose_set(struct env_var *ev, int flags, const void *value) 52 { 53 u_long v; 54 char *eptr; 55 56 v = strtoul(value, &eptr, 0); 57 if (*(const char *)value == 0 || *eptr != 0) { 58 printf("invalid module_verbose '%s'\n", (const char *)value); 59 return (CMD_ERROR); 60 } 61 module_verbose = (int)v; 62 if (module_verbose < MODULE_VERBOSE_TWIDDLE) { 63 /* A hack for now; we do not want twiddling */ 64 twiddle_divisor(UINT_MAX); 65 } 66 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 67 68 return (CMD_OK); 69 } 70 71 /* 72 * Detect possible console(s) to use. If preferred console(s) have been 73 * specified, mark them as active. Else, mark the first probed console 74 * as active. Also create the console variable. 75 */ 76 void 77 cons_probe(void) 78 { 79 int cons; 80 int active; 81 char *prefconsole; 82 char module_verbose_buf[8]; 83 84 TSENTER(); 85 86 /* We want a callback to install the new value when these vars change. */ 87 snprintf(module_verbose_buf, sizeof(module_verbose_buf), "%d", 88 module_verbose); 89 env_setenv("module_verbose", EV_VOLATILE, module_verbose_buf, 90 module_verbose_set, env_nounset); 91 env_setenv("twiddle_divisor", EV_VOLATILE, "16", twiddle_set, 92 env_nounset); 93 94 /* Do all console probes */ 95 for (cons = 0; consoles[cons] != NULL; cons++) { 96 consoles[cons]->c_flags = 0; 97 consoles[cons]->c_probe(consoles[cons]); 98 } 99 /* Now find the first working one */ 100 active = -1; 101 for (cons = 0; consoles[cons] != NULL && active == -1; cons++) { 102 consoles[cons]->c_flags = 0; 103 consoles[cons]->c_probe(consoles[cons]); 104 if (consoles[cons]->c_flags == (C_PRESENTIN | C_PRESENTOUT)) 105 active = cons; 106 } 107 /* Force a console even if all probes failed */ 108 if (active == -1) 109 active = 0; 110 111 /* Check to see if a console preference has already been registered */ 112 prefconsole = getenv("console"); 113 if (prefconsole != NULL) 114 prefconsole = strdup(prefconsole); 115 if (prefconsole != NULL) { 116 unsetenv("console"); /* we want to replace this */ 117 cons_change(prefconsole); 118 } else { 119 consoles[active]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT; 120 consoles[active]->c_init(0); 121 prefconsole = strdup(consoles[active]->c_name); 122 } 123 124 printf("Consoles: "); 125 for (cons = 0; consoles[cons] != NULL; cons++) 126 if (consoles[cons]->c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) 127 printf("%s ", consoles[cons]->c_desc); 128 printf("\n"); 129 130 if (prefconsole != NULL) { 131 env_setenv("console", EV_VOLATILE, prefconsole, cons_set, 132 env_nounset); 133 free(prefconsole); 134 } 135 136 TSEXIT(); 137 } 138 139 int 140 getchar(void) 141 { 142 int cons; 143 int rv; 144 145 /* Loop forever polling all active consoles */ 146 for (;;) { 147 for (cons = 0; consoles[cons] != NULL; cons++) { 148 if ((consoles[cons]->c_flags & 149 (C_PRESENTIN | C_ACTIVEIN)) == 150 (C_PRESENTIN | C_ACTIVEIN) && 151 ((rv = consoles[cons]->c_in()) != -1)) 152 return (rv); 153 } 154 } 155 } 156 157 int 158 ischar(void) 159 { 160 int cons; 161 162 for (cons = 0; consoles[cons] != NULL; cons++) 163 if ((consoles[cons]->c_flags & (C_PRESENTIN | C_ACTIVEIN)) == 164 (C_PRESENTIN | C_ACTIVEIN) && 165 (consoles[cons]->c_ready() != 0)) 166 return (1); 167 return (0); 168 } 169 170 void 171 putchar(int c) 172 { 173 int cons; 174 175 /* Expand newlines */ 176 if (c == '\n') 177 putchar('\r'); 178 179 for (cons = 0; consoles[cons] != NULL; cons++) { 180 if ((consoles[cons]->c_flags & (C_PRESENTOUT | C_ACTIVEOUT)) == 181 (C_PRESENTOUT | C_ACTIVEOUT)) 182 consoles[cons]->c_out(c); 183 } 184 } 185 186 /* 187 * Find the console with the specified name. 188 */ 189 static int 190 cons_find(const char *name) 191 { 192 int cons; 193 194 for (cons = 0; consoles[cons] != NULL; cons++) 195 if (strcmp(consoles[cons]->c_name, name) == 0) 196 return (cons); 197 return (-1); 198 } 199 200 /* 201 * Select one or more consoles. 202 */ 203 static int 204 cons_set(struct env_var *ev, int flags, const void *value) 205 { 206 int ret; 207 208 if ((value == NULL) || (cons_check(value) == 0)) { 209 /* 210 * Return CMD_OK instead of CMD_ERROR to prevent forth syntax 211 * error, which would prevent it processing any further 212 * loader.conf entries. 213 */ 214 return (CMD_OK); 215 } 216 217 ret = cons_change(value); 218 if (ret != CMD_OK) 219 return (ret); 220 221 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 222 return (CMD_OK); 223 } 224 225 /* 226 * Check that at least one the consoles listed in *string is valid 227 */ 228 static int 229 cons_check(const char *string) 230 { 231 int cons, found, failed; 232 char *curpos, *dup, *next; 233 234 dup = next = strdup(string); 235 found = failed = 0; 236 while (next != NULL) { 237 curpos = strsep(&next, " ,"); 238 if (*curpos != '\0') { 239 cons = cons_find(curpos); 240 if (cons == -1) { 241 printf("console %s is invalid!\n", curpos); 242 failed++; 243 } else { 244 found++; 245 } 246 } 247 } 248 249 free(dup); 250 251 if (found == 0) 252 printf("no valid consoles!\n"); 253 254 if (found == 0 || failed != 0) { 255 printf("Available consoles:\n"); 256 for (cons = 0; consoles[cons] != NULL; cons++) 257 printf(" %s\n", consoles[cons]->c_name); 258 } 259 260 return (found); 261 } 262 263 /* 264 * Activate all the valid consoles listed in *string and disable all others. 265 */ 266 static int 267 cons_change(const char *string) 268 { 269 int cons, active; 270 char *curpos, *dup, *next; 271 272 /* Disable all consoles */ 273 for (cons = 0; consoles[cons] != NULL; cons++) { 274 consoles[cons]->c_flags &= ~(C_ACTIVEIN | C_ACTIVEOUT); 275 } 276 277 /* Enable selected consoles */ 278 dup = next = strdup(string); 279 active = 0; 280 while (next != NULL) { 281 curpos = strsep(&next, " ,"); 282 if (*curpos == '\0') 283 continue; 284 cons = cons_find(curpos); 285 if (cons >= 0) { 286 consoles[cons]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT; 287 consoles[cons]->c_init(0); 288 if ((consoles[cons]->c_flags & 289 (C_PRESENTIN | C_PRESENTOUT)) == 290 (C_PRESENTIN | C_PRESENTOUT)) { 291 active++; 292 continue; 293 } 294 295 if (active != 0) { 296 /* 297 * If no consoles have initialised we 298 * wouldn't see this. 299 */ 300 printf("console %s failed to initialize\n", 301 consoles[cons]->c_name); 302 } 303 } 304 } 305 306 free(dup); 307 308 if (active == 0) { 309 /* 310 * All requested consoles failed to initialise, 311 * try to recover. 312 */ 313 for (cons = 0; consoles[cons] != NULL; cons++) { 314 consoles[cons]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT; 315 consoles[cons]->c_init(0); 316 if ((consoles[cons]->c_flags & 317 (C_PRESENTIN | C_PRESENTOUT)) == 318 (C_PRESENTIN | C_PRESENTOUT)) 319 active++; 320 } 321 322 if (active == 0) 323 return (CMD_ERROR); /* Recovery failed. */ 324 } 325 326 return (CMD_OK); 327 } 328 329 /* 330 * Change the twiddle divisor. 331 * 332 * The user can set the twiddle_divisor variable to directly control how fast 333 * the progress twiddle spins, useful for folks with slow serial consoles. The 334 * code to monitor changes to the variable and propagate them to the twiddle 335 * routines has to live somewhere. Twiddling is console-related so it's here. 336 */ 337 static int 338 twiddle_set(struct env_var *ev, int flags, const void *value) 339 { 340 u_long tdiv; 341 char *eptr; 342 343 tdiv = strtoul(value, &eptr, 0); 344 if (*(const char *)value == 0 || *eptr != 0) { 345 printf("invalid twiddle_divisor '%s'\n", (const char *)value); 346 return (CMD_ERROR); 347 } 348 twiddle_divisor((u_int)tdiv); 349 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 350 351 return (CMD_OK); 352 } 353