slkern/kprintf.c

287 lines
8.3 KiB
C
Raw Normal View History

// This is NEW CODE replacing the old printf with one based on my libc
#include "types.h"
#include "param.h"
#include "riscv.h"
#include "defs.h"
#include "kprintf.h"
#include "sched.h"
volatile int kprintf_shouldlock = 0;
sched_spinlock_t kprintf_spin;
// These are non-standard, but the other *printf functions need to be implemented somehow,
// so fnprintf/vfnprintf just use a callback function for output of n bytes of string output.
typedef int(*_libc_fnprintf_fn_t)(const char* str, int n, void* udata);
int _LIBC_PRINTF_CALLCONV _libc_fnprintf(_libc_fnprintf_fn_t fn, void* udata, const char* fmt, ...);
int _LIBC_PRINTF_CALLCONV _libc_vfnprintf(_libc_fnprintf_fn_t fn, void* udata, const char* fmt, va_list list);
// These are non-standard, but the other *printf functions need to be implemented somehow,
// so fnprintf/vfnprintf just use a callback function for output of n bytes of string output.
int _LIBC_PRINTF_CALLCONV _libc_fnprintf(_libc_fnprintf_fn_t fn, void* udata, const char* fmt, ...) {
va_list varargs;
va_start(varargs, fmt);
uintptr_t result = _libc_vfnprintf(fn, udata, fmt, varargs);
va_end(varargs);
return result;
}
#define DUMPTOFN() \
if (dayswithoutincident) { \
if (fn(f-dayswithoutincident, dayswithoutincident, udata) != dayswithoutincident) { \
return EOF; \
} \
written += dayswithoutincident; \
dayswithoutincident = 0; \
}
volatile int kprintf_inpanic = 0;
void kprintf_panic(const char* reason) {
kprintf_shouldlock = 0;
kprintf("KERNEL PANIC: %s\n", reason);
kprintf_inpanic = 1;
// TODO: Disable interrupts?
while (1) {
// Hot loop
}
}
int _LIBC_PRINTF_CALLCONV _libc_vfnprintf(_libc_fnprintf_fn_t fn, void* udata, const char* fmt, va_list list) {
int dayswithoutincident = 0;
int written = 0;
const char* f = fmt;
int c;
while ((c = *f)) {
if (c == '%') {
DUMPTOFN();
//int has_decimals = 0;
int decimals = 0;
nextctrl:
int tc = *(f+1);
int longcount = 0; // for counting %ld/%lld
switch (tc) {
case '%': {
f++;
dayswithoutincident++; // Just let the second '%' be printed with the next string part
} break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': {
decimals = decimals * 10 + (tc - '0');
f++;
} goto nextctrl;
#ifdef UNUSED
case '.': {
has_decimals = 1;
f++;
} goto nextctrl;
case 'f': {
char buf[20];
int i = 20;
double fval = va_arg(list, double);
//fval = -543.210987654321;
long ival = (long) fval;
int isneg = ival < 0;
unsigned long uval = isneg ? -ival : ival;
do {
buf[--i] = (char)('0' + uval % 10);
uval /= 10;
} while (uval > 0);
if (ival < 0) {
buf[--i] = '-';
}
int nwr = fn(buf+i, 20-i, udata);
if (nwr != 20-i) {
return EOF;
}
written += nwr;
if (!has_decimals) {
decimals = 6;
}
double x = fabs(fval - (double) ival);
if (fn(".", 1, udata) != 1) {
return EOF;
}
written++;
while (decimals > 0) {
x *= 10;
decimals--;
int ix = (int) x;
char xch = (char) ('0'+(ix%10));
if (fn(&xch, 1, udata) != 1) {
return EOF;
}
written++;
}
f++;
} break;
#endif
case 'S': { // Formatted substring, I thought there was a standard % for it
char* s = va_arg(list, char*);
if (!s) {
s = "(null)";
}
int nwr = _libc_vfnprintf(fn, udata, s, list);
if (nwr == EOF) {
return EOF;
}
written += nwr;
f++;
} break;
case 's': {
char* s = va_arg(list, char*);
if (!s) {
s = "(null)";
}
int len = strlen(s);
int nwr = fn(s, len, udata);
if (nwr != len) {
return EOF;
}
written += nwr;
f++;
} break;
case 'c': {
int chr = va_arg(list, int);
char chrc = (char) chr;
if (fn(&chrc, 1, udata) != 1) {
return EOF;
}
written++;
f++;
} break;
case 'l': // %ld/%lx is just handled as a special case of %d/%x
longcount++;
f++;
goto nextctrl;
case 'u': // Unsigned is just handled as a special case of %d
case 'd': {
// TODO: Testing/edge case for negative maximum?
char buf[20];
int i = 20;
if (longcount) {
long val = va_arg(list, long);
int isneg = ((tc == 'd') && (val < 0));
unsigned long uval = isneg ? -val : val;
// Not actually needed, unless you want a full string: buf[i--] = '0';
do {
buf[--i] = (char)('0' + uval % 10);
uval /= 10;
} while (uval > 0);
if (isneg) {
buf[--i] = '-';
}
} else {
int val = va_arg(list, int);
int isneg = ((tc == 'd') && (val < 0));
unsigned int uval = isneg ? -val : val;
// Not actually needed, unless you want a full string: buf[i--] = '0';
do {
buf[--i] = (char)('0' + uval % 10);
uval /= 10;
} while (uval > 0);
if (isneg) {
buf[--i] = '-';
}
}
int nwr = fn(buf+i, 20-i, udata);
if (nwr != 20-i) {
return EOF;
}
written += nwr;
f++;
} break;
case 'p': // Pointer is treated as %lx
longcount++;
case 'x':
case 'X': { // Hex is handled a separate case to %d because the loop is slightly slower and sign is never used
const char* digits = (tc == 'x') ? "0123456789abcdef" : "0123456789ABCDEF";
char buf[16]; // Size is easier to predict for hex, two characters of digits per byte
int i = 16;
if (longcount) {
unsigned long uval = va_arg(list, unsigned long);
// Not actually needed, unless you want a full string: buf[i--] = '0';
do {
buf[--i] = digits[uval % 16];
uval /= 16;
} while (uval > 0);
} else {
unsigned int uval = va_arg(list, unsigned int);
// Not actually needed, unless you want a full string: buf[i--] = '0';
do {
buf[--i] = digits[uval % 16];
uval /= 16;
} while (uval > 0);
}
int nwr = fn(buf+i, 16-i, udata);
if (nwr != 16-i) {
return EOF;
}
written += nwr;
f++;
} break;
default: { // For now just print %x??? when 'x' is unknown, but this is probably unsafe and an error should be reported properly instead (TODO)
// Also skip the argument (assume all arguments are word-sized for now)
/* void* _ignored = */ va_arg(list, void*);
if (fn(f, 2, udata) != 2) {
return EOF;
}
if (fn("???", 3, udata) != 3) {
return EOF;
}
written+=5;
f++;
}
}
} else {
dayswithoutincident++;
}
f++;
}
DUMPTOFN();
return written;
}
int _kprintf_backend(const char* str, int n, void* udata) {
for (int i = 0; i < n; i++) {
consputc(str[i]);
}
return n;
}
int _LIBC_PRINTF_CALLCONV kprintf(const char* fmt, ...) {
//void* udata = (void*)((uintptr_t)1); // stdout
int shouldlock = kprintf_shouldlock;
if (shouldlock) {
acquire(&kprintf_spin);
}
va_list varargs;
va_start(varargs, fmt);
uintptr_t result = _libc_vfnprintf(&_kprintf_backend, NULL, fmt, varargs);
va_end(varargs);
if (shouldlock) {
release(&kprintf_spin);
}
return result;
}
void kprintf_init() {
initlock(&kprintf_spin, "kprintf_spin");
kprintf_shouldlock = 1;
}