Commit f0ae09a8 authored by Yong-Xuan Wang's avatar Yong-Xuan Wang Committed by Paul Walmsley
Browse files

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: default avatarYong-Xuan Wang <yongxuan.wang@sifive.com>
Reviewed-by: default avatarAndy Chiu <andybnac@gmail.com>
Tested-by: default avatarAndy Chiu <andybnac@gmail.com>
Link: https://patch.msgid.link/20251013091318.467864-3-yongxuan.wang@sifive.com


Signed-off-by: default avatarPaul Walmsley <pjw@kernel.org>
parent 6efb1a94
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
// 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();
}