mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
selftests: riscv: Add test for the Vector ptrace interface
Add a test case that does some basic verification of the Vector ptrace interface. This forks a child process then using ptrace to inspect and manipulate the v31 register of the child. Signed-off-by: Yong-Xuan Wang <yongxuan.wang@sifive.com> Reviewed-by: Andy Chiu <andybnac@gmail.com> Tested-by: Andy Chiu <andybnac@gmail.com> Link: https://patch.msgid.link/20251013091318.467864-3-yongxuan.wang@sifive.com Signed-off-by: Paul Walmsley <pjw@kernel.org>
This commit is contained in:
committed by
Paul Walmsley
parent
6efb1a9462
commit
f0ae09a892
@@ -2,7 +2,7 @@
|
||||
# Copyright (C) 2021 ARM Limited
|
||||
# Originally tools/testing/arm64/abi/Makefile
|
||||
|
||||
TEST_GEN_PROGS := v_initval vstate_prctl
|
||||
TEST_GEN_PROGS := v_initval vstate_prctl vstate_ptrace
|
||||
TEST_GEN_PROGS_EXTENDED := vstate_exec_nolibc v_exec_initval_nolibc
|
||||
|
||||
include ../../lib.mk
|
||||
@@ -26,3 +26,6 @@ $(OUTPUT)/v_initval: v_initval.c $(OUTPUT)/sys_hwprobe.o $(OUTPUT)/v_helpers.o
|
||||
$(OUTPUT)/v_exec_initval_nolibc: v_exec_initval_nolibc.c
|
||||
$(CC) -nostdlib -static -include ../../../../include/nolibc/nolibc.h \
|
||||
-Wall $(CFLAGS) $(LDFLAGS) $^ -o $@ -lgcc
|
||||
|
||||
$(OUTPUT)/vstate_ptrace: vstate_ptrace.c $(OUTPUT)/sys_hwprobe.o $(OUTPUT)/v_helpers.o
|
||||
$(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
|
||||
|
||||
134
tools/testing/selftests/riscv/vector/vstate_ptrace.c
Normal file
134
tools/testing/selftests/riscv/vector/vstate_ptrace.c
Normal file
@@ -0,0 +1,134 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <linux/elf.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/wait.h>
|
||||
#include "../../kselftest.h"
|
||||
#include "v_helpers.h"
|
||||
|
||||
int parent_set_val, child_set_val;
|
||||
|
||||
static long do_ptrace(enum __ptrace_request op, pid_t pid, long type, size_t size, void *data)
|
||||
{
|
||||
struct iovec v_iovec = {
|
||||
.iov_len = size,
|
||||
.iov_base = data
|
||||
};
|
||||
|
||||
return ptrace(op, pid, type, &v_iovec);
|
||||
}
|
||||
|
||||
static int do_child(void)
|
||||
{
|
||||
int out;
|
||||
|
||||
if (ptrace(PTRACE_TRACEME, -1, NULL, NULL)) {
|
||||
ksft_perror("PTRACE_TRACEME failed\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
asm volatile (".option push\n\t"
|
||||
".option arch, +v\n\t"
|
||||
".option norvc\n\t"
|
||||
"vsetivli x0, 1, e32, m1, ta, ma\n\t"
|
||||
"vmv.s.x v31, %[in]\n\t"
|
||||
"ebreak\n\t"
|
||||
"vmv.x.s %[out], v31\n\t"
|
||||
".option pop\n\t"
|
||||
: [out] "=r" (out)
|
||||
: [in] "r" (child_set_val));
|
||||
|
||||
if (out != parent_set_val)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static void do_parent(pid_t child)
|
||||
{
|
||||
int status;
|
||||
void *data = NULL;
|
||||
|
||||
/* Attach to the child */
|
||||
while (waitpid(child, &status, 0)) {
|
||||
if (WIFEXITED(status)) {
|
||||
ksft_test_result(WEXITSTATUS(status) == 0, "SETREGSET vector\n");
|
||||
goto out;
|
||||
} else if (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGTRAP)) {
|
||||
size_t size;
|
||||
void *data, *v31;
|
||||
struct __riscv_v_regset_state *v_regset_hdr;
|
||||
struct user_regs_struct *gpreg;
|
||||
|
||||
size = sizeof(*v_regset_hdr);
|
||||
data = malloc(size);
|
||||
if (!data)
|
||||
goto out;
|
||||
v_regset_hdr = (struct __riscv_v_regset_state *)data;
|
||||
|
||||
if (do_ptrace(PTRACE_GETREGSET, child, NT_RISCV_VECTOR, size, data))
|
||||
goto out;
|
||||
|
||||
ksft_print_msg("vlenb %ld\n", v_regset_hdr->vlenb);
|
||||
data = realloc(data, size + v_regset_hdr->vlenb * 32);
|
||||
if (!data)
|
||||
goto out;
|
||||
v_regset_hdr = (struct __riscv_v_regset_state *)data;
|
||||
v31 = (void *)(data + size + v_regset_hdr->vlenb * 31);
|
||||
size += v_regset_hdr->vlenb * 32;
|
||||
|
||||
if (do_ptrace(PTRACE_GETREGSET, child, NT_RISCV_VECTOR, size, data))
|
||||
goto out;
|
||||
|
||||
ksft_test_result(*(int *)v31 == child_set_val, "GETREGSET vector\n");
|
||||
|
||||
*(int *)v31 = parent_set_val;
|
||||
if (do_ptrace(PTRACE_SETREGSET, child, NT_RISCV_VECTOR, size, data))
|
||||
goto out;
|
||||
|
||||
/* move the pc forward */
|
||||
size = sizeof(*gpreg);
|
||||
data = realloc(data, size);
|
||||
gpreg = (struct user_regs_struct *)data;
|
||||
|
||||
if (do_ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, size, data))
|
||||
goto out;
|
||||
|
||||
gpreg->pc += 4;
|
||||
if (do_ptrace(PTRACE_SETREGSET, child, NT_PRSTATUS, size, data))
|
||||
goto out;
|
||||
}
|
||||
|
||||
ptrace(PTRACE_CONT, child, NULL, NULL);
|
||||
}
|
||||
|
||||
out:
|
||||
free(data);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
pid_t child;
|
||||
|
||||
ksft_set_plan(2);
|
||||
if (!is_vector_supported() && !is_xtheadvector_supported())
|
||||
ksft_exit_skip("Vector not supported\n");
|
||||
|
||||
srandom(getpid());
|
||||
parent_set_val = rand();
|
||||
child_set_val = rand();
|
||||
|
||||
child = fork();
|
||||
if (child < 0)
|
||||
ksft_exit_fail_msg("Fork failed %d\n", child);
|
||||
|
||||
if (!child)
|
||||
return do_child();
|
||||
|
||||
do_parent(child);
|
||||
|
||||
ksft_finished();
|
||||
}
|
||||
Reference in New Issue
Block a user