Files
retrobsd/sys/kernel/subr_prf.c
Serge Vakulenko 585773955b Fix include paths in the kernel sources.
Max32 kernel successfully compiled with kconfig utility.
2015-08-31 00:21:41 -07:00

577 lines
14 KiB
C

/*
* 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 <sys/param.h>
#include <sys/user.h>
#include <sys/buf.h>
#include <sys/msgbuf.h>
#include <sys/conf.h>
#include <sys/ioctl.h>
#include <sys/tty.h>
#include <sys/reboot.h>
#include <sys/systm.h>
#include <sys/syslog.h>
#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, "<base><arg>*");
*
* where <base> 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<BITTWO,BITONE>
*
* 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<dwidth && s[n]; n++)
continue;
width -= n;
if (! ladjust && width > 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, "<base><arg>*");
* Where <base> 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<BITTWO,BITONE>
*/
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);
}