From be70fc505edb75163c7bd78b7c19e621cadaa092 Mon Sep 17 00:00:00 2001 From: Tony Zhu Date: Tue, 21 Nov 2023 16:27:28 +0800 Subject: [PATCH] SPLITLOCK test: add split lock test for cpu This test app will use "lock addl" instruction to trigger #AC in hardware and kernel. Signed-off-by: Tony Zhu --- splitlock/.gitignore | 2 + splitlock/Makefile | 12 +++++ splitlock/sl_test.c | 121 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+) create mode 100644 splitlock/.gitignore create mode 100644 splitlock/Makefile create mode 100644 splitlock/sl_test.c diff --git a/splitlock/.gitignore b/splitlock/.gitignore new file mode 100644 index 0000000..17439ba --- /dev/null +++ b/splitlock/.gitignore @@ -0,0 +1,2 @@ +*.o +sl_test diff --git a/splitlock/Makefile b/splitlock/Makefile new file mode 100644 index 0000000..c3e1b06 --- /dev/null +++ b/splitlock/Makefile @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) 2022 Intel Corporation. + +BIN := sl_test + +all: $(BIN) + +sl_test: sl_test.c + gcc $^ -o $@ + +clean: + rm -rf $(BIN) *.o diff --git a/splitlock/sl_test.c b/splitlock/sl_test.c new file mode 100644 index 0000000..c443762 --- /dev/null +++ b/splitlock/sl_test.c @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2022 Intel Corporation. + +/* + * Split lock test app to check if the locked instruction will trigger #AC + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void catch_sigbus(int sig) +{ + printf("Caught SIGBUS/#AC due to split locked access\n"); + + exit(-1); +} + +/* + * Atomically add 1 to *iptr using "lock addl" instruction. + * Since *iptr crosses two cache lines, #AC is generated by the split lock. + */ +void do_split_locked_inst(int *iptr) +{ + /* + * The distance between iptr and next cache line is 3 bytes. + * Operand size in "addl" is 4 bytes. So iptr will span two cache + * lines. "lock addl" instruction will trigger #AC in hardware + * and kernel either delivers SIGBUS to this process or re-execute + * the instruction depending on + * /sys/kernel/debug/x86/split_lock/user_mode setting. + */ + asm volatile ("lock addl $1, %0\n\t" + : "=m" (*iptr)); +} + +/* + * Test SIGBUS delivered after a lock instruction generates #AC for split lock + * operand *iptr. + */ +void test_sigbus(int *iptr) +{ + pid_t pid; + + pid = fork(); + if (pid) + return; + + /* + * The locked instruction will trigger #AC and kernel will deliver + * SIGBUS to this process. The SIGBUS handler in this process will + * verify that the signal is delivered and the process is killed then. + */ + do_split_locked_inst(iptr); +} + +int main(int argc, char **argv) +{ + int *iptr; + char *cptr; + char *line = NULL; + int ret = 0; + size_t len; + FILE *fp; + + fp = fopen("/proc/cpuinfo", "r"); + if (!fp) + return 1; + + while (getline(&line, &len, fp) != -1) { + if (!strncmp(line, "flags", 5)) { + if (strstr(line, "split_lock_detect")) + ret = 1; + break; + } + } + + free(line); + fclose(fp); + if (!ret) { + printf("split_lock_dect: [FAIL]\n"); + return !ret; + } + + signal(SIGBUS, catch_sigbus); + + /* + * Enable Alignment Checking on x86_64. + * This will generate alignment check on not only split lock but also + * on any misalignment. + * Turn on this for reference only. + */ + /* __asm__("pushf\norl $0x40000,(%rsp)\npopf"); */ + + /* aligned_alloc() provides 64-byte aligned memory */ + cptr = (char *)aligned_alloc(64, 128); + + /* + * Increment the pointer by 61, making it 3 bytes away from the next + * cache line and 4-byte *iptr across two cache line. + */ + iptr = (int *)(cptr + 61); + + test_sigbus(iptr); + + free(cptr); + + printf("Split Lock test - exits normally\n"); + + return 0; +}