1 /* 2 * Copyright (c) 2014-2023, Arm Limited and Contributors. All rights reserved. 3 * 4 * SPDX-License-Identifier: BSD-3-Clause 5 */ 6 7 #include <assert.h> 8 #include <stdarg.h> 9 #include <stdbool.h> 10 #include <stddef.h> 11 #include <stdint.h> 12 #include <stdio.h> 13 14 #define get_num_va_args(_args, _lcount) \ 15 (((_lcount) > 1) ? va_arg(_args, long long int) : \ 16 (((_lcount) == 1) ? va_arg(_args, long int) : \ 17 va_arg(_args, int))) 18 19 #define get_unum_va_args(_args, _lcount) \ 20 (((_lcount) > 1) ? va_arg(_args, unsigned long long int) : \ 21 (((_lcount) == 1) ? va_arg(_args, unsigned long int) : \ 22 va_arg(_args, unsigned int))) 23 24 static int string_print(const char *str) 25 { 26 int count = 0; 27 28 assert(str != NULL); 29 30 for ( ; *str != '\0'; str++) { 31 (void)putchar(*str); 32 count++; 33 } 34 35 return count; 36 } 37 38 static int unsigned_num_print(unsigned long long int unum, unsigned int radix, 39 char padc, int padn, bool uppercase) 40 { 41 /* Just need enough space to store 64 bit decimal integer */ 42 char num_buf[20]; 43 int i = 0, count = 0; 44 unsigned int rem; 45 46 /* num_buf is only large enough for radix >= 10 */ 47 if (radix < 10U) { 48 assert(0); 49 return 0; 50 } 51 52 do { 53 rem = (uint32_t)(unum % radix); 54 if (rem < 0xaU) { 55 num_buf[i] = '0' + rem; 56 } else if (uppercase) { 57 num_buf[i] = 'A' + (rem - 0xaU); 58 } else { 59 num_buf[i] = 'a' + (rem - 0xaU); 60 } 61 i++; 62 unum /= radix; 63 } while (unum > 0U); 64 65 if (padn > 0) { 66 while (i < padn) { 67 (void)putchar((int32_t)padc); 68 count++; 69 padn--; 70 } 71 } 72 73 while (--i >= 0) { 74 (void)putchar((int32_t)num_buf[i]); 75 count++; 76 } 77 78 return count; 79 } 80 81 /******************************************************************* 82 * Reduced format print for Trusted firmware. 83 * The following type specifiers are supported by this print 84 * %x - hexadecimal format 85 * %s - string format 86 * %d or %i - signed decimal format 87 * %c - character format 88 * %u - unsigned decimal format 89 * %p - pointer format 90 * 91 * The following length specifiers are supported by this print 92 * %l - long int (64-bit on AArch64) 93 * %ll - long long int (64-bit on AArch64) 94 * %z - size_t sized integer formats (64 bit on AArch64) 95 * 96 * The following padding specifiers are supported by this print 97 * %0NN - Left-pad the number with 0s (NN is a decimal number) 98 * %NN - Left-pad the number with spaces (NN is a decimal number) 99 * 100 * The print exits on all other formats specifiers other than valid 101 * combinations of the above specifiers. 102 *******************************************************************/ 103 int vprintf(const char *fmt, va_list args) 104 { 105 int l_count; 106 long long int num; 107 unsigned long long int unum; 108 const char *str; 109 char padc = '\0'; /* Padding character */ 110 int padn; /* Number of characters to pad */ 111 int count = 0; /* Number of printed characters */ 112 bool uppercase; /* Print characters in uppercase */ 113 114 while (*fmt != '\0') { 115 uppercase = false; 116 l_count = 0; 117 padn = 0; 118 119 if (*fmt == '%') { 120 fmt++; 121 /* Check the format specifier */ 122 loop: 123 switch (*fmt) { 124 case '%': 125 (void)putchar((int32_t)'%'); 126 break; 127 case 'i': /* Fall through to next one */ 128 case 'd': 129 num = get_num_va_args(args, l_count); 130 if (num < 0) { 131 (void)putchar((int32_t)'-'); 132 unum = (unsigned long long int)-num; 133 padn--; 134 } else { 135 unum = (unsigned long long int)num; 136 } 137 138 count += unsigned_num_print(unum, 10, 139 padc, padn, uppercase); 140 break; 141 case 'c': 142 (void)putchar(va_arg(args, int)); 143 count++; 144 break; 145 case 's': 146 str = va_arg(args, const char *); 147 count += string_print(str); 148 break; 149 case 'p': 150 unum = (uintptr_t)va_arg(args, void *); 151 if (unum > 0U) { 152 count += string_print("0x"); 153 padn -= 2; 154 } 155 156 count += unsigned_num_print(unum, 16, 157 padc, padn, uppercase); 158 break; 159 case 'X': 160 uppercase = true; 161 // fall through 162 case 'x': 163 unum = get_unum_va_args(args, l_count); 164 count += unsigned_num_print(unum, 16, 165 padc, padn, uppercase); 166 break; 167 case 'z': 168 if (sizeof(size_t) == 8U) { 169 l_count = 2; 170 } 171 172 fmt++; 173 goto loop; 174 case 'l': 175 l_count++; 176 fmt++; 177 goto loop; 178 case 'u': 179 unum = get_unum_va_args(args, l_count); 180 count += unsigned_num_print(unum, 10, 181 padc, padn, uppercase); 182 break; 183 case '0': 184 padc = '0'; 185 padn = 0; 186 fmt++; 187 188 for (;;) { 189 char ch = *fmt; 190 if ((ch < '0') || (ch > '9')) { 191 goto loop; 192 } 193 padn = (padn * 10) + (ch - '0'); 194 fmt++; 195 } 196 assert(0); /* Unreachable */ 197 case '1': 198 case '2': 199 case '3': 200 case '4': 201 case '5': 202 case '6': 203 case '7': 204 case '8': 205 case '9': 206 padc = ' '; 207 padn = 0; 208 209 for (;;) { 210 char ch = *fmt; 211 if ((ch < '0') || (ch > '9')) { 212 goto loop; 213 } 214 padn = (padn * 10) + (ch - '0'); 215 fmt++; 216 } 217 assert(0); /* Unreachable */ 218 default: 219 /* Exit on any other format specifier */ 220 return -1; 221 } 222 fmt++; 223 continue; 224 } 225 (void)putchar(*fmt); 226 fmt++; 227 count++; 228 } 229 230 return count; 231 } 232 233 int printf(const char *fmt, ...) 234 { 235 int count; 236 va_list va; 237 238 va_start(va, fmt); 239 count = vprintf(fmt, va); 240 va_end(va); 241 242 return count; 243 } 244