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