xref: /openbmc/linux/arch/x86/boot/printf.c (revision 9b706aee)
196ae6ea0SThomas Gleixner /* -*- linux-c -*- ------------------------------------------------------- *
296ae6ea0SThomas Gleixner  *
396ae6ea0SThomas Gleixner  *   Copyright (C) 1991, 1992 Linus Torvalds
496ae6ea0SThomas Gleixner  *   Copyright 2007 rPath, Inc. - All Rights Reserved
596ae6ea0SThomas Gleixner  *
696ae6ea0SThomas Gleixner  *   This file is part of the Linux kernel, and is made available under
796ae6ea0SThomas Gleixner  *   the terms of the GNU General Public License version 2.
896ae6ea0SThomas Gleixner  *
996ae6ea0SThomas Gleixner  * ----------------------------------------------------------------------- */
1096ae6ea0SThomas Gleixner 
1196ae6ea0SThomas Gleixner /*
1296ae6ea0SThomas Gleixner  * arch/i386/boot/printf.c
1396ae6ea0SThomas Gleixner  *
1496ae6ea0SThomas Gleixner  * Oh, it's a waste of space, but oh-so-yummy for debugging.  This
1596ae6ea0SThomas Gleixner  * version of printf() does not include 64-bit support.  "Live with
1696ae6ea0SThomas Gleixner  * it."
1796ae6ea0SThomas Gleixner  *
1896ae6ea0SThomas Gleixner  */
1996ae6ea0SThomas Gleixner 
2096ae6ea0SThomas Gleixner #include "boot.h"
2196ae6ea0SThomas Gleixner 
2296ae6ea0SThomas Gleixner static int skip_atoi(const char **s)
2396ae6ea0SThomas Gleixner {
2496ae6ea0SThomas Gleixner 	int i = 0;
2596ae6ea0SThomas Gleixner 
2696ae6ea0SThomas Gleixner 	while (isdigit(**s))
2796ae6ea0SThomas Gleixner 		i = i * 10 + *((*s)++) - '0';
2896ae6ea0SThomas Gleixner 	return i;
2996ae6ea0SThomas Gleixner }
3096ae6ea0SThomas Gleixner 
3196ae6ea0SThomas Gleixner #define ZEROPAD	1		/* pad with zero */
3296ae6ea0SThomas Gleixner #define SIGN	2		/* unsigned/signed long */
3396ae6ea0SThomas Gleixner #define PLUS	4		/* show plus */
3496ae6ea0SThomas Gleixner #define SPACE	8		/* space if plus */
3596ae6ea0SThomas Gleixner #define LEFT	16		/* left justified */
369b706aeeSDenys Vlasenko #define SMALL	32		/* Must be 32 == 0x20 */
379b706aeeSDenys Vlasenko #define SPECIAL	64		/* 0x */
3896ae6ea0SThomas Gleixner 
3996ae6ea0SThomas Gleixner #define do_div(n,base) ({ \
4096ae6ea0SThomas Gleixner int __res; \
4196ae6ea0SThomas Gleixner __res = ((unsigned long) n) % (unsigned) base; \
4296ae6ea0SThomas Gleixner n = ((unsigned long) n) / (unsigned) base; \
4396ae6ea0SThomas Gleixner __res; })
4496ae6ea0SThomas Gleixner 
4596ae6ea0SThomas Gleixner static char *number(char *str, long num, int base, int size, int precision,
4696ae6ea0SThomas Gleixner 		    int type)
4796ae6ea0SThomas Gleixner {
489b706aeeSDenys Vlasenko 	/* we are called with base 8, 10 or 16, only, thus don't need "G..."  */
499b706aeeSDenys Vlasenko 	static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */
509b706aeeSDenys Vlasenko 
519b706aeeSDenys Vlasenko 	char tmp[66];
529b706aeeSDenys Vlasenko 	char c, sign, locase;
5396ae6ea0SThomas Gleixner 	int i;
5496ae6ea0SThomas Gleixner 
559b706aeeSDenys Vlasenko 	/* locase = 0 or 0x20. ORing digits or letters with 'locase'
569b706aeeSDenys Vlasenko 	 * produces same digits or (maybe lowercased) letters */
579b706aeeSDenys Vlasenko 	locase = (type & SMALL);
5896ae6ea0SThomas Gleixner 	if (type & LEFT)
5996ae6ea0SThomas Gleixner 		type &= ~ZEROPAD;
6096ae6ea0SThomas Gleixner 	if (base < 2 || base > 36)
6196ae6ea0SThomas Gleixner 		return 0;
6296ae6ea0SThomas Gleixner 	c = (type & ZEROPAD) ? '0' : ' ';
6396ae6ea0SThomas Gleixner 	sign = 0;
6496ae6ea0SThomas Gleixner 	if (type & SIGN) {
6596ae6ea0SThomas Gleixner 		if (num < 0) {
6696ae6ea0SThomas Gleixner 			sign = '-';
6796ae6ea0SThomas Gleixner 			num = -num;
6896ae6ea0SThomas Gleixner 			size--;
6996ae6ea0SThomas Gleixner 		} else if (type & PLUS) {
7096ae6ea0SThomas Gleixner 			sign = '+';
7196ae6ea0SThomas Gleixner 			size--;
7296ae6ea0SThomas Gleixner 		} else if (type & SPACE) {
7396ae6ea0SThomas Gleixner 			sign = ' ';
7496ae6ea0SThomas Gleixner 			size--;
7596ae6ea0SThomas Gleixner 		}
7696ae6ea0SThomas Gleixner 	}
7796ae6ea0SThomas Gleixner 	if (type & SPECIAL) {
7896ae6ea0SThomas Gleixner 		if (base == 16)
7996ae6ea0SThomas Gleixner 			size -= 2;
8096ae6ea0SThomas Gleixner 		else if (base == 8)
8196ae6ea0SThomas Gleixner 			size--;
8296ae6ea0SThomas Gleixner 	}
8396ae6ea0SThomas Gleixner 	i = 0;
8496ae6ea0SThomas Gleixner 	if (num == 0)
8596ae6ea0SThomas Gleixner 		tmp[i++] = '0';
8696ae6ea0SThomas Gleixner 	else
8796ae6ea0SThomas Gleixner 		while (num != 0)
889b706aeeSDenys Vlasenko 			tmp[i++] = (digits[do_div(num, base)] | locase);
8996ae6ea0SThomas Gleixner 	if (i > precision)
9096ae6ea0SThomas Gleixner 		precision = i;
9196ae6ea0SThomas Gleixner 	size -= precision;
9296ae6ea0SThomas Gleixner 	if (!(type & (ZEROPAD + LEFT)))
9396ae6ea0SThomas Gleixner 		while (size-- > 0)
9496ae6ea0SThomas Gleixner 			*str++ = ' ';
9596ae6ea0SThomas Gleixner 	if (sign)
9696ae6ea0SThomas Gleixner 		*str++ = sign;
9796ae6ea0SThomas Gleixner 	if (type & SPECIAL) {
9896ae6ea0SThomas Gleixner 		if (base == 8)
9996ae6ea0SThomas Gleixner 			*str++ = '0';
10096ae6ea0SThomas Gleixner 		else if (base == 16) {
10196ae6ea0SThomas Gleixner 			*str++ = '0';
1029b706aeeSDenys Vlasenko 			*str++ = ('X' | locase);
10396ae6ea0SThomas Gleixner 		}
10496ae6ea0SThomas Gleixner 	}
10596ae6ea0SThomas Gleixner 	if (!(type & LEFT))
10696ae6ea0SThomas Gleixner 		while (size-- > 0)
10796ae6ea0SThomas Gleixner 			*str++ = c;
10896ae6ea0SThomas Gleixner 	while (i < precision--)
10996ae6ea0SThomas Gleixner 		*str++ = '0';
11096ae6ea0SThomas Gleixner 	while (i-- > 0)
11196ae6ea0SThomas Gleixner 		*str++ = tmp[i];
11296ae6ea0SThomas Gleixner 	while (size-- > 0)
11396ae6ea0SThomas Gleixner 		*str++ = ' ';
11496ae6ea0SThomas Gleixner 	return str;
11596ae6ea0SThomas Gleixner }
11696ae6ea0SThomas Gleixner 
11796ae6ea0SThomas Gleixner int vsprintf(char *buf, const char *fmt, va_list args)
11896ae6ea0SThomas Gleixner {
11996ae6ea0SThomas Gleixner 	int len;
12096ae6ea0SThomas Gleixner 	unsigned long num;
12196ae6ea0SThomas Gleixner 	int i, base;
12296ae6ea0SThomas Gleixner 	char *str;
12396ae6ea0SThomas Gleixner 	const char *s;
12496ae6ea0SThomas Gleixner 
12596ae6ea0SThomas Gleixner 	int flags;		/* flags to number() */
12696ae6ea0SThomas Gleixner 
12796ae6ea0SThomas Gleixner 	int field_width;	/* width of output field */
12896ae6ea0SThomas Gleixner 	int precision;		/* min. # of digits for integers; max
12996ae6ea0SThomas Gleixner 				   number of chars for from string */
13096ae6ea0SThomas Gleixner 	int qualifier;		/* 'h', 'l', or 'L' for integer fields */
13196ae6ea0SThomas Gleixner 
13296ae6ea0SThomas Gleixner 	for (str = buf; *fmt; ++fmt) {
13396ae6ea0SThomas Gleixner 		if (*fmt != '%') {
13496ae6ea0SThomas Gleixner 			*str++ = *fmt;
13596ae6ea0SThomas Gleixner 			continue;
13696ae6ea0SThomas Gleixner 		}
13796ae6ea0SThomas Gleixner 
13896ae6ea0SThomas Gleixner 		/* process flags */
13996ae6ea0SThomas Gleixner 		flags = 0;
14096ae6ea0SThomas Gleixner 	      repeat:
14196ae6ea0SThomas Gleixner 		++fmt;		/* this also skips first '%' */
14296ae6ea0SThomas Gleixner 		switch (*fmt) {
14396ae6ea0SThomas Gleixner 		case '-':
14496ae6ea0SThomas Gleixner 			flags |= LEFT;
14596ae6ea0SThomas Gleixner 			goto repeat;
14696ae6ea0SThomas Gleixner 		case '+':
14796ae6ea0SThomas Gleixner 			flags |= PLUS;
14896ae6ea0SThomas Gleixner 			goto repeat;
14996ae6ea0SThomas Gleixner 		case ' ':
15096ae6ea0SThomas Gleixner 			flags |= SPACE;
15196ae6ea0SThomas Gleixner 			goto repeat;
15296ae6ea0SThomas Gleixner 		case '#':
15396ae6ea0SThomas Gleixner 			flags |= SPECIAL;
15496ae6ea0SThomas Gleixner 			goto repeat;
15596ae6ea0SThomas Gleixner 		case '0':
15696ae6ea0SThomas Gleixner 			flags |= ZEROPAD;
15796ae6ea0SThomas Gleixner 			goto repeat;
15896ae6ea0SThomas Gleixner 		}
15996ae6ea0SThomas Gleixner 
16096ae6ea0SThomas Gleixner 		/* get field width */
16196ae6ea0SThomas Gleixner 		field_width = -1;
16296ae6ea0SThomas Gleixner 		if (isdigit(*fmt))
16396ae6ea0SThomas Gleixner 			field_width = skip_atoi(&fmt);
16496ae6ea0SThomas Gleixner 		else if (*fmt == '*') {
16596ae6ea0SThomas Gleixner 			++fmt;
16696ae6ea0SThomas Gleixner 			/* it's the next argument */
16796ae6ea0SThomas Gleixner 			field_width = va_arg(args, int);
16896ae6ea0SThomas Gleixner 			if (field_width < 0) {
16996ae6ea0SThomas Gleixner 				field_width = -field_width;
17096ae6ea0SThomas Gleixner 				flags |= LEFT;
17196ae6ea0SThomas Gleixner 			}
17296ae6ea0SThomas Gleixner 		}
17396ae6ea0SThomas Gleixner 
17496ae6ea0SThomas Gleixner 		/* get the precision */
17596ae6ea0SThomas Gleixner 		precision = -1;
17696ae6ea0SThomas Gleixner 		if (*fmt == '.') {
17796ae6ea0SThomas Gleixner 			++fmt;
17896ae6ea0SThomas Gleixner 			if (isdigit(*fmt))
17996ae6ea0SThomas Gleixner 				precision = skip_atoi(&fmt);
18096ae6ea0SThomas Gleixner 			else if (*fmt == '*') {
18196ae6ea0SThomas Gleixner 				++fmt;
18296ae6ea0SThomas Gleixner 				/* it's the next argument */
18396ae6ea0SThomas Gleixner 				precision = va_arg(args, int);
18496ae6ea0SThomas Gleixner 			}
18596ae6ea0SThomas Gleixner 			if (precision < 0)
18696ae6ea0SThomas Gleixner 				precision = 0;
18796ae6ea0SThomas Gleixner 		}
18896ae6ea0SThomas Gleixner 
18996ae6ea0SThomas Gleixner 		/* get the conversion qualifier */
19096ae6ea0SThomas Gleixner 		qualifier = -1;
19196ae6ea0SThomas Gleixner 		if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
19296ae6ea0SThomas Gleixner 			qualifier = *fmt;
19396ae6ea0SThomas Gleixner 			++fmt;
19496ae6ea0SThomas Gleixner 		}
19596ae6ea0SThomas Gleixner 
19696ae6ea0SThomas Gleixner 		/* default base */
19796ae6ea0SThomas Gleixner 		base = 10;
19896ae6ea0SThomas Gleixner 
19996ae6ea0SThomas Gleixner 		switch (*fmt) {
20096ae6ea0SThomas Gleixner 		case 'c':
20196ae6ea0SThomas Gleixner 			if (!(flags & LEFT))
20296ae6ea0SThomas Gleixner 				while (--field_width > 0)
20396ae6ea0SThomas Gleixner 					*str++ = ' ';
20496ae6ea0SThomas Gleixner 			*str++ = (unsigned char)va_arg(args, int);
20596ae6ea0SThomas Gleixner 			while (--field_width > 0)
20696ae6ea0SThomas Gleixner 				*str++ = ' ';
20796ae6ea0SThomas Gleixner 			continue;
20896ae6ea0SThomas Gleixner 
20996ae6ea0SThomas Gleixner 		case 's':
21096ae6ea0SThomas Gleixner 			s = va_arg(args, char *);
21196ae6ea0SThomas Gleixner 			len = strnlen(s, precision);
21296ae6ea0SThomas Gleixner 
21396ae6ea0SThomas Gleixner 			if (!(flags & LEFT))
21496ae6ea0SThomas Gleixner 				while (len < field_width--)
21596ae6ea0SThomas Gleixner 					*str++ = ' ';
21696ae6ea0SThomas Gleixner 			for (i = 0; i < len; ++i)
21796ae6ea0SThomas Gleixner 				*str++ = *s++;
21896ae6ea0SThomas Gleixner 			while (len < field_width--)
21996ae6ea0SThomas Gleixner 				*str++ = ' ';
22096ae6ea0SThomas Gleixner 			continue;
22196ae6ea0SThomas Gleixner 
22296ae6ea0SThomas Gleixner 		case 'p':
22396ae6ea0SThomas Gleixner 			if (field_width == -1) {
22496ae6ea0SThomas Gleixner 				field_width = 2 * sizeof(void *);
22596ae6ea0SThomas Gleixner 				flags |= ZEROPAD;
22696ae6ea0SThomas Gleixner 			}
22796ae6ea0SThomas Gleixner 			str = number(str,
22896ae6ea0SThomas Gleixner 				     (unsigned long)va_arg(args, void *), 16,
22996ae6ea0SThomas Gleixner 				     field_width, precision, flags);
23096ae6ea0SThomas Gleixner 			continue;
23196ae6ea0SThomas Gleixner 
23296ae6ea0SThomas Gleixner 		case 'n':
23396ae6ea0SThomas Gleixner 			if (qualifier == 'l') {
23496ae6ea0SThomas Gleixner 				long *ip = va_arg(args, long *);
23596ae6ea0SThomas Gleixner 				*ip = (str - buf);
23696ae6ea0SThomas Gleixner 			} else {
23796ae6ea0SThomas Gleixner 				int *ip = va_arg(args, int *);
23896ae6ea0SThomas Gleixner 				*ip = (str - buf);
23996ae6ea0SThomas Gleixner 			}
24096ae6ea0SThomas Gleixner 			continue;
24196ae6ea0SThomas Gleixner 
24296ae6ea0SThomas Gleixner 		case '%':
24396ae6ea0SThomas Gleixner 			*str++ = '%';
24496ae6ea0SThomas Gleixner 			continue;
24596ae6ea0SThomas Gleixner 
24696ae6ea0SThomas Gleixner 			/* integer number formats - set up the flags and "break" */
24796ae6ea0SThomas Gleixner 		case 'o':
24896ae6ea0SThomas Gleixner 			base = 8;
24996ae6ea0SThomas Gleixner 			break;
25096ae6ea0SThomas Gleixner 
25196ae6ea0SThomas Gleixner 		case 'x':
2529b706aeeSDenys Vlasenko 			flags |= SMALL;
2539b706aeeSDenys Vlasenko 		case 'X':
25496ae6ea0SThomas Gleixner 			base = 16;
25596ae6ea0SThomas Gleixner 			break;
25696ae6ea0SThomas Gleixner 
25796ae6ea0SThomas Gleixner 		case 'd':
25896ae6ea0SThomas Gleixner 		case 'i':
25996ae6ea0SThomas Gleixner 			flags |= SIGN;
26096ae6ea0SThomas Gleixner 		case 'u':
26196ae6ea0SThomas Gleixner 			break;
26296ae6ea0SThomas Gleixner 
26396ae6ea0SThomas Gleixner 		default:
26496ae6ea0SThomas Gleixner 			*str++ = '%';
26596ae6ea0SThomas Gleixner 			if (*fmt)
26696ae6ea0SThomas Gleixner 				*str++ = *fmt;
26796ae6ea0SThomas Gleixner 			else
26896ae6ea0SThomas Gleixner 				--fmt;
26996ae6ea0SThomas Gleixner 			continue;
27096ae6ea0SThomas Gleixner 		}
27196ae6ea0SThomas Gleixner 		if (qualifier == 'l')
27296ae6ea0SThomas Gleixner 			num = va_arg(args, unsigned long);
27396ae6ea0SThomas Gleixner 		else if (qualifier == 'h') {
27496ae6ea0SThomas Gleixner 			num = (unsigned short)va_arg(args, int);
27596ae6ea0SThomas Gleixner 			if (flags & SIGN)
27696ae6ea0SThomas Gleixner 				num = (short)num;
27796ae6ea0SThomas Gleixner 		} else if (flags & SIGN)
27896ae6ea0SThomas Gleixner 			num = va_arg(args, int);
27996ae6ea0SThomas Gleixner 		else
28096ae6ea0SThomas Gleixner 			num = va_arg(args, unsigned int);
28196ae6ea0SThomas Gleixner 		str = number(str, num, base, field_width, precision, flags);
28296ae6ea0SThomas Gleixner 	}
28396ae6ea0SThomas Gleixner 	*str = '\0';
28496ae6ea0SThomas Gleixner 	return str - buf;
28596ae6ea0SThomas Gleixner }
28696ae6ea0SThomas Gleixner 
28796ae6ea0SThomas Gleixner int sprintf(char *buf, const char *fmt, ...)
28896ae6ea0SThomas Gleixner {
28996ae6ea0SThomas Gleixner 	va_list args;
29096ae6ea0SThomas Gleixner 	int i;
29196ae6ea0SThomas Gleixner 
29296ae6ea0SThomas Gleixner 	va_start(args, fmt);
29396ae6ea0SThomas Gleixner 	i = vsprintf(buf, fmt, args);
29496ae6ea0SThomas Gleixner 	va_end(args);
29596ae6ea0SThomas Gleixner 	return i;
29696ae6ea0SThomas Gleixner }
29796ae6ea0SThomas Gleixner 
29896ae6ea0SThomas Gleixner int printf(const char *fmt, ...)
29996ae6ea0SThomas Gleixner {
30096ae6ea0SThomas Gleixner 	char printf_buf[1024];
30196ae6ea0SThomas Gleixner 	va_list args;
30296ae6ea0SThomas Gleixner 	int printed;
30396ae6ea0SThomas Gleixner 
30496ae6ea0SThomas Gleixner 	va_start(args, fmt);
30596ae6ea0SThomas Gleixner 	printed = vsprintf(printf_buf, fmt, args);
30696ae6ea0SThomas Gleixner 	va_end(args);
30796ae6ea0SThomas Gleixner 
30896ae6ea0SThomas Gleixner 	puts(printf_buf);
30996ae6ea0SThomas Gleixner 
31096ae6ea0SThomas Gleixner 	return printed;
31196ae6ea0SThomas Gleixner }
312