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 count += unsigned_num_print(unum, 10, 138 padc, padn, uppercase); 139 break; 140 case 'c': 141 (void)putchar(va_arg(args, int)); 142 count++; 143 break; 144 case 's': 145 str = va_arg(args, const char *); 146 count += string_print(str); 147 break; 148 case 'p': 149 unum = (uintptr_t)va_arg(args, void *); 150 if (unum > 0U) { 151 count += string_print("0x"); 152 padn -= 2; 153 } 154 155 count += unsigned_num_print(unum, 16, 156 padc, padn, uppercase); 157 break; 158 case 'X': 159 uppercase = true; 160 // fall through 161 case 'x': 162 unum = get_unum_va_args(args, l_count); 163 count += unsigned_num_print(unum, 16, 164 padc, padn, uppercase); 165 break; 166 case 'z': 167 if (sizeof(size_t) == 8U) 168 l_count = 2; 169 170 fmt++; 171 goto loop; 172 case 'l': 173 l_count++; 174 fmt++; 175 goto loop; 176 case 'u': 177 unum = get_unum_va_args(args, l_count); 178 count += unsigned_num_print(unum, 10, 179 padc, padn, uppercase); 180 break; 181 case '0': 182 padc = '0'; 183 padn = 0; 184 fmt++; 185 186 for (;;) { 187 char ch = *fmt; 188 if ((ch < '0') || (ch > '9')) { 189 goto loop; 190 } 191 padn = (padn * 10) + (ch - '0'); 192 fmt++; 193 } 194 assert(0); /* Unreachable */ 195 case '1': 196 case '2': 197 case '3': 198 case '4': 199 case '5': 200 case '6': 201 case '7': 202 case '8': 203 case '9': 204 padc = ' '; 205 padn = 0; 206 207 for (;;) { 208 char ch = *fmt; 209 if ((ch < '0') || (ch > '9')) { 210 goto loop; 211 } 212 padn = (padn * 10) + (ch - '0'); 213 fmt++; 214 } 215 assert(0); /* Unreachable */ 216 default: 217 /* Exit on any other format specifier */ 218 return -1; 219 } 220 fmt++; 221 continue; 222 } 223 (void)putchar(*fmt); 224 fmt++; 225 count++; 226 } 227 228 return count; 229 } 230 231 int printf(const char *fmt, ...) 232 { 233 int count; 234 va_list va; 235 236 va_start(va, fmt); 237 count = vprintf(fmt, va); 238 va_end(va); 239 240 return count; 241 } 242