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