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