Signed-off-by: Nicholas Piggin <npig...@gmail.com>
---
 powerpc/Makefile.common |   1 +
 powerpc/atomics.c       | 190 ++++++++++++++++++++++++++++++++++++++++
 powerpc/unittests.cfg   |   9 ++
 3 files changed, 200 insertions(+)
 create mode 100644 powerpc/atomics.c

diff --git a/powerpc/Makefile.common b/powerpc/Makefile.common
index caa807f2..697f5735 100644
--- a/powerpc/Makefile.common
+++ b/powerpc/Makefile.common
@@ -10,6 +10,7 @@ tests-common = \
        $(TEST_DIR)/spapr_hcall.elf \
        $(TEST_DIR)/rtas.elf \
        $(TEST_DIR)/emulator.elf \
+       $(TEST_DIR)/atomics.elf \
        $(TEST_DIR)/tm.elf \
        $(TEST_DIR)/smp.elf \
        $(TEST_DIR)/sprs.elf \
diff --git a/powerpc/atomics.c b/powerpc/atomics.c
new file mode 100644
index 00000000..f2e7a3e3
--- /dev/null
+++ b/powerpc/atomics.c
@@ -0,0 +1,190 @@
+/*
+ * Test some powerpc instructions
+ */
+#include <stdint.h>
+#include <libcflat.h>
+#include <migrate.h>
+#include <asm/processor.h>
+
+static int verbose;
+
+#define RSV_SIZE 128
+
+static uint8_t granule[RSV_SIZE] __attribute((__aligned__(RSV_SIZE)));
+
+static void test_lwarx_stwcx(void)
+{
+       unsigned int *var = (unsigned int *)granule;
+       unsigned int old;
+       unsigned int result;
+
+       report_prefix_push("lwarx/stwcx.");
+
+       *var = 0;
+       asm volatile ("1:"
+                     "lwarx    %0,0,%2;"
+                     "stwcx.   %1,0,%2;"
+                     "bne-     1b;"
+                     : "=&r"(old) : "r"(1), "r"(var) : "cr0", "memory");
+       report(old == 0 && *var == 1, "simple update");
+
+       *var = 0;
+       asm volatile ("li       %0,0;"
+                     "stwcx.   %1,0,%2;"
+                     "stwcx.   %1,0,%2;"
+                     "bne-     1f;"
+                     "li       %0,1;"
+                     "1:"
+                     : "=&r"(result)
+                     : "r"(1), "r"(var) : "cr0", "memory");
+       report(result == 0 && *var == 0, "failed stwcx. (no reservation)");
+
+       *var = 0;
+       asm volatile ("li       %0,0;"
+                     "lwarx    %1,0,%4;"
+                     "stw      %3,0(%4);"
+                     "stwcx.   %2,0,%4;"
+                     "bne-     1f;"
+                     "li       %0,1;"
+                     "1:"
+                     : "=&r"(result), "=&r"(old)
+                     : "r"(1), "r"(2), "r"(var) : "cr0", "memory");
+       report(result == 0 && *var == 2, "failed stwcx. (intervening store)");
+
+       report_prefix_pop();
+}
+
+static void test_lqarx_stqcx(void)
+{
+       union {
+               __int128_t var;
+               struct {
+#if  __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+                       unsigned long var1;
+                       unsigned long var2;
+#else
+                       unsigned long var2;
+                       unsigned long var1;
+#endif
+               };
+       } var __attribute__((aligned(16)));
+       register unsigned long new1 asm("r8");
+       register unsigned long new2 asm("r9");
+       register unsigned long old1 asm("r10");
+       register unsigned long old2 asm("r11");
+       unsigned int result;
+
+       var.var1 = 1;
+       var.var2 = 2;
+
+       (void)new2;
+       (void)old2;
+
+       report_prefix_push("lqarx/stqcx.");
+
+       old1 = 0;
+       old2 = 0;
+       new1 = 3;
+       new2 = 4;
+       asm volatile ("1:"
+                     "lqarx    %0,0,%4;"
+                     "stqcx.   %2,0,%4;"
+                     "bne-     1b;"
+                     : "=&r"(old1), "=&r"(old2)
+                     : "r"(new1), "r"(new2), "r"(&var)
+                     : "cr0", "memory");
+
+       report(old1 == 2 && old2 == 1 && var.var1 == 4 && var.var2 == 3,
+                       "simple update");
+
+       var.var1 = 1;
+       var.var2 = 2;
+       new1 = 3;
+       new2 = 4;
+       asm volatile ("li       %0,0;"
+                     "stqcx.   %1,0,%3;"
+                     "stqcx.   %1,0,%3;"
+                     "bne-     1f;"
+                     "li       %0,1;"
+                     "1:"
+                     : "=&r"(result)
+                     : "r"(new1), "r"(new2), "r"(&var)
+                     : "cr0", "memory");
+       report(result == 0 && var.var1 == 1 && var.var2 == 2,
+                       "failed stqcx. (no reservation)");
+
+       var.var1 = 1;
+       var.var2 = 2;
+       new1 = 3;
+       new2 = 4;
+       asm volatile ("li       %0,0;"
+                     "lqarx    %1,0,%6;"
+                     "std      %5,0(%6);"
+                     "stqcx.   %3,0,%6;"
+                     "bne-     1f;"
+                     "li       %0,1;"
+                     "1:"
+                     : "=&r"(result), "=&r"(old1), "=&r"(old2)
+                     : "r"(new1), "r"(new2), "r"(0), "r"(&var)
+                     : "cr0", "memory");
+       report(result == 0 && (var.var1 == 0 || var.var2 == 0),
+                       "failed stqcx. (intervening store)");
+
+       report_prefix_pop();
+}
+
+static void test_migrate_reserve(void)
+{
+       unsigned int *var = (unsigned int *)granule;
+       unsigned int old;
+
+       /* ensure incorrect value does not succeed */
+       report_prefix_push("migrate reservation");
+
+       *var = 0x12345;
+       asm volatile ("lwarx    %0,0,%1" : "=&r"(old) : "r"(var) : "memory");
+       migrate();
+       *var = 0;
+       asm volatile ("stwcx.   %0,0,%1" : : "r"(0xbad), "r"(var) : "cr0", 
"memory");
+       report(*var == 0, "migrate reserve update fails with concurrently 
modified value");
+
+#if 0
+XXX this will not work with KVM and for QEMU it only works with record/replay -
+something the harness is not equipped to test.
+
+       /* ensure reservation succeds */
+       report_prefix_push("migrate reservation");
+
+       *var = 0x12345;
+       asm volatile ("lwarx    %0,0,%1" : "=&r"(old) : "r"(var) : "memory");
+       migrate();
+       asm volatile ("stwcx.   %0,0,%1" : : "r"(0xf070), "r"(var) : "cr0", 
"memory");
+       report(*var == 0xf070, "migrate reserve update succeeds with unmodified 
value");
+#endif
+}
+
+int main(int argc, char **argv)
+{
+       int i;
+       bool migrate = false;
+
+       for (i = 1; i < argc; i++) {
+               if (strcmp(argv[i], "-v") == 0) {
+                       verbose = 1;
+               }
+               if (strcmp(argv[i], "-m") == 0) {
+                       migrate = true;
+               }
+       }
+
+       report_prefix_push("atomics");
+
+       test_lwarx_stwcx();
+       test_lqarx_stqcx();
+       if (migrate)
+               test_migrate_reserve();
+
+       report_prefix_pop();
+
+       return report_summary();
+}
diff --git a/powerpc/unittests.cfg b/powerpc/unittests.cfg
index 727712bb..9f71ea93 100644
--- a/powerpc/unittests.cfg
+++ b/powerpc/unittests.cfg
@@ -80,6 +80,15 @@ smp = 2
 file = smp.elf
 smp = 8,threads=4
 
+[atomics]
+file = atomics.elf
+
+[atomics-migration]
+file = atomics.elf
+machine = pseries
+extra_params = -append '-m'
+groups = migration
+
 [h_cede_tm]
 file = tm.elf
 machine = pseries
-- 
2.42.0

Reply via email to