/* * Copyright (c) 1986 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ #include #include #include #include #include #include #include #include #include #include #define TOCONS 0x1 #define TOTTY 0x2 #define TOLOG 0x4 /* * In case console is off, * panicstr contains argument to last * call to panic. */ char *panicstr; /* * Print a character on console or users terminal. * If destination is console then the last MSGBUFS characters * are saved in msgbuf for inspection later. */ static void putchar (c, flags, tp) int c, flags; register struct tty *tp; { if (flags & TOTTY) { register int s = spltty(); if (tp && (tp->t_state & (TS_CARR_ON | TS_ISOPEN)) == (TS_CARR_ON | TS_ISOPEN)) { if (c == '\n') (void) ttyoutput('\r', tp); (void) ttyoutput(c, tp); ttstart(tp); } splx(s); } #ifdef LOG_ENABLED if ((flags & TOLOG) && c != '\0' && c != '\r' && c != 0177) { char sym = c; logwrt (&sym, 1, logMSG); } #endif if ((flags & TOCONS) && c != '\0') cnputc(c); } static unsigned mkhex (unsigned ch) { ch &= 15; if (ch > 9) return ch + 'a' - 10; return ch + '0'; } /* * Put a NUL-terminated ASCII number (base <= 16) in a buffer in reverse * order; return an optional length and a pointer to the last character * written in the buffer (i.e., the first character of the string). * The buffer pointed to by `nbuf' must have length >= MAXNBUF. */ static char * ksprintn (char *nbuf, unsigned long ul, int base, int width, int *lenp) { char *p; p = nbuf; *p = 0; for (;;) { *++p = mkhex (ul % base); ul /= base; if (--width > 0) continue; if (! ul) break; } if (lenp) *lenp = p - nbuf; return (p); } void puts(char *s, int flags, struct tty *ttyp) { while(*s) putchar(*(s++), flags, ttyp); } /* * Scaled down version of printf(3). * Two additional formats: %b anf %D. * Based on FreeBSD sources. * Heavily rewritten by Serge Vakulenko. * * The format %b is supported to decode error registers. * Its usage is: * * printf("reg=%b\n", regval, "*"); * * where is the output base expressed as a control character, e.g. * \10 gives octal; \20 gives hex. Each arg is a sequence of characters, * the first of which gives the bit number to be inspected (origin 1), and * the next characters (up to a control character, i.e. a character <= 32), * give the name of the register. Thus: * * kvprintf("reg=%b\n", 3, "\10\2BITTWO\1BITONE\n"); * * would produce output: * * reg=3 * * The format %D -- Hexdump, takes a pointer. Sharp flag - use `:' as * a separator, instead of a space. For example: * * ("%6D", ptr) -> XX XX XX XX XX XX * ("%#*D", len, ptr) -> XX:XX:XX:XX ... */ #define PUTC(C) putchar(C,flags,ttyp) #define HION "\e[1m" #define HIOFF "\e[0m" static void prf (fmt, ap, flags, ttyp) register char *fmt; register u_int *ap; int flags; struct tty *ttyp; { #define va_arg(ap,type) *(type*) (void*) (ap++) char *q, nbuf [sizeof(long) * 8 + 1]; const char *s; int c, padding, base, lflag, ladjust, sharpflag, neg, dot, size; int n, width, dwidth, uppercase, extrazeros, sign; unsigned long ul; #ifdef KERNEL_HIGHLIGHT puts(HION, flags, ttyp); #endif if (! fmt) fmt = "(null)\n"; for (;;) { while ((c = *fmt++) != '%') { if (! c) { #ifdef KERNEL_HIGHLIGHT puts(HIOFF, flags, ttyp); #endif return; } PUTC (c); } padding = ' '; width = 0; extrazeros = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0; sign = 0; dot = 0; uppercase = 0; dwidth = -1; reswitch: c = *fmt++; switch (c) { case '.': dot = 1; padding = ' '; dwidth = 0; goto reswitch; case '#': sharpflag = 1; goto reswitch; case '+': sign = -1; goto reswitch; case '-': ladjust = 1; goto reswitch; case '%': PUTC (c); break; case '*': if (! dot) { width = va_arg (ap, int); if (width < 0) { ladjust = !ladjust; width = -width; } } else { dwidth = va_arg (ap, int); } goto reswitch; case '0': if (! dot) { padding = '0'; goto reswitch; } case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': for (n=0; ; ++fmt) { n = n * 10 + c - '0'; c = *fmt; if (c < '0' || c > '9') break; } if (dot) dwidth = n; else width = n; goto reswitch; case 'b': ul = va_arg (ap, int); s = va_arg (ap, const char*); q = ksprintn (nbuf, ul, *s++, -1, 0); while (*q) PUTC (*q--); if (! ul) break; size = 0; while (*s) { n = *s++; if ((char) (ul >> (n-1)) & 1) { PUTC (size ? ',' : '<'); for (; (n = *s) > ' '; ++s) PUTC (n); size = 1; } else { while (*s > ' ') ++s; } } if (size) PUTC ('>'); break; case 'c': if (! ladjust && width > 0) { while (width--) PUTC (' '); } PUTC (va_arg (ap, int)); if (ladjust && width > 0) { while (width--) PUTC (' '); } break; case 'D': s = va_arg (ap, const char*); if (! width) width = 16; if (sharpflag) padding = ':'; while (width--) { c = *s++; PUTC (mkhex (c >> 4)); PUTC (mkhex (c)); if (width) PUTC (padding); } break; case 'd': ul = lflag ? va_arg (ap, long) : va_arg (ap, int); if (! sign) sign = 1; base = 10; goto number; case 'l': lflag = 1; goto reswitch; case 'o': ul = lflag ? va_arg (ap, unsigned long) : va_arg (ap, unsigned int); base = 8; goto nosign; case 'p': ul = (size_t) va_arg (ap, void*); if (! ul) { s = "(nil)"; goto const_string; } base = 16; sharpflag = (width == 0); goto nosign; case 'n': ul = lflag ? va_arg (ap, unsigned long) : sign ? (unsigned long) va_arg (ap, int) : va_arg (ap, unsigned int); base = 10; goto number; case 's': s = va_arg (ap, char*); if (! s) s = (const char*) "(null)"; const_string: if (! dot) n = strlen (s); else for (n=0; n 0) { while (width--) PUTC (' '); } while (n--) PUTC (*s++); if (ladjust && width > 0) { while (width--) PUTC (' '); } break; case 'u': ul = lflag ? va_arg (ap, unsigned long) : va_arg (ap, unsigned int); base = 10; goto nosign; case 'x': case 'X': ul = lflag ? va_arg (ap, unsigned long) : va_arg (ap, unsigned int); base = 16; uppercase = (c == 'X'); goto nosign; case 'z': case 'Z': ul = lflag ? va_arg (ap, unsigned long) : sign ? (unsigned long) va_arg (ap, int) : va_arg (ap, unsigned int); base = 16; uppercase = (c == 'Z'); goto number; nosign: sign = 0; number: if (sign && ((long) ul != 0L)) { if ((long) ul < 0L) { neg = '-'; ul = -(long) ul; } else if (sign < 0) neg = '+'; } if (dwidth >= (int) sizeof(nbuf)) { extrazeros = dwidth - sizeof(nbuf) + 1; dwidth = sizeof(nbuf) - 1; } s = ksprintn (nbuf, ul, base, dwidth, &size); if (sharpflag && ul != 0) { if (base == 8) size++; else if (base == 16) size += 2; } if (neg) size++; if (! ladjust && width && padding == ' ' && (width -= size) > 0) { do { PUTC (' '); } while (--width > 0); } if (neg) PUTC (neg); if (sharpflag && ul != 0) { if (base == 8) { PUTC ('0'); } else if (base == 16) { PUTC ('0'); PUTC (uppercase ? 'X' : 'x'); } } if (extrazeros) { do { PUTC ('0'); } while (--extrazeros > 0); } if (! ladjust && width && (width -= size) > 0) { do { PUTC (padding); } while (--width > 0); } for (; *s; --s) { if (uppercase && *s>='a' && *s<='z') { PUTC (*s + 'A' - 'a'); } else { PUTC (*s); } } if (ladjust && width && (width -= size) > 0) { do { PUTC (' '); } while (--width > 0); } break; default: PUTC ('%'); if (lflag) PUTC ('l'); PUTC (c); break; } } #ifdef KERNEL_HIGHLIGHT puts(HIOFF,flags,ttyp); #endif } static void logpri (level) int level; { putchar ('<', TOLOG, (struct tty*) 0); prf ("%u", &level, TOLOG, (struct tty*) 0); putchar ('>', TOLOG, (struct tty*) 0); } /* * Scaled down version of C Library printf. * Used to print diagnostic information directly on console tty. * Since it is not interrupt driven, all system activities are * suspended. Printf should not be used for chit-chat. * * One additional format: %b is supported to decode error registers. * Usage is: * printf("reg=%b\n", regval, "*"); * Where is the output base expressed as a control character, * e.g. \10 gives octal; \20 gives hex. Each arg is a sequence of * characters, the first of which gives the bit number to be inspected * (origin 1), and the next characters (up to a control character, i.e. * a character <= 32), give the name of the register. Thus * printf("reg=%b\n", 3, "\10\2BITTWO\1BITONE\n"); * would produce output: * reg=3 */ void printf(char *fmt, ...) { prf(fmt, &fmt + 1, TOCONS | TOLOG, (struct tty *)0); } /* * Microchip MPLABX C32 compiler generates calls to _printf_s() * and other strange names. */ #ifdef __MPLABX__ void _printf_s(char *fmt, ...) __attribute__((alias ("printf"))); void _printf_cdnopuxX(char *fmt, ...) __attribute__((alias ("printf"))); void _printf_cdnopsuxX(char *fmt, ...) __attribute__((alias ("printf"))); #endif /* * Uprintf prints to the current user's terminal, * guarantees not to sleep (so could be called by interrupt routines; * but prints on the tty of the current process) * and does no watermark checking - (so no verbose messages). * NOTE: with current kernel mapping scheme, the user structure is * not guaranteed to be accessible at interrupt level (see seg.h); * a savemap/restormap would be needed here or in putchar if uprintf * was to be used at interrupt time. */ void uprintf (char *fmt, ...) { register struct tty *tp; tp = u.u_ttyp; if (tp == NULL) return; if (ttycheckoutq (tp, 1)) prf (fmt, &fmt+1, TOTTY, tp); } /* * tprintf prints on the specified terminal (console if none) * and logs the message. It is designed for error messages from * single-open devices, and may be called from interrupt level * (does not sleep). */ void tprintf (register struct tty *tp, char *fmt, ...) { int flags = TOTTY | TOLOG; logpri (LOG_INFO); if (tp == (struct tty*) NULL) tp = &cnttys[0]; if (ttycheckoutq (tp, 0) == 0) flags = TOLOG; prf (fmt, &fmt + 1, flags, tp); #ifdef LOG_ENABLED logwakeup (logMSG); #endif } /* * Log writes to the log buffer, * and guarantees not to sleep (so can be called by interrupt routines). * If there is no process reading the log yet, it writes to the console also. */ /*VARARGS2*/ void log (int level, char *fmt, ...) { register int s = splhigh(); logpri(level); prf(fmt, &fmt + 1, TOLOG, (struct tty *)0); splx(s); #ifdef LOG_ENABLED if (! logisopen(logMSG)) #endif prf(fmt, &fmt + 1, TOCONS, (struct tty *)0); #ifdef LOG_ENABLED logwakeup(logMSG); #endif } /* * Panic is called on unresolvable fatal errors. * It prints "panic: mesg", and then reboots. * If we are called twice, then we avoid trying to * sync the disks as this often leads to recursive panics. */ void panic(s) char *s; { int bootopt = RB_HALT | RB_DUMP; if (panicstr) { bootopt |= RB_NOSYNC; } else { panicstr = s; } printf ("panic: %s\n", s); boot (rootdev, bootopt); }