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 29 #include <stand.h> 30 #include <string.h> 31 32 #include "bootstrap.h" 33 /* 34 * Core console support 35 */ 36 37 static int cons_set(struct env_var *ev, int flags, const void *value); 38 static int cons_find(const char *name); 39 static int cons_check(const char *string); 40 static int cons_change(const char *string); 41 static int twiddle_set(struct env_var *ev, int flags, const void *value); 42 43 /* 44 * Detect possible console(s) to use. If preferred console(s) have been 45 * specified, mark them as active. Else, mark the first probed console 46 * as active. Also create the console variable. 47 */ 48 void 49 cons_probe(void) 50 { 51 int cons; 52 int active; 53 char *prefconsole; 54 55 /* We want a callback to install the new value when this var changes. */ 56 env_setenv("twiddle_divisor", EV_VOLATILE, "1", twiddle_set, 57 env_nounset); 58 59 /* Do all console probes */ 60 for (cons = 0; consoles[cons] != NULL; cons++) { 61 consoles[cons]->c_flags = 0; 62 consoles[cons]->c_probe(consoles[cons]); 63 } 64 /* Now find the first working one */ 65 active = -1; 66 for (cons = 0; consoles[cons] != NULL && active == -1; cons++) { 67 consoles[cons]->c_flags = 0; 68 consoles[cons]->c_probe(consoles[cons]); 69 if (consoles[cons]->c_flags == (C_PRESENTIN | C_PRESENTOUT)) 70 active = cons; 71 } 72 /* Force a console even if all probes failed */ 73 if (active == -1) 74 active = 0; 75 76 /* Check to see if a console preference has already been registered */ 77 prefconsole = getenv("console"); 78 if (prefconsole != NULL) 79 prefconsole = strdup(prefconsole); 80 if (prefconsole != NULL) { 81 unsetenv("console"); /* we want to replace this */ 82 cons_change(prefconsole); 83 } else { 84 consoles[active]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT; 85 consoles[active]->c_init(consoles[active], 0); 86 prefconsole = strdup(consoles[active]->c_name); 87 } 88 89 printf("Consoles: "); 90 for (cons = 0; consoles[cons] != NULL; cons++) 91 if (consoles[cons]->c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) 92 printf("%s ", consoles[cons]->c_desc); 93 printf("\n"); 94 95 if (prefconsole != NULL) { 96 env_setenv("console", EV_VOLATILE, prefconsole, cons_set, 97 env_nounset); 98 free(prefconsole); 99 } 100 } 101 102 void 103 cons_mode(int raw) 104 { 105 int cons; 106 107 for (cons = 0; consoles[cons] != NULL; cons++) { 108 if (raw == 0) 109 consoles[cons]->c_flags &= ~C_MODERAW; 110 else 111 consoles[cons]->c_flags |= C_MODERAW; 112 } 113 } 114 115 int 116 getchar(void) 117 { 118 int cons; 119 int flags = C_PRESENTIN | C_ACTIVEIN; 120 int rv; 121 122 /* 123 * Loop forever polling all active consoles. Somewhat strangely, 124 * this code expects all ->c_in() implementations to effectively do an 125 * ischar() check first, returning -1 if there's not a char ready. 126 */ 127 for (;;) { 128 for (cons = 0; consoles[cons] != NULL; cons++) { 129 if ((consoles[cons]->c_flags & flags) == flags && 130 ((rv = consoles[cons]->c_in(consoles[cons])) != -1)) 131 return (rv); 132 } 133 delay(30 * 1000); /* delay 30ms */ 134 } 135 } 136 137 int 138 ischar(void) 139 { 140 int cons; 141 142 for (cons = 0; consoles[cons] != NULL; cons++) 143 if ((consoles[cons]->c_flags & (C_PRESENTIN | C_ACTIVEIN)) == 144 (C_PRESENTIN | C_ACTIVEIN) && 145 (consoles[cons]->c_ready(consoles[cons]) != 0)) 146 return (1); 147 return (0); 148 } 149 150 void 151 putchar(int c) 152 { 153 int cons; 154 155 /* Expand newlines if not in raw mode */ 156 for (cons = 0; consoles[cons] != NULL; cons++) 157 if ((consoles[cons]->c_flags & (C_PRESENTOUT | C_ACTIVEOUT)) == 158 (C_PRESENTOUT | C_ACTIVEOUT)) { 159 if (c == '\n' && 160 (consoles[cons]->c_flags & C_MODERAW) == 0) 161 consoles[cons]->c_out(consoles[cons], '\r'); 162 consoles[cons]->c_out(consoles[cons], c); 163 } 164 } 165 166 /* 167 * Find the console with the specified name. 168 */ 169 static int 170 cons_find(const char *name) 171 { 172 int cons; 173 174 for (cons = 0; consoles[cons] != NULL; cons++) 175 if (strcmp(consoles[cons]->c_name, name) == 0) 176 return (cons); 177 return (-1); 178 } 179 180 /* 181 * Select one or more consoles. 182 */ 183 static int 184 cons_set(struct env_var *ev, int flags, const void *value) 185 { 186 int ret, cons; 187 char *list, *tmp; 188 const char *console; 189 190 if ((value == NULL) || (cons_check(value) == 0)) { 191 /* 192 * Return CMD_OK instead of CMD_ERROR to prevent forth syntax 193 * error, which would prevent it processing any further 194 * loader.conf entries. 195 */ 196 return (CMD_OK); 197 } 198 199 ret = cons_change(value); 200 if (ret != CMD_OK) 201 return (ret); 202 203 /* 204 * build list of active consoles. 205 * Note, we need to preserve the ordered device list in 'value'. 206 */ 207 list = NULL; 208 console = value; 209 while (console[0] != '\0') { 210 /* Find corresponding entry from consoles array. */ 211 for (cons = 0; consoles[cons] != NULL; cons++) { 212 const char *name = consoles[cons]->c_name; 213 size_t len = strlen(name); 214 215 if (strncmp(name, console, len) != 0) 216 continue; 217 len++; 218 if (console[len] == ',' || 219 console[len] == ' ' || 220 console[len] == '\0') { 221 break; 222 } 223 } 224 225 /* Skip to delimiter */ 226 while (console[0] != ',' && 227 console[0] != ' ' && 228 console[0] != '\0') 229 console++; 230 /* Skip to next name */ 231 while (console[0] == ',' || console[0] == ' ') 232 console++; 233 234 /* no such console? */ 235 if (consoles[cons] == NULL) 236 continue; 237 238 if ((consoles[cons]->c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) == 239 (C_ACTIVEIN | C_ACTIVEOUT)) { 240 if (list == NULL) { 241 list = strdup(consoles[cons]->c_name); 242 } else { 243 if (asprintf(&tmp, "%s,%s", list, 244 consoles[cons]->c_name) > 0) { 245 free(list); 246 list = tmp; 247 } 248 } 249 } 250 } 251 252 /* 253 * set console variable. 254 */ 255 if (list != NULL) { 256 (void) env_setenv(ev->ev_name, flags | EV_NOHOOK, list, 257 NULL, NULL); 258 } else { 259 (void) env_setenv(ev->ev_name, flags | EV_NOHOOK, value, 260 NULL, NULL); 261 } 262 free(list); 263 return (CMD_OK); 264 } 265 266 /* 267 * Check that at least one the consoles listed in *string is valid 268 */ 269 static int 270 cons_check(const char *string) 271 { 272 int cons, found, failed; 273 char *curpos, *dup, *next; 274 275 dup = next = strdup(string); 276 found = failed = 0; 277 while (next != NULL) { 278 curpos = strsep(&next, " ,"); 279 if (*curpos != '\0') { 280 cons = cons_find(curpos); 281 if (cons == -1) { 282 printf("console %s is invalid!\n", curpos); 283 failed++; 284 } else { 285 if ((consoles[cons]->c_flags & 286 (C_PRESENTIN | C_PRESENTOUT)) != 287 (C_PRESENTIN | C_PRESENTOUT)) { 288 failed++; 289 } else 290 found++; 291 } 292 } 293 } 294 295 free(dup); 296 297 if (found == 0) 298 printf("no valid consoles!\n"); 299 300 if (found == 0 || failed != 0) { 301 printf("Available consoles:\n"); 302 for (cons = 0; consoles[cons] != NULL; cons++) { 303 printf(" %s", consoles[cons]->c_name); 304 if (consoles[cons]->c_devinfo != NULL) 305 consoles[cons]->c_devinfo(consoles[cons]); 306 printf("\n"); 307 } 308 } 309 310 return (found); 311 } 312 313 314 /* 315 * Activate all the valid consoles listed in *string and disable all others. 316 */ 317 static int 318 cons_change(const char *string) 319 { 320 int cons, active; 321 char *curpos, *dup, *next; 322 323 /* Disable all consoles */ 324 for (cons = 0; consoles[cons] != NULL; cons++) { 325 consoles[cons]->c_flags &= ~(C_ACTIVEIN | C_ACTIVEOUT); 326 } 327 328 /* Enable selected consoles */ 329 dup = next = strdup(string); 330 active = 0; 331 while (next != NULL) { 332 curpos = strsep(&next, " ,"); 333 if (*curpos == '\0') 334 continue; 335 cons = cons_find(curpos); 336 if (cons >= 0) { 337 consoles[cons]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT; 338 consoles[cons]->c_init(consoles[cons], 0); 339 if ((consoles[cons]->c_flags & 340 (C_ACTIVEIN | C_ACTIVEOUT)) == 341 (C_ACTIVEIN | C_ACTIVEOUT)) { 342 active++; 343 continue; 344 } 345 346 if (active != 0) { 347 /* 348 * If no consoles have initialised we wouldn't 349 * see this. 350 */ 351 printf("console %s failed to initialize\n", 352 consoles[cons]->c_name); 353 } 354 } 355 } 356 357 free(dup); 358 359 if (active == 0) { 360 /* 361 * All requested consoles failed to initialise, try to recover. 362 */ 363 for (cons = 0; consoles[cons] != NULL; cons++) { 364 consoles[cons]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT; 365 consoles[cons]->c_init(consoles[cons], 0); 366 if ((consoles[cons]->c_flags & 367 (C_ACTIVEIN | C_ACTIVEOUT)) == 368 (C_ACTIVEIN | C_ACTIVEOUT)) 369 active++; 370 } 371 372 if (active == 0) 373 return (CMD_ERROR); /* Recovery failed. */ 374 } 375 376 return (CMD_OK); 377 } 378 379 /* 380 * Change the twiddle divisor. 381 * 382 * The user can set the twiddle_divisor variable to directly control how fast 383 * the progress twiddle spins, useful for folks with slow serial consoles. The 384 * code to monitor changes to the variable and propagate them to the twiddle 385 * routines has to live somewhere. Twiddling is console-related so it's here. 386 */ 387 static int 388 twiddle_set(struct env_var *ev, int flags, const void *value) 389 { 390 ulong_t tdiv; 391 char *eptr; 392 393 tdiv = strtoul(value, &eptr, 0); 394 if (*(const char *)value == 0 || *eptr != 0) { 395 printf("invalid twiddle_divisor '%s'\n", (const char *)value); 396 return (CMD_ERROR); 397 } 398 twiddle_divisor((uint_t)tdiv); 399 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 400 401 return (CMD_OK); 402 } 403 404 COMMAND_SET(console, "console", "console info", command_console); 405 406 static int 407 command_console(int argc, char *argv[]) 408 { 409 if (argc > 1) 410 printf("%s: list info about available consoles\n", argv[0]); 411 412 printf("Current console: %s\n", getenv("console")); 413 printf("Available consoles:\n"); 414 for (int cons = 0; consoles[cons] != NULL; cons++) { 415 printf(" %s", consoles[cons]->c_name); 416 if (consoles[cons]->c_devinfo != NULL) 417 consoles[cons]->c_devinfo(consoles[cons]); 418 printf("\n"); 419 } 420 421 return (CMD_OK); 422 } 423