Skip to content

Commit ce5e3f9

Browse files
nivedita76ardbiesheuvel
authored andcommitted
efi/printf: Add 64-bit and 8-bit integer support
Support 'll' qualifier for long long by copying the decimal printing code from lib/vsprintf.c. For simplicity, the 32-bit code is used on 64-bit architectures as well. Support 'hh' qualifier for signed/unsigned char type integers. Signed-off-by: Arvind Sankar <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Ard Biesheuvel <[email protected]>
1 parent 29a2806 commit ce5e3f9

File tree

1 file changed

+143
-27
lines changed

1 file changed

+143
-27
lines changed

drivers/firmware/efi/libstub/vsprintf.c

Lines changed: 143 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,7 @@
77
* ----------------------------------------------------------------------- */
88

99
/*
10-
* Oh, it's a waste of space, but oh-so-yummy for debugging. This
11-
* version of printf() does not include 64-bit support. "Live with
12-
* it."
13-
*
10+
* Oh, it's a waste of space, but oh-so-yummy for debugging.
1411
*/
1512

1613
#include <stdarg.h>
@@ -28,6 +25,86 @@ static int skip_atoi(const char **s)
2825
return i;
2926
}
3027

28+
/*
29+
* put_dec_full4 handles numbers in the range 0 <= r < 10000.
30+
* The multiplier 0xccd is round(2^15/10), and the approximation
31+
* r/10 == (r * 0xccd) >> 15 is exact for all r < 16389.
32+
*/
33+
static
34+
void put_dec_full4(char *buf, unsigned int r)
35+
{
36+
int i;
37+
38+
for (i = 0; i < 3; i++) {
39+
unsigned int q = (r * 0xccd) >> 15;
40+
*buf++ = '0' + (r - q * 10);
41+
r = q;
42+
}
43+
*buf++ = '0' + r;
44+
}
45+
46+
/* put_dec is copied from lib/vsprintf.c with small modifications */
47+
48+
/*
49+
* Call put_dec_full4 on x % 10000, return x / 10000.
50+
* The approximation x/10000 == (x * 0x346DC5D7) >> 43
51+
* holds for all x < 1,128,869,999. The largest value this
52+
* helper will ever be asked to convert is 1,125,520,955.
53+
* (second call in the put_dec code, assuming n is all-ones).
54+
*/
55+
static
56+
unsigned int put_dec_helper4(char *buf, unsigned int x)
57+
{
58+
unsigned int q = (x * 0x346DC5D7ULL) >> 43;
59+
60+
put_dec_full4(buf, x - q * 10000);
61+
return q;
62+
}
63+
64+
/* Based on code by Douglas W. Jones found at
65+
* <http://www.cs.uiowa.edu/~jones/bcd/decimal.html#sixtyfour>
66+
* (with permission from the author).
67+
* Performs no 64-bit division and hence should be fast on 32-bit machines.
68+
*/
69+
static
70+
int put_dec(char *buf, unsigned long long n)
71+
{
72+
unsigned int d3, d2, d1, q, h;
73+
char *p = buf;
74+
75+
d1 = ((unsigned int)n >> 16); /* implicit "& 0xffff" */
76+
h = (n >> 32);
77+
d2 = (h ) & 0xffff;
78+
d3 = (h >> 16); /* implicit "& 0xffff" */
79+
80+
/* n = 2^48 d3 + 2^32 d2 + 2^16 d1 + d0
81+
= 281_4749_7671_0656 d3 + 42_9496_7296 d2 + 6_5536 d1 + d0 */
82+
q = 656 * d3 + 7296 * d2 + 5536 * d1 + ((unsigned int)n & 0xffff);
83+
q = put_dec_helper4(p, q);
84+
p += 4;
85+
86+
q += 7671 * d3 + 9496 * d2 + 6 * d1;
87+
q = put_dec_helper4(p, q);
88+
p += 4;
89+
90+
q += 4749 * d3 + 42 * d2;
91+
q = put_dec_helper4(p, q);
92+
p += 4;
93+
94+
q += 281 * d3;
95+
q = put_dec_helper4(p, q);
96+
p += 4;
97+
98+
put_dec_full4(p, q);
99+
p += 4;
100+
101+
/* strip off the extra 0's we printed */
102+
while (p > buf && p[-1] == '0')
103+
--p;
104+
105+
return p - buf;
106+
}
107+
31108
#define ZEROPAD 1 /* pad with zero */
32109
#define SIGN 2 /* unsigned/signed long */
33110
#define PLUS 4 /* show plus */
@@ -36,13 +113,7 @@ static int skip_atoi(const char **s)
36113
#define SMALL 32 /* Must be 32 == 0x20 */
37114
#define SPECIAL 64 /* 0x */
38115

