287 lines
8.3 KiB
C
287 lines
8.3 KiB
C
// 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;
|
|
}
|