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