u-boot-brain/lib/tiny-printf.c
Andre Przywara 59d07ee08e SPL: tiny-printf: avoid any BSS usage
As printf calls may be executed quite early, we should avoid using any
BSS stored variables, since some boards put BSS in DRAM, which may not
have been initialised yet.
Explicitly mark those "static global" variables as belonging to the
.data section, to keep tiny-printf clear of any BSS usage.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
2016-07-08 12:50:34 -04:00

188 lines
3.0 KiB
C

/*
* Tiny printf version for SPL
*
* Copied from:
* http://www.sparetimelabs.com/printfrevisited/printfrevisited.php
*
* Copyright (C) 2004,2008 Kustaa Nyholm
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include <common.h>
#include <stdarg.h>
#include <serial.h>
/*
* This code in here may execute before the DRAM is initialised, so
* we should make sure that it doesn't touch BSS, which some boards
* put in DRAM.
*/
static char *bf __attribute__ ((section(".data")));
static char zs __attribute__ ((section(".data")));
/* Current position in sprintf() output string */
static char *outstr __attribute__ ((section(".data")));
static void out(char c)
{
*bf++ = c;
}
static void out_dgt(char dgt)
{
out(dgt + (dgt < 10 ? '0' : 'a' - 10));
zs = 1;
}
static void div_out(unsigned int *num, unsigned int div)
{
unsigned char dgt = 0;
while (*num >= div) {
*num -= div;
dgt++;
}
if (zs || dgt > 0)
out_dgt(dgt);
}
int _vprintf(const char *fmt, va_list va, void (*putc)(const char ch))
{
char ch;
char *p;
unsigned int num;
char buf[12];
unsigned int div;
while ((ch = *(fmt++))) {
if (ch != '%') {
putc(ch);
} else {
bool lz = false;
int width = 0;
ch = *(fmt++);
if (ch == '0') {
ch = *(fmt++);
lz = 1;
}
if (ch >= '0' && ch <= '9') {
width = 0;
while (ch >= '0' && ch <= '9') {
width = (width * 10) + ch - '0';
ch = *fmt++;
}
}
bf = buf;
p = bf;
zs = 0;
switch (ch) {
case '\0':
goto abort;
case 'u':
case 'd':
num = va_arg(va, unsigned int);
if (ch == 'd' && (int)num < 0) {
num = -(int)num;
out('-');
}
if (!num) {
out_dgt(0);
} else {
for (div = 1000000000; div; div /= 10)
div_out(&num, div);
}
break;
case 'x':
num = va_arg(va, unsigned int);
if (!num) {
out_dgt(0);
} else {
for (div = 0x10000000; div; div /= 0x10)
div_out(&num, div);
}
break;
case 'c':
out((char)(va_arg(va, int)));
break;
case 's':
p = va_arg(va, char*);
break;
case '%':
out('%');
default:
break;
}
*bf = 0;
bf = p;
while (*bf++ && width > 0)
width--;
while (width-- > 0)
putc(lz ? '0' : ' ');
if (p) {
while ((ch = *p++))
putc(ch);
}
}
}
abort:
return 0;
}
int vprintf(const char *fmt, va_list va)
{
return _vprintf(fmt, va, putc);
}
int printf(const char *fmt, ...)
{
va_list va;
int ret;
va_start(va, fmt);
ret = _vprintf(fmt, va, putc);
va_end(va);
return ret;
}
static void putc_outstr(char ch)
{
*outstr++ = ch;
}
int sprintf(char *buf, const char *fmt, ...)
{
va_list va;
int ret;
va_start(va, fmt);
outstr = buf;
ret = _vprintf(fmt, va, putc_outstr);
va_end(va);
*outstr = '\0';
return ret;
}
/* Note that size is ignored */
int snprintf(char *buf, size_t size, const char *fmt, ...)
{
va_list va;
int ret;
va_start(va, fmt);
outstr = buf;
ret = _vprintf(fmt, va, putc_outstr);
va_end(va);
*outstr = '\0';
return ret;
}