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