Signed-off-by: Nicholas Piggin <npiggin@xxxxxxxxx> --- 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