1/* 2 * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) 3 * 4 * SPDX-License-Identifier: GPL-2.0-only 5 */ 6 7#include <printf.h> 8#include <types.h> 9#include <vargs.h> 10#include <elfloader_common.h> 11 12/* 13 * Maximum space needed to print an integer in any base. 14 * 15 * We set this to log_2(2**64) + 1 + safety margin ~= 80. 16 */ 17#define MAX_INT_BUFF_SIZE 80 18 19/* 20 * Function to process a simple character. "payload" may point 21 * to arbitrary state needed by the "write_char" function. 22 */ 23typedef void write_char_fn(void *payload, int c); 24 25/* Write a NUL-terminated string to the given 'write_char' function. */ 26static void write_string(write_char_fn write_char, void *payload, const char *str) 27{ 28 int i; 29 for (i = 0; str[i] != 0; i++) { 30 write_char(payload, str[i]); 31 } 32} 33 34/* 35 * Write the given unsigned number "n" to the given write_char function. 36 * 37 * We only support bases up to 16. 38 */ 39static void write_num(write_char_fn write_char, void *payload, 40 int base, unsigned long n) 41{ 42 static const char hex[] = "0123456789abcdef"; 43 char buff[MAX_INT_BUFF_SIZE]; 44 int k = MAX_INT_BUFF_SIZE - 1; 45 46 /* Special case for "0". */ 47 if (n == 0) { 48 write_string(write_char, payload, "0"); 49 return; 50 } 51 52 /* NUL-terminate. */ 53 buff[k--] = 0; 54 55 /* Generate the number. */ 56 while (n > 0) { 57 buff[k] = hex[n % base]; 58 n /= base; 59 k--; 60 } 61 62 /* Print the number. */ 63 write_string(write_char, payload, &buff[k + 1]); 64} 65 66/* 67 * Print a printf-style string to the given write_char function. 68 */ 69static void vxprintf(write_char_fn write_char, void *payload, 70 const char *format, va_list args) 71{ 72 int d, i; 73 char c, *s; 74 unsigned long p, ul; 75 int escape_mode = 0; 76 77 /* Iterate over the format list. */ 78 for (i = 0; format[i] != 0; i++) { 79 /* Handle simple characters. */ 80 if (!escape_mode && format[i] != '%') { 81 write_char(payload, format[i]); 82 continue; 83 } 84 85 /* Handle the percent escape character. */ 86 if (format[i] == '%') { 87 if (!escape_mode) { 88 /* Entering escape mode. */ 89 escape_mode = 1; 90 } else { 91 /* Already in escape mode; print a percent. */ 92 write_char(payload, format[i]); 93 escape_mode = 0; 94 } 95 continue; 96 } 97 98 /* Handle the modifier. */ 99 switch (format[i]) { 100 /* Ignore printf modifiers we don't support. */ 101 case '0': 102 case '1': 103 case '2': 104 case '3': 105 case '4': 106 case '5': 107 case '6': 108 case '7': 109 case '8': 110 case '9': 111 case '-': 112 case '.': 113 break; 114 115 /* String. */ 116 case 's': 117 s = va_arg(args, char *); 118 write_string(write_char, payload, s); 119 escape_mode = 0; 120 break; 121 122 /* Pointers. */ 123 case 'p': 124 p = va_arg(args, unsigned long); 125 write_num(write_char, payload, 16, p); 126 escape_mode = 0; 127 break; 128 129 /* Hex number. */ 130 case 'x': 131 d = va_arg(args, int); 132 write_num(write_char, payload, 16, d); 133 escape_mode = 0; 134 break; 135 136 /* Decimal number. */ 137 case 'd': 138 case 'u': 139 d = va_arg(args, int); 140 write_num(write_char, payload, 10, d); 141 escape_mode = 0; 142 break; 143 144 /* Character. */ 145 case 'c': 146 c = va_arg(args, int); 147 write_char(payload, c); 148 escape_mode = 0; 149 break; 150 151 /* Long number. */ 152 case 'l': 153 switch (format[++i]) { 154 case 'u': 155 ul = va_arg(args, unsigned long); 156 write_num(write_char, payload, 10, ul); 157 break; 158 159 case 'x': 160 ul = va_arg(args, unsigned long); 161 write_num(write_char, payload, 16, ul); 162 break; 163 164 default: 165 write_char(payload, '?'); 166 } 167 escape_mode = 0; 168 break; 169 170 /* Unknown. */ 171 default: 172 write_char(payload, '?'); 173 escape_mode = 0; 174 break; 175 } 176 } 177} 178 179/* 180 * Simple printf/puts implementation. 181 */ 182 183static void arch_write_char(void *num_chars_printed_ptr, int c) 184{ 185 int *num_chars_printed = (int *)num_chars_printed_ptr; 186 187 /* For now, console output goes into a UART on every platform eventually 188 * and we write a '\r' (CR) before every '\n' (LF) unconditinally. If there 189 * will even be a console that works differently, we can still add a 190 * configuration flag that allows disabling this feature. 191 */ 192 if (c == '\n') { 193 /* TODO: There is no "(*num_chars_printed)++;" here, as the CR char has 194 * never been counted by any platform specific implementations in 195 * the past. For now the behavior is kept, but it seem quite 196 * wrong to hide this. Check if any code depends really on this 197 * and consider counting the CR char also. 198 */ 199 plat_console_putchar('\r'); 200 } 201 202 (*num_chars_printed)++; 203 plat_console_putchar(c); 204} 205 206int printf(const char *format, ...) 207{ 208 int n = 0; 209 va_list args; 210 va_start(args, format); 211 vxprintf(arch_write_char, &n, format, args); 212 va_end(args); 213 return n; 214} 215 216int puts(const char *str) 217{ 218 int n = 0; 219 write_string(arch_write_char, &n, str); 220 arch_write_char(&n, '\n'); 221 return n; 222} 223 224/* 225 * Simple sprintf implementation. 226 */ 227 228struct sprintf_payload { 229 char *buff; 230 int n; 231}; 232 233static void sprintf_write_char(void *payload, int c) 234{ 235 struct sprintf_payload *p = (struct sprintf_payload *)payload; 236 p->buff[p->n] = c; 237 p->n++; 238} 239 240int sprintf(char *buff, const char *format, ...) 241{ 242 struct sprintf_payload p = {buff, 0}; 243 va_list args; 244 va_start(args, format); 245 vxprintf(sprintf_write_char, &p, format, args); 246 va_end(args); 247 return p.n; 248} 249