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