diff mbox series

[v2,2/4] KVM: Implement strchr() and basic sprintf()

Message ID 20240514120713.12308-3-mdoucha@suse.cz
State Accepted
Headers show
Series Add functional test for AMD VMSAVE/VMLOAD instructions | expand

Commit Message

Martin Doucha May 14, 2024, 12:07 p.m. UTC
Add basic implementation of sprintf() that supports string, pointer
and integer arguments but without advanced formatting options like
field alignment and padding.

Signed-off-by: Martin Doucha <mdoucha@suse.cz>
---

Changes since v1:
- #include <stdarg.h> instead of defining custom va_list macros

The C standard requires that <stdarg.h> must support freestanding environment
so it's safe to #include it in KVM guest code.

 testcases/kernel/kvm/include/kvm_guest.h |   7 +
 testcases/kernel/kvm/lib_guest.c         | 312 +++++++++++++++++++++++
 2 files changed, 319 insertions(+)
diff mbox series

Patch

diff --git a/testcases/kernel/kvm/include/kvm_guest.h b/testcases/kernel/kvm/include/kvm_guest.h
index 96f246155..3cfafa313 100644
--- a/testcases/kernel/kvm/include/kvm_guest.h
+++ b/testcases/kernel/kvm/include/kvm_guest.h
@@ -8,6 +8,8 @@ 
 #ifndef KVM_GUEST_H_
 #define KVM_GUEST_H_
 
+#include <stdarg.h>
+
 /* The main LTP include dir is intentionally excluded during payload build */
 #include "../../../../include/tst_res_flags.h"
 #undef TERRNO
@@ -49,6 +51,11 @@  void *memcpy(void *dest, const void *src, size_t size);
 char *strcpy(char *dest, const char *src);
 char *strcat(char *dest, const char *src);
 size_t strlen(const char *str);
+char *strchr(const char *s, int c);
+char *strrchr(const char *s, int c);
+
+int vsprintf(char *dest, const char *fmt, va_list ap);
+int sprintf(char *dest, const char *fmt, ...);
 
 /* Exit the VM by looping on a HLT instruction forever */
 void kvm_exit(void) __attribute__((noreturn));
diff --git a/testcases/kernel/kvm/lib_guest.c b/testcases/kernel/kvm/lib_guest.c
index f3e21d3d6..73a76ccb1 100644
--- a/testcases/kernel/kvm/lib_guest.c
+++ b/testcases/kernel/kvm/lib_guest.c
@@ -76,6 +76,74 @@  size_t strlen(const char *str)
 	return ret;
 }
 
+char *strchr(const char *s, int c)
+{
+	for (; *s; s++) {
+		if (*s == c)
+			return (char *)s;
+	}
+
+	return NULL;
+}
+
+char *strrchr(const char *s, int c)
+{
+	const char *ret = NULL;
+
+	for (; *s; s++) {
+		if (*s == c)
+			ret = s;
+	}
+
+	return (char *)ret;
+}
+
+#if defined(__x86_64__) && !defined(__ILP32__)
+uint64_t u64divu16(uint64_t a, uint16_t b)
+{
+	return a / b;
+}
+
+unsigned int u64modu16(uint64_t a, uint16_t b)
+{
+	return a % b;
+}
+
+#else /* defined(__x86_64__) && !defined(__ILP32__) */
+
+/* u64 short division helpers to avoid need to link libgcc on 32bit archs */
+uint64_t u64divu16(uint64_t a, uint16_t b)
+{
+	uint64_t ret = 0;
+	uint32_t tmp = a >> 32;
+
+	ret = tmp / b;
+	ret <<= 32;
+	tmp %= b;
+	tmp <<= 16;
+	tmp |= (a >> 16) & 0xffff;
+	ret |= (tmp / b) << 16;
+	tmp %= b;
+	tmp <<= 16;
+	tmp |= a & 0xffff;
+	ret |= tmp / b;
+	return ret;
+}
+
+unsigned int u64modu16(uint64_t a, uint16_t b)
+{
+	uint32_t tmp = a >> 32;
+
+	tmp %= b;
+	tmp <<= 16;
+	tmp |= (a >> 16) & 0xffff;
+	tmp %= b;
+	tmp <<= 16;
+	tmp |= a & 0xffff;
+	return tmp % b;
+}
+#endif /* defined(__x86_64__) && !defined(__ILP32__) */
+
 char *ptr2hex(char *dest, uintptr_t val)
 {
 	unsigned int i;
@@ -95,6 +163,250 @@  char *ptr2hex(char *dest, uintptr_t val)
 	return ret;
 }
 
