1*6112cec5SJoshua M. Clulow /* 2*6112cec5SJoshua M. Clulow * This file and its contents are supplied under the terms of the 3*6112cec5SJoshua M. Clulow * Common Development and Distribution License ("CDDL"), version 1.0. 4*6112cec5SJoshua M. Clulow * You may only use this file in accordance with the terms of version 5*6112cec5SJoshua M. Clulow * 1.0 of the CDDL. 6*6112cec5SJoshua M. Clulow * 7*6112cec5SJoshua M. Clulow * A full copy of the text of the CDDL should have accompanied this 8*6112cec5SJoshua M. Clulow * source. A copy of the CDDL is also available via the Internet at 9*6112cec5SJoshua M. Clulow * http://www.illumos.org/license/CDDL. 10*6112cec5SJoshua M. Clulow */ 11*6112cec5SJoshua M. Clulow 12*6112cec5SJoshua M. Clulow /* 13*6112cec5SJoshua M. Clulow * Copyright 2020 Oxide Computer Company 14*6112cec5SJoshua M. Clulow */ 15*6112cec5SJoshua M. Clulow 16*6112cec5SJoshua M. Clulow #ifdef _KERNEL 17*6112cec5SJoshua M. Clulow #include <sys/types.h> 18*6112cec5SJoshua M. Clulow #include <sys/sunddi.h> 19*6112cec5SJoshua M. Clulow #else 20*6112cec5SJoshua M. Clulow #include <stdio.h> 21*6112cec5SJoshua M. Clulow #include <stdlib.h> 22*6112cec5SJoshua M. Clulow #include <strings.h> 23*6112cec5SJoshua M. Clulow #include <sys/utsname.h> 24*6112cec5SJoshua M. Clulow #include <sys/systeminfo.h> 25*6112cec5SJoshua M. Clulow #endif 26*6112cec5SJoshua M. Clulow #include <sys/debug.h> 27*6112cec5SJoshua M. Clulow 28*6112cec5SJoshua M. Clulow /* 29*6112cec5SJoshua M. Clulow * Rendering of the boot banner, used on the system and zone consoles. 30*6112cec5SJoshua M. Clulow */ 31*6112cec5SJoshua M. Clulow 32*6112cec5SJoshua M. Clulow typedef enum ilstr_errno { 33*6112cec5SJoshua M. Clulow ILSTR_ERROR_OK = 0, 34*6112cec5SJoshua M. Clulow ILSTR_ERROR_NOMEM, 35*6112cec5SJoshua M. Clulow ILSTR_ERROR_OVERFLOW, 36*6112cec5SJoshua M. Clulow } ilstr_errno_t; 37*6112cec5SJoshua M. Clulow 38*6112cec5SJoshua M. Clulow typedef struct ilstr { 39*6112cec5SJoshua M. Clulow char *ils_data; 40*6112cec5SJoshua M. Clulow size_t ils_datalen; 41*6112cec5SJoshua M. Clulow size_t ils_strlen; 42*6112cec5SJoshua M. Clulow uint_t ils_errno; 43*6112cec5SJoshua M. Clulow int ils_kmflag; 44*6112cec5SJoshua M. Clulow } ilstr_t; 45*6112cec5SJoshua M. Clulow 46*6112cec5SJoshua M. Clulow static void 47*6112cec5SJoshua M. Clulow ilstr_init(ilstr_t *ils, int kmflag) 48*6112cec5SJoshua M. Clulow { 49*6112cec5SJoshua M. Clulow bzero(ils, sizeof (*ils)); 50*6112cec5SJoshua M. Clulow ils->ils_kmflag = kmflag; 51*6112cec5SJoshua M. Clulow } 52*6112cec5SJoshua M. Clulow 53*6112cec5SJoshua M. Clulow static void 54*6112cec5SJoshua M. Clulow ilstr_reset(ilstr_t *ils) 55*6112cec5SJoshua M. Clulow { 56*6112cec5SJoshua M. Clulow if (ils->ils_strlen > 0) { 57*6112cec5SJoshua M. Clulow /* 58*6112cec5SJoshua M. Clulow * Truncate the string but do not free the buffer so that we 59*6112cec5SJoshua M. Clulow * can use it again without further allocation. 60*6112cec5SJoshua M. Clulow */ 61*6112cec5SJoshua M. Clulow ils->ils_data[0] = '\0'; 62*6112cec5SJoshua M. Clulow ils->ils_strlen = 0; 63*6112cec5SJoshua M. Clulow } 64*6112cec5SJoshua M. Clulow ils->ils_errno = ILSTR_ERROR_OK; 65*6112cec5SJoshua M. Clulow } 66*6112cec5SJoshua M. Clulow 67*6112cec5SJoshua M. Clulow static void 68*6112cec5SJoshua M. Clulow ilstr_fini(ilstr_t *ils) 69*6112cec5SJoshua M. Clulow { 70*6112cec5SJoshua M. Clulow if (ils->ils_data != NULL) { 71*6112cec5SJoshua M. Clulow #ifdef _KERNEL 72*6112cec5SJoshua M. Clulow kmem_free(ils->ils_data, ils->ils_datalen); 73*6112cec5SJoshua M. Clulow #else 74*6112cec5SJoshua M. Clulow free(ils->ils_data); 75*6112cec5SJoshua M. Clulow #endif 76*6112cec5SJoshua M. Clulow } 77*6112cec5SJoshua M. Clulow } 78*6112cec5SJoshua M. Clulow 79*6112cec5SJoshua M. Clulow static void 80*6112cec5SJoshua M. Clulow ilstr_append_str(ilstr_t *ils, const char *s) 81*6112cec5SJoshua M. Clulow { 82*6112cec5SJoshua M. Clulow size_t len; 83*6112cec5SJoshua M. Clulow size_t chunksz = 64; 84*6112cec5SJoshua M. Clulow 85*6112cec5SJoshua M. Clulow if (ils->ils_errno != ILSTR_ERROR_OK) { 86*6112cec5SJoshua M. Clulow return; 87*6112cec5SJoshua M. Clulow } 88*6112cec5SJoshua M. Clulow 89*6112cec5SJoshua M. Clulow if ((len = strlen(s)) < 1) { 90*6112cec5SJoshua M. Clulow return; 91*6112cec5SJoshua M. Clulow } 92*6112cec5SJoshua M. Clulow 93*6112cec5SJoshua M. Clulow /* 94*6112cec5SJoshua M. Clulow * Check to ensure that the new string length does not overflow, 95*6112cec5SJoshua M. Clulow * leaving room for the termination byte: 96*6112cec5SJoshua M. Clulow */ 97*6112cec5SJoshua M. Clulow if (len >= SIZE_MAX - ils->ils_strlen - 1) { 98*6112cec5SJoshua M. Clulow ils->ils_errno = ILSTR_ERROR_OVERFLOW; 99*6112cec5SJoshua M. Clulow return; 100*6112cec5SJoshua M. Clulow } 101*6112cec5SJoshua M. Clulow size_t new_strlen = ils->ils_strlen + len; 102*6112cec5SJoshua M. Clulow 103*6112cec5SJoshua M. Clulow if (new_strlen + 1 >= ils->ils_datalen) { 104*6112cec5SJoshua M. Clulow size_t new_datalen = ils->ils_datalen; 105*6112cec5SJoshua M. Clulow char *new_data; 106*6112cec5SJoshua M. Clulow 107*6112cec5SJoshua M. Clulow /* 108*6112cec5SJoshua M. Clulow * Grow the string buffer to make room for the new string. 109*6112cec5SJoshua M. Clulow */ 110*6112cec5SJoshua M. Clulow while (new_datalen < new_strlen + 1) { 111*6112cec5SJoshua M. Clulow if (chunksz >= SIZE_MAX - new_datalen) { 112*6112cec5SJoshua M. Clulow ils->ils_errno = ILSTR_ERROR_OVERFLOW; 113*6112cec5SJoshua M. Clulow return; 114*6112cec5SJoshua M. Clulow } 115*6112cec5SJoshua M. Clulow new_datalen += chunksz; 116*6112cec5SJoshua M. Clulow } 117*6112cec5SJoshua M. Clulow 118*6112cec5SJoshua M. Clulow #ifdef _KERNEL 119*6112cec5SJoshua M. Clulow new_data = kmem_alloc(new_datalen, ils->ils_kmflag); 120*6112cec5SJoshua M. Clulow #else 121*6112cec5SJoshua M. Clulow new_data = malloc(new_datalen); 122*6112cec5SJoshua M. Clulow #endif 123*6112cec5SJoshua M. Clulow if (new_data == NULL) { 124*6112cec5SJoshua M. Clulow ils->ils_errno = ILSTR_ERROR_NOMEM; 125*6112cec5SJoshua M. Clulow return; 126*6112cec5SJoshua M. Clulow } 127*6112cec5SJoshua M. Clulow 128*6112cec5SJoshua M. Clulow if (ils->ils_data != NULL) { 129*6112cec5SJoshua M. Clulow bcopy(ils->ils_data, new_data, ils->ils_strlen + 1); 130*6112cec5SJoshua M. Clulow #ifdef _KERNEL 131*6112cec5SJoshua M. Clulow kmem_free(ils->ils_data, ils->ils_datalen); 132*6112cec5SJoshua M. Clulow #else 133*6112cec5SJoshua M. Clulow free(ils->ils_data); 134*6112cec5SJoshua M. Clulow #endif 135*6112cec5SJoshua M. Clulow } 136*6112cec5SJoshua M. Clulow 137*6112cec5SJoshua M. Clulow ils->ils_data = new_data; 138*6112cec5SJoshua M. Clulow ils->ils_datalen = new_datalen; 139*6112cec5SJoshua M. Clulow } 140*6112cec5SJoshua M. Clulow 141*6112cec5SJoshua M. Clulow bcopy(s, ils->ils_data + ils->ils_strlen, len + 1); 142*6112cec5SJoshua M. Clulow ils->ils_strlen = new_strlen; 143*6112cec5SJoshua M. Clulow } 144*6112cec5SJoshua M. Clulow 145*6112cec5SJoshua M. Clulow #ifdef _KERNEL 146*6112cec5SJoshua M. Clulow static void 147*6112cec5SJoshua M. Clulow ilstr_append_uint(ilstr_t *ils, uint_t n) 148*6112cec5SJoshua M. Clulow { 149*6112cec5SJoshua M. Clulow char buf[64]; 150*6112cec5SJoshua M. Clulow 151*6112cec5SJoshua M. Clulow if (ils->ils_errno != ILSTR_ERROR_OK) { 152*6112cec5SJoshua M. Clulow return; 153*6112cec5SJoshua M. Clulow } 154*6112cec5SJoshua M. Clulow 155*6112cec5SJoshua M. Clulow VERIFY3U(snprintf(buf, sizeof (buf), "%u", n), <, sizeof (buf)); 156*6112cec5SJoshua M. Clulow 157*6112cec5SJoshua M. Clulow ilstr_append_str(ils, buf); 158*6112cec5SJoshua M. Clulow } 159*6112cec5SJoshua M. Clulow #endif 160*6112cec5SJoshua M. Clulow 161*6112cec5SJoshua M. Clulow static void 162*6112cec5SJoshua M. Clulow ilstr_append_char(ilstr_t *ils, char c) 163*6112cec5SJoshua M. Clulow { 164*6112cec5SJoshua M. Clulow char buf[2]; 165*6112cec5SJoshua M. Clulow 166*6112cec5SJoshua M. Clulow if (ils->ils_errno != ILSTR_ERROR_OK) { 167*6112cec5SJoshua M. Clulow return; 168*6112cec5SJoshua M. Clulow } 169*6112cec5SJoshua M. Clulow 170*6112cec5SJoshua M. Clulow buf[0] = c; 171*6112cec5SJoshua M. Clulow buf[1] = '\0'; 172*6112cec5SJoshua M. Clulow 173*6112cec5SJoshua M. Clulow ilstr_append_str(ils, buf); 174*6112cec5SJoshua M. Clulow } 175*6112cec5SJoshua M. Clulow 176*6112cec5SJoshua M. Clulow static ilstr_errno_t 177*6112cec5SJoshua M. Clulow ilstr_errno(ilstr_t *ils) 178*6112cec5SJoshua M. Clulow { 179*6112cec5SJoshua M. Clulow return (ils->ils_errno); 180*6112cec5SJoshua M. Clulow } 181*6112cec5SJoshua M. Clulow 182*6112cec5SJoshua M. Clulow static const char * 183*6112cec5SJoshua M. Clulow ilstr_cstr(ilstr_t *ils) 184*6112cec5SJoshua M. Clulow { 185*6112cec5SJoshua M. Clulow return (ils->ils_data); 186*6112cec5SJoshua M. Clulow } 187*6112cec5SJoshua M. Clulow 188*6112cec5SJoshua M. Clulow static size_t 189*6112cec5SJoshua M. Clulow ilstr_len(ilstr_t *ils) 190*6112cec5SJoshua M. Clulow { 191*6112cec5SJoshua M. Clulow return (ils->ils_strlen); 192*6112cec5SJoshua M. Clulow } 193*6112cec5SJoshua M. Clulow 194*6112cec5SJoshua M. Clulow static const char * 195*6112cec5SJoshua M. Clulow ilstr_errstr(ilstr_t *ils) 196*6112cec5SJoshua M. Clulow { 197*6112cec5SJoshua M. Clulow switch (ils->ils_errno) { 198*6112cec5SJoshua M. Clulow case ILSTR_ERROR_OK: 199*6112cec5SJoshua M. Clulow return ("ok"); 200*6112cec5SJoshua M. Clulow case ILSTR_ERROR_NOMEM: 201*6112cec5SJoshua M. Clulow return ("could not allocate memory"); 202*6112cec5SJoshua M. Clulow case ILSTR_ERROR_OVERFLOW: 203*6112cec5SJoshua M. Clulow return ("tried to construct too large a string"); 204*6112cec5SJoshua M. Clulow default: 205*6112cec5SJoshua M. Clulow return ("unknown error"); 206*6112cec5SJoshua M. Clulow } 207*6112cec5SJoshua M. Clulow } 208*6112cec5SJoshua M. Clulow 209*6112cec5SJoshua M. Clulow /* 210*6112cec5SJoshua M. Clulow * Expand a boot banner template string. The following expansion tokens 211*6112cec5SJoshua M. Clulow * are supported: 212*6112cec5SJoshua M. Clulow * 213*6112cec5SJoshua M. Clulow * ^^ a literal caret 214*6112cec5SJoshua M. Clulow * ^s the base kernel name (utsname.sysname) 215*6112cec5SJoshua M. Clulow * ^o the operating system name ("illumos") 216*6112cec5SJoshua M. Clulow * ^v the operating system version (utsname.version) 217*6112cec5SJoshua M. Clulow * ^r the operating system release (utsname.release) 218*6112cec5SJoshua M. Clulow * ^w the native address width in bits (e.g., "32" or "64") 219*6112cec5SJoshua M. Clulow */ 220*6112cec5SJoshua M. Clulow static void 221*6112cec5SJoshua M. Clulow bootbanner_expand_template(const char *input, ilstr_t *output) 222*6112cec5SJoshua M. Clulow { 223*6112cec5SJoshua M. Clulow size_t pos = 0; 224*6112cec5SJoshua M. Clulow enum { 225*6112cec5SJoshua M. Clulow ST_REST, 226*6112cec5SJoshua M. Clulow ST_CARET, 227*6112cec5SJoshua M. Clulow } state = ST_REST; 228*6112cec5SJoshua M. Clulow 229*6112cec5SJoshua M. Clulow #ifndef _KERNEL 230*6112cec5SJoshua M. Clulow struct utsname utsname; 231*6112cec5SJoshua M. Clulow bzero(&utsname, sizeof (utsname)); 232*6112cec5SJoshua M. Clulow (void) uname(&utsname); 233*6112cec5SJoshua M. Clulow #endif 234*6112cec5SJoshua M. Clulow 235*6112cec5SJoshua M. Clulow for (;;) { 236*6112cec5SJoshua M. Clulow char c = input[pos]; 237*6112cec5SJoshua M. Clulow 238*6112cec5SJoshua M. Clulow if (c == '\0') { 239*6112cec5SJoshua M. Clulow /* 240*6112cec5SJoshua M. Clulow * Even if the template came to an end mid way through 241*6112cec5SJoshua M. Clulow * a caret expansion, it seems best to just print what 242*6112cec5SJoshua M. Clulow * we have and drive on. The onus will be on the 243*6112cec5SJoshua M. Clulow * distributor to ensure their templates are 244*6112cec5SJoshua M. Clulow * well-formed at build time. 245*6112cec5SJoshua M. Clulow */ 246*6112cec5SJoshua M. Clulow break; 247*6112cec5SJoshua M. Clulow } 248*6112cec5SJoshua M. Clulow 249*6112cec5SJoshua M. Clulow switch (state) { 250*6112cec5SJoshua M. Clulow case ST_REST: 251*6112cec5SJoshua M. Clulow if (c == '^') { 252*6112cec5SJoshua M. Clulow state = ST_CARET; 253*6112cec5SJoshua M. Clulow } else { 254*6112cec5SJoshua M. Clulow ilstr_append_char(output, c); 255*6112cec5SJoshua M. Clulow } 256*6112cec5SJoshua M. Clulow pos++; 257*6112cec5SJoshua M. Clulow continue; 258*6112cec5SJoshua M. Clulow 259*6112cec5SJoshua M. Clulow case ST_CARET: 260*6112cec5SJoshua M. Clulow if (c == '^') { 261*6112cec5SJoshua M. Clulow ilstr_append_char(output, c); 262*6112cec5SJoshua M. Clulow } else if (c == 's') { 263*6112cec5SJoshua M. Clulow ilstr_append_str(output, utsname.sysname); 264*6112cec5SJoshua M. Clulow } else if (c == 'o') { 265*6112cec5SJoshua M. Clulow ilstr_append_str(output, "illumos"); 266*6112cec5SJoshua M. Clulow } else if (c == 'r') { 267*6112cec5SJoshua M. Clulow ilstr_append_str(output, utsname.release); 268*6112cec5SJoshua M. Clulow } else if (c == 'v') { 269*6112cec5SJoshua M. Clulow ilstr_append_str(output, utsname.version); 270*6112cec5SJoshua M. Clulow } else if (c == 'w') { 271*6112cec5SJoshua M. Clulow #ifdef _KERNEL 272*6112cec5SJoshua M. Clulow ilstr_append_uint(output, 273*6112cec5SJoshua M. Clulow NBBY * (uint_t)sizeof (void *)); 274*6112cec5SJoshua M. Clulow #else 275*6112cec5SJoshua M. Clulow char *bits; 276*6112cec5SJoshua M. Clulow char buf[32]; 277*6112cec5SJoshua M. Clulow int r; 278*6112cec5SJoshua M. Clulow 279*6112cec5SJoshua M. Clulow if ((r = sysinfo(SI_ADDRESS_WIDTH, buf, 280*6112cec5SJoshua M. Clulow sizeof (buf))) > 0 && 281*6112cec5SJoshua M. Clulow r < (int)sizeof (buf)) { 282*6112cec5SJoshua M. Clulow bits = buf; 283*6112cec5SJoshua M. Clulow } else { 284*6112cec5SJoshua M. Clulow bits = "64"; 285*6112cec5SJoshua M. Clulow } 286*6112cec5SJoshua M. Clulow 287*6112cec5SJoshua M. Clulow ilstr_append_str(output, bits); 288*6112cec5SJoshua M. Clulow #endif 289*6112cec5SJoshua M. Clulow } else { 290*6112cec5SJoshua M. Clulow /* 291*6112cec5SJoshua M. Clulow * Try to make it obvious what went wrong: 292*6112cec5SJoshua M. Clulow */ 293*6112cec5SJoshua M. Clulow ilstr_append_str(output, "!^"); 294*6112cec5SJoshua M. Clulow ilstr_append_char(output, c); 295*6112cec5SJoshua M. Clulow ilstr_append_str(output, " UNKNOWN!"); 296*6112cec5SJoshua M. Clulow } 297*6112cec5SJoshua M. Clulow state = ST_REST; 298*6112cec5SJoshua M. Clulow pos++; 299*6112cec5SJoshua M. Clulow continue; 300*6112cec5SJoshua M. Clulow } 301*6112cec5SJoshua M. Clulow } 302*6112cec5SJoshua M. Clulow } 303*6112cec5SJoshua M. Clulow 304*6112cec5SJoshua M. Clulow static void 305*6112cec5SJoshua M. Clulow bootbanner_print_one(ilstr_t *s, void (*printfunc)(const char *, uint_t), 306*6112cec5SJoshua M. Clulow const char *template, uint_t *nump) 307*6112cec5SJoshua M. Clulow { 308*6112cec5SJoshua M. Clulow ilstr_reset(s); 309*6112cec5SJoshua M. Clulow 310*6112cec5SJoshua M. Clulow bootbanner_expand_template(template, s); 311*6112cec5SJoshua M. Clulow 312*6112cec5SJoshua M. Clulow if (ilstr_errno(s) == ILSTR_ERROR_OK) { 313*6112cec5SJoshua M. Clulow if (ilstr_len(s) > 0) { 314*6112cec5SJoshua M. Clulow printfunc(ilstr_cstr(s), *nump); 315*6112cec5SJoshua M. Clulow *nump += 1; 316*6112cec5SJoshua M. Clulow } 317*6112cec5SJoshua M. Clulow } else { 318*6112cec5SJoshua M. Clulow char ebuf[128]; 319*6112cec5SJoshua M. Clulow 320*6112cec5SJoshua M. Clulow snprintf(ebuf, sizeof (ebuf), "boot banner error: %s", 321*6112cec5SJoshua M. Clulow ilstr_errstr(s)); 322*6112cec5SJoshua M. Clulow 323*6112cec5SJoshua M. Clulow printfunc(ebuf, *nump); 324*6112cec5SJoshua M. Clulow *nump += 1; 325*6112cec5SJoshua M. Clulow } 326*6112cec5SJoshua M. Clulow } 327*6112cec5SJoshua M. Clulow 328*6112cec5SJoshua M. Clulow /* 329*6112cec5SJoshua M. Clulow * This routine should be called during early system boot to render the boot 330*6112cec5SJoshua M. Clulow * banner on the system console, and during zone boot to do so on the zone 331*6112cec5SJoshua M. Clulow * console. 332*6112cec5SJoshua M. Clulow * 333*6112cec5SJoshua M. Clulow * The "printfunc" argument is a callback function. When passed a string, the 334*6112cec5SJoshua M. Clulow * function must print it in a fashion appropriate for the context. The 335*6112cec5SJoshua M. Clulow * callback will only be called while within the call to bootbanner_print(). 336*6112cec5SJoshua M. Clulow * The "kmflag" value accepts the same values as kmem_alloc(9F) in the kernel, 337*6112cec5SJoshua M. Clulow * and is ignored otherwise. 338*6112cec5SJoshua M. Clulow */ 339*6112cec5SJoshua M. Clulow void 340*6112cec5SJoshua M. Clulow bootbanner_print(void (*printfunc)(const char *, uint_t), int kmflag) 341*6112cec5SJoshua M. Clulow { 342*6112cec5SJoshua M. Clulow ilstr_t s; 343*6112cec5SJoshua M. Clulow uint_t num = 0; 344*6112cec5SJoshua M. Clulow 345*6112cec5SJoshua M. Clulow ilstr_init(&s, kmflag); 346*6112cec5SJoshua M. Clulow 347*6112cec5SJoshua M. Clulow bootbanner_print_one(&s, printfunc, BOOTBANNER1, &num); 348*6112cec5SJoshua M. Clulow bootbanner_print_one(&s, printfunc, BOOTBANNER2, &num); 349*6112cec5SJoshua M. Clulow bootbanner_print_one(&s, printfunc, BOOTBANNER3, &num); 350*6112cec5SJoshua M. Clulow bootbanner_print_one(&s, printfunc, BOOTBANNER4, &num); 351*6112cec5SJoshua M. Clulow bootbanner_print_one(&s, printfunc, BOOTBANNER5, &num); 352*6112cec5SJoshua M. Clulow 353*6112cec5SJoshua M. Clulow ilstr_fini(&s); 354*6112cec5SJoshua M. Clulow } 355