16112cec5SJoshua M. Clulow /*
26112cec5SJoshua M. Clulow  * This file and its contents are supplied under the terms of the
36112cec5SJoshua M. Clulow  * Common Development and Distribution License ("CDDL"), version 1.0.
46112cec5SJoshua M. Clulow  * You may only use this file in accordance with the terms of version
56112cec5SJoshua M. Clulow  * 1.0 of the CDDL.
66112cec5SJoshua M. Clulow  *
76112cec5SJoshua M. Clulow  * A full copy of the text of the CDDL should have accompanied this
86112cec5SJoshua M. Clulow  * source.  A copy of the CDDL is also available via the Internet at
96112cec5SJoshua M. Clulow  * http://www.illumos.org/license/CDDL.
106112cec5SJoshua M. Clulow  */
116112cec5SJoshua M. Clulow 
126112cec5SJoshua M. Clulow /*
13*b210e777SJoshua M. Clulow  * Copyright 2023 Oxide Computer Company
146112cec5SJoshua M. Clulow  */
156112cec5SJoshua M. Clulow 
166112cec5SJoshua M. Clulow #ifdef _KERNEL
176112cec5SJoshua M. Clulow #include <sys/types.h>
186112cec5SJoshua M. Clulow #include <sys/sunddi.h>
196112cec5SJoshua M. Clulow #else
206112cec5SJoshua M. Clulow #include <stdio.h>
216112cec5SJoshua M. Clulow #include <stdlib.h>
226112cec5SJoshua M. Clulow #include <strings.h>
236112cec5SJoshua M. Clulow #include <sys/utsname.h>
246112cec5SJoshua M. Clulow #include <sys/systeminfo.h>
256112cec5SJoshua M. Clulow #endif
266112cec5SJoshua M. Clulow #include <sys/debug.h>
27*b210e777SJoshua M. Clulow #include <sys/ilstr.h>
286112cec5SJoshua M. Clulow 
296112cec5SJoshua M. Clulow /*
306112cec5SJoshua M. Clulow  * Rendering of the boot banner, used on the system and zone consoles.
316112cec5SJoshua M. Clulow  */
326112cec5SJoshua M. Clulow 
336112cec5SJoshua M. Clulow /*
346112cec5SJoshua M. Clulow  * Expand a boot banner template string.  The following expansion tokens
356112cec5SJoshua M. Clulow  * are supported:
366112cec5SJoshua M. Clulow  *
376112cec5SJoshua M. Clulow  *	^^	a literal caret
386112cec5SJoshua M. Clulow  *	^s	the base kernel name (utsname.sysname)
396112cec5SJoshua M. Clulow  *	^o	the operating system name ("illumos")
406112cec5SJoshua M. Clulow  *	^v	the operating system version (utsname.version)
416112cec5SJoshua M. Clulow  *	^r	the operating system release (utsname.release)
426112cec5SJoshua M. Clulow  *	^w	the native address width in bits (e.g., "32" or "64")
436112cec5SJoshua M. Clulow  */
446112cec5SJoshua M. Clulow static void
bootbanner_expand_template(const char * input,ilstr_t * output)456112cec5SJoshua M. Clulow bootbanner_expand_template(const char *input, ilstr_t *output)
466112cec5SJoshua M. Clulow {
476112cec5SJoshua M. Clulow 	size_t pos = 0;
486112cec5SJoshua M. Clulow 	enum {
496112cec5SJoshua M. Clulow 		ST_REST,
506112cec5SJoshua M. Clulow 		ST_CARET,
516112cec5SJoshua M. Clulow 	} state = ST_REST;
526112cec5SJoshua M. Clulow 
536112cec5SJoshua M. Clulow #ifndef _KERNEL
546112cec5SJoshua M. Clulow 	struct utsname utsname;
556112cec5SJoshua M. Clulow 	bzero(&utsname, sizeof (utsname));
566112cec5SJoshua M. Clulow 	(void) uname(&utsname);
576112cec5SJoshua M. Clulow #endif
586112cec5SJoshua M. Clulow 
596112cec5SJoshua M. Clulow 	for (;;) {
606112cec5SJoshua M. Clulow 		char c = input[pos];
616112cec5SJoshua M. Clulow 
626112cec5SJoshua M. Clulow 		if (c == '\0') {
636112cec5SJoshua M. Clulow 			/*
646112cec5SJoshua M. Clulow 			 * Even if the template came to an end mid way through
656112cec5SJoshua M. Clulow 			 * a caret expansion, it seems best to just print what
666112cec5SJoshua M. Clulow 			 * we have and drive on.  The onus will be on the
676112cec5SJoshua M. Clulow 			 * distributor to ensure their templates are
686112cec5SJoshua M. Clulow 			 * well-formed at build time.
696112cec5SJoshua M. Clulow 			 */
706112cec5SJoshua M. Clulow 			break;
716112cec5SJoshua M. Clulow 		}
726112cec5SJoshua M. Clulow 
736112cec5SJoshua M. Clulow 		switch (state) {
746112cec5SJoshua M. Clulow 		case ST_REST:
756112cec5SJoshua M. Clulow 			if (c == '^') {
766112cec5SJoshua M. Clulow 				state = ST_CARET;
776112cec5SJoshua M. Clulow 			} else {
786112cec5SJoshua M. Clulow 				ilstr_append_char(output, c);
796112cec5SJoshua M. Clulow 			}
806112cec5SJoshua M. Clulow 			pos++;
816112cec5SJoshua M. Clulow 			continue;
826112cec5SJoshua M. Clulow 
836112cec5SJoshua M. Clulow 		case ST_CARET:
846112cec5SJoshua M. Clulow 			if (c == '^') {
856112cec5SJoshua M. Clulow 				ilstr_append_char(output, c);
866112cec5SJoshua M. Clulow 			} else if (c == 's') {
876112cec5SJoshua M. Clulow 				ilstr_append_str(output, utsname.sysname);
886112cec5SJoshua M. Clulow 			} else if (c == 'o') {
896112cec5SJoshua M. Clulow 				ilstr_append_str(output, "illumos");
906112cec5SJoshua M. Clulow 			} else if (c == 'r') {
916112cec5SJoshua M. Clulow 				ilstr_append_str(output, utsname.release);
926112cec5SJoshua M. Clulow 			} else if (c == 'v') {
936112cec5SJoshua M. Clulow 				ilstr_append_str(output, utsname.version);
946112cec5SJoshua M. Clulow 			} else if (c == 'w') {
956112cec5SJoshua M. Clulow #ifdef _KERNEL
96*b210e777SJoshua M. Clulow 				ilstr_aprintf(output, "%u",
976112cec5SJoshua M. Clulow 				    NBBY * (uint_t)sizeof (void *));
986112cec5SJoshua M. Clulow #else
996112cec5SJoshua M. Clulow 				char *bits;
1006112cec5SJoshua M. Clulow 				char buf[32];
1016112cec5SJoshua M. Clulow 				int r;
1026112cec5SJoshua M. Clulow 
1036112cec5SJoshua M. Clulow 				if ((r = sysinfo(SI_ADDRESS_WIDTH, buf,
1046112cec5SJoshua M. Clulow 				    sizeof (buf))) > 0 &&
1056112cec5SJoshua M. Clulow 				    r < (int)sizeof (buf)) {
1066112cec5SJoshua M. Clulow 					bits = buf;
1076112cec5SJoshua M. Clulow 				} else {
1086112cec5SJoshua M. Clulow 					bits = "64";
1096112cec5SJoshua M. Clulow 				}
1106112cec5SJoshua M. Clulow 
1116112cec5SJoshua M. Clulow 				ilstr_append_str(output, bits);
1126112cec5SJoshua M. Clulow #endif
1136112cec5SJoshua M. Clulow 			} else {
1146112cec5SJoshua M. Clulow 				/*
1156112cec5SJoshua M. Clulow 				 * Try to make it obvious what went wrong:
1166112cec5SJoshua M. Clulow 				 */
1176112cec5SJoshua M. Clulow 				ilstr_append_str(output, "!^");
1186112cec5SJoshua M. Clulow 				ilstr_append_char(output, c);
1196112cec5SJoshua M. Clulow 				ilstr_append_str(output, " UNKNOWN!");
1206112cec5SJoshua M. Clulow 			}
1216112cec5SJoshua M. Clulow 			state = ST_REST;
1226112cec5SJoshua M. Clulow 			pos++;
1236112cec5SJoshua M. Clulow 			continue;
1246112cec5SJoshua M. Clulow 		}
1256112cec5SJoshua M. Clulow 	}
1266112cec5SJoshua M. Clulow }
1276112cec5SJoshua M. Clulow 
1286112cec5SJoshua M. Clulow static void
bootbanner_print_one(ilstr_t * s,void (* printfunc)(const char *,uint_t),const char * template,uint_t * nump)1296112cec5SJoshua M. Clulow bootbanner_print_one(ilstr_t *s, void (*printfunc)(const char *, uint_t),
1306112cec5SJoshua M. Clulow     const char *template, uint_t *nump)
1316112cec5SJoshua M. Clulow {
1326112cec5SJoshua M. Clulow 	ilstr_reset(s);
1336112cec5SJoshua M. Clulow 
1346112cec5SJoshua M. Clulow 	bootbanner_expand_template(template, s);
1356112cec5SJoshua M. Clulow 
1366112cec5SJoshua M. Clulow 	if (ilstr_errno(s) == ILSTR_ERROR_OK) {
1376112cec5SJoshua M. Clulow 		if (ilstr_len(s) > 0) {
1386112cec5SJoshua M. Clulow 			printfunc(ilstr_cstr(s), *nump);
1396112cec5SJoshua M. Clulow 			*nump += 1;
1406112cec5SJoshua M. Clulow 		}
1416112cec5SJoshua M. Clulow 	} else {
1426112cec5SJoshua M. Clulow 		char ebuf[128];
1436112cec5SJoshua M. Clulow 
1446112cec5SJoshua M. Clulow 		snprintf(ebuf, sizeof (ebuf), "boot banner error: %s",
1456112cec5SJoshua M. Clulow 		    ilstr_errstr(s));
1466112cec5SJoshua M. Clulow 
1476112cec5SJoshua M. Clulow 		printfunc(ebuf, *nump);
1486112cec5SJoshua M. Clulow 		*nump += 1;
1496112cec5SJoshua M. Clulow 	}
1506112cec5SJoshua M. Clulow }
1516112cec5SJoshua M. Clulow 
1526112cec5SJoshua M. Clulow /*
1536112cec5SJoshua M. Clulow  * This routine should be called during early system boot to render the boot
1546112cec5SJoshua M. Clulow  * banner on the system console, and during zone boot to do so on the zone
1556112cec5SJoshua M. Clulow  * console.
1566112cec5SJoshua M. Clulow  *
1576112cec5SJoshua M. Clulow  * The "printfunc" argument is a callback function.  When passed a string, the
1586112cec5SJoshua M. Clulow  * function must print it in a fashion appropriate for the context.  The
1596112cec5SJoshua M. Clulow  * callback will only be called while within the call to bootbanner_print().
1606112cec5SJoshua M. Clulow  */
1616112cec5SJoshua M. Clulow void
bootbanner_print(void (* printfunc)(const char *,uint_t))162*b210e777SJoshua M. Clulow bootbanner_print(void (*printfunc)(const char *, uint_t))
1636112cec5SJoshua M. Clulow {
164*b210e777SJoshua M. Clulow 	/*
165*b210e777SJoshua M. Clulow 	 * To avoid the need to allocate in early boot, we'll use a static
166*b210e777SJoshua M. Clulow 	 * buffer four times the size of a tasteful terminal width.  Note that
167*b210e777SJoshua M. Clulow 	 * ilstr will allow us to produce diagnostic output if this buffer
168*b210e777SJoshua M. Clulow 	 * would have been overrun.
169*b210e777SJoshua M. Clulow 	 */
170*b210e777SJoshua M. Clulow 	char sbuf[80 * 4];
1716112cec5SJoshua M. Clulow 	ilstr_t s;
1726112cec5SJoshua M. Clulow 	uint_t num = 0;
1736112cec5SJoshua M. Clulow 
174*b210e777SJoshua M. Clulow 	ilstr_init_prealloc(&s, sbuf, sizeof (sbuf));
1756112cec5SJoshua M. Clulow 
1766112cec5SJoshua M. Clulow 	bootbanner_print_one(&s, printfunc, BOOTBANNER1, &num);
1776112cec5SJoshua M. Clulow 	bootbanner_print_one(&s, printfunc, BOOTBANNER2, &num);
1786112cec5SJoshua M. Clulow 	bootbanner_print_one(&s, printfunc, BOOTBANNER3, &num);
1796112cec5SJoshua M. Clulow 	bootbanner_print_one(&s, printfunc, BOOTBANNER4, &num);
1806112cec5SJoshua M. Clulow 	bootbanner_print_one(&s, printfunc, BOOTBANNER5, &num);
1816112cec5SJoshua M. Clulow 
1826112cec5SJoshua M. Clulow 	ilstr_fini(&s);
1836112cec5SJoshua M. Clulow }
184