Skip to content

Commit

Permalink
SPLITLOCK test: add split lock test for cpu
Browse files Browse the repository at this point in the history
This test app will use "lock addl" instruction to trigger #AC in hardware
and kernel.

Signed-off-by: Tony Zhu <[email protected]>
  • Loading branch information
Tony Zhu committed Nov 30, 2023
1 parent 189f232 commit be70fc5
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 0 deletions.
2 changes: 2 additions & 0 deletions splitlock/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.o
sl_test
12 changes: 12 additions & 0 deletions splitlock/Makefile
Original file line number Diff line number Diff line change
@@ -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
121 changes: 121 additions & 0 deletions splitlock/sl_test.c
Original file line number Diff line number Diff line change
@@ -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 <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <stdlib.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <stdio.h>

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;
}

0 comments on commit be70fc5

Please sign in to comment.