blob: 198295a7b16f3c270663551d50bc8af8387cb99e [file] [log] [blame] [edit]
/*
* Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: GPL-2.0-only
*/
#include <printf.h>
#include <types.h>
#include <vargs.h>
#include <elfloader_common.h>
/*
* Maximum space needed to print an integer in any base.
*
* We set this to log_2(2**64) + 1 + safety margin ~= 80.
*/
#define MAX_INT_BUFF_SIZE 80
/*
* Function to process a simple character. "payload" may point
* to arbitrary state needed by the "write_char" function.
*/
typedef void write_char_fn(
void *payload,
int c);
/* Write a NUL-terminated string to the given 'write_char' function. */
static void write_string(
write_char_fn write_char,
void *payload,
const char *str)
{
for (unsigned int i = 0; str[i] != 0; i++) {
write_char(payload, str[i]);
}
}
/*
* Write the given unsigned number "n" to the given write_char function.
*
* We only support bases up to 16.
*/
static void write_num(
write_char_fn write_char,
void *payload,
unsigned int base,
uintmax_t n)
{
static const char hex[] = "0123456789abcdef";
char buff[MAX_INT_BUFF_SIZE];
unsigned int k = MAX_INT_BUFF_SIZE - 1;
/* Special case for "0". */
if (n == 0) {
write_string(write_char, payload, "0");
return;
}
/* NUL-terminate. */
buff[k--] = 0;
/* Generate the number. */
while (n > 0) {
buff[k] = hex[n % base];
n /= base;
k--;
}
/* Print the number. */
write_string(write_char, payload, &buff[k + 1]);
}
/*
* Print a printf-style string to the given write_char function.
*/
static void vxprintf(
write_char_fn write_char,
void *payload,
const char *format,
va_list args)
{
int escape_mode = 0;
/* Iterate over the format list. */
for (unsigned int i = 0; format[i] != 0; i++) {
/* Handle simple characters. */
if (!escape_mode && format[i] != '%') {
write_char(payload, format[i]);
continue;
}
/* Handle the percent escape character. */
if (format[i] == '%') {
if (!escape_mode) {
/* Entering escape mode. */
escape_mode = 1;
} else {
/* Already in escape mode; print a percent. */
write_char(payload, format[i]);
escape_mode = 0;
}
continue;
}
/* Handle the modifier. */
switch (format[i]) {
/* Ignore printf modifiers we don't support. */
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '-':
case '.':
break;
/* String. */
case 's':
write_string(write_char, payload, va_arg(args, char *));
escape_mode = 0;
break;
/* Pointers. */
case 'p':
write_num(write_char, payload, 16, (uintptr_t)va_arg(args, void *));
escape_mode = 0;
break;
/* Hex number. */
case 'x':
write_num(write_char, payload, 16, va_arg(args, int));
escape_mode = 0;
break;
/* Decimal number. */
case 'd':
case 'u':
write_num(write_char, payload, 10, va_arg(args, int));
escape_mode = 0;
break;
/* Character. */
case 'c':
write_char(payload, va_arg(args, int));
escape_mode = 0;
break;
/* size_t number. */
case 'z':
switch (format[++i]) {
case 'd':
case 'u':
write_num(write_char, payload, 10,
va_arg(args, size_t));
break;
case 'x':
write_num(write_char, payload, 16,
va_arg(args, size_t));
break;
default:
write_char(payload, '?');
}
escape_mode = 0;
break;
/* Long number. */
case 'l':
switch (format[++i]) {
case 'd':
case 'u':
write_num(write_char, payload, 10,
va_arg(args, unsigned long));
break;
case 'x':
write_num(write_char, payload, 16,
va_arg(args, unsigned long));
break;
/* Long Long number. */
case 'l':
switch (format[++i]) {
case 'd':
case 'u':
write_num(write_char, payload, 10,
va_arg(args, unsigned long long));
break;
case 'x':
write_num(write_char, payload, 16,
va_arg(args, unsigned long long));
break;
default:
write_char(payload, '?');
}
break;
default:
write_char(payload, '?');
}
escape_mode = 0;
break;
/* Unknown. */
default:
write_char(payload, '?');
escape_mode = 0;
break;
}
}
}
/*
* Simple printf/puts implementation.
*/
typedef struct {
unsigned int cnt;
} arch_write_char_ctx_t;
static void arch_write_char(
void *payload,
int c)
{
arch_write_char_ctx_t *ctx = payload;
plat_console_putchar(c);
/* do do not really know what plat_console_putchar() does effectively, all
* we do know is that we have given it one character.
*/
ctx->cnt++;
}
int printf(
const char *format,
...)
{
arch_write_char_ctx_t ctx = {0};
va_list args;
va_start(args, format);
vxprintf(arch_write_char, &ctx, format, args);
va_end(args);
return (int)ctx.cnt;
}
int puts(
const char *str)
{
arch_write_char_ctx_t ctx = {0};
write_string(arch_write_char, &ctx, str);
arch_write_char(&ctx, '\n');
return (int)ctx.cnt;
}
/*
* Simple sprintf implementation.
*/
struct sprintf_payload {
char *buff;
int n;
};
static void sprintf_write_char(
void *payload,
int c)
{
struct sprintf_payload *p = (struct sprintf_payload *)payload;
p->buff[p->n] = c;
p->n++;
}
int sprintf(
char *buff,
const char *format,
...)
{
struct sprintf_payload p = {buff, 0};
va_list args;
va_start(args, format);
vxprintf(sprintf_write_char, &p, format, args);
va_end(args);
return p.n;
}