162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci#include "test_util.h" 362306a36Sopenharmony_ci#include "kvm_util.h" 462306a36Sopenharmony_ci#include "ucall_common.h" 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#define APPEND_BUFFER_SAFE(str, end, v) \ 762306a36Sopenharmony_cido { \ 862306a36Sopenharmony_ci GUEST_ASSERT(str < end); \ 962306a36Sopenharmony_ci *str++ = (v); \ 1062306a36Sopenharmony_ci} while (0) 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_cistatic int isdigit(int ch) 1362306a36Sopenharmony_ci{ 1462306a36Sopenharmony_ci return (ch >= '0') && (ch <= '9'); 1562306a36Sopenharmony_ci} 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistatic int skip_atoi(const char **s) 1862306a36Sopenharmony_ci{ 1962306a36Sopenharmony_ci int i = 0; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci while (isdigit(**s)) 2262306a36Sopenharmony_ci i = i * 10 + *((*s)++) - '0'; 2362306a36Sopenharmony_ci return i; 2462306a36Sopenharmony_ci} 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define ZEROPAD 1 /* pad with zero */ 2762306a36Sopenharmony_ci#define SIGN 2 /* unsigned/signed long */ 2862306a36Sopenharmony_ci#define PLUS 4 /* show plus */ 2962306a36Sopenharmony_ci#define SPACE 8 /* space if plus */ 3062306a36Sopenharmony_ci#define LEFT 16 /* left justified */ 3162306a36Sopenharmony_ci#define SMALL 32 /* Must be 32 == 0x20 */ 3262306a36Sopenharmony_ci#define SPECIAL 64 /* 0x */ 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define __do_div(n, base) \ 3562306a36Sopenharmony_ci({ \ 3662306a36Sopenharmony_ci int __res; \ 3762306a36Sopenharmony_ci \ 3862306a36Sopenharmony_ci __res = ((uint64_t) n) % (uint32_t) base; \ 3962306a36Sopenharmony_ci n = ((uint64_t) n) / (uint32_t) base; \ 4062306a36Sopenharmony_ci __res; \ 4162306a36Sopenharmony_ci}) 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic char *number(char *str, const char *end, long num, int base, int size, 4462306a36Sopenharmony_ci int precision, int type) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci /* we are called with base 8, 10 or 16, only, thus don't need "G..." */ 4762306a36Sopenharmony_ci static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */ 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci char tmp[66]; 5062306a36Sopenharmony_ci char c, sign, locase; 5162306a36Sopenharmony_ci int i; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci /* 5462306a36Sopenharmony_ci * locase = 0 or 0x20. ORing digits or letters with 'locase' 5562306a36Sopenharmony_ci * produces same digits or (maybe lowercased) letters 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_ci locase = (type & SMALL); 5862306a36Sopenharmony_ci if (type & LEFT) 5962306a36Sopenharmony_ci type &= ~ZEROPAD; 6062306a36Sopenharmony_ci if (base < 2 || base > 16) 6162306a36Sopenharmony_ci return NULL; 6262306a36Sopenharmony_ci c = (type & ZEROPAD) ? '0' : ' '; 6362306a36Sopenharmony_ci sign = 0; 6462306a36Sopenharmony_ci if (type & SIGN) { 6562306a36Sopenharmony_ci if (num < 0) { 6662306a36Sopenharmony_ci sign = '-'; 6762306a36Sopenharmony_ci num = -num; 6862306a36Sopenharmony_ci size--; 6962306a36Sopenharmony_ci } else if (type & PLUS) { 7062306a36Sopenharmony_ci sign = '+'; 7162306a36Sopenharmony_ci size--; 7262306a36Sopenharmony_ci } else if (type & SPACE) { 7362306a36Sopenharmony_ci sign = ' '; 7462306a36Sopenharmony_ci size--; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci if (type & SPECIAL) { 7862306a36Sopenharmony_ci if (base == 16) 7962306a36Sopenharmony_ci size -= 2; 8062306a36Sopenharmony_ci else if (base == 8) 8162306a36Sopenharmony_ci size--; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci i = 0; 8462306a36Sopenharmony_ci if (num == 0) 8562306a36Sopenharmony_ci tmp[i++] = '0'; 8662306a36Sopenharmony_ci else 8762306a36Sopenharmony_ci while (num != 0) 8862306a36Sopenharmony_ci tmp[i++] = (digits[__do_div(num, base)] | locase); 8962306a36Sopenharmony_ci if (i > precision) 9062306a36Sopenharmony_ci precision = i; 9162306a36Sopenharmony_ci size -= precision; 9262306a36Sopenharmony_ci if (!(type & (ZEROPAD + LEFT))) 9362306a36Sopenharmony_ci while (size-- > 0) 9462306a36Sopenharmony_ci APPEND_BUFFER_SAFE(str, end, ' '); 9562306a36Sopenharmony_ci if (sign) 9662306a36Sopenharmony_ci APPEND_BUFFER_SAFE(str, end, sign); 9762306a36Sopenharmony_ci if (type & SPECIAL) { 9862306a36Sopenharmony_ci if (base == 8) 9962306a36Sopenharmony_ci APPEND_BUFFER_SAFE(str, end, '0'); 10062306a36Sopenharmony_ci else if (base == 16) { 10162306a36Sopenharmony_ci APPEND_BUFFER_SAFE(str, end, '0'); 10262306a36Sopenharmony_ci APPEND_BUFFER_SAFE(str, end, 'x'); 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci if (!(type & LEFT)) 10662306a36Sopenharmony_ci while (size-- > 0) 10762306a36Sopenharmony_ci APPEND_BUFFER_SAFE(str, end, c); 10862306a36Sopenharmony_ci while (i < precision--) 10962306a36Sopenharmony_ci APPEND_BUFFER_SAFE(str, end, '0'); 11062306a36Sopenharmony_ci while (i-- > 0) 11162306a36Sopenharmony_ci APPEND_BUFFER_SAFE(str, end, tmp[i]); 11262306a36Sopenharmony_ci while (size-- > 0) 11362306a36Sopenharmony_ci APPEND_BUFFER_SAFE(str, end, ' '); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return str; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ciint guest_vsnprintf(char *buf, int n, const char *fmt, va_list args) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci char *str, *end; 12162306a36Sopenharmony_ci const char *s; 12262306a36Sopenharmony_ci uint64_t num; 12362306a36Sopenharmony_ci int i, base; 12462306a36Sopenharmony_ci int len; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci int flags; /* flags to number() */ 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci int field_width; /* width of output field */ 12962306a36Sopenharmony_ci int precision; /* 13062306a36Sopenharmony_ci * min. # of digits for integers; max 13162306a36Sopenharmony_ci * number of chars for from string 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_ci int qualifier; /* 'h', 'l', or 'L' for integer fields */ 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci end = buf + n; 13662306a36Sopenharmony_ci GUEST_ASSERT(buf < end); 13762306a36Sopenharmony_ci GUEST_ASSERT(n > 0); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci for (str = buf; *fmt; ++fmt) { 14062306a36Sopenharmony_ci if (*fmt != '%') { 14162306a36Sopenharmony_ci APPEND_BUFFER_SAFE(str, end, *fmt); 14262306a36Sopenharmony_ci continue; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* process flags */ 14662306a36Sopenharmony_ci flags = 0; 14762306a36Sopenharmony_cirepeat: 14862306a36Sopenharmony_ci ++fmt; /* this also skips first '%' */ 14962306a36Sopenharmony_ci switch (*fmt) { 15062306a36Sopenharmony_ci case '-': 15162306a36Sopenharmony_ci flags |= LEFT; 15262306a36Sopenharmony_ci goto repeat; 15362306a36Sopenharmony_ci case '+': 15462306a36Sopenharmony_ci flags |= PLUS; 15562306a36Sopenharmony_ci goto repeat; 15662306a36Sopenharmony_ci case ' ': 15762306a36Sopenharmony_ci flags |= SPACE; 15862306a36Sopenharmony_ci goto repeat; 15962306a36Sopenharmony_ci case '#': 16062306a36Sopenharmony_ci flags |= SPECIAL; 16162306a36Sopenharmony_ci goto repeat; 16262306a36Sopenharmony_ci case '0': 16362306a36Sopenharmony_ci flags |= ZEROPAD; 16462306a36Sopenharmony_ci goto repeat; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* get field width */ 16862306a36Sopenharmony_ci field_width = -1; 16962306a36Sopenharmony_ci if (isdigit(*fmt)) 17062306a36Sopenharmony_ci field_width = skip_atoi(&fmt); 17162306a36Sopenharmony_ci else if (*fmt == '*') { 17262306a36Sopenharmony_ci ++fmt; 17362306a36Sopenharmony_ci /* it's the next argument */ 17462306a36Sopenharmony_ci field_width = va_arg(args, int); 17562306a36Sopenharmony_ci if (field_width < 0) { 17662306a36Sopenharmony_ci field_width = -field_width; 17762306a36Sopenharmony_ci flags |= LEFT; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci /* get the precision */ 18262306a36Sopenharmony_ci precision = -1; 18362306a36Sopenharmony_ci if (*fmt == '.') { 18462306a36Sopenharmony_ci ++fmt; 18562306a36Sopenharmony_ci if (isdigit(*fmt)) 18662306a36Sopenharmony_ci precision = skip_atoi(&fmt); 18762306a36Sopenharmony_ci else if (*fmt == '*') { 18862306a36Sopenharmony_ci ++fmt; 18962306a36Sopenharmony_ci /* it's the next argument */ 19062306a36Sopenharmony_ci precision = va_arg(args, int); 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci if (precision < 0) 19362306a36Sopenharmony_ci precision = 0; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci /* get the conversion qualifier */ 19762306a36Sopenharmony_ci qualifier = -1; 19862306a36Sopenharmony_ci if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') { 19962306a36Sopenharmony_ci qualifier = *fmt; 20062306a36Sopenharmony_ci ++fmt; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* 20462306a36Sopenharmony_ci * Play nice with %llu, %llx, etc. KVM selftests only support 20562306a36Sopenharmony_ci * 64-bit builds, so just treat %ll* the same as %l*. 20662306a36Sopenharmony_ci */ 20762306a36Sopenharmony_ci if (qualifier == 'l' && *fmt == 'l') 20862306a36Sopenharmony_ci ++fmt; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* default base */ 21162306a36Sopenharmony_ci base = 10; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci switch (*fmt) { 21462306a36Sopenharmony_ci case 'c': 21562306a36Sopenharmony_ci if (!(flags & LEFT)) 21662306a36Sopenharmony_ci while (--field_width > 0) 21762306a36Sopenharmony_ci APPEND_BUFFER_SAFE(str, end, ' '); 21862306a36Sopenharmony_ci APPEND_BUFFER_SAFE(str, end, 21962306a36Sopenharmony_ci (uint8_t)va_arg(args, int)); 22062306a36Sopenharmony_ci while (--field_width > 0) 22162306a36Sopenharmony_ci APPEND_BUFFER_SAFE(str, end, ' '); 22262306a36Sopenharmony_ci continue; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci case 's': 22562306a36Sopenharmony_ci s = va_arg(args, char *); 22662306a36Sopenharmony_ci len = strnlen(s, precision); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (!(flags & LEFT)) 22962306a36Sopenharmony_ci while (len < field_width--) 23062306a36Sopenharmony_ci APPEND_BUFFER_SAFE(str, end, ' '); 23162306a36Sopenharmony_ci for (i = 0; i < len; ++i) 23262306a36Sopenharmony_ci APPEND_BUFFER_SAFE(str, end, *s++); 23362306a36Sopenharmony_ci while (len < field_width--) 23462306a36Sopenharmony_ci APPEND_BUFFER_SAFE(str, end, ' '); 23562306a36Sopenharmony_ci continue; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci case 'p': 23862306a36Sopenharmony_ci if (field_width == -1) { 23962306a36Sopenharmony_ci field_width = 2 * sizeof(void *); 24062306a36Sopenharmony_ci flags |= SPECIAL | SMALL | ZEROPAD; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci str = number(str, end, 24362306a36Sopenharmony_ci (uint64_t)va_arg(args, void *), 16, 24462306a36Sopenharmony_ci field_width, precision, flags); 24562306a36Sopenharmony_ci continue; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci case 'n': 24862306a36Sopenharmony_ci if (qualifier == 'l') { 24962306a36Sopenharmony_ci long *ip = va_arg(args, long *); 25062306a36Sopenharmony_ci *ip = (str - buf); 25162306a36Sopenharmony_ci } else { 25262306a36Sopenharmony_ci int *ip = va_arg(args, int *); 25362306a36Sopenharmony_ci *ip = (str - buf); 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci continue; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci case '%': 25862306a36Sopenharmony_ci APPEND_BUFFER_SAFE(str, end, '%'); 25962306a36Sopenharmony_ci continue; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci /* integer number formats - set up the flags and "break" */ 26262306a36Sopenharmony_ci case 'o': 26362306a36Sopenharmony_ci base = 8; 26462306a36Sopenharmony_ci break; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci case 'x': 26762306a36Sopenharmony_ci flags |= SMALL; 26862306a36Sopenharmony_ci case 'X': 26962306a36Sopenharmony_ci base = 16; 27062306a36Sopenharmony_ci break; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci case 'd': 27362306a36Sopenharmony_ci case 'i': 27462306a36Sopenharmony_ci flags |= SIGN; 27562306a36Sopenharmony_ci case 'u': 27662306a36Sopenharmony_ci break; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci default: 27962306a36Sopenharmony_ci APPEND_BUFFER_SAFE(str, end, '%'); 28062306a36Sopenharmony_ci if (*fmt) 28162306a36Sopenharmony_ci APPEND_BUFFER_SAFE(str, end, *fmt); 28262306a36Sopenharmony_ci else 28362306a36Sopenharmony_ci --fmt; 28462306a36Sopenharmony_ci continue; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci if (qualifier == 'l') 28762306a36Sopenharmony_ci num = va_arg(args, uint64_t); 28862306a36Sopenharmony_ci else if (qualifier == 'h') { 28962306a36Sopenharmony_ci num = (uint16_t)va_arg(args, int); 29062306a36Sopenharmony_ci if (flags & SIGN) 29162306a36Sopenharmony_ci num = (int16_t)num; 29262306a36Sopenharmony_ci } else if (flags & SIGN) 29362306a36Sopenharmony_ci num = va_arg(args, int); 29462306a36Sopenharmony_ci else 29562306a36Sopenharmony_ci num = va_arg(args, uint32_t); 29662306a36Sopenharmony_ci str = number(str, end, num, base, field_width, precision, flags); 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci GUEST_ASSERT(str < end); 30062306a36Sopenharmony_ci *str = '\0'; 30162306a36Sopenharmony_ci return str - buf; 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ciint guest_snprintf(char *buf, int n, const char *fmt, ...) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci va_list va; 30762306a36Sopenharmony_ci int len; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci va_start(va, fmt); 31062306a36Sopenharmony_ci len = guest_vsnprintf(buf, n, fmt, va); 31162306a36Sopenharmony_ci va_end(va); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci return len; 31462306a36Sopenharmony_ci} 315