From 80c80346ebd8a36b778cb634732b427862de7cb1 Mon Sep 17 00:00:00 2001 From: Roque Arcudia Hernandez Date: Fri, 1 Nov 2024 21:17:20 +0000 Subject: [PATCH 01/12] hw/usb: Use __attribute__((packed)) vs __packed __packed is non standard and is not present in clang-cl. __attribute__((packed)) has the same semantics. Signed-off-by: Erwin Jansen Signed-off-by: Roque Arcudia Hernandez Reviewed-by: Pierrick Bouvier Reviewed-by: Thomas Huth Message-ID: <20241101211720.3354111-1-roqueh@google.com> Signed-off-by: Thomas Huth --- include/hw/usb/dwc2-regs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/hw/usb/dwc2-regs.h b/include/hw/usb/dwc2-regs.h index 523b112c5e..b8b4266543 100644 --- a/include/hw/usb/dwc2-regs.h +++ b/include/hw/usb/dwc2-regs.h @@ -838,7 +838,7 @@ struct dwc2_dma_desc { uint32_t status; uint32_t buf; -} __packed; +} QEMU_PACKED; /* Host Mode DMA descriptor status quadlet */ From e1f1ccb8f0a476198ce8adff566db26b7143e975 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Wed, 23 Oct 2024 01:59:18 +0200 Subject: [PATCH 02/12] target/s390x: Fix the floating-point multiply-and-add NaN rules Order the helper arguments to match the Principles of Operation. Implement the "Results: MULTIPLY AND ADD" table in pickNaNMulAdd(). Reported-by: Peter Maydell Signed-off-by: Ilya Leoshkevich Reviewed-by: Peter Maydell Message-ID: <20241023000147.34035-2-iii@linux.ibm.com> Signed-off-by: Thomas Huth --- fpu/softfloat-specialize.c.inc | 19 +++++++++++++++++++ target/s390x/tcg/fpu_helper.c | 8 ++++---- target/s390x/tcg/vec_fpu_helper.c | 12 ++++++------ 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index b5a3208050..9bca03c4ae 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -597,6 +597,25 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, float_raise(float_flag_invalid | float_flag_invalid_imz, status); } return 3; /* default NaN */ +#elif defined(TARGET_S390X) + if (infzero) { + float_raise(float_flag_invalid | float_flag_invalid_imz, status); + return 3; + } + + if (is_snan(a_cls)) { + return 0; + } else if (is_snan(b_cls)) { + return 1; + } else if (is_snan(c_cls)) { + return 2; + } else if (is_qnan(a_cls)) { + return 0; + } else if (is_qnan(b_cls)) { + return 1; + } else { + return 2; + } #elif defined(TARGET_SPARC) /* For (inf,0,nan) return c. */ if (infzero) { diff --git a/target/s390x/tcg/fpu_helper.c b/target/s390x/tcg/fpu_helper.c index d8bd5748fa..5041c13962 100644 --- a/target/s390x/tcg/fpu_helper.c +++ b/target/s390x/tcg/fpu_helper.c @@ -780,7 +780,7 @@ uint32_t HELPER(kxb)(CPUS390XState *env, Int128 a, Int128 b) uint64_t HELPER(maeb)(CPUS390XState *env, uint64_t f1, uint64_t f2, uint64_t f3) { - float32 ret = float32_muladd(f2, f3, f1, 0, &env->fpu_status); + float32 ret = float32_muladd(f3, f2, f1, 0, &env->fpu_status); handle_exceptions(env, false, GETPC()); return ret; } @@ -789,7 +789,7 @@ uint64_t HELPER(maeb)(CPUS390XState *env, uint64_t f1, uint64_t HELPER(madb)(CPUS390XState *env, uint64_t f1, uint64_t f2, uint64_t f3) { - float64 ret = float64_muladd(f2, f3, f1, 0, &env->fpu_status); + float64 ret = float64_muladd(f3, f2, f1, 0, &env->fpu_status); handle_exceptions(env, false, GETPC()); return ret; } @@ -798,7 +798,7 @@ uint64_t HELPER(madb)(CPUS390XState *env, uint64_t f1, uint64_t HELPER(mseb)(CPUS390XState *env, uint64_t f1, uint64_t f2, uint64_t f3) { - float32 ret = float32_muladd(f2, f3, f1, float_muladd_negate_c, + float32 ret = float32_muladd(f3, f2, f1, float_muladd_negate_c, &env->fpu_status); handle_exceptions(env, false, GETPC()); return ret; @@ -808,7 +808,7 @@ uint64_t HELPER(mseb)(CPUS390XState *env, uint64_t f1, uint64_t HELPER(msdb)(CPUS390XState *env, uint64_t f1, uint64_t f2, uint64_t f3) { - float64 ret = float64_muladd(f2, f3, f1, float_muladd_negate_c, + float64 ret = float64_muladd(f3, f2, f1, float_muladd_negate_c, &env->fpu_status); handle_exceptions(env, false, GETPC()); return ret; diff --git a/target/s390x/tcg/vec_fpu_helper.c b/target/s390x/tcg/vec_fpu_helper.c index 75cf605b9f..1bbaa82fe8 100644 --- a/target/s390x/tcg/vec_fpu_helper.c +++ b/target/s390x/tcg/vec_fpu_helper.c @@ -621,8 +621,8 @@ static void vfma32(S390Vector *v1, const S390Vector *v2, const S390Vector *v3, int i; for (i = 0; i < 4; i++) { - const float32 a = s390_vec_read_float32(v2, i); - const float32 b = s390_vec_read_float32(v3, i); + const float32 a = s390_vec_read_float32(v3, i); + const float32 b = s390_vec_read_float32(v2, i); const float32 c = s390_vec_read_float32(v4, i); float32 ret = float32_muladd(a, b, c, flags, &env->fpu_status); @@ -645,8 +645,8 @@ static void vfma64(S390Vector *v1, const S390Vector *v2, const S390Vector *v3, int i; for (i = 0; i < 2; i++) { - const float64 a = s390_vec_read_float64(v2, i); - const float64 b = s390_vec_read_float64(v3, i); + const float64 a = s390_vec_read_float64(v3, i); + const float64 b = s390_vec_read_float64(v2, i); const float64 c = s390_vec_read_float64(v4, i); const float64 ret = float64_muladd(a, b, c, flags, &env->fpu_status); @@ -664,8 +664,8 @@ static void vfma128(S390Vector *v1, const S390Vector *v2, const S390Vector *v3, const S390Vector *v4, CPUS390XState *env, bool s, int flags, uintptr_t retaddr) { - const float128 a = s390_vec_read_float128(v2); - const float128 b = s390_vec_read_float128(v3); + const float128 a = s390_vec_read_float128(v3); + const float128 b = s390_vec_read_float128(v2); const float128 c = s390_vec_read_float128(v4); uint8_t vxc, vec_exc = 0; float128 ret; From 6dc293540e1ceb82d569224a810f59c9a9e4f822 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Wed, 23 Oct 2024 01:59:19 +0200 Subject: [PATCH 03/12] tests/tcg/s390x: Add the floating-point multiply-and-add test Add a test to prevent regressions. Share some useful pieces with the vfminmax test. Remove the duplicates from the floating point class values. Signed-off-by: Ilya Leoshkevich Reviewed-by: Peter Maydell Message-ID: <20241023000147.34035-3-iii@linux.ibm.com> Signed-off-by: Thomas Huth --- tests/tcg/s390x/Makefile.target | 5 +- tests/tcg/s390x/float.h | 104 ++++++++++++++ tests/tcg/s390x/fma.c | 233 ++++++++++++++++++++++++++++++++ tests/tcg/s390x/vfminmax.c | 225 +++++++++++------------------- 4 files changed, 421 insertions(+), 146 deletions(-) create mode 100644 tests/tcg/s390x/float.h create mode 100644 tests/tcg/s390x/fma.c diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target index 2dab4f4582..da5fe71a40 100644 --- a/tests/tcg/s390x/Makefile.target +++ b/tests/tcg/s390x/Makefile.target @@ -74,8 +74,11 @@ $(Z13_TESTS): CFLAGS+=-march=z13 -O2 TESTS+=$(Z13_TESTS) ifneq ($(CROSS_CC_HAS_Z14),) -Z14_TESTS=vfminmax +Z14_TESTS=fma vfminmax +fma: float.h +fma: LDFLAGS+=-lm vfminmax: LDFLAGS+=-lm +vfminmax: float.h $(Z14_TESTS): CFLAGS+=-march=z14 -O2 TESTS+=$(Z14_TESTS) endif diff --git a/tests/tcg/s390x/float.h b/tests/tcg/s390x/float.h new file mode 100644 index 0000000000..9d1682b8fc --- /dev/null +++ b/tests/tcg/s390x/float.h @@ -0,0 +1,104 @@ +/* + * Helpers for floating-point tests. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef FLOAT_H +#define FLOAT_H + +/* + * Floating-point value classes. + */ +#define N_FORMATS 3 +#define CLASS_MINUS_INF 0 +#define CLASS_MINUS_FN 1 +#define CLASS_MINUS_ZERO 2 +#define CLASS_PLUS_ZERO 3 +#define CLASS_PLUS_FN 4 +#define CLASS_PLUS_INF 5 +#define CLASS_QNAN 6 +#define CLASS_SNAN 7 +#define N_SIGNED_CLASSES 8 +static const size_t float_sizes[N_FORMATS] = { + /* M4 == 2: short */ 4, + /* M4 == 3: long */ 8, + /* M4 == 4: extended */ 16, +}; +static const size_t e_bits[N_FORMATS] = { + /* M4 == 2: short */ 8, + /* M4 == 3: long */ 11, + /* M4 == 4: extended */ 15, +}; +struct float_class { + size_t n; + unsigned char v[2][16]; +}; +static const struct float_class signed_floats[N_FORMATS][N_SIGNED_CLASSES] = { + /* M4 == 2: short */ + { + /* -inf */ {1, {{0xff, 0x80, 0x00, 0x00}}}, + /* -Fn */ {2, {{0xc2, 0x28, 0x00, 0x00}, + {0xc2, 0x29, 0x00, 0x00}}}, + /* -0 */ {1, {{0x80, 0x00, 0x00, 0x00}}}, + /* +0 */ {1, {{0x00, 0x00, 0x00, 0x00}}}, + /* +Fn */ {2, {{0x42, 0x28, 0x00, 0x00}, + {0x42, 0x2a, 0x00, 0x00}}}, + /* +inf */ {1, {{0x7f, 0x80, 0x00, 0x00}}}, + /* QNaN */ {2, {{0x7f, 0xff, 0xff, 0xff}, + {0x7f, 0xff, 0xff, 0xfe}}}, + /* SNaN */ {2, {{0x7f, 0xbf, 0xff, 0xff}, + {0x7f, 0xbf, 0xff, 0xfd}}}, + }, + + /* M4 == 3: long */ + { + /* -inf */ {1, {{0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* -Fn */ {2, {{0xc0, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xc0, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* -0 */ {1, {{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* +0 */ {1, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* +Fn */ {2, {{0x40, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x40, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* +inf */ {1, {{0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* QNaN */ {2, {{0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe}}}, + /* SNaN */ {2, {{0x7f, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x7f, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd}}}, + }, + + /* M4 == 4: extended */ + { + /* -inf */ {1, {{0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* -Fn */ {2, {{0xc0, 0x04, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xc0, 0x04, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* -0 */ {1, {{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* +0 */ {1, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* +Fn */ {2, {{0x40, 0x04, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x40, 0x04, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* +inf */ {1, {{0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* QNaN */ {2, {{0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe}}}, + /* SNaN */ {2, {{0x7f, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x7f, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd}}}, + }, +}; +static const unsigned char default_nans[N_FORMATS][16] = { + /* M4 == 2: short */ {0x7f, 0xc0, 0x00, 0x00}, + /* M4 == 3: long */ {0x7f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + /* M4 == 4: extended */ {0x7f, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, +}; + +static void dump_v(FILE *f, const void *v, size_t n) +{ + for (int i = 0; i < n; i++) { + fprintf(f, "%02x", ((const unsigned char *)v)[i]); + } +} + +static void snan_to_qnan(char *v, int fmt) +{ + size_t bit = 1 + e_bits[fmt]; + v[bit / 8] |= 1 << (7 - (bit % 8)); +} + +#endif diff --git a/tests/tcg/s390x/fma.c b/tests/tcg/s390x/fma.c new file mode 100644 index 0000000000..6872f59a7a --- /dev/null +++ b/tests/tcg/s390x/fma.c @@ -0,0 +1,233 @@ +/* + * Test floating-point multiply-and-add instructions. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include +#include +#include +#include +#include "float.h" + +union val { + float e; + double d; + long double x; + char buf[16]; +}; + +/* + * PoP tables as close to the original as possible. + */ +static const char *table1[N_SIGNED_CLASSES][N_SIGNED_CLASSES] = { + /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */ + {/* -inf */ "P(+inf)", "P(+inf)", "Xi: T(dNaN)", "Xi: T(dNaN)", "P(-inf)", "P(-inf)", "P(b)", "Xi: T(b*)"}, + {/* -Fn */ "P(+inf)", "P(a*b)", "P(+0)", "P(-0)", "P(a*b)", "P(-inf)", "P(b)", "Xi: T(b*)"}, + {/* -0 */ "Xi: T(dNaN)", "P(+0)", "P(+0)", "P(-0)", "P(-0)", "Xi: T(dNaN)", "P(b)", "Xi: T(b*)"}, + {/* +0 */ "Xi: T(dNaN)", "P(-0)", "P(-0)", "P(+0)", "P(+0)", "Xi: T(dNaN)", "P(b)", "Xi: T(b*)"}, + {/* +Fn */ "P(-inf)", "P(a*b)", "P(-0)", "P(+0)", "P(a*b)", "P(+inf)", "P(b)", "Xi: T(b*)"}, + {/* +inf */ "P(-inf)", "P(-inf)", "Xi: T(dNaN)", "Xi: T(dNaN)", "P(+inf)", "P(+inf)", "P(b)", "Xi: T(b*)"}, + {/* QNaN */ "P(a)", "P(a)", "P(a)", "P(a)", "P(a)", "P(a)", "P(a)", "Xi: T(b*)"}, + {/* SNaN */ "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)"}, +}; + +static const char *table2[N_SIGNED_CLASSES][N_SIGNED_CLASSES] = { + /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */ + {/* -inf */ "T(-inf)", "T(-inf)", "T(-inf)", "T(-inf)", "T(-inf)", "Xi: T(dNaN)", "T(c)", "Xi: T(c*)"}, + {/* -Fn */ "T(-inf)", "R(p+c)", "R(p)", "R(p)", "R(p+c)", "T(+inf)", "T(c)", "Xi: T(c*)"}, + {/* -0 */ "T(-inf)", "R(c)", "T(-0)", "Rezd", "R(c)", "T(+inf)", "T(c)", "Xi: T(c*)"}, + {/* +0 */ "T(-inf)", "R(c)", "Rezd", "T(+0)", "R(c)", "T(+inf)", "T(c)", "Xi: T(c*)"}, + {/* +Fn */ "T(-inf)", "R(p+c)", "R(p)", "R(p)", "R(p+c)", "T(+inf)", "T(c)", "Xi: T(c*)"}, + {/* +inf */ "Xi: T(dNaN)", "T(+inf)", "T(+inf)", "T(+inf)", "T(+inf)", "T(+inf)", "T(c)", "Xi: T(c*)"}, + {/* QNaN */ "T(p)", "T(p)", "T(p)", "T(p)", "T(p)", "T(p)", "T(p)", "Xi: T(c*)"}, + /* SNaN: can't happen */ +}; + +static void interpret_tables(union val *r, bool *xi, int fmt, + int cls_a, const union val *a, + int cls_b, const union val *b, + int cls_c, const union val *c) +{ + const char *spec1 = table1[cls_a][cls_b]; + const char *spec2; + union val p; + int cls_p; + + *xi = false; + + if (strcmp(spec1, "P(-inf)") == 0) { + cls_p = CLASS_MINUS_INF; + } else if (strcmp(spec1, "P(+inf)") == 0) { + cls_p = CLASS_PLUS_INF; + } else if (strcmp(spec1, "P(-0)") == 0) { + cls_p = CLASS_MINUS_ZERO; + } else if (strcmp(spec1, "P(+0)") == 0) { + cls_p = CLASS_PLUS_ZERO; + } else if (strcmp(spec1, "P(a)") == 0) { + cls_p = cls_a; + memcpy(&p, a, sizeof(p)); + } else if (strcmp(spec1, "P(b)") == 0) { + cls_p = cls_b; + memcpy(&p, b, sizeof(p)); + } else if (strcmp(spec1, "P(a*b)") == 0) { + /* + * In the general case splitting fma into multiplication and addition + * doesn't work, but this is the case with our test inputs. + */ + cls_p = cls_a == cls_b ? CLASS_PLUS_FN : CLASS_MINUS_FN; + switch (fmt) { + case 0: + p.e = a->e * b->e; + break; + case 1: + p.d = a->d * b->d; + break; + case 2: + p.x = a->x * b->x; + break; + default: + fprintf(stderr, "Unsupported fmt: %d\n", fmt); + exit(1); + } + } else if (strcmp(spec1, "Xi: T(dNaN)") == 0) { + memcpy(r, default_nans[fmt], sizeof(*r)); + *xi = true; + return; + } else if (strcmp(spec1, "Xi: T(a*)") == 0) { + memcpy(r, a, sizeof(*r)); + snan_to_qnan(r->buf, fmt); + *xi = true; + return; + } else if (strcmp(spec1, "Xi: T(b*)") == 0) { + memcpy(r, b, sizeof(*r)); + snan_to_qnan(r->buf, fmt); + *xi = true; + return; + } else { + fprintf(stderr, "Unsupported spec1: %s\n", spec1); + exit(1); + } + + spec2 = table2[cls_p][cls_c]; + if (strcmp(spec2, "T(-inf)") == 0) { + memcpy(r, signed_floats[fmt][CLASS_MINUS_INF].v[0], sizeof(*r)); + } else if (strcmp(spec2, "T(+inf)") == 0) { + memcpy(r, signed_floats[fmt][CLASS_PLUS_INF].v[0], sizeof(*r)); + } else if (strcmp(spec2, "T(-0)") == 0) { + memcpy(r, signed_floats[fmt][CLASS_MINUS_ZERO].v[0], sizeof(*r)); + } else if (strcmp(spec2, "T(+0)") == 0 || strcmp(spec2, "Rezd") == 0) { + memcpy(r, signed_floats[fmt][CLASS_PLUS_ZERO].v[0], sizeof(*r)); + } else if (strcmp(spec2, "R(c)") == 0 || strcmp(spec2, "T(c)") == 0) { + memcpy(r, c, sizeof(*r)); + } else if (strcmp(spec2, "R(p)") == 0 || strcmp(spec2, "T(p)") == 0) { + memcpy(r, &p, sizeof(*r)); + } else if (strcmp(spec2, "R(p+c)") == 0 || strcmp(spec2, "T(p+c)") == 0) { + switch (fmt) { + case 0: + r->e = p.e + c->e; + break; + case 1: + r->d = p.d + c->d; + break; + case 2: + r->x = p.x + c->x; + break; + default: + fprintf(stderr, "Unsupported fmt: %d\n", fmt); + exit(1); + } + } else if (strcmp(spec2, "Xi: T(dNaN)") == 0) { + memcpy(r, default_nans[fmt], sizeof(*r)); + *xi = true; + } else if (strcmp(spec2, "Xi: T(c*)") == 0) { + memcpy(r, c, sizeof(*r)); + snan_to_qnan(r->buf, fmt); + *xi = true; + } else { + fprintf(stderr, "Unsupported spec2: %s\n", spec2); + exit(1); + } +} + +struct iter { + int fmt; + int cls[3]; + int val[3]; +}; + +static bool iter_next(struct iter *it) +{ + int i; + + for (i = 2; i >= 0; i--) { + if (++it->val[i] != signed_floats[it->fmt][it->cls[i]].n) { + return true; + } + it->val[i] = 0; + + if (++it->cls[i] != N_SIGNED_CLASSES) { + return true; + } + it->cls[i] = 0; + } + + return ++it->fmt != N_FORMATS; +} + +int main(void) +{ + int ret = EXIT_SUCCESS; + struct iter it = {}; + + do { + size_t n = float_sizes[it.fmt]; + union val a, b, c, exp, res; + bool xi_exp, xi; + + memcpy(&a, signed_floats[it.fmt][it.cls[0]].v[it.val[0]], sizeof(a)); + memcpy(&b, signed_floats[it.fmt][it.cls[1]].v[it.val[1]], sizeof(b)); + memcpy(&c, signed_floats[it.fmt][it.cls[2]].v[it.val[2]], sizeof(c)); + + interpret_tables(&exp, &xi_exp, it.fmt, + it.cls[1], &b, it.cls[2], &c, it.cls[0], &a); + + memcpy(&res, &a, sizeof(res)); + feclearexcept(FE_ALL_EXCEPT); + switch (it.fmt) { + case 0: + asm("maebr %[a],%[b],%[c]" + : [a] "+f" (res.e) : [b] "f" (b.e), [c] "f" (c.e)); + break; + case 1: + asm("madbr %[a],%[b],%[c]" + : [a] "+f" (res.d) : [b] "f" (b.d), [c] "f" (c.d)); + break; + case 2: + asm("wfmaxb %[a],%[c],%[b],%[a]" + : [a] "+v" (res.x) : [b] "v" (b.x), [c] "v" (c.x)); + break; + default: + fprintf(stderr, "Unsupported fmt: %d\n", it.fmt); + exit(1); + } + xi = fetestexcept(FE_ALL_EXCEPT) == FE_INVALID; + + if (memcmp(&res, &exp, n) != 0 || xi != xi_exp) { + fprintf(stderr, "[ FAILED ] "); + dump_v(stderr, &b, n); + fprintf(stderr, " * "); + dump_v(stderr, &c, n); + fprintf(stderr, " + "); + dump_v(stderr, &a, n); + fprintf(stderr, ": actual="); + dump_v(stderr, &res, n); + fprintf(stderr, "/%d, expected=", (int)xi); + dump_v(stderr, &exp, n); + fprintf(stderr, "/%d\n", (int)xi_exp); + ret = EXIT_FAILURE; + } + } while (iter_next(&it)); + + return ret; +} diff --git a/tests/tcg/s390x/vfminmax.c b/tests/tcg/s390x/vfminmax.c index 22629df160..e66285f762 100644 --- a/tests/tcg/s390x/vfminmax.c +++ b/tests/tcg/s390x/vfminmax.c @@ -4,6 +4,8 @@ #include #include +#include "float.h" + /* * vfmin/vfmax instruction execution. */ @@ -21,98 +23,21 @@ static void vfminmax(unsigned int op, unsigned int m4, unsigned int m5, unsigned int m6, void *v1, const void *v2, const void *v3) { - insn[3] = (m6 << 4) | m5; - insn[4] = (m4 << 4) | 0x0e; - insn[5] = op; + insn[3] = (m6 << 4) | m5; + insn[4] = (m4 << 4) | 0x0e; + insn[5] = op; asm("vl %%v25,%[v2]\n" "vl %%v26,%[v3]\n" "ex 0,%[insn]\n" "vst %%v24,%[v1]\n" : [v1] "=m" (*(char (*)[16])v1) - : [v2] "m" (*(char (*)[16])v2) - , [v3] "m" (*(char (*)[16])v3) - , [insn] "m"(insn) + : [v2] "m" (*(const char (*)[16])v2) + , [v3] "m" (*(const char (*)[16])v3) + , [insn] "m" (insn) : "v24", "v25", "v26"); } -/* - * Floating-point value classes. - */ -#define N_FORMATS 3 -#define N_SIGNED_CLASSES 8 -static const size_t float_sizes[N_FORMATS] = { - /* M4 == 2: short */ 4, - /* M4 == 3: long */ 8, - /* M4 == 4: extended */ 16, -}; -static const size_t e_bits[N_FORMATS] = { - /* M4 == 2: short */ 8, - /* M4 == 3: long */ 11, - /* M4 == 4: extended */ 15, -}; -static const unsigned char signed_floats[N_FORMATS][N_SIGNED_CLASSES][2][16] = { - /* M4 == 2: short */ - { - /* -inf */ {{0xff, 0x80, 0x00, 0x00}, - {0xff, 0x80, 0x00, 0x00}}, - /* -Fn */ {{0xc2, 0x28, 0x00, 0x00}, - {0xc2, 0x29, 0x00, 0x00}}, - /* -0 */ {{0x80, 0x00, 0x00, 0x00}, - {0x80, 0x00, 0x00, 0x00}}, - /* +0 */ {{0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00}}, - /* +Fn */ {{0x42, 0x28, 0x00, 0x00}, - {0x42, 0x2a, 0x00, 0x00}}, - /* +inf */ {{0x7f, 0x80, 0x00, 0x00}, - {0x7f, 0x80, 0x00, 0x00}}, - /* QNaN */ {{0x7f, 0xff, 0xff, 0xff}, - {0x7f, 0xff, 0xff, 0xfe}}, - /* SNaN */ {{0x7f, 0xbf, 0xff, 0xff}, - {0x7f, 0xbf, 0xff, 0xfd}}, - }, - - /* M4 == 3: long */ - { - /* -inf */ {{0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - /* -Fn */ {{0xc0, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0xc0, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - /* -0 */ {{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - /* +0 */ {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - /* +Fn */ {{0x40, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x40, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - /* +inf */ {{0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - /* QNaN */ {{0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, - {0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe}}, - /* SNaN */ {{0x7f, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, - {0x7f, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd}}, - }, - - /* M4 == 4: extended */ - { - /* -inf */ {{0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - /* -Fn */ {{0xc0, 0x04, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0xc0, 0x04, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - /* -0 */ {{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - /* +0 */ {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - /* +Fn */ {{0x40, 0x04, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x40, 0x04, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - /* +inf */ {{0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - /* QNaN */ {{0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, - {0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe}}, - /* SNaN */ {{0x7f, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, - {0x7f, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd}}, - }, -}; - /* * PoP tables as close to the original as possible. */ @@ -285,13 +210,6 @@ struct signed_test { }, }; -static void dump_v(FILE *f, const void *v, size_t n) -{ - for (int i = 0; i < n; i++) { - fprintf(f, "%02x", ((const unsigned char *)v)[i]); - } -} - static int signed_test(struct signed_test *test, int m4, int m5, const void *v1_exp, bool xi_exp, const void *v2, const void *v3) @@ -320,10 +238,28 @@ static int signed_test(struct signed_test *test, int m4, int m5, return 0; } -static void snan_to_qnan(char *v, int m4) +struct iter { + int cls[2]; + int val[2]; +}; + +static bool iter_next(struct iter *it, int fmt) { - size_t bit = 1 + e_bits[m4 - 2]; - v[bit / 8] |= 1 << (7 - (bit % 8)); + int i; + + for (i = 1; i >= 0; i--) { + if (++it->val[i] != signed_floats[fmt][it->cls[i]].n) { + return true; + } + it->val[i] = 0; + + if (++it->cls[i] != N_SIGNED_CLASSES) { + return true; + } + it->cls[i] = 0; + } + + return false; } int main(void) @@ -333,72 +269,71 @@ int main(void) for (i = 0; i < sizeof(signed_tests) / sizeof(signed_tests[0]); i++) { struct signed_test *test = &signed_tests[i]; - int m4; + int fmt; - for (m4 = 2; m4 <= 4; m4++) { - const unsigned char (*floats)[2][16] = signed_floats[m4 - 2]; - size_t float_size = float_sizes[m4 - 2]; + for (fmt = 0; fmt < N_FORMATS; fmt++) { + size_t float_size = float_sizes[fmt]; + int m4 = fmt + 2; int m5; for (m5 = 0; m5 <= 8; m5 += 8) { char v1_exp[16], v2[16], v3[16]; bool xi_exp = false; + struct iter it = {}; int pos = 0; - int i2; - for (i2 = 0; i2 < N_SIGNED_CLASSES * 2; i2++) { - int i3; + do { + const char *spec = test->table[it.cls[0]][it.cls[1]]; - for (i3 = 0; i3 < N_SIGNED_CLASSES * 2; i3++) { - const char *spec = test->table[i2 / 2][i3 / 2]; + memcpy(&v2[pos], + signed_floats[fmt][it.cls[0]].v[it.val[0]], + float_size); + memcpy(&v3[pos], + signed_floats[fmt][it.cls[1]].v[it.val[1]], + float_size); + if (strcmp(spec, "T(a)") == 0 || + strcmp(spec, "Xi: T(a)") == 0) { + memcpy(&v1_exp[pos], &v2[pos], float_size); + } else if (strcmp(spec, "T(b)") == 0 || + strcmp(spec, "Xi: T(b)") == 0) { + memcpy(&v1_exp[pos], &v3[pos], float_size); + } else if (strcmp(spec, "Xi: T(a*)") == 0) { + memcpy(&v1_exp[pos], &v2[pos], float_size); + snan_to_qnan(&v1_exp[pos], fmt); + } else if (strcmp(spec, "Xi: T(b*)") == 0) { + memcpy(&v1_exp[pos], &v3[pos], float_size); + snan_to_qnan(&v1_exp[pos], fmt); + } else if (strcmp(spec, "T(M(a,b))") == 0) { + /* + * Comparing floats is risky, since the compiler might + * generate the same instruction that we are testing. + * Compare ints instead. This works, because we get + * here only for +-Fn, and the corresponding test + * values have identical exponents. + */ + int v2_int = *(int *)&v2[pos]; + int v3_int = *(int *)&v3[pos]; - memcpy(&v2[pos], floats[i2 / 2][i2 % 2], float_size); - memcpy(&v3[pos], floats[i3 / 2][i3 % 2], float_size); - if (strcmp(spec, "T(a)") == 0 || - strcmp(spec, "Xi: T(a)") == 0) { + if ((v2_int < v3_int) == + ((test->op == VFMIN) != (v2_int < 0))) { memcpy(&v1_exp[pos], &v2[pos], float_size); - } else if (strcmp(spec, "T(b)") == 0 || - strcmp(spec, "Xi: T(b)") == 0) { - memcpy(&v1_exp[pos], &v3[pos], float_size); - } else if (strcmp(spec, "Xi: T(a*)") == 0) { - memcpy(&v1_exp[pos], &v2[pos], float_size); - snan_to_qnan(&v1_exp[pos], m4); - } else if (strcmp(spec, "Xi: T(b*)") == 0) { - memcpy(&v1_exp[pos], &v3[pos], float_size); - snan_to_qnan(&v1_exp[pos], m4); - } else if (strcmp(spec, "T(M(a,b))") == 0) { - /* - * Comparing floats is risky, since the compiler - * might generate the same instruction that we are - * testing. Compare ints instead. This works, - * because we get here only for +-Fn, and the - * corresponding test values have identical - * exponents. - */ - int v2_int = *(int *)&v2[pos]; - int v3_int = *(int *)&v3[pos]; - - if ((v2_int < v3_int) == - ((test->op == VFMIN) != (v2_int < 0))) { - memcpy(&v1_exp[pos], &v2[pos], float_size); - } else { - memcpy(&v1_exp[pos], &v3[pos], float_size); - } } else { - fprintf(stderr, "Unexpected spec: %s\n", spec); - return 1; - } - xi_exp |= spec[0] == 'X'; - pos += float_size; - - if ((m5 & 8) || pos == 16) { - ret |= signed_test(test, m4, m5, - v1_exp, xi_exp, v2, v3); - pos = 0; - xi_exp = false; + memcpy(&v1_exp[pos], &v3[pos], float_size); } + } else { + fprintf(stderr, "Unexpected spec: %s\n", spec); + return 1; } - } + xi_exp |= spec[0] == 'X'; + pos += float_size; + + if ((m5 & 8) || pos == 16) { + ret |= signed_test(test, m4, m5, + v1_exp, xi_exp, v2, v3); + pos = 0; + xi_exp = false; + } + } while (iter_next(&it, fmt)); if (pos != 0) { ret |= signed_test(test, m4, m5, v1_exp, xi_exp, v2, v3); From 0271fdc650b212533b8aeaecbedfe8ccf6bbbef3 Mon Sep 17 00:00:00 2001 From: Jared Rossi Date: Thu, 14 Nov 2024 19:27:42 -0500 Subject: [PATCH 04/12] docs/system/s390x/bootdevices: Update loadparm documentation Update documentation to include per-device loadparm support. Signed-off-by: Jared Rossi Reviewed-by: Thomas Huth Message-ID: <20241115002742.3576842-1-jrossi@linux.ibm.com> Signed-off-by: Thomas Huth --- docs/system/s390x/bootdevices.rst | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/docs/system/s390x/bootdevices.rst b/docs/system/s390x/bootdevices.rst index 1a1a764c1c..97b3914785 100644 --- a/docs/system/s390x/bootdevices.rst +++ b/docs/system/s390x/bootdevices.rst @@ -79,7 +79,29 @@ The second way to use this parameter is to use a number in the range from 0 to 31. The numbers that can be used here correspond to the numbers that are shown when using the ``PROMPT`` option, and the s390-ccw bios will then try to automatically boot the kernel that is associated with the given number. -Note that ``0`` can be used to boot the default entry. +Note that ``0`` can be used to boot the default entry. If the machine +``loadparm`` is not assigned a value, then the default entry is used. + +By default, the machine ``loadparm`` applies to all boot devices. If multiple +devices are assigned a ``bootindex`` and the ``loadparm`` is to be different +between them, an independent ``loadparm`` may be assigned on a per-device basis. + +An example guest using per-device ``loadparm``:: + + qemu-system-s390x -drive if=none,id=dr1,file=primary.qcow2 \ + -device virtio-blk,drive=dr1,bootindex=1 \ + -drive if=none,id=dr2,file=secondary.qcow2 \ + -device virtio-blk,drive=dr2,bootindex=2,loadparm=3 + +In this case, the primary boot device will attempt to IPL using the default +entry (because no ``loadparm`` is specified for this device or for the +machine). If that device fails to boot, the secondary device will attempt to +IPL using entry number 3. + +If a ``loadparm`` is specified on both the machine and a device, the per-device +value will superseded the machine value. Per-device ``loadparm`` values are +only used for devices with an assigned ``bootindex``. The machine ``loadparm`` +is used when attempting to boot without a ``bootindex``. Booting from a network device From b8c5fdc6588f82d95807be0eb2215d215a3ba16e Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 11 Nov 2024 11:55:06 +0100 Subject: [PATCH 05/12] docs/system/bootindex: Make it clear that s390x can also boot from virtio-net Let's make it clear that s390x can also boot from virtio-net, to avoid that people think that s390x can only boot from disk devices. Reported-by: Boris Fiuczynski Message-ID: <20241111105506.264640-1-thuth@redhat.com> Reviewed-by: Prasad Pandit Reviewed-by: Boris Fiuczynski Signed-off-by: Thomas Huth --- docs/system/bootindex.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/system/bootindex.rst b/docs/system/bootindex.rst index 988f7b3beb..5e1b33ee22 100644 --- a/docs/system/bootindex.rst +++ b/docs/system/bootindex.rst @@ -53,7 +53,7 @@ booting. For instance, the x86 PC BIOS boot specification allows only one disk to be bootable. If boot from disk fails for some reason, the x86 BIOS won't retry booting from other disk. It can still try to boot from floppy or net, though. In the case of s390x BIOS, the BIOS will try up to -8 total devices, any number of which may be disks. +8 total devices, any number of which may be disks or virtio-net devices. Sometimes, firmware cannot map the device path QEMU wants firmware to boot from to a boot method. It doesn't happen for devices the firmware From 6e7c96ae61e0542e97d385084f1f2281a0331054 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 13 Nov 2024 12:47:41 +0100 Subject: [PATCH 06/12] hw/s390x: Restrict "loadparm" property to devices that can be used for booting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit bb185de423 ("s390x: Add individual loadparm assignment to CCW device") added a "loadparm" property to all CCW devices. This was a little bit unfortunate, since this property is only useful for devices that can be used for booting, but certainly it is not useful for devices like virtio-gpu or virtio-tablet. Thus let's restrict the property to CCW devices that we can boot from (i.e. virtio-block, virtio-net and vfio-ccw devices). Message-ID: <20241113114741.681096-1-thuth@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Jared Rossi Signed-off-by: Thomas Huth --- hw/s390x/ccw-device.c | 4 +--- hw/s390x/ccw-device.h | 5 +++++ hw/s390x/virtio-ccw-blk.c | 1 + hw/s390x/virtio-ccw-net.c | 1 + hw/vfio/ccw.c | 1 + 5 files changed, 9 insertions(+), 3 deletions(-) diff --git a/hw/s390x/ccw-device.c b/hw/s390x/ccw-device.c index 230cc09e03..30f2fb486f 100644 --- a/hw/s390x/ccw-device.c +++ b/hw/s390x/ccw-device.c @@ -73,7 +73,7 @@ static void ccw_device_set_loadparm(Object *obj, Visitor *v, s390_ipl_fmt_loadparm(dev->loadparm, val, errp); } -static const PropertyInfo ccw_loadparm = { +const PropertyInfo ccw_loadparm = { .name = "ccw_loadparm", .description = "Up to 8 chars in set of [A-Za-z0-9. ] to pass" " to the guest loader/kernel", @@ -85,8 +85,6 @@ static Property ccw_device_properties[] = { DEFINE_PROP_CSS_DEV_ID("devno", CcwDevice, devno), DEFINE_PROP_CSS_DEV_ID_RO("dev_id", CcwDevice, dev_id), DEFINE_PROP_CSS_DEV_ID_RO("subch_id", CcwDevice, subch_id), - DEFINE_PROP("loadparm", CcwDevice, loadparm, ccw_loadparm, - typeof(uint8_t[8])), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/s390x/ccw-device.h b/hw/s390x/ccw-device.h index 1e1737c0f3..4439feb140 100644 --- a/hw/s390x/ccw-device.h +++ b/hw/s390x/ccw-device.h @@ -51,4 +51,9 @@ static inline CcwDevice *to_ccw_dev_fast(DeviceState *d) OBJECT_DECLARE_TYPE(CcwDevice, CCWDeviceClass, CCW_DEVICE) +extern const PropertyInfo ccw_loadparm; + +#define DEFINE_PROP_CCW_LOADPARM(_n, _s, _f) \ + DEFINE_PROP(_n, _s, _f, ccw_loadparm, typeof(uint8_t[8])) + #endif diff --git a/hw/s390x/virtio-ccw-blk.c b/hw/s390x/virtio-ccw-blk.c index 8e0e58b77d..2364432c6e 100644 --- a/hw/s390x/virtio-ccw-blk.c +++ b/hw/s390x/virtio-ccw-blk.c @@ -48,6 +48,7 @@ static Property virtio_ccw_blk_properties[] = { VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), + DEFINE_PROP_CCW_LOADPARM("loadparm", CcwDevice, loadparm), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/s390x/virtio-ccw-net.c b/hw/s390x/virtio-ccw-net.c index 484e617659..a4a3f65c7e 100644 --- a/hw/s390x/virtio-ccw-net.c +++ b/hw/s390x/virtio-ccw-net.c @@ -51,6 +51,7 @@ static Property virtio_ccw_net_properties[] = { VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), + DEFINE_PROP_CCW_LOADPARM("loadparm", CcwDevice, loadparm), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index 24703c814a..c1cd7736cd 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -662,6 +662,7 @@ static Property vfio_ccw_properties[] = { DEFINE_PROP_LINK("iommufd", VFIOCCWDevice, vdev.iommufd, TYPE_IOMMUFD_BACKEND, IOMMUFDBackend *), #endif + DEFINE_PROP_CCW_LOADPARM("loadparm", CcwDevice, loadparm), DEFINE_PROP_END_OF_LIST(), }; From 429442e52d94f890fa194a151e8cd649b04e9e63 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 15 Nov 2024 15:12:02 +0100 Subject: [PATCH 07/12] hw: Add "loadparm" property to scsi disk devices for booting on s390x While adding the new flexible boot order feature on s390x recently, we missed to add the "loadparm" property to the scsi-hd and scsi-cd devices. This property is required on s390x to pass the information to the boot loader about which kernel should be started or whether the boot menu should be shown. But even more serious: The missing property is now causing trouble with the corresponding libvirt patches that assume that the "loadparm" property is either settable for all bootable devices (when the "boot order" feature is implemented in QEMU), or none (meaning the behaviour of older QEMUs that only allowed one "loadparm" at the machine level). To fix this broken situation, let's implement the "loadparm" property in for the SCSI devices, too. Message-ID: <20241115141202.1877294-1-thuth@redhat.com> Acked-by: Eric Farman Signed-off-by: Thomas Huth --- hw/core/qdev-properties-system.c | 26 +++++++++++++++++ hw/s390x/ipl.c | 19 ++++--------- hw/scsi/scsi-disk.c | 43 +++++++++++++++++++++++++++++ include/hw/qdev-properties-system.h | 3 ++ 4 files changed, 78 insertions(+), 13 deletions(-) diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index 35deef05f3..a61c5ee6dd 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -58,6 +58,32 @@ static bool check_prop_still_unset(Object *obj, const char *name, return false; } +bool qdev_prop_sanitize_s390x_loadparm(uint8_t *loadparm, const char *str, + Error **errp) +{ + int i, len; + + len = strlen(str); + if (len > 8) { + error_setg(errp, "'loadparm' can only contain up to 8 characters"); + return false; + } + + for (i = 0; i < len; i++) { + uint8_t c = qemu_toupper(str[i]); /* mimic HMC */ + + if (qemu_isalnum(c) || c == '.' || c == ' ') { + loadparm[i] = c; + } else { + error_setg(errp, + "invalid character in 'loadparm': '%c' (ASCII 0x%02x)", + c, c); + return false; + } + } + + return true; +} /* --- drive --- */ diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index dc02b0fdda..30734661ad 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -418,21 +418,9 @@ static uint64_t s390_ipl_map_iplb_chain(IplParameterBlock *iplb_chain) void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp) { - int i; - /* Initialize the loadparm with spaces */ memset(loadparm, ' ', LOADPARM_LEN); - for (i = 0; i < LOADPARM_LEN && str[i]; i++) { - uint8_t c = qemu_toupper(str[i]); /* mimic HMC */ - - if (qemu_isalnum(c) || c == '.' || c == ' ') { - loadparm[i] = c; - } else { - error_setg(errp, "LOADPARM: invalid character '%c' (ASCII 0x%02x)", - c, c); - return; - } - } + qdev_prop_sanitize_s390x_loadparm(loadparm, str, errp); } void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t *ebcdic_lp) @@ -452,6 +440,7 @@ static bool s390_build_iplb(DeviceState *dev_st, IplParameterBlock *iplb) SCSIDevice *sd; int devtype; uint8_t *lp; + g_autofree void *scsi_lp = NULL; /* * Currently allow IPL only from CCW devices. @@ -463,6 +452,10 @@ static bool s390_build_iplb(DeviceState *dev_st, IplParameterBlock *iplb) switch (devtype) { case CCW_DEVTYPE_SCSI: sd = SCSI_DEVICE(dev_st); + scsi_lp = object_property_get_str(OBJECT(sd), "loadparm", NULL); + if (scsi_lp && strlen(scsi_lp) > 0) { + lp = scsi_lp; + } iplb->len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN); iplb->blk0_len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN - S390_IPLB_HEADER_LEN); diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index cb222da7a5..8e553487d5 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -32,6 +32,7 @@ #include "migration/vmstate.h" #include "hw/scsi/emulation.h" #include "scsi/constants.h" +#include "sysemu/arch_init.h" #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "hw/block/block.h" @@ -111,6 +112,7 @@ struct SCSIDiskState { char *vendor; char *product; char *device_id; + char *loadparm; /* only for s390x */ bool tray_open; bool tray_locked; /* @@ -3135,6 +3137,43 @@ BlockAIOCB *scsi_dma_writev(int64_t offset, QEMUIOVector *iov, return blk_aio_pwritev(s->qdev.conf.blk, offset, iov, 0, cb, cb_opaque); } +static char *scsi_property_get_loadparm(Object *obj, Error **errp) +{ + return g_strdup(SCSI_DISK_BASE(obj)->loadparm); +} + +static void scsi_property_set_loadparm(Object *obj, const char *value, + Error **errp) +{ + void *lp_str; + + if (object_property_get_int(obj, "bootindex", NULL) < 0) { + error_setg(errp, "'loadparm' is only valid for boot devices"); + return; + } + + lp_str = g_malloc0(strlen(value)); + if (!qdev_prop_sanitize_s390x_loadparm(lp_str, value, errp)) { + g_free(lp_str); + return; + } + SCSI_DISK_BASE(obj)->loadparm = lp_str; +} + +static void scsi_property_add_specifics(DeviceClass *dc) +{ + ObjectClass *oc = OBJECT_CLASS(dc); + + /* The loadparm property is only supported on s390x */ + if (arch_type & QEMU_ARCH_S390X) { + object_class_property_add_str(oc, "loadparm", + scsi_property_get_loadparm, + scsi_property_set_loadparm); + object_class_property_set_description(oc, "loadparm", + "load parameter (s390x only)"); + } +} + static void scsi_disk_base_class_initfn(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -3218,6 +3257,8 @@ static void scsi_hd_class_initfn(ObjectClass *klass, void *data) dc->desc = "virtual SCSI disk"; device_class_set_props(dc, scsi_hd_properties); dc->vmsd = &vmstate_scsi_disk_state; + + scsi_property_add_specifics(dc); } static const TypeInfo scsi_hd_info = { @@ -3258,6 +3299,8 @@ static void scsi_cd_class_initfn(ObjectClass *klass, void *data) dc->desc = "virtual SCSI CD-ROM"; device_class_set_props(dc, scsi_cd_properties); dc->vmsd = &vmstate_scsi_disk_state; + + scsi_property_add_specifics(dc); } static const TypeInfo scsi_cd_info = { diff --git a/include/hw/qdev-properties-system.h b/include/hw/qdev-properties-system.h index cdcc63056e..7ec37f6316 100644 --- a/include/hw/qdev-properties-system.h +++ b/include/hw/qdev-properties-system.h @@ -3,6 +3,9 @@ #include "hw/qdev-properties.h" +bool qdev_prop_sanitize_s390x_loadparm(uint8_t *loadparm, const char *str, + Error **errp); + extern const PropertyInfo qdev_prop_chr; extern const PropertyInfo qdev_prop_macaddr; extern const PropertyInfo qdev_prop_reserved_region; From 8c797468116d19940fb758efa749eae414616e3a Mon Sep 17 00:00:00 2001 From: Jared Rossi Date: Fri, 8 Nov 2024 14:41:36 -0500 Subject: [PATCH 08/12] pc-bios/s390x: Initialize cdrom type to false for each IPL device Clear information about cdrom type so that current IPL device isn't tainted by stale data from previous devices. Signed-off-by: Jared Rossi Reviewed-by: Thomas Huth Message-ID: <20241108194136.2833932-1-jrossi@linux.ibm.com> Signed-off-by: Thomas Huth --- pc-bios/s390-ccw/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c index a4d1c05aac..7509755e36 100644 --- a/pc-bios/s390-ccw/main.c +++ b/pc-bios/s390-ccw/main.c @@ -242,6 +242,7 @@ static bool find_boot_device(void) static int virtio_setup(void) { VDev *vdev = virtio_get_device(); + vdev->is_cdrom = false; int ret; switch (vdev->senseid.cu_model) { From 1056ca1e70dc6e0458238141bcebfb7810cede6d Mon Sep 17 00:00:00 2001 From: Jared Rossi Date: Thu, 14 Nov 2024 11:19:52 -0500 Subject: [PATCH 09/12] pc-bios/s390x: Initialize machine loadparm before probing IPL devices Commit bb185de423 ("s390x: Add individual loadparm assignment to CCW device") allowed boot devices to be assigned a loadparm value independent of the machine value, however, when no boot devices are defined, the machine loadparm becomes ignored. Therefore, let's check the machine loadparm prior to probing the devices. Signed-off-by: Jared Rossi Reviewed-by: Thomas Huth Message-ID: <20241114161952.3508554-1-jrossi@linux.ibm.com> Signed-off-by: Thomas Huth --- pc-bios/s390-ccw/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c index 7509755e36..76bf743900 100644 --- a/pc-bios/s390-ccw/main.c +++ b/pc-bios/s390-ccw/main.c @@ -191,7 +191,7 @@ static void boot_setup(void) { char lpmsg[] = "LOADPARM=[________]\n"; - if (memcmp(iplb.loadparm, NO_LOADPARM, LOADPARM_LEN) != 0) { + if (have_iplb && memcmp(iplb.loadparm, NO_LOADPARM, LOADPARM_LEN) != 0) { ebcdic_to_ascii((char *) iplb.loadparm, loadparm_str, LOADPARM_LEN); } else { sclp_get_loadparm_ascii(loadparm_str); @@ -316,6 +316,7 @@ void main(void) css_setup(); have_iplb = store_iplb(&iplb); if (!have_iplb) { + boot_setup(); probe_boot_device(); } From 6ba1f714c00f8839a8df9f643e0058f00da3fd25 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 11 Nov 2024 14:11:20 +0100 Subject: [PATCH 10/12] pc-bios/s390-ccw: Re-initialize receive queue index before each boot attempt Now that we can boot from multiple boot devices, we have to make sure to reinitialize static variables like rx_last_idx to avoid that they contain garbage data during the second boot attempt (which can lead to crashes when the code tries to access the wrong ring data). Message-ID: <20241111131120.317796-1-thuth@redhat.com> Reviewed-by: Jared Rossi Signed-off-by: Thomas Huth --- pc-bios/s390-ccw/virtio-net.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pc-bios/s390-ccw/virtio-net.c b/pc-bios/s390-ccw/virtio-net.c index f9854a22c3..578c89d0c5 100644 --- a/pc-bios/s390-ccw/virtio-net.c +++ b/pc-bios/s390-ccw/virtio-net.c @@ -51,6 +51,8 @@ int virtio_net_init(void *mac_addr) void *buf; int i; + rx_last_idx = 0; + vdev->guest_features[0] = VIRTIO_NET_F_MAC_BIT; virtio_setup_ccw(vdev); From 8cf9190fc18ea46ad98f3132276e130c92188234 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 18 Nov 2024 11:10:14 +0100 Subject: [PATCH 11/12] pc-bios: Update the s390 bios images with the recent fixes Add the fixes from the previous three commits to the binary, too. Signed-off-by: Thomas Huth --- pc-bios/s390-ccw.img | Bin 79608 -> 79608 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img index f9bac253066d71b64659a76d216e04040a365355..0cbedf0fa65fb87a9e19b3bcf0c4b81002801b5c 100644 GIT binary patch delta 16562 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 Go%}DbRbLeV From 4483d98ab82671165276026b09287053328c94d4 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 18 Nov 2024 15:32:26 +0000 Subject: [PATCH 12/12] .gitlab-ci.d: Raise timeout on cross-accel build jobs to 60m The current 30 minute timeout on the cross_accel_build_job template is a bit low: sometimes if the k8s runners are running slow the can hit it, for example this cross-arm64-xen-only job hit the 30 minute timeout while still not quite finished with the compile: https://gitlab.com/qemu-project/qemu/-/jobs/8401277985 This is partly a "runner performance can be unpredictable" issue: https://gitlab.com/qemu-project/qemu/-/jobs/8391726482 is the same job from just a day earlier and it finished in 16 minutes. But we already have build jobs that are higher timeouts than 30 minutes, so we have headroom to raise the timeout here to something we're less likely to hit on a slow runner. Bump the cross_accel_build_job timeout to 60 mins. Signed-off-by: Peter Maydell Message-ID: <20241118153226.1524542-1-peter.maydell@linaro.org> Reviewed-by: Thomas Huth Signed-off-by: Thomas Huth --- .gitlab-ci.d/crossbuild-template.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.d/crossbuild-template.yml b/.gitlab-ci.d/crossbuild-template.yml index 45a9810355..303943f818 100644 --- a/.gitlab-ci.d/crossbuild-template.yml +++ b/.gitlab-ci.d/crossbuild-template.yml @@ -57,7 +57,7 @@ extends: .base_job_template stage: build image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:$QEMU_CI_CONTAINER_TAG - timeout: 30m + timeout: 60m cache: paths: - ccache/