Merge tag 'powerpc-6.6-6' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc...
[platform/kernel/linux-starfive.git] / tools / testing / selftests / kvm / lib / guest_sprintf.c
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 }