+char *u64tostr(char *dest, uint64_t val, uint16_t base, int caps)
+{
+	unsigned int i;
+	uintptr_t tmp = u64divu16(val, base);
+	char hex = caps ? 'A' : 'a';
+	char *ret = dest;
+
+	for (i = 1; tmp; i++, tmp = u64divu16(tmp, base))
+		;
+
+	dest[i] = '\0';
+
+	do {
+		tmp = u64modu16(val, base);
+		dest[--i] = tmp + (tmp >= 10 ? hex - 10 : '0');
+		val = u64divu16(val, base);
+	} while (i);
+
+	return ret;
+}
+
+char *i64tostr(char *dest, int64_t val)
+{
+	if (val < 0) {
+		dest[0] = '-';
+		u64tostr(dest + 1, -val, 10, 0);
+		return dest;
+	}
+
+	return u64tostr(dest, val, 10, 0);
+}
+
+int vsprintf(char *dest, const char *fmt, va_list ap)
+{
+	va_list args;
+	int ret = 0;
+	char conv;
+	uint64_t u64val = 0;
+	int64_t i64val = 0;
+	const char * const uint_conv = "ouxX";
+
+	va_copy(args, ap);
+
+	for (; *fmt; fmt++) {
+		if (*fmt != '%') {
+			dest[ret++] = *fmt;
+			continue;
+		}
+
+		conv = 0;
+		fmt++;
+
+		switch (*fmt) {
+		case '%':
+			dest[ret++] = *fmt;
+			break;
+
+		case 'c':
+			dest[ret++] = va_arg(args, int);
+			break;
+
+		case 's':
+			strcpy(dest + ret, va_arg(args, const char *));
+			ret += strlen(dest + ret);
+			break;
+
+		case 'p':
+			strcpy(dest + ret, "0x");
+			ptr2hex(dest + ret + 2,
+				(uintptr_t)va_arg(args, void *));
+			ret += strlen(dest + ret);
+			break;
+
+		case 'l':
+			fmt++;
+
+			switch (*fmt) {
+			case 'l':
+				fmt++;
+
+				if (*fmt == 'd' || *fmt == 'i') {
+					i64val = va_arg(args, long long);
+					conv = *fmt;
+					break;
+				}
+
+				if (strchr(uint_conv, *fmt)) {
+					u64val = va_arg(args,
+						unsigned long long);
+					conv = *fmt;
+					break;
+				}
+
+				va_end(args);
+				return -1;
+
+			case 'd':
+			case 'i':
+				i64val = va_arg(args, long);
+				conv = *fmt;
+				break;
+
+			default:
+				if (strchr(uint_conv, *fmt)) {
+					u64val = va_arg(args,
+						unsigned long);
+					conv = *fmt;
+					break;
+				}
+
+				va_end(args);
+				return -1;
+			}
+			break;
+
+		case 'h':
+			fmt++;
+
+			switch (*fmt) {
+			case 'h':
+				fmt++;
+
+				if (*fmt == 'd' || *fmt == 'i') {
+					i64val = (signed char)va_arg(args, int);
+					conv = *fmt;
+					break;
+				}
+
+				if (strchr(uint_conv, *fmt)) {
+					u64val = (unsigned char)va_arg(args,
+						unsigned int);
+					conv = *fmt;
+					break;
+				}
+
+				va_end(args);
+				return -1;
+
+			case 'd':
+			case 'i':
+				i64val = (short int)va_arg(args, int);
+				conv = *fmt;
+				break;
+
+			default:
+				if (strchr(uint_conv, *fmt)) {
+					u64val = (unsigned short int)va_arg(
+						args, unsigned int);
+					conv = *fmt;
+					break;
+				}
+
+				va_end(args);
+				return -1;
+			}
+			break;
+
+		case 'z':
+			fmt++;
+
+			if (*fmt == 'd' || *fmt == 'i') {
+				i64val = va_arg(args, ssize_t);
+				conv = *fmt;
+				break;
+			}
+
+			if (strchr(uint_conv, *fmt)) {
+				u64val = va_arg(args, size_t);
+				conv = *fmt;
+				break;
+			}
+
+			va_end(args);
+			return -1;
+
+		case 'd':
+		case 'i':
+			i64val = va_arg(args, int);
+			conv = *fmt;
+			break;
+
+		default:
+			if (strchr(uint_conv, *fmt)) {
+				u64val = va_arg(args, unsigned int);
+				conv = *fmt;
+				break;
+			}
+
+			va_end(args);
+			return -1;
+		}
+
+		switch (conv) {
+		case 0:
+			continue;
+
+		case 'd':
+		case 'i':
+			i64tostr(dest + ret, i64val);
+			ret += strlen(dest + ret);
+			break;
+
+		case 'o':
+			u64tostr(dest + ret, u64val, 8, 0);
+			ret += strlen(dest + ret);
+			break;
+
+		case 'u':
+			u64tostr(dest + ret, u64val, 10, 0);
+			ret += strlen(dest + ret);
+			break;
+
+		case 'x':
+			u64tostr(dest + ret, u64val, 16, 0);
+			ret += strlen(dest + ret);
+			break;
+
+		case 'X':
+			u64tostr(dest + ret, u64val, 16, 1);
+			ret += strlen(dest + ret);
+			break;
+
+		default:
+			va_end(args);
+			return -1;
+		}
+	}
+
+	va_end(args);
+	dest[ret++] = '\0';
+	return ret;
+}
+
+int sprintf(char *dest, const char *fmt, ...)
+{
+	va_list args;
+	int ret;
+
+	va_start(args, fmt);
+	ret = vsprintf(dest, fmt, args);
+	va_end(args);
+	return ret;
+}
+
 void *tst_heap_alloc_aligned(size_t size, size_t align)
 {
 	uintptr_t addr = (uintptr_t)heap_end;