From nobody Tue Dec 16 03:10:44 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6562BC001E0 for ; Sat, 29 Jul 2023 00:37:33 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229871AbjG2Aha (ORCPT ); Fri, 28 Jul 2023 20:37:30 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58378 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232083AbjG2AhO (ORCPT ); Fri, 28 Jul 2023 20:37:14 -0400 Received: from mail-pg1-x54a.google.com (mail-pg1-x54a.google.com [IPv6:2607:f8b0:4864:20::54a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 550DB3C22 for ; Fri, 28 Jul 2023 17:36:56 -0700 (PDT) Received: by mail-pg1-x54a.google.com with SMTP id 41be03b00d2f7-563fc38db94so1613699a12.0 for ; Fri, 28 Jul 2023 17:36:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1690591016; x=1691195816; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:reply-to:from:to:cc:subject:date:message-id:reply-to; bh=zoZLLV8R20eg7tJsXNLPRDCKR5FXoU0sMSGBAwU17tY=; b=gIJR6odFc5Hkpq4A6lJmUlgjcsTQKzLGNi55/3FcYE6gNkohE3tIICG8rorU9SEQW3 oOkGp1PPhmT+o9bMhPParO5kjuxQRODz0VLEoEDaRrgeg9fdD6yguME2pxdCbVcgqcy0 Q5gXksV/eBoBtZC7Fos9LKlKvAH3fk3Qhg3jH8HCaQvCJCMaigY/KEuz4oXnHwI+521M 7JHK9qVb7H5bJgBtud2neE88tCRUlq1YmGMfeL98sp72VUccTOClUY++GrP5b47yyZD/ bk3cIlaxWDIns4poXsSY8AP4w11SWrcvvvLnoTPOQDcz0uoeh+rbUaoQw5w/XoBRkqUG vMQw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1690591016; x=1691195816; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:reply-to:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=zoZLLV8R20eg7tJsXNLPRDCKR5FXoU0sMSGBAwU17tY=; b=e/FYHygSaUeJev+0LrJxBdX6NJltfeotNsaHPjpfcmhTFkZkFr4fXs3UKQ08+dOkjo Sn9KL+GXrPC8gotA4Xoe6/K8gSNtS1v6m0MwfSoJUTuTpOPKyYe/SoYH085vEH/YfuDy ig24LpnX3xn2kT/sJJfva0yoQU1Nd1cgOLv49NVV0aMkY+Wcd8LQAv1f+8S1ioJTT+oV SylBUanCa7vxQ2Lh2ldZZodxOMd+ArC3llCjWW9TJSjnfqtrODmL479iFXm8Mb5q4rFF neyuyEqRxvaNw9Q5KGKVMwj8se8N5mZojyfoTAw8/xklRY0n4UA6o9VH40Ihq3BDWT/K 2mNg== X-Gm-Message-State: ABy/qLYY3XgGH/XK6WuRteWyBH2iD63fO34aTov25YiqgDmFmGKXWEtI byKlZNFohjrFypvGBtcBZD6Oxw6jcIg= X-Google-Smtp-Source: APBJJlGnKNgEBanM0pNB6kivwAILTcmV3SgQ6dkzSb0RKOpFIQB854F/xZNKDJ2RHCN/IfjUaRuBeXIma2E= X-Received: from zagreus.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:5c37]) (user=seanjc job=sendgmr) by 2002:a17:903:2309:b0:1b3:bfa6:d064 with SMTP id d9-20020a170903230900b001b3bfa6d064mr12998plh.1.1690591015774; Fri, 28 Jul 2023 17:36:55 -0700 (PDT) Reply-To: Sean Christopherson Date: Fri, 28 Jul 2023 17:36:14 -0700 In-Reply-To: <20230729003643.1053367-1-seanjc@google.com> Mime-Version: 1.0 References: <20230729003643.1053367-1-seanjc@google.com> X-Mailer: git-send-email 2.41.0.487.g6d72f3e995-goog Message-ID: <20230729003643.1053367-6-seanjc@google.com> Subject: [PATCH v4 05/34] KVM: selftests: Add guest_snprintf() to KVM selftests From: Sean Christopherson To: Paolo Bonzini , Marc Zyngier , Oliver Upton , Christian Borntraeger , Janosch Frank , Claudio Imbrenda Cc: kvm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, kvmarm@lists.linux.dev, linux-kernel@vger.kernel.org, Sean Christopherson , Thomas Huth , "=?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?=" , Aaron Lewis Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Aaron Lewis Add a local version of guest_snprintf() for use in the guest. Having a local copy allows the guest access to string formatting options without dependencies on LIBC. LIBC is problematic because it heavily relies on both AVX-512 instructions and a TLS, neither of which are guaranteed to be set up in the guest. The file guest_sprintf.c was lifted from arch/x86/boot/printf.c and adapted to work in the guest, including the addition of buffer length. I.e. s/sprintf/snprintf/ The functions where prefixed with "guest_" to allow guests to explicitly call them. A string formatted by this function is expected to succeed or die. If something goes wrong during the formatting process a GUEST_ASSERT() will be thrown. Signed-off-by: Aaron Lewis Link: https://lore.kernel.org/all/mtdi6smhur5rqffvpu7qux7mptonw223y2653x2nw= zvgm72nlo@zyc4w3kwl3rg [sean: add a link to the discussion of other options] Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/Makefile | 1 + .../testing/selftests/kvm/include/test_util.h | 3 + .../testing/selftests/kvm/lib/guest_sprintf.c | 307 ++++++++++++++++++ 3 files changed, 311 insertions(+) create mode 100644 tools/testing/selftests/kvm/lib/guest_sprintf.c diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests= /kvm/Makefile index f6c14ab476ac..f65889f5a083 100644 --- a/tools/testing/selftests/kvm/Makefile +++ b/tools/testing/selftests/kvm/Makefile @@ -23,6 +23,7 @@ LIBKVM +=3D lib/guest_modes.c LIBKVM +=3D lib/io.c LIBKVM +=3D lib/kvm_util.c LIBKVM +=3D lib/memstress.c +LIBKVM +=3D lib/guest_sprintf.c LIBKVM +=3D lib/rbtree.c LIBKVM +=3D lib/sparsebit.c LIBKVM +=3D lib/test_util.c diff --git a/tools/testing/selftests/kvm/include/test_util.h b/tools/testin= g/selftests/kvm/include/test_util.h index a4bea44f990c..7a5907da1719 100644 --- a/tools/testing/selftests/kvm/include/test_util.h +++ b/tools/testing/selftests/kvm/include/test_util.h @@ -185,4 +185,7 @@ static inline uint32_t atoi_non_negative(const char *na= me, const char *num_str) return num; } =20 +int guest_vsnprintf(char *buf, int n, const char *fmt, va_list args); +int guest_snprintf(char *buf, int n, const char *fmt, ...); + #endif /* SELFTEST_KVM_TEST_UTIL_H */ diff --git a/tools/testing/selftests/kvm/lib/guest_sprintf.c b/tools/testin= g/selftests/kvm/lib/guest_sprintf.c new file mode 100644 index 000000000000..c4a69d8aeb68 --- /dev/null +++ b/tools/testing/selftests/kvm/lib/guest_sprintf.c @@ -0,0 +1,307 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include "test_util.h" +#include "kvm_util.h" +#include "ucall_common.h" + +#define APPEND_BUFFER_SAFE(str, end, v) \ +do { \ + GUEST_ASSERT(str < end); \ + *str++ =3D (v); \ +} while (0) + +static int isdigit(int ch) +{ + return (ch >=3D '0') && (ch <=3D '9'); +} + +static int skip_atoi(const char **s) +{ + int i =3D 0; + + while (isdigit(**s)) + i =3D i * 10 + *((*s)++) - '0'; + return i; +} + +#define ZEROPAD 1 /* pad with zero */ +#define SIGN 2 /* unsigned/signed long */ +#define PLUS 4 /* show plus */ +#define SPACE 8 /* space if plus */ +#define LEFT 16 /* left justified */ +#define SMALL 32 /* Must be 32 =3D=3D 0x20 */ +#define SPECIAL 64 /* 0x */ + +#define __do_div(n, base) \ +({ \ + int __res; \ + \ + __res =3D ((uint64_t) n) % (uint32_t) base; \ + n =3D ((uint64_t) n) / (uint32_t) base; \ + __res; \ +}) + +static char *number(char *str, const char *end, long num, int base, int si= ze, + int precision, int type) +{ + /* we are called with base 8, 10 or 16, only, thus don't need "G..." */ + static const char digits[16] =3D "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUV= WXYZ"; */ + + char tmp[66]; + char c, sign, locase; + int i; + + /* + * locase =3D 0 or 0x20. ORing digits or letters with 'locase' + * produces same digits or (maybe lowercased) letters + */ + locase =3D (type & SMALL); + if (type & LEFT) + type &=3D ~ZEROPAD; + if (base < 2 || base > 16) + return NULL; + c =3D (type & ZEROPAD) ? '0' : ' '; + sign =3D 0; + if (type & SIGN) { + if (num < 0) { + sign =3D '-'; + num =3D -num; + size--; + } else if (type & PLUS) { + sign =3D '+'; + size--; + } else if (type & SPACE) { + sign =3D ' '; + size--; + } + } + if (type & SPECIAL) { + if (base =3D=3D 16) + size -=3D 2; + else if (base =3D=3D 8) + size--; + } + i =3D 0; + if (num =3D=3D 0) + tmp[i++] =3D '0'; + else + while (num !=3D 0) + tmp[i++] =3D (digits[__do_div(num, base)] | locase); + if (i > precision) + precision =3D i; + size -=3D precision; + if (!(type & (ZEROPAD + LEFT))) + while (size-- > 0) + APPEND_BUFFER_SAFE(str, end, ' '); + if (sign) + APPEND_BUFFER_SAFE(str, end, sign); + if (type & SPECIAL) { + if (base =3D=3D 8) + APPEND_BUFFER_SAFE(str, end, '0'); + else if (base =3D=3D 16) { + APPEND_BUFFER_SAFE(str, end, '0'); + APPEND_BUFFER_SAFE(str, end, 'x'); + } + } + if (!(type & LEFT)) + while (size-- > 0) + APPEND_BUFFER_SAFE(str, end, c); + while (i < precision--) + APPEND_BUFFER_SAFE(str, end, '0'); + while (i-- > 0) + APPEND_BUFFER_SAFE(str, end, tmp[i]); + while (size-- > 0) + APPEND_BUFFER_SAFE(str, end, ' '); + + return str; +} + +int guest_vsnprintf(char *buf, int n, const char *fmt, va_list args) +{ + char *str, *end; + const char *s; + uint64_t num; + int i, base; + int len; + + int flags; /* flags to number() */ + + int field_width; /* width of output field */ + int precision; /* + * min. # of digits for integers; max + * number of chars for from string + */ + int qualifier; /* 'h', 'l', or 'L' for integer fields */ + + end =3D buf + n; + GUEST_ASSERT(buf < end); + GUEST_ASSERT(n > 0); + + for (str =3D buf; *fmt; ++fmt) { + if (*fmt !=3D '%') { + APPEND_BUFFER_SAFE(str, end, *fmt); + continue; + } + + /* process flags */ + flags =3D 0; +repeat: + ++fmt; /* this also skips first '%' */ + switch (*fmt) { + case '-': + flags |=3D LEFT; + goto repeat; + case '+': + flags |=3D PLUS; + goto repeat; + case ' ': + flags |=3D SPACE; + goto repeat; + case '#': + flags |=3D SPECIAL; + goto repeat; + case '0': + flags |=3D ZEROPAD; + goto repeat; + } + + /* get field width */ + field_width =3D -1; + if (isdigit(*fmt)) + field_width =3D skip_atoi(&fmt); + else if (*fmt =3D=3D '*') { + ++fmt; + /* it's the next argument */ + field_width =3D va_arg(args, int); + if (field_width < 0) { + field_width =3D -field_width; + flags |=3D LEFT; + } + } + + /* get the precision */ + precision =3D -1; + if (*fmt =3D=3D '.') { + ++fmt; + if (isdigit(*fmt)) + precision =3D skip_atoi(&fmt); + else if (*fmt =3D=3D '*') { + ++fmt; + /* it's the next argument */ + precision =3D va_arg(args, int); + } + if (precision < 0) + precision =3D 0; + } + + /* get the conversion qualifier */ + qualifier =3D -1; + if (*fmt =3D=3D 'h' || *fmt =3D=3D 'l' || *fmt =3D=3D 'L') { + qualifier =3D *fmt; + ++fmt; + } + + /* default base */ + base =3D 10; + + switch (*fmt) { + case 'c': + if (!(flags & LEFT)) + while (--field_width > 0) + APPEND_BUFFER_SAFE(str, end, ' '); + APPEND_BUFFER_SAFE(str, end, + (uint8_t)va_arg(args, int)); + while (--field_width > 0) + APPEND_BUFFER_SAFE(str, end, ' '); + continue; + + case 's': + s =3D va_arg(args, char *); + len =3D strnlen(s, precision); + + if (!(flags & LEFT)) + while (len < field_width--) + APPEND_BUFFER_SAFE(str, end, ' '); + for (i =3D 0; i < len; ++i) + APPEND_BUFFER_SAFE(str, end, *s++); + while (len < field_width--) + APPEND_BUFFER_SAFE(str, end, ' '); + continue; + + case 'p': + if (field_width =3D=3D -1) { + field_width =3D 2 * sizeof(void *); + flags |=3D SPECIAL | SMALL | ZEROPAD; + } + str =3D number(str, end, + (uint64_t)va_arg(args, void *), 16, + field_width, precision, flags); + continue; + + case 'n': + if (qualifier =3D=3D 'l') { + long *ip =3D va_arg(args, long *); + *ip =3D (str - buf); + } else { + int *ip =3D va_arg(args, int *); + *ip =3D (str - buf); + } + continue; + + case '%': + APPEND_BUFFER_SAFE(str, end, '%'); + continue; + + /* integer number formats - set up the flags and "break" */ + case 'o': + base =3D 8; + break; + + case 'x': + flags |=3D SMALL; + case 'X': + base =3D 16; + break; + + case 'd': + case 'i': + flags |=3D SIGN; + case 'u': + break; + + default: + APPEND_BUFFER_SAFE(str, end, '%'); + if (*fmt) + APPEND_BUFFER_SAFE(str, end, *fmt); + else + --fmt; + continue; + } + if (qualifier =3D=3D 'l') + num =3D va_arg(args, uint64_t); + else if (qualifier =3D=3D 'h') { + num =3D (uint16_t)va_arg(args, int); + if (flags & SIGN) + num =3D (int16_t)num; + } else if (flags & SIGN) + num =3D va_arg(args, int); + else + num =3D va_arg(args, uint32_t); + str =3D number(str, end, num, base, field_width, precision, flags); + } + + GUEST_ASSERT(str < end); + *str =3D '\0'; + return str - buf; +} + +int guest_snprintf(char *buf, int n, const char *fmt, ...) +{ + va_list va; + int len; + + va_start(va, fmt); + len =3D guest_vsnprintf(buf, n, fmt, va); + va_end(va); + + return len; +} --=20 2.41.0.487.g6d72f3e995-goog