1 /* 2 * Copyright (c) 2017-2022, 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 <stdint.h> 10 11 #include <common/debug.h> 12 #include <plat/common/platform.h> 13 14 #define CHECK_AND_PUT_CHAR(buf, size, chars_printed, ch) \ 15 do { \ 16 if ((chars_printed) < (size)) { \ 17 *(buf) = (ch); \ 18 (buf)++; \ 19 } \ 20 (chars_printed)++; \ 21 } while (false) 22 23 static void string_print(char **s, size_t n, size_t *chars_printed, 24 const char *str) 25 { 26 while (*str != '\0') { 27 CHECK_AND_PUT_CHAR(*s, n, *chars_printed, *str); 28 str++; 29 } 30 } 31 32 static void unsigned_num_print(char **s, size_t n, size_t *chars_printed, 33 unsigned long long int unum, 34 unsigned int radix, char padc, int padn, 35 bool capitalise) 36 { 37 /* Just need enough space to store 64 bit decimal integer */ 38 char num_buf[20]; 39 int i = 0; 40 int width; 41 unsigned int rem; 42 char ascii_a = capitalise ? 'A' : 'a'; 43 44 if (radix < 10) { 45 ERROR("snprintf: unsupported radix '%u'.", radix); 46 plat_panic_handler(); 47 assert(0); /* Unreachable */ 48 } 49 50 do { 51 rem = unum % radix; 52 if (rem < 10U) { 53 num_buf[i] = '0' + rem; 54 } else { 55 num_buf[i] = ascii_a + (rem - 10U); 56 } 57 i++; 58 unum /= radix; 59 } while (unum > 0U); 60 61 width = i; 62 for (i = padn - width; i > 0; i--) { 63 CHECK_AND_PUT_CHAR(*s, n, *chars_printed, padc); 64 } 65 for (i = width; i > 0; i--) { 66 CHECK_AND_PUT_CHAR(*s, n, *chars_printed, num_buf[i - 1]); 67 } 68 for (i = width + padn; i < 0; i++) { 69 CHECK_AND_PUT_CHAR(*s, n, *chars_printed, padc); 70 } 71 } 72 73 /******************************************************************* 74 * Reduced vsnprintf to be used for Trusted firmware. 75 * The following type specifiers are supported: 76 * 77 * %x (or %X) - hexadecimal format 78 * %d or %i - signed decimal format 79 * %s - string format 80 * %u - unsigned decimal format 81 * %p - pointer format 82 * 83 * The following padding specifiers are supported by this print 84 * %0NN - Left-pad the number with 0s (NN is a decimal number) 85 * %NN - Left-pad the number or string with spaces (NN is a decimal number) 86 * %-NN - Right-pad the number or string with spaces (NN is a decimal number) 87 * 88 * The function panics on all other formats specifiers. 89 * 90 * It returns the number of characters that would be written if the 91 * buffer was big enough. If it returns a value lower than n, the 92 * whole string has been written. 93 *******************************************************************/ 94 int vsnprintf(char *s, size_t n, const char *fmt, va_list args) 95 { 96 int num; 97 unsigned long long int unum; 98 char *str; 99 char padc; /* Padding character */ 100 int padn; /* Number of characters to pad */ 101 bool left; 102 bool capitalise; 103 size_t chars_printed = 0U; 104 105 if (n == 0U) { 106 /* There isn't space for anything. */ 107 } else if (n == 1U) { 108 /* Buffer is too small to actually write anything else. */ 109 *s = '\0'; 110 n = 0U; 111 } else { 112 /* Reserve space for the terminator character. */ 113 n--; 114 } 115 116 while (*fmt != '\0') { 117 left = false; 118 padc ='\0'; 119 padn = 0; 120 capitalise = false; 121 122 if (*fmt == '%') { 123 fmt++; 124 /* Check the format specifier. */ 125 loop: 126 switch (*fmt) { 127 case '%': 128 CHECK_AND_PUT_CHAR(s, n, chars_printed, '%'); 129 break; 130 case '0': 131 case '1': 132 case '2': 133 case '3': 134 case '4': 135 case '5': 136 case '6': 137 case '7': 138 case '8': 139 case '9': 140 padc = (*fmt == '0') ? '0' : ' '; 141 for (padn = 0; *fmt >= '0' && *fmt <= '9'; fmt++) { 142 padn = (padn * 10) + (*fmt - '0'); 143 } 144 if (left) { 145 padn = -padn; 146 } 147 goto loop; 148 case '-': 149 left = true; 150 fmt++; 151 goto loop; 152 153 case 'i': 154 case 'd': 155 num = va_arg(args, int); 156 157 if (num < 0) { 158 CHECK_AND_PUT_CHAR(s, n, chars_printed, 159 '-'); 160 unum = (unsigned int)-num; 161 } else { 162 unum = (unsigned int)num; 163 } 164 165 unsigned_num_print(&s, n, &chars_printed, 166 unum, 10, padc, padn, false); 167 break; 168 case 's': 169 str = va_arg(args, char *); 170 string_print(&s, n, &chars_printed, str); 171 break; 172 case 'u': 173 unum = va_arg(args, unsigned int); 174 unsigned_num_print(&s, n, &chars_printed, 175 unum, 10, padc, padn, false); 176 break; 177 case 'p': 178 unum = (uintptr_t)va_arg(args, void *); 179 if (unum > 0U) { 180 string_print(&s, n, &chars_printed, "0x"); 181 padn -= 2; 182 } 183 unsigned_num_print(&s, n, &chars_printed, 184 unum, 16, padc, padn, false); 185 break; 186 case 'X': 187 capitalise = true; 188 case 'x': 189 unum = va_arg(args, unsigned int); 190 unsigned_num_print(&s, n, &chars_printed, 191 unum, 16, padc, padn, 192 capitalise); 193 break; 194 195 default: 196 /* Panic on any other format specifier. */ 197 ERROR("snprintf: specifier with ASCII code '%d' not supported.", 198 *fmt); 199 plat_panic_handler(); 200 assert(0); /* Unreachable */ 201 } 202 fmt++; 203 continue; 204 } 205 206 CHECK_AND_PUT_CHAR(s, n, chars_printed, *fmt); 207 208 fmt++; 209 } 210 211 if (n > 0U) { 212 *s = '\0'; 213 } 214 215 return (int)chars_printed; 216 } 217 218 /******************************************************************* 219 * Reduced snprintf to be used for Trusted firmware. 220 * The following type specifiers are supported: 221 * 222 * %x (or %X) - hexadecimal format 223 * %d or %i - signed decimal format 224 * %s - string format 225 * %u - unsigned decimal format 226 * %p - pointer format 227 * 228 * The following padding specifiers are supported by this print 229 * %0NN - Left-pad the number with 0s (NN is a decimal number) 230 * %NN - Left-pad the number or string with spaces (NN is a decimal number) 231 * %-NN - Right-pad the number or string with spaces (NN is a decimal number) 232 * 233 * The function panics on all other formats specifiers. 234 * 235 * It returns the number of characters that would be written if the 236 * buffer was big enough. If it returns a value lower than n, the 237 * whole string has been written. 238 *******************************************************************/ 239 int snprintf(char *s, size_t n, const char *fmt, ...) 240 { 241 int count; 242 va_list all_args; 243 244 va_start(all_args, fmt); 245 count = vsnprintf(s, n, fmt, all_args); 246 va_end(all_args); 247 248 return count; 249 } 250