summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--common/printf.c110
-rw-r--r--common/vboot_hash.c20
-rw-r--r--include/printf.h64
3 files changed, 138 insertions, 56 deletions
diff --git a/common/printf.c b/common/printf.c
index d6b0298034..b356e0e58a 100644
--- a/common/printf.c
+++ b/common/printf.c
@@ -11,6 +11,13 @@
static const char error_str[] = "ERROR";
+#define MAX_FORMAT 1024 /* Maximum chars in a single format field */
+
+static int hexdigit(int c)
+{
+ return c > 9 ? (c + 'a' - 10) : (c + '0');
+}
+
int vfnprintf(int (*addchar)(void *context, int c), void *context,
const char *format, va_list args)
{
@@ -22,6 +29,7 @@ int vfnprintf(int (*addchar)(void *context, int c), void *context,
int is_left;
int pad_zero;
int pad_width;
+ int precision;
char *vstr;
int vlen;
@@ -62,38 +70,72 @@ int vfnprintf(int (*addchar)(void *context, int c), void *context,
/* Count padding length */
pad_width = 0;
- while (c >= '0' && c <= '9') {
- pad_width = (10 * pad_width) + c - '0';
+ if (c == '*') {
+ pad_width = va_arg(args, int);
c = *format++;
+ } else {
+ while (c >= '0' && c <= '9') {
+ pad_width = (10 * pad_width) + c - '0';
+ c = *format++;
+ }
}
- if (pad_width > 80) {
- /* Sanity check for width failed */
+ if (pad_width < 0 || pad_width > MAX_FORMAT) {
+ /* Sanity check for precision failed */
format = error_str;
continue;
}
+ /* Count precision */
+ precision = 0;
+ if (c == '.') {
+ c = *format++;
+ if (c == '*') {
+ precision = va_arg(args, int);
+ c = *format++;
+ } else {
+ while (c >= '0' && c <= '9') {
+ precision = (10 * precision) + c - '0';
+ c = *format++;
+ }
+ }
+ if (precision < 0 || precision > MAX_FORMAT) {
+ /* Sanity check for precision failed */
+ format = error_str;
+ continue;
+ }
+ }
+
if (c == 's') {
vstr = va_arg(args, char *);
if (vstr == NULL)
vstr = "(NULL)";
+ } else if (c == 'h') {
+ /* Hex dump output */
+ vstr = va_arg(args, char *);
+
+ if (!precision) {
+ /* Hex dump requires precision */
+ format = error_str;
+ continue;
+ }
+
+ for ( ; precision; precision--, vstr++) {
+ dropped_chars |=
+ addchar(context,
+ hexdigit((*vstr >> 4) & 0x0f));
+ dropped_chars |=
+ addchar(context,
+ hexdigit(*vstr & 0x0f));
+ }
+
+ continue;
} else {
uint64_t v;
int is_negative = 0;
int is_64bit = 0;
int base = 10;
- int fixed_point = 0;
-
- /* Handle fixed point numbers */
- if (c == '.') {
- c = *format++;
- if (c < '0' || c > '9') {
- format = error_str;
- continue;
- }
- fixed_point = c - '0';
- c = *format++;
- }
+ /* Handle length */
if (c == 'l') {
is_64bit = 1;
c = *format++;
@@ -103,7 +145,7 @@ int vfnprintf(int (*addchar)(void *context, int c), void *context,
if (c == 'T') {
v = get_time().val;
is_64bit = 1;
- fixed_point = 6;
+ precision = 6;
} else if (is_64bit) {
v = va_arg(args, uint64_t);
} else {
@@ -148,11 +190,20 @@ int vfnprintf(int (*addchar)(void *context, int c), void *context,
vstr = intbuf + sizeof(intbuf) - 1;
*(vstr) = '\0';
- /* Handle digits to right of decimal for fixed point
- * numbers. */
- for (vlen = 0; vlen < fixed_point; vlen++)
+ /*
+ * Fixed-point precision must fit in our buffer.
+ * Leave space for "0." and the terminating null.
+ */
+ if (precision > sizeof(intbuf) - 3)
+ precision = sizeof(intbuf) - 3;
+
+ /*
+ * Handle digits to right of decimal for fixed point
+ * numbers.
+ */
+ for (vlen = 0; vlen < precision; vlen++)
*(--vstr) = '0' + uint64divmod(&v, 10);
- if (fixed_point)
+ if (precision)
*(--vstr) = '.';
if (!v)
@@ -170,15 +221,30 @@ int vfnprintf(int (*addchar)(void *context, int c), void *context,
if (is_negative)
*(--vstr) = '-';
+
+ /*
+ * Precision field was interpreted by fixed-point
+ * logic, so clear it.
+ */
+ precision = 0;
}
/* Copy string (or stringified integer) */
vlen = strlen(vstr);
+
+ /* No padding strings to wider than the precision */
+ if (precision > 0 && pad_width > precision)
+ pad_width = precision;
+
+ /* If precision is zero, print everything */
+ if (!precision)
+ precision = MAX(vlen, pad_width);
+
while (vlen < pad_width && !is_left) {
dropped_chars |= addchar(context, pad_zero ? '0' : ' ');
vlen++;
}
- while (*vstr)
+ while (*vstr && --precision >= 0)
dropped_chars |= addchar(context, *vstr++);
while (vlen < pad_width && is_left) {
dropped_chars |= addchar(context, ' ');
diff --git a/common/vboot_hash.c b/common/vboot_hash.c
index 6f944622da..3362917e0d 100644
--- a/common/vboot_hash.c
+++ b/common/vboot_hash.c
@@ -143,14 +143,9 @@ void vboot_hash_task(void)
size);
curr_pos += size;
if (curr_pos >= data_size) {
- int i;
-
hash = SHA256_final(&ctx);
-
- CPRINTF("[%T hash done ");
- for (i = 0; i < SHA256_DIGEST_SIZE; i++)
- CPRINTF("%02x", hash[i]);
- CPUTS("]\n");
+ CPRINTF("[%T hash done %.*h]\n",
+ SHA256_DIGEST_SIZE, hash);
}
/*
@@ -196,14 +191,11 @@ static int command_hash(int argc, char **argv)
ccprintf("Offset: 0x%08x\n", data_offset);
ccprintf("Size: 0x%08x (%d)\n", data_size, data_size);
ccprintf("Digest: ");
- if (vboot_hash_in_progress()) {
+ if (vboot_hash_in_progress())
ccprintf("(in progress)\n");
- } else {
- int i;
- for (i = 0; i < SHA256_DIGEST_SIZE; i++)
- ccprintf("%02x", hash[i]);
- ccprintf("\n");
- }
+ else
+ ccprintf("%.*h\n", SHA256_DIGEST_SIZE, hash);
+
return EC_SUCCESS;
}
diff --git a/include/printf.h b/include/printf.h
index 66e8d0c8b5..fdc80c2943 100644
--- a/include/printf.h
+++ b/include/printf.h
@@ -11,31 +11,55 @@
#include <stdarg.h> /* For va_list */
#include "common.h"
-/* SUPPORTED FORMAT CODES:
- * char (%c)
- * string (%s)
- * native int (signed/unsigned) (%d / %u / %x / %X)
- * int32_t / uint32_t (%d / %x / %X)
- * int64_t / uint64_t (%ld / %lu / %lx / %lX)
- * pointer (%p)
- * And the following special format codes:
- * current time in sec (%T) - interpreted as "%.6T" for fixed-point format
- * including padding (%-5s, %8d, %08x, %016lx)
- *
- * Floating point output (%f / %g) is not supported, but there is a fixed-point
- * extension for integers; a padding option of .N (where N is a number) will
- * put a decimal before that many digits. For example, printing 123 with
- * format code %.6d will result in "0.000123". This is most useful for
- * printing times, voltages, and currents. */
-
-
-/* Print formatted output to a function, like vfprintf()
+/*
+ * Printf formatting: % [flags] [width] [.precision] [length] [type]
+ *
+ * Flags may be any/all of the following, and must occur in the following
+ * order if present:
+ * - '0' = prefixed with 0's instead of spaces (%08x)
+ * - '-' = left-justify instead of right-justify (%-5s)
+ *
+ * Width is the minimum output width, and may be:
+ * - A number ("0" - "255")
+ * - '*' = use next integer argument as width
+ *
+ * Precision must be preceded by a decimal point, and may be:
+ * - A number ("0" - "255")
+ * - '*' = use next integer argument as precision
+ *
+ * For integers, precision will put a decimal point before that many digits.
+ * So snprintf(buf, size, "%.6d", 123) sets buf="0.000123". This is most
+ * useful for printing times, voltages, and currents.
+ *
+ * Length may be:
+ * - 'l' = integer is 64-bit instead of native 32-bit
+ *
+ * Type may be:
+ * - 'c' - character
+ * - 's' - null-terminated ASCII string
+ * - 'h' - binary data, print as hex; precision is length of data in bytes.
+ * So "%.8h" prints 8 bytes of binary data
+ * - 'p' - pointer
+ * - 'd' - signed integer
+ * - 'u' - unsigned integer
+ * - 'x' - unsigned integer, print as lower-case hexadecimal
+ * - 'X' - unsigned integer, print as upper-case hexadecimal
+ * - 'b' - unsigned integer, print as binary
+ *
+ * Special format codes:
+ * - "%T" - current time in seconds - interpreted as "%.6T" for precision.
+ * This does NOT use up any arguments.
+ */
+
+/**
+ * Print formatted output to a function, like vfprintf()
*
* addchar() will be called for every character to be printed, with the context
* pointer passed to vfnprintf(). addchar() should return 0 if the character
* was accepted or non-zero if the character was dropped due to overflow.
*
- * Returns error if output was truncated. */
+ * Returns error if output was truncated.
+ */
int vfnprintf(int (*addchar)(void *context, int c), void *context,
const char *format, va_list args);