printk: make it 64-bit compatible

On 64-bit systems the most notable difference is due to longs and
pointers being 64-bit wide. Therefore there must be a distinction
between ints and longs. Similar to the prf.c case, this patch properly
implements the h, hh, l, ll and z length modifiers as well as some small
cleanups.

Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
This commit is contained in:
Nicolas Pitre 2019-06-13 22:44:07 -04:00 committed by Andrew Boie
commit 2b32059a61
3 changed files with 91 additions and 58 deletions

View file

@ -33,9 +33,9 @@ extern "C" {
* - character: \%c * - character: \%c
* - percent: \%\% * - percent: \%\%
* *
* Field width (with or without leading zeroes) are supported. * Field width (with or without leading zeroes) is supported.
* Length attributes such as 'h' and 'l' are supported. However, * Length attributes h, hh, l, ll and z are supported. However, integral
* integral values with %lld and %lli are only printed if they fit in 32 bits, * values with %lld and %lli are only printed if they fit in a long
* otherwise 'ERR' is printed. Full 64-bit values may be printed with %llx. * otherwise 'ERR' is printed. Full 64-bit values may be printed with %llx.
* Flags and precision attributes are not supported. * Flags and precision attributes are not supported.
* *

View file

@ -19,6 +19,7 @@
#include <linker/sections.h> #include <linker/sections.h>
#include <syscall_handler.h> #include <syscall_handler.h>
#include <logging/log.h> #include <logging/log.h>
#include <sys/types.h>
typedef int (*out_func_t)(int c, void *ctx); typedef int (*out_func_t)(int c, void *ctx);
@ -103,7 +104,7 @@ void z_vprintk(out_func_t out, void *ctx, const char *fmt, va_list ap)
int might_format = 0; /* 1 if encountered a '%' */ int might_format = 0; /* 1 if encountered a '%' */
enum pad_type padding = PAD_NONE; enum pad_type padding = PAD_NONE;
int min_width = -1; int min_width = -1;
int long_ctr = 0; char length_mod = 0;
/* fmt has already been adjusted if needed */ /* fmt has already been adjusted if needed */
@ -115,7 +116,7 @@ void z_vprintk(out_func_t out, void *ctx, const char *fmt, va_list ap)
might_format = 1; might_format = 1;
min_width = -1; min_width = -1;
padding = PAD_NONE; padding = PAD_NONE;
long_ctr = 0; length_mod = 0;
} }
} else { } else {
switch (*fmt) { switch (*fmt) {
@ -148,34 +149,39 @@ void z_vprintk(out_func_t out, void *ctx, const char *fmt, va_list ap)
padding = PAD_SPACE_BEFORE; padding = PAD_SPACE_BEFORE;
} }
goto still_might_format; goto still_might_format;
case 'l':
long_ctr++;
/* Fall through */
case 'z':
case 'h': case 'h':
/* FIXME: do nothing for these modifiers */ case 'l':
case 'z':
if (*fmt == 'h' && length_mod == 'h') {
length_mod = 'H';
} else if (*fmt == 'l' && length_mod == 'l') {
length_mod = 'L';
} else if (length_mod == 0) {
length_mod = *fmt;
} else {
out((int)'%', ctx);
out((int)*fmt, ctx);
break;
}
goto still_might_format; goto still_might_format;
case 'd': case 'd':
case 'i': { case 'i': {
s32_t d; long d;
if (long_ctr == 0) { if (length_mod == 'z') {
d = va_arg(ap, int); d = va_arg(ap, ssize_t);
} else if (long_ctr == 1) { } else if (length_mod == 'l') {
long ld = va_arg(ap, long); d = va_arg(ap, long);
if (ld > INT32_MAX || ld < INT32_MIN) { } else if (length_mod == 'L') {
print_err(out, ctx);
break;
}
d = (s32_t)ld;
} else {
long long lld = va_arg(ap, long long); long long lld = va_arg(ap, long long);
if (lld > INT32_MAX || if (lld > __LONG_MAX__ ||
lld < INT32_MIN) { lld < ~__LONG_MAX__) {
print_err(out, ctx); print_err(out, ctx);
break; break;
} }
d = (s32_t)lld; d = lld;
} else {
d = va_arg(ap, int);
} }
if (d < 0) { if (d < 0) {
@ -188,25 +194,22 @@ void z_vprintk(out_func_t out, void *ctx, const char *fmt, va_list ap)
break; break;
} }
case 'u': { case 'u': {
u32_t u; unsigned long u;
if (long_ctr == 0) { if (length_mod == 'z') {
u = va_arg(ap, unsigned int); u = va_arg(ap, size_t);
} else if (long_ctr == 1) { } else if (length_mod == 'l') {
long lu = va_arg(ap, unsigned long); u = va_arg(ap, unsigned long);
if (lu > INT32_MAX) { } else if (length_mod == 'L') {
print_err(out, ctx);
break;
}
u = (u32_t)lu;
} else {
unsigned long long llu = unsigned long long llu =
va_arg(ap, unsigned long long); va_arg(ap, unsigned long long);
if (llu > INT32_MAX) { if (llu > ~0UL) {
print_err(out, ctx); print_err(out, ctx);
break; break;
} }
u = (u32_t)llu; u = llu;
} else {
u = va_arg(ap, unsigned int);
} }
_printk_dec_ulong(out, ctx, u, padding, _printk_dec_ulong(out, ctx, u, padding,
@ -224,10 +227,14 @@ void z_vprintk(out_func_t out, void *ctx, const char *fmt, va_list ap)
case 'X': { case 'X': {
unsigned long long x; unsigned long long x;
if (long_ctr < 2) { if (*fmt == 'p') {
x = (uintptr_t)va_arg(ap, void *);
} else if (length_mod == 'l') {
x = va_arg(ap, unsigned long); x = va_arg(ap, unsigned long);
} else { } else if (length_mod == 'L') {
x = va_arg(ap, unsigned long long); x = va_arg(ap, unsigned long long);
} else {
x = va_arg(ap, unsigned int);
} }
_printk_hex_ulong(out, ctx, x, padding, _printk_hex_ulong(out, ctx, x, padding,
@ -363,12 +370,16 @@ Z_SYSCALL_HANDLER(k_str_out, c, n)
* printf-like formatting is available. * printf-like formatting is available.
* *
* Available formatting: * Available formatting:
* - %x/%X: outputs a 32-bit number in ABCDWXYZ format. All eight digits * - %x/%X: outputs a number in hexadecimal format
* are printed: if less than 8 characters are needed, leading zeroes * - %s: outputs a null-terminated string
* are displayed. * - %p: pointer, same as %x with a 0x prefix
* - %s: output a null-terminated string * - %u: outputs a number in unsigned decimal format
* - %p: pointer, same as %x * - %d/%i: outputs a number in signed decimal format
* - %d/%i/%u: outputs a 32-bit number in unsigned decimal format. *
* Field width (with or without leading zeroes) is supported.
* Length attributes h, hh, l, ll and z are supported. However, integral
* values with %lld and %lli are only printed if they fit in a long
* otherwise 'ERR' is printed. Full 64-bit values may be printed with %llx.
* *
* @param fmt formatted string to output * @param fmt formatted string to output
* *
@ -402,15 +413,17 @@ static void _printk_hex_ulong(out_func_t out, void *ctx,
enum pad_type padding, enum pad_type padding,
int min_width) int min_width)
{ {
int size = sizeof(num) * 2; int shift = sizeof(num) * 8;
int found_largest_digit = 0; int found_largest_digit = 0;
int remaining = 16; /* 16 digits max */ int remaining = 16; /* 16 digits max */
int digits = 0; int digits = 0;
char nibble;
for (; size != 0; size--) { while (shift >= 4) {
char nibble = (num >> ((size - 1) << 2) & 0xf); shift -= 4;
nibble = (num >> shift) & 0xf;
if (nibble != 0 || found_largest_digit != 0 || size == 1) { if (nibble != 0 || found_largest_digit != 0 || shift == 0) {
found_largest_digit = 1; found_largest_digit = 1;
nibble += nibble > 9 ? 87 : 48; nibble += nibble > 9 ? 87 : 48;
out((int)nibble, ctx); out((int)nibble, ctx);
@ -436,10 +449,10 @@ static void _printk_hex_ulong(out_func_t out, void *ctx,
} }
/** /**
* @brief Output an unsigned long (32-bit) in decimal format * @brief Output an unsigned long in decimal format
*
* Output an unsigned long on output installed by platform at init time.
* *
* Output an unsigned long on output installed by platform at init time. Only
* works with 32-bit values.
* @param num Number to output * @param num Number to output
* *
* @return N/A * @return N/A
@ -448,21 +461,25 @@ static void _printk_dec_ulong(out_func_t out, void *ctx,
const unsigned long num, enum pad_type padding, const unsigned long num, enum pad_type padding,
int min_width) int min_width)
{ {
unsigned long pos = 999999999; unsigned long pos = 1000000000;
unsigned long remainder = num; unsigned long remainder = num;
int found_largest_digit = 0; int found_largest_digit = 0;
int remaining = 10; /* 10 digits max */ int remaining = sizeof(long) * 5 / 2;
int digits = 1; int digits = 1;
if (sizeof(long) == 8) {
pos *= 10000000000;
}
/* make sure we don't skip if value is zero */ /* make sure we don't skip if value is zero */
if (min_width <= 0) { if (min_width <= 0) {
min_width = 1; min_width = 1;
} }
while (pos >= 9) { while (pos >= 10) {
if (found_largest_digit != 0 || remainder > pos) { if (found_largest_digit != 0 || remainder >= pos) {
found_largest_digit = 1; found_largest_digit = 1;
out((int)((remainder / (pos + 1)) + 48), ctx); out((int)(remainder / pos + 48), ctx);
digits++; digits++;
} else if (remaining <= min_width } else if (remaining <= min_width
&& padding < PAD_SPACE_AFTER) { && padding < PAD_SPACE_AFTER) {
@ -470,7 +487,7 @@ static void _printk_dec_ulong(out_func_t out, void *ctx,
digits++; digits++;
} }
remaining--; remaining--;
remainder %= (pos + 1); remainder %= pos;
pos /= 10; pos /= 10;
} }
out((int)(remainder + 48), ctx); out((int)(remainder + 48), ctx);

View file

@ -15,6 +15,8 @@ void __printk_hook_install(int (*fn)(int));
void *__printk_get_hook(void); void *__printk_get_hook(void);
int (*_old_char_out)(int); int (*_old_char_out)(int);
#ifndef CONFIG_64BIT
char *expected = "22 113 10000 32768 40000 22\n" char *expected = "22 113 10000 32768 40000 22\n"
"p 112 -10000 -32768 -40000 -22\n" "p 112 -10000 -32768 -40000 -22\n"
"0xcafebabe 0x0000beef\n" "0xcafebabe 0x0000beef\n"
@ -27,7 +29,21 @@ char *expected = "22 113 10000 32768 40000 22\n"
"255 42 abcdef 0x0000002a 42\n" "255 42 abcdef 0x0000002a 42\n"
"ERR -1 ERR ffffffffffffffff\n" "ERR -1 ERR ffffffffffffffff\n"
; ;
#else
char *expected = "22 113 10000 32768 40000 22\n"
"p 112 -10000 -32768 -40000 -22\n"
"0xcafebabe 0x0000beef\n"
"0x1 0x01 0x0001 0x00000001 0x0000000000000001\n"
"0x1 0x 1 0x 1 0x 1\n"
"42 42 0042 00000042\n"
"-42 -42 -042 -0000042\n"
"42 42 42 42\n"
"42 42 0042 00000042\n"
"255 42 abcdef 0x0000002a 42\n"
"68719476735 -1 18446744073709551615 ffffffffffffffff\n"
;
#endif
size_t stv = 22; size_t stv = 22;
unsigned char uc = 'q'; unsigned char uc = 'q';