1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2023 Oxide Computer Company 14 */ 15 16 #ifdef _KERNEL 17 #include <sys/types.h> 18 #include <sys/sunddi.h> 19 #else 20 #include <stdio.h> 21 #include <stdlib.h> 22 #include <strings.h> 23 #include <sys/utsname.h> 24 #include <sys/systeminfo.h> 25 #endif 26 #include <sys/debug.h> 27 #include <sys/ilstr.h> 28 29 /* 30 * Rendering of the boot banner, used on the system and zone consoles. 31 */ 32 33 /* 34 * Expand a boot banner template string. The following expansion tokens 35 * are supported: 36 * 37 * ^^ a literal caret 38 * ^s the base kernel name (utsname.sysname) 39 * ^o the operating system name ("illumos") 40 * ^v the operating system version (utsname.version) 41 * ^r the operating system release (utsname.release) 42 * ^w the native address width in bits (e.g., "32" or "64") 43 */ 44 static void 45 bootbanner_expand_template(const char *input, ilstr_t *output) 46 { 47 size_t pos = 0; 48 enum { 49 ST_REST, 50 ST_CARET, 51 } state = ST_REST; 52 53 #ifndef _KERNEL 54 struct utsname utsname; 55 bzero(&utsname, sizeof (utsname)); 56 (void) uname(&utsname); 57 #endif 58 59 for (;;) { 60 char c = input[pos]; 61 62 if (c == '\0') { 63 /* 64 * Even if the template came to an end mid way through 65 * a caret expansion, it seems best to just print what 66 * we have and drive on. The onus will be on the 67 * distributor to ensure their templates are 68 * well-formed at build time. 69 */ 70 break; 71 } 72 73 switch (state) { 74 case ST_REST: 75 if (c == '^') { 76 state = ST_CARET; 77 } else { 78 ilstr_append_char(output, c); 79 } 80 pos++; 81 continue; 82 83 case ST_CARET: 84 if (c == '^') { 85 ilstr_append_char(output, c); 86 } else if (c == 's') { 87 ilstr_append_str(output, utsname.sysname); 88 } else if (c == 'o') { 89 ilstr_append_str(output, "illumos"); 90 } else if (c == 'r') { 91 ilstr_append_str(output, utsname.release); 92 } else if (c == 'v') { 93 ilstr_append_str(output, utsname.version); 94 } else if (c == 'w') { 95 #ifdef _KERNEL 96 ilstr_aprintf(output, "%u", 97 NBBY * (uint_t)sizeof (void *)); 98 #else 99 char *bits; 100 char buf[32]; 101 int r; 102 103 if ((r = sysinfo(SI_ADDRESS_WIDTH, buf, 104 sizeof (buf))) > 0 && 105 r < (int)sizeof (buf)) { 106 bits = buf; 107 } else { 108 bits = "64"; 109 } 110 111 ilstr_append_str(output, bits); 112 #endif 113 } else { 114 /* 115 * Try to make it obvious what went wrong: 116 */ 117 ilstr_append_str(output, "!^"); 118 ilstr_append_char(output, c); 119 ilstr_append_str(output, " UNKNOWN!"); 120 } 121 state = ST_REST; 122 pos++; 123 continue; 124 } 125 } 126 } 127 128 static void 129 bootbanner_print_one(ilstr_t *s, void (*printfunc)(const char *, uint_t), 130 const char *template, uint_t *nump) 131 { 132 ilstr_reset(s); 133 134 bootbanner_expand_template(template, s); 135 136 if (ilstr_errno(s) == ILSTR_ERROR_OK) { 137 if (ilstr_len(s) > 0) { 138 printfunc(ilstr_cstr(s), *nump); 139 *nump += 1; 140 } 141 } else { 142 char ebuf[128]; 143 144 snprintf(ebuf, sizeof (ebuf), "boot banner error: %s", 145 ilstr_errstr(s)); 146 147 printfunc(ebuf, *nump); 148 *nump += 1; 149 } 150 } 151 152 /* 153 * This routine should be called during early system boot to render the boot 154 * banner on the system console, and during zone boot to do so on the zone 155 * console. 156 * 157 * The "printfunc" argument is a callback function. When passed a string, the 158 * function must print it in a fashion appropriate for the context. The 159 * callback will only be called while within the call to bootbanner_print(). 160 */ 161 void 162 bootbanner_print(void (*printfunc)(const char *, uint_t)) 163 { 164 /* 165 * To avoid the need to allocate in early boot, we'll use a static 166 * buffer four times the size of a tasteful terminal width. Note that 167 * ilstr will allow us to produce diagnostic output if this buffer 168 * would have been overrun. 169 */ 170 char sbuf[80 * 4]; 171 ilstr_t s; 172 uint_t num = 0; 173 174 ilstr_init_prealloc(&s, sbuf, sizeof (sbuf)); 175 176 bootbanner_print_one(&s, printfunc, BOOTBANNER1, &num); 177 bootbanner_print_one(&s, printfunc, BOOTBANNER2, &num); 178 bootbanner_print_one(&s, printfunc, BOOTBANNER3, &num); 179 bootbanner_print_one(&s, printfunc, BOOTBANNER4, &num); 180 bootbanner_print_one(&s, printfunc, BOOTBANNER5, &num); 181 182 ilstr_fini(&s); 183 } 184