39-
#define __do_div(n, base) ({ \
40-
int __res; \
41-
__res = ((unsigned long) n) % (unsigned) base; \
42-
n = ((unsigned long) n) / (unsigned) base; \
43-
__res; })
44-
45-
static char *number(char *str, long num, int base, int size, int precision,
116+
static char *number(char *str, long long num, int base, int size, int precision,
46117
int type)
47118
{
48119
/* we are called with base 8, 10 or 16, only, thus don't need "G..." */
@@ -57,8 +128,6 @@ static char *number(char *str, long num, int base, int size, int precision,
57128
locase = (type & SMALL);
58129
if (type & LEFT)
59130
type &= ~ZEROPAD;
60-
if (base < 2 || base > 16)
61-
return NULL;
62131
c = (type & ZEROPAD) ? '0' : ' ';
63132
sign = 0;
64133
if (type & SIGN) {
@@ -83,9 +152,28 @@ static char *number(char *str, long num, int base, int size, int precision,
83152
i = 0;
84153
if (num == 0)
85154
tmp[i++] = '0';
86-
else
87-
while (num != 0)
88-
tmp[i++] = (digits[__do_div(num, base)] | locase);
155+
else {
156+
switch (base) {
157+
case 10:
158+
i += put_dec(&tmp[i], num);
159+
break;
160+
case 8:
161+
while (num != 0) {
162+
tmp[i++] = '0' + (num & 07);
163+
num = (unsigned long long)num >> 3;
164+
}
165+
break;
166+
case 16:
167+
while (num != 0) {
168+
tmp[i++] = digits[num & 0xf] | locase;
169+
num = (unsigned long long)num >> 4;
170+
}
171+
break;
172+
default:
173+
unreachable();
174+
}
175+
}
176+
89177
if (i > precision)
90178
precision = i;
91179
size -= precision;
@@ -117,7 +205,7 @@ static char *number(char *str, long num, int base, int size, int precision,
117205
int vsprintf(char *buf, const char *fmt, va_list args)
118206
{
119207
int len;
120-
unsigned long num;
208+
unsigned long long num;
121209
int i, base;
122210
char *str;
123211
const char *s;
@@ -127,7 +215,7 @@ int vsprintf(char *buf, const char *fmt, va_list args)
127215
int field_width; /* width of output field */
128216
int precision; /* min. # of digits for integers; max
129217
number of chars for from string */
130-
int qualifier; /* 'h' or 'l' for integer fields */
218+
int qualifier; /* 'h', 'hh', 'l' or 'll' for integer fields */
131219

132220
for (str = buf; *fmt; ++fmt) {
133221
if (*fmt != '%') {
@@ -191,6 +279,10 @@ int vsprintf(char *buf, const char *fmt, va_list args)
191279
if (*fmt == 'h' || *fmt == 'l') {
192280
qualifier = *fmt;
193281
++fmt;
282+
if (qualifier == *fmt) {
283+
qualifier -= 'a'-'A';
284+
++fmt;
285+
}
194286
}
195287

196288
/* default base */
@@ -260,16 +352,40 @@ int vsprintf(char *buf, const char *fmt, va_list args)
260352
--fmt;
261353
continue;
262354
}
263-
if (qualifier == 'l') {
264-
num = va_arg(args, unsigned long);
265-
} else if (qualifier == 'h') {
266-
num = (unsigned short)va_arg(args, int);
267-
if (flags & SIGN)
268-
num = (short)num;
269-
} else if (flags & SIGN) {
270-
num = va_arg(args, int);
355+
if (flags & SIGN) {
356+
switch (qualifier) {
357+
case 'L':
358+
num = va_arg(args, long long);
359+
break;
360+
case 'l':
361+
num = va_arg(args, long);
362+
break;
363+
case 'h':
364+
num = (short)va_arg(args, int);
365+
break;
366+
case 'H':
367+
num = (signed char)va_arg(args, int);
368+
break;
369+
default:
370+
num = va_arg(args, int);
371+
}
271372
} else {
272-
num = va_arg(args, unsigned int);
373+
switch (qualifier) {
374+
case 'L':
375+
num = va_arg(args, unsigned long long);
376+
break;
377+
case 'l':
378+
num = va_arg(args, unsigned long);
379+
break;
380+
case 'h':
381+
num = (unsigned short)va_arg(args, int);
382+
break;
383+
case 'H':
384+
num = (unsigned char)va_arg(args, int);
385+
break;
386+
default:
387+
num = va_arg(args, unsigned int);
388+
}
273389
}
274390
str = number(str, num, base, field_width, precision, flags);
275391
}

0 commit comments

Comments
 (0)