1 // SPDX-License-Identifier: GPL-2.0-only 2 #include "test_util.h" 3 #include "kvm_util.h" 4 #include "ucall_common.h" 5 6 #define APPEND_BUFFER_SAFE(str, end, v) \ 7 do { \ 8 GUEST_ASSERT(str < end); \ 9 *str++ = (v); \ 10 } while (0) 11 12 static int isdigit(int ch) 13 { 14 return (ch >= '0') && (ch <= '9'); 15 } 16 17 static int skip_atoi(const char **s) 18 { 19 int i = 0; 20 21 while (isdigit(**s)) 22 i = i * 10 + *((*s)++) - '0'; 23 return i; 24 } 25 26 #define ZEROPAD 1 /* pad with zero */ 27 #define SIGN 2 /* unsigned/signed long */ 28 #define PLUS 4 /* show plus */ 29 #define SPACE 8 /* space if plus */ 30 #define LEFT 16 /* left justified */ 31 #define SMALL 32 /* Must be 32 == 0x20 */ 32 #define SPECIAL 64 /* 0x */ 33 34 #define __do_div(n, base) \ 35 ({ \ 36 int __res; \ 37 \ 38 __res = ((uint64_t) n) % (uint32_t) base; \ 39 n = ((uint64_t) n) / (uint32_t) base; \ 40 __res; \ 41 }) 42 43 static char *number(char *str, const char *end, long num, int base, int size, 44 int precision, int type) 45 { 46 /* we are called with base 8, 10 or 16, only, thus don't need "G..." */ 47 static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */ 48 49 char tmp[66]; 50 char c, sign, locase; 51 int i; 52 53 /* 54 * locase = 0 or 0x20. ORing digits or letters with 'locase' 55 * produces same digits or (maybe lowercased) letters 56 */ 57 locase = (type & SMALL); 58 if (type & LEFT) 59 type &= ~ZEROPAD; 60 if (base < 2 || base > 16) 61 return NULL; 62 c = (type & ZEROPAD) ? '0' : ' '; 63 sign = 0; 64 if (type & SIGN) { 65 if (num < 0) { 66 sign = '-'; 67 num = -num; 68 size--; 69 } else if (type & PLUS) { 70 sign = '+'; 71 size--; 72 } else if (type & SPACE) { 73 sign = ' '; 74 size--; 75 } 76 } 77 if (type & SPECIAL) { 78 if (base == 16) 79 size -= 2; 80 else if (base == 8) 81 size--; 82 } 83 i = 0; 84 if (num == 0) 85 tmp[i++] = '0'; 86 else 87 while (num != 0) 88 tmp[i++] = (digits[__do_div(num, base)] | locase); 89 if (i > precision) 90 precision = i; 91 size -= precision; 92 if (!(type & (ZEROPAD + LEFT))) 93 while (size-- > 0) 94 APPEND_BUFFER_SAFE(str, end, ' '); 95 if (sign) 96 APPEND_BUFFER_SAFE(str, end, sign); 97 if (type & SPECIAL) { 98 if (base == 8) 99 APPEND_BUFFER_SAFE(str, end, '0'); 100 else if (base == 16) { 101 APPEND_BUFFER_SAFE(str, end, '0'); 102 APPEND_BUFFER_SAFE(str, end, 'x'); 103 } 104 } 105 if (!(type & LEFT)) 106 while (size-- > 0) 107 APPEND_BUFFER_SAFE(str, end, c); 108 while (i < precision--) 109 APPEND_BUFFER_SAFE(str, end, '0'); 110 while (i-- > 0) 111 APPEND_BUFFER_SAFE(str, end, tmp[i]); 112 while (size-- > 0) 113 APPEND_BUFFER_SAFE(str, end, ' '); 114 115 return str; 116 } 117 118 int guest_vsnprintf(char *buf, int n, const char *fmt, va_list args) 119 { 120 char *str, *end; 121 const char *s; 122 uint64_t num; 123 int i, base; 124 int len; 125 126 int flags; /* flags to number() */ 127 128 int field_width; /* width of output field */ 129 int precision; /* 130 * min. # of digits for integers; max 131 * number of chars for from string 132 */ 133 int qualifier; /* 'h', 'l', or 'L' for integer fields */ 134 135 end = buf + n; 136 GUEST_ASSERT(buf < end); 137 GUEST_ASSERT(n > 0); 138 139 for (str = buf; *fmt; ++fmt) { 140 if (*fmt != '%') { 141 APPEND_BUFFER_SAFE(str, end, *fmt); 142 continue; 143 } 144 145 /* process flags */ 146 flags = 0; 147 repeat: 148 ++fmt; /* this also skips first '%' */ 149 switch (*fmt) { 150 case '-': 151 flags |= LEFT; 152 goto repeat; 153 case '+': 154 flags |= PLUS; 155 goto repeat; 156 case ' ': 157 flags |= SPACE; 158 goto repeat; 159 case '#': 160 flags |= SPECIAL; 161 goto repeat; 162 case '0': 163 flags |= ZEROPAD; 164 goto repeat; 165 } 166 167 /* get field width */ 168 field_width = -1; 169 if (isdigit(*fmt)) 170 field_width = skip_atoi(&fmt); 171 else if (*fmt == '*') { 172 ++fmt; 173 /* it's the next argument */ 174 field_width = va_arg(args, int); 175 if (field_width < 0) { 176 field_width = -field_width; 177 flags |= LEFT; 178 } 179 } 180 181 /* get the precision */ 182 precision = -1; 183 if (*fmt == '.') { 184 ++fmt; 185 if (isdigit(*fmt)) 186 precision = skip_atoi(&fmt); 187 else if (*fmt == '*') { 188 ++fmt; 189 /* it's the next argument */ 190 precision = va_arg(args, int); 191 } 192 if (precision < 0) 193 precision = 0; 194 } 195 196 /* get the conversion qualifier */ 197 qualifier = -1; 198 if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') { 199 qualifier = *fmt; 200 ++fmt; 201 } 202 203 /* 204 * Play nice with %llu, %llx, etc. KVM selftests only support 205 * 64-bit builds, so just treat %ll* the same as %l*. 206 */ 207 if (qualifier == 'l' && *fmt == 'l') 208 ++fmt; 209 210 /* default base */ 211 base = 10; 212 213 switch (*fmt) { 214 case 'c': 215 if (!(flags & LEFT)) 216 while (--field_width > 0) 217 APPEND_BUFFER_SAFE(str, end, ' '); 218 APPEND_BUFFER_SAFE(str, end, 219 (uint8_t)va_arg(args, int)); 220 while (--field_width > 0) 221 APPEND_BUFFER_SAFE(str, end, ' '); 222 continue; 223 224 case 's': 225 s = va_arg(args, char *); 226 len = strnlen(s, precision); 227 228 if (!(flags & LEFT)) 229 while (len < field_width--) 230 APPEND_BUFFER_SAFE(str, end, ' '); 231 for (i = 0; i < len; ++i) 232 APPEND_BUFFER_SAFE(str, end, *s++); 233 while (len < field_width--) 234 APPEND_BUFFER_SAFE(str, end, ' '); 235 continue; 236 237 case 'p': 238 if (field_width == -1) { 239 field_width = 2 * sizeof(void *); 240 flags |= SPECIAL | SMALL | ZEROPAD; 241 } 242 str = number(str, end, 243 (uint64_t)va_arg(args, void *), 16, 244 field_width, precision, flags); 245 continue; 246 247 case 'n': 248 if (qualifier == 'l') { 249 long *ip = va_arg(args, long *); 250 *ip = (str - buf); 251 } else { 252 int *ip = va_arg(args, int *); 253 *ip = (str - buf); 254 } 255 continue; 256 257 case '%': 258 APPEND_BUFFER_SAFE(str, end, '%'); 259 continue; 260 261 /* integer number formats - set up the flags and "break" */ 262 case 'o': 263 base = 8; 264 break; 265 266 case 'x': 267 flags |= SMALL; 268 case 'X': 269 base = 16; 270 break; 271 272 case 'd': 273 case 'i': 274 flags |= SIGN; 275 case 'u': 276 break; 277 278 default: 279 APPEND_BUFFER_SAFE(str, end, '%'); 280 if (*fmt) 281 APPEND_BUFFER_SAFE(str, end, *fmt); 282 else 283 --fmt; 284 continue; 285 } 286 if (qualifier == 'l') 287 num = va_arg(args, uint64_t); 288 else if (qualifier == 'h') { 289 num = (uint16_t)va_arg(args, int); 290 if (flags & SIGN) 291 num = (int16_t)num; 292 } else if (flags & SIGN) 293 num = va_arg(args, int); 294 else 295 num = va_arg(args, uint32_t); 296 str = number(str, end, num, base, field_width, precision, flags); 297 } 298 299 GUEST_ASSERT(str < end); 300 *str = '\0'; 301 return str - buf; 302 } 303 304 int guest_snprintf(char *buf, int n, const char *fmt, ...) 305 { 306 va_list va; 307 int len; 308 309 va_start(va, fmt); 310 len = guest_vsnprintf(buf, n, fmt, va); 311 va_end(va); 312 313 return len; 314 } 315