@@ -11,6 +11,8 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <linux/auxvec.h>
+#include <linux/compiler.h>
#include <sys/auxv.h>
#include <sys/prctl.h>
#include <asm/hwcap.h>
@@ -452,6 +454,107 @@ static void lrcpc3_sigill(void)
: "=r" (data0), "=r" (data1) : "r" (src) :);
}
+static void ignore_signal(int sig, siginfo_t *info, void *context)
+{
+ ucontext_t *uc = context;
+
+ uc->uc_mcontext.pc += 4;
+}
+
+static void ls64_sigill(void)
+{
+ struct sigaction ign, old;
+ char src[64] __aligned(64) = { 1 };
+
+ /*
+ * LS64, LS64_V, LS64_ACCDATA require target memory to be
+ * Device/Non-cacheable and the completer supports these
+ * instructions, otherwise we'll receive a SIGBUS. Since
+ * we are only testing the ABI here, so just ignore the
+ * SIGBUS and see if we can execute the instructions
+ * without receiving a SIGILL. Restore the handler of
+ * SIGBUS after this test.
+ */
+ ign.sa_sigaction = ignore_signal;
+ ign.sa_flags = SA_SIGINFO | SA_RESTART;
+ sigemptyset(&ign.sa_mask);
+ sigaction(SIGBUS, &ign, &old);
+
+ register void *xn asm ("x8") = src;
+ register u64 xt_1 asm ("x0");
+ register u64 __maybe_unused xt_2 asm ("x1");
+ register u64 __maybe_unused xt_3 asm ("x2");
+ register u64 __maybe_unused xt_4 asm ("x3");
+ register u64 __maybe_unused xt_5 asm ("x4");
+ register u64 __maybe_unused xt_6 asm ("x5");
+ register u64 __maybe_unused xt_7 asm ("x6");
+ register u64 __maybe_unused xt_8 asm ("x7");
+
+ /* LD64B x0, [x8] */
+ asm volatile(".inst 0xf83fd100" : "=r" (xt_1) : "r" (xn));
+
+ /* ST64B x0, [x8] */
+ asm volatile(".inst 0xf83f9100" : : "r" (xt_1), "r" (xn));
+
+ sigaction(SIGBUS, &old, NULL);
+}
+
+static void ls64_v_sigill(void)
+{
+ struct sigaction ign, old;
+ char dst[64] __aligned(64);
+
+ /* See comment in ls64_sigill() */
+ ign.sa_sigaction = ignore_signal;
+ ign.sa_flags = SA_SIGINFO | SA_RESTART;
+ sigemptyset(&ign.sa_mask);
+ sigaction(SIGBUS, &ign, &old);
+
+ register void *xn asm ("x8") = dst;
+ register u64 xt_1 asm ("x0") = 1;
+ register u64 __maybe_unused xt_2 asm ("x1") = 2;
+ register u64 __maybe_unused xt_3 asm ("x2") = 3;
+ register u64 __maybe_unused xt_4 asm ("x3") = 4;
+ register u64 __maybe_unused xt_5 asm ("x4") = 5;
+ register u64 __maybe_unused xt_6 asm ("x5") = 6;
+ register u64 __maybe_unused xt_7 asm ("x6") = 7;
+ register u64 __maybe_unused xt_8 asm ("x7") = 8;
+ register u64 st asm ("x9");
+
+ /* ST64BV x9, x0, [x8] */
+ asm volatile(".inst 0xf829b100" : "=r" (st) : "r" (xt_1), "r" (xn));
+
+ sigaction(SIGBUS, &old, NULL);
+}
+
+static void ls64_accdata_sigill(void)
+{
+ struct sigaction ign, old;
+ char dst[64] __aligned(64);
+
+ /* See comment in ls64_sigill() */
+ ign.sa_sigaction = ignore_signal;
+ ign.sa_flags = SA_SIGINFO | SA_RESTART;
+ sigemptyset(&ign.sa_mask);
+ sigaction(SIGBUS, &ign, &old);
+
+ register void *xn asm ("x8") = dst;
+ register u64 xt_1 asm ("x0") = 1;
+ register u64 __maybe_unused xt_2 asm ("x1") = 2;
+ register u64 __maybe_unused xt_3 asm ("x2") = 3;
+ register u64 __maybe_unused xt_4 asm ("x3") = 4;
+ register u64 __maybe_unused xt_5 asm ("x4") = 5;
+ register u64 __maybe_unused xt_6 asm ("x5") = 6;
+ register u64 __maybe_unused xt_7 asm ("x6") = 7;
+ register u64 __maybe_unused xt_8 asm ("x7") = 8;
+ register u64 st asm ("x9");
+
+ /* ST64BV0 x9, x0, [x8] */
+ asm volatile(".inst 0xf829a100" : "=r" (st) : "r" (xt_1), "r" (xn));
+
+ sigaction(SIGBUS, &old, NULL);
+}
+
static const struct hwcap_data {
const char *name;
unsigned long at_hwcap;
@@ -867,6 +970,30 @@ static const struct hwcap_data {
.sigill_fn = hbc_sigill,
.sigill_reliable = true,
},
+ {
+ .name = "LS64",
+ .at_hwcap = AT_HWCAP3,
+ .hwcap_bit = HWCAP3_LS64,
+ .cpuinfo = "ls64",
+ .sigill_fn = ls64_sigill,
+ .sigill_reliable = true,
+ },
+ {
+ .name = "LS64_V",
+ .at_hwcap = AT_HWCAP3,
+ .hwcap_bit = HWCAP3_LS64_V,
+ .cpuinfo = "ls64_v",
+ .sigill_fn = ls64_v_sigill,
+ .sigill_reliable = true,
+ },
+ {
+ .name = "LS64_ACCDATA",
+ .at_hwcap = AT_HWCAP3,
+ .hwcap_bit = HWCAP3_LS64_ACCDATA,
+ .cpuinfo = "ls64_accdata",
+ .sigill_fn = ls64_accdata_sigill,
+ .sigill_reliable = true,
+ },
};
typedef void (*sighandler_fn)(int, siginfo_t *, void *);