Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,6 @@
vstate_exec_nolibc
vstate_prctl
v_initval
v_exec_initval_nolibc
vstate_ptrace
validate_v_ptrace
@@ -0,0 +1,39 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2021 ARM Limited
# Originally tools/testing/arm64/abi/Makefile
TEST_GEN_PROGS := v_initval vstate_prctl vstate_ptrace validate_v_ptrace
TEST_GEN_PROGS_EXTENDED := vstate_exec_nolibc v_exec_initval_nolibc
TEST_GEN_LIBS := v_helpers.c sys_hwprobe.c
include ../../lib.mk
TEST_GEN_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(TEST_GEN_LIBS))
$(OUTPUT)/sys_hwprobe.o: ../hwprobe/sys_hwprobe.S
$(CC) -static -c -o $@ $(CFLAGS) $^
$(OUTPUT)/v_helpers.o: v_helpers.c
$(CC) -static -c -o $@ $(CFLAGS) $^
$(OUTPUT)/vstate_prctl: vstate_prctl.c $(OUTPUT)/sys_hwprobe.o $(OUTPUT)/v_helpers.o
$(CC) -static -o $@ $(CFLAGS) $(LDFLAGS) $^
$(OUTPUT)/vstate_exec_nolibc: vstate_exec_nolibc.c
$(CC) -nostdlib -static -include ../../../../include/nolibc/nolibc.h \
-Wall $(CFLAGS) $(LDFLAGS) $^ -o $@ -lgcc
$(OUTPUT)/v_initval: v_initval.c $(OUTPUT)/sys_hwprobe.o $(OUTPUT)/v_helpers.o
$(CC) -static -o $@ $(CFLAGS) $(LDFLAGS) $^
$(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) $^
$(OUTPUT)/validate_v_ptrace: validate_v_ptrace.c $(OUTPUT)/sys_hwprobe.o $(OUTPUT)/v_helpers.o
$(CC) -static -o $@ $(CFLAGS) $(LDFLAGS) $^
EXTRA_CLEAN += $(TEST_GEN_OBJ)
@@ -0,0 +1,90 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Get values of vector registers as soon as the program starts to test if
* is properly cleaning the values before starting a new program. Vector
* registers are caller saved, so no function calls may happen before reading
* the values. To further ensure consistency, this file is compiled without
* libc and without auto-vectorization.
*
* To be "clean" all values must be all zeroes.
*/
#define __stringify_1(x...) #x
#define __stringify(x...) __stringify_1(x)
int main(int argc, char **argv)
{
char value = 0;
unsigned long vl;
if (argc > 2 && strcmp(argv[2], "x"))
asm volatile (
// 0 | zimm[10:0] | rs1 | 1 1 1 | rd |1010111| vsetvli
// vsetvli t4, x0, e8, m1, d1
".4byte 0b00000000000000000111111011010111\n\t"
"mv %[vl], t4\n\t"
: [vl] "=r" (vl) : : "t4"
);
else
asm volatile (
".option push\n\t"
".option arch, +v\n\t"
"vsetvli %[vl], x0, e8, m1, ta, ma\n\t"
".option pop\n\t"
: [vl] "=r" (vl)
);
#define CHECK_VECTOR_REGISTER(register) ({ \
for (int i = 0; i < vl; i++) { \
asm volatile ( \
".option push\n\t" \
".option arch, +v\n\t" \
"vmv.x.s %0, " __stringify(register) "\n\t" \
"vsrl.vi " __stringify(register) ", " __stringify(register) ", 8\n\t" \
".option pop\n\t" \
: "=r" (value)); \
if (value != 0x00) { \
printf("Register " __stringify(register) \
" values not clean! value: %u\n", value); \
exit(-1); \
} \
} \
})
CHECK_VECTOR_REGISTER(v0);
CHECK_VECTOR_REGISTER(v1);
CHECK_VECTOR_REGISTER(v2);
CHECK_VECTOR_REGISTER(v3);
CHECK_VECTOR_REGISTER(v4);
CHECK_VECTOR_REGISTER(v5);
CHECK_VECTOR_REGISTER(v6);
CHECK_VECTOR_REGISTER(v7);
CHECK_VECTOR_REGISTER(v8);
CHECK_VECTOR_REGISTER(v9);
CHECK_VECTOR_REGISTER(v10);
CHECK_VECTOR_REGISTER(v11);
CHECK_VECTOR_REGISTER(v12);
CHECK_VECTOR_REGISTER(v13);
CHECK_VECTOR_REGISTER(v14);
CHECK_VECTOR_REGISTER(v15);
CHECK_VECTOR_REGISTER(v16);
CHECK_VECTOR_REGISTER(v17);
CHECK_VECTOR_REGISTER(v18);
CHECK_VECTOR_REGISTER(v19);
CHECK_VECTOR_REGISTER(v20);
CHECK_VECTOR_REGISTER(v21);
CHECK_VECTOR_REGISTER(v22);
CHECK_VECTOR_REGISTER(v23);
CHECK_VECTOR_REGISTER(v24);
CHECK_VECTOR_REGISTER(v25);
CHECK_VECTOR_REGISTER(v26);
CHECK_VECTOR_REGISTER(v27);
CHECK_VECTOR_REGISTER(v28);
CHECK_VECTOR_REGISTER(v29);
CHECK_VECTOR_REGISTER(v30);
CHECK_VECTOR_REGISTER(v31);
#undef CHECK_VECTOR_REGISTER
return 0;
}
@@ -0,0 +1,91 @@
// SPDX-License-Identifier: GPL-2.0-only
#include "../hwprobe/hwprobe.h"
#include <asm/vendor/thead.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
bool is_xtheadvector_supported(void)
{
struct riscv_hwprobe pair;
pair.key = RISCV_HWPROBE_KEY_VENDOR_EXT_THEAD_0;
riscv_hwprobe(&pair, 1, 0, NULL, 0);
return pair.value & RISCV_HWPROBE_VENDOR_EXT_XTHEADVECTOR;
}
bool is_vector_supported(void)
{
struct riscv_hwprobe pair;
pair.key = RISCV_HWPROBE_KEY_IMA_EXT_0;
riscv_hwprobe(&pair, 1, 0, NULL, 0);
return pair.value & RISCV_HWPROBE_EXT_ZVE32X;
}
unsigned long get_vr_len(void)
{
unsigned long vlenb;
if (is_vector_supported()) {
asm volatile("csrr %[vlenb], vlenb" : [vlenb] "=r"(vlenb));
return vlenb;
}
if (is_xtheadvector_supported()) {
asm volatile (
// 0 | zimm[10:0] | rs1 | 1 1 1 | rd | 1010111 | vsetvli
// vsetvli t4, x0, e8, m1, d1
".4byte 0b00000000000000000111111011010111\n\t"
"mv %[vlenb], t4\n\t"
: [vlenb] "=r"(vlenb) : : "memory", "t4");
return vlenb;
}
printf("WARNING: vector not supported\n");
return 0;
}
int launch_test(char *next_program, int test_inherit, int xtheadvector)
{
char *exec_argv[4], *exec_envp[1];
int rc, pid, status;
pid = fork();
if (pid < 0) {
printf("fork failed %d", pid);
return -1;
}
if (!pid) {
exec_argv[0] = next_program;
exec_argv[1] = test_inherit != 0 ? "x" : NULL;
exec_argv[2] = xtheadvector != 0 ? "x" : NULL;
exec_argv[3] = NULL;
exec_envp[0] = NULL;
/* launch the program again to check inherit */
rc = execve(next_program, exec_argv, exec_envp);
if (rc) {
perror("execve");
printf("child execve failed %d\n", rc);
exit(-1);
}
}
rc = waitpid(-1, &status, 0);
if (rc < 0) {
printf("waitpid failed\n");
return -3;
}
if ((WIFEXITED(status) && WEXITSTATUS(status) == -1) ||
WIFSIGNALED(status)) {
printf("child exited abnormally\n");
return -4;
}
return WEXITSTATUS(status);
}
@@ -0,0 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <stdbool.h>
bool is_xtheadvector_supported(void);
bool is_vector_supported(void);
unsigned long get_vr_len(void);
int launch_test(char *next_program, int test_inherit, int xtheadvector);
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: GPL-2.0-only
#include "kselftest_harness.h"
#include "v_helpers.h"
#define NEXT_PROGRAM "./v_exec_initval_nolibc"
TEST(v_initval)
{
int xtheadvector = 0;
if (!is_vector_supported()) {
if (is_xtheadvector_supported())
xtheadvector = 1;
else
SKIP(return, "Vector not supported");
}
ASSERT_EQ(0, launch_test(NEXT_PROGRAM, 0, xtheadvector));
}
TEST_HARNESS_MAIN
@@ -0,0 +1,919 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <sys/ptrace.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/uio.h>
#include <unistd.h>
#include <errno.h>
#include <linux/ptrace.h>
#include <linux/elf.h>
#include "kselftest_harness.h"
#include "v_helpers.h"
#define SR_FS_DIRTY 0x00006000UL
#define CSR_VXRM_SHIFT 1
volatile unsigned long chld_lock;
TEST(ptrace_v_not_enabled)
{
pid_t pid;
if (!(is_vector_supported() || is_xtheadvector_supported()))
SKIP(return, "Vector not supported");
chld_lock = 1;
pid = (pid_t)syscall(SYS_clone, SIGCHLD, 0, NULL, 0, NULL);
ASSERT_LE(0, pid)
TH_LOG("clone: %m");
if (pid == 0) {
while (chld_lock == 1)
asm volatile("" : : "g"(chld_lock) : "memory");
asm volatile ("ebreak" : : : );
} else {
struct __riscv_v_regset_state *regset_data;
unsigned long vlenb = get_vr_len();
size_t regset_size;
struct iovec iov;
int status;
int ret;
/* attach */
ASSERT_EQ(0, ptrace(PTRACE_ATTACH, pid, NULL, NULL));
ASSERT_EQ(pid, waitpid(pid, &status, 0));
ASSERT_TRUE(WIFSTOPPED(status));
/* unlock */
ASSERT_EQ(0, ptrace(PTRACE_POKEDATA, pid, &chld_lock, 0));
/* resume and wait for ebreak */
ASSERT_EQ(0, ptrace(PTRACE_CONT, pid, NULL, NULL));
ASSERT_EQ(pid, waitpid(pid, &status, 0));
ASSERT_TRUE(WIFSTOPPED(status));
/* try to read vector registers from the tracee */
regset_size = sizeof(*regset_data) + vlenb * 32;
regset_data = calloc(1, regset_size);
iov.iov_base = regset_data;
iov.iov_len = regset_size;
/* V extension is available, but not yet enabled for the tracee */
errno = 0;
ret = ptrace(PTRACE_GETREGSET, pid, NT_RISCV_VECTOR, &iov);
ASSERT_EQ(ENODATA, errno);
ASSERT_EQ(-1, ret);
/* cleanup */
free(regset_data);
ASSERT_EQ(0, kill(pid, SIGKILL));
}
}
TEST(ptrace_v_early_debug)
{
static volatile unsigned long vstart;
static volatile unsigned long vtype;
static volatile unsigned long vlenb;
static volatile unsigned long vcsr;
static volatile unsigned long vl;
bool xtheadvector;
pid_t pid;
if (!(is_vector_supported() || is_xtheadvector_supported()))
SKIP(return, "Vector not supported");
xtheadvector = is_xtheadvector_supported();
chld_lock = 1;
pid = fork();
ASSERT_LE(0, pid)
TH_LOG("fork: %m");
if (pid == 0) {
unsigned long vxsat, vxrm;
vlenb = get_vr_len();
while (chld_lock == 1)
asm volatile ("" : : "g"(chld_lock) : "memory");
asm volatile (
"csrr %[vstart], vstart\n"
"csrr %[vtype], vtype\n"
"csrr %[vl], vl\n"
: [vtype] "=r"(vtype), [vstart] "=r"(vstart), [vl] "=r"(vl)
:
: "memory");
/* no 'is_xtheadvector_supported()' here to avoid clobbering v-state by syscall */
if (xtheadvector) {
asm volatile (
"csrs sstatus, %[bit]\n"
"csrr %[vxsat], vxsat\n"
"csrr %[vxrm], vxrm\n"
: [vxsat] "=r"(vxsat), [vxrm] "=r"(vxrm)
: [bit] "r" (SR_FS_DIRTY)
: "memory");
vcsr = vxsat | vxrm << CSR_VXRM_SHIFT;
} else {
asm volatile (
"csrr %[vcsr], vcsr\n"
: [vcsr] "=r"(vcsr)
:
: "memory");
}
asm volatile (
".option push\n"
".option norvc\n"
"ebreak\n"
".option pop\n");
} else {
struct __riscv_v_regset_state *regset_data;
unsigned long vstart_csr;
unsigned long vlenb_csr;
unsigned long vtype_csr;
unsigned long vcsr_csr;
unsigned long vl_csr;
size_t regset_size;
struct iovec iov;
int status;
/* attach */
ASSERT_EQ(0, ptrace(PTRACE_ATTACH, pid, NULL, NULL));
ASSERT_EQ(pid, waitpid(pid, &status, 0));
ASSERT_TRUE(WIFSTOPPED(status));
/* unlock */
ASSERT_EQ(0, ptrace(PTRACE_POKEDATA, pid, &chld_lock, 0));
/* resume and wait for ebreak */
ASSERT_EQ(0, ptrace(PTRACE_CONT, pid, NULL, NULL));
ASSERT_EQ(pid, waitpid(pid, &status, 0));
ASSERT_TRUE(WIFSTOPPED(status));
/* read tracee vector csr regs using ptrace PEEKDATA */
errno = 0;
vstart_csr = ptrace(PTRACE_PEEKDATA, pid, &vstart, NULL);
ASSERT_FALSE((errno != 0) && (vstart_csr == -1));
errno = 0;
vl_csr = ptrace(PTRACE_PEEKDATA, pid, &vl, NULL);
ASSERT_FALSE((errno != 0) && (vl_csr == -1));
errno = 0;
vtype_csr = ptrace(PTRACE_PEEKDATA, pid, &vtype, NULL);
ASSERT_FALSE((errno != 0) && (vtype_csr == -1));
errno = 0;
vcsr_csr = ptrace(PTRACE_PEEKDATA, pid, &vcsr, NULL);
ASSERT_FALSE((errno != 0) && (vcsr_csr == -1));
errno = 0;
vlenb_csr = ptrace(PTRACE_PEEKDATA, pid, &vlenb, NULL);
ASSERT_FALSE((errno != 0) && (vlenb_csr == -1));
/* read tracee csr regs using ptrace GETREGSET */
regset_size = sizeof(*regset_data) + vlenb_csr * 32;
regset_data = calloc(1, regset_size);
iov.iov_base = regset_data;
iov.iov_len = regset_size;
ASSERT_EQ(0, ptrace(PTRACE_GETREGSET, pid, NT_RISCV_VECTOR, &iov));
/* compare */
EXPECT_EQ(vstart_csr, regset_data->vstart);
EXPECT_EQ(vtype_csr, regset_data->vtype);
EXPECT_EQ(vlenb_csr, regset_data->vlenb);
EXPECT_EQ(vcsr_csr, regset_data->vcsr);
EXPECT_EQ(vl_csr, regset_data->vl);
/* cleanup */
free(regset_data);
ASSERT_EQ(0, kill(pid, SIGKILL));
}
}
TEST(ptrace_v_syscall_clobbering)
{
pid_t pid;
if (!is_vector_supported() && !is_xtheadvector_supported())
SKIP(return, "Vector not supported");
chld_lock = 1;
pid = fork();
ASSERT_LE(0, pid)
TH_LOG("fork: %m");
if (pid == 0) {
unsigned long vl;
while (chld_lock == 1)
asm volatile("" : : "g"(chld_lock) : "memory");
if (is_xtheadvector_supported()) {
asm volatile (
// 0 | zimm[10:0] | rs1 | 1 1 1 | rd |1010111| vsetvli
// vsetvli t4, x0, e16, m2, d1
".4byte 0b00000000010100000111111011010111\n"
"mv %[new_vl], t4\n"
: [new_vl] "=r" (vl) : : "t4");
} else {
asm volatile (
".option push\n"
".option arch, +zve32x\n"
"vsetvli %[new_vl], x0, e16, m2, tu, mu\n"
".option pop\n"
: [new_vl] "=r"(vl) : : );
}
while (1) {
asm volatile (
".option push\n"
".option norvc\n"
"ebreak\n"
".option pop\n");
sleep(0);
}
} else {
struct __riscv_v_regset_state *regset_data;
unsigned long vlenb = get_vr_len();
struct user_regs_struct regs;
size_t regset_size;
struct iovec iov;
int status;
/* attach */
ASSERT_EQ(0, ptrace(PTRACE_ATTACH, pid, NULL, NULL));
ASSERT_EQ(pid, waitpid(pid, &status, 0));
ASSERT_TRUE(WIFSTOPPED(status));
/* unlock */
ASSERT_EQ(0, ptrace(PTRACE_POKEDATA, pid, &chld_lock, 0));
/* resume and wait for the 1st ebreak */
ASSERT_EQ(0, ptrace(PTRACE_CONT, pid, NULL, NULL));
ASSERT_EQ(pid, waitpid(pid, &status, 0));
ASSERT_TRUE(WIFSTOPPED(status));
/* read tracee vector csr regs using ptrace GETREGSET */
regset_size = sizeof(*regset_data) + vlenb * 32;
regset_data = calloc(1, regset_size);
iov.iov_base = regset_data;
iov.iov_len = regset_size;
ASSERT_EQ(0, ptrace(PTRACE_GETREGSET, pid, NT_RISCV_VECTOR, &iov));
/* verify initial vsetvli settings */
if (is_xtheadvector_supported()) {
EXPECT_EQ(5UL, regset_data->vtype);
} else {
EXPECT_EQ(9UL, regset_data->vtype);
}
EXPECT_EQ(regset_data->vlenb, regset_data->vl);
EXPECT_EQ(vlenb, regset_data->vlenb);
EXPECT_EQ(0UL, regset_data->vstart);
EXPECT_EQ(0UL, regset_data->vcsr);
/* skip 1st ebreak, then resume and wait for the 2nd ebreak */
iov.iov_base = &regs;
iov.iov_len = sizeof(regs);
ASSERT_EQ(0, ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov));
regs.pc += 4;
ASSERT_EQ(0, ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov));
ASSERT_EQ(0, ptrace(PTRACE_CONT, pid, NULL, NULL));
ASSERT_EQ(pid, waitpid(pid, &status, 0));
ASSERT_TRUE(WIFSTOPPED(status));
/* read tracee vtype using ptrace GETREGSET */
iov.iov_base = regset_data;
iov.iov_len = regset_size;
ASSERT_EQ(0, ptrace(PTRACE_GETREGSET, pid, NT_RISCV_VECTOR, &iov));
/* verify that V state is illegal after syscall */
EXPECT_EQ((1UL << (__riscv_xlen - 1)), regset_data->vtype);
EXPECT_EQ(vlenb, regset_data->vlenb);
EXPECT_EQ(0UL, regset_data->vstart);
EXPECT_EQ(0UL, regset_data->vcsr);
EXPECT_EQ(0UL, regset_data->vl);
/* cleanup */
free(regset_data);
ASSERT_EQ(0, kill(pid, SIGKILL));
}
}
FIXTURE(v_csr_invalid)
{
};
FIXTURE_SETUP(v_csr_invalid)
{
}
FIXTURE_TEARDOWN(v_csr_invalid)
{
}
#define VECTOR_1_0 _BITUL(0)
#define XTHEAD_VECTOR_0_7 _BITUL(1)
#define vector_test(x) ((x) & VECTOR_1_0)
#define xthead_test(x) ((x) & XTHEAD_VECTOR_0_7)
/* modifications of the initial vsetvli settings */
FIXTURE_VARIANT(v_csr_invalid)
{
unsigned long vstart;
unsigned long vl;
unsigned long vtype;
unsigned long vcsr;
unsigned long vlenb_mul;
unsigned long vlenb_min;
unsigned long vlenb_max;
unsigned long spec;
};
/* unexpected vlenb value */
FIXTURE_VARIANT_ADD(v_csr_invalid, new_vlenb)
{
.vstart = 0x0,
.vl = 0x0,
.vtype = 0x3,
.vcsr = 0x0,
.vlenb_mul = 0x2,
.vlenb_min = 0x0,
.vlenb_max = 0x0,
.spec = VECTOR_1_0 | XTHEAD_VECTOR_0_7,
};
/* invalid reserved bits in vcsr */
FIXTURE_VARIANT_ADD(v_csr_invalid, vcsr_invalid_reserved_bits)
{
.vstart = 0x0,
.vl = 0x0,
.vtype = 0x3,
.vcsr = 0x1UL << 8,
.vlenb_mul = 0x1,
.vlenb_min = 0x0,
.vlenb_max = 0x0,
.spec = VECTOR_1_0 | XTHEAD_VECTOR_0_7,
};
/* invalid reserved bits in vtype */
FIXTURE_VARIANT_ADD(v_csr_invalid, vtype_invalid_reserved_bits)
{
.vstart = 0x0,
.vl = 0x0,
.vtype = (0x1UL << 8) | 0x3,
.vcsr = 0x0,
.vlenb_mul = 0x1,
.vlenb_min = 0x0,
.vlenb_max = 0x0,
.spec = VECTOR_1_0 | XTHEAD_VECTOR_0_7,
};
/* set vill bit */
FIXTURE_VARIANT_ADD(v_csr_invalid, invalid_vill_bit)
{
.vstart = 0x0,
.vl = 0x0,
.vtype = (0x1UL << (__riscv_xlen - 1)) | 0x3,
.vcsr = 0x0,
.vlenb_mul = 0x1,
.vlenb_min = 0x0,
.vlenb_max = 0x0,
.spec = VECTOR_1_0 | XTHEAD_VECTOR_0_7,
};
/* reserved vsew value: vsew > 3 */
FIXTURE_VARIANT_ADD(v_csr_invalid, reserved_vsew)
{
.vstart = 0x0,
.vl = 0x0,
.vtype = 0x4UL << 3,
.vcsr = 0x0,
.vlenb_mul = 0x1,
.vlenb_min = 0x0,
.vlenb_max = 0x0,
.spec = VECTOR_1_0,
};
/* XTheadVector: unsupported non-zero VEDIV value */
FIXTURE_VARIANT_ADD(v_csr_invalid, reserved_vediv)
{
.vstart = 0x0,
.vl = 0x0,
.vtype = 0x3UL << 5,
.vcsr = 0x0,
.vlenb_mul = 0x1,
.vlenb_min = 0x0,
.vlenb_max = 0x0,
.spec = XTHEAD_VECTOR_0_7,
};
/* reserved vlmul value: vlmul == 4 */
FIXTURE_VARIANT_ADD(v_csr_invalid, reserved_vlmul)
{
.vstart = 0x0,
.vl = 0x0,
.vtype = 0x4,
.vcsr = 0x0,
.vlenb_mul = 0x1,
.vlenb_min = 0x0,
.vlenb_max = 0x0,
.spec = VECTOR_1_0,
};
/* invalid fractional LMUL for VLEN <= 256: LMUL= 1/8, SEW = 64 */
FIXTURE_VARIANT_ADD(v_csr_invalid, frac_lmul1)
{
.vstart = 0x0,
.vl = 0x0,
.vtype = 0x1d,
.vcsr = 0x0,
.vlenb_mul = 0x1,
.vlenb_min = 0x0,
.vlenb_max = 0x20,
.spec = VECTOR_1_0,
};
/* invalid integral LMUL for VLEN <= 16: LMUL= 2, SEW = 64 */
FIXTURE_VARIANT_ADD(v_csr_invalid, int_lmul1)
{
.vstart = 0x0,
.vl = 0x0,
.vtype = 0x19,
.vcsr = 0x0,
.vlenb_mul = 0x1,
.vlenb_min = 0x0,
.vlenb_max = 0x2,
.spec = VECTOR_1_0,
};
/* XTheadVector: invalid integral LMUL for VLEN <= 16: LMUL= 2, SEW = 64 */
FIXTURE_VARIANT_ADD(v_csr_invalid, int_lmul2)
{
.vstart = 0x0,
.vl = 0x0,
.vtype = 0xd,
.vcsr = 0x0,
.vlenb_mul = 0x1,
.vlenb_min = 0x0,
.vlenb_max = 0x2,
.spec = XTHEAD_VECTOR_0_7,
};
/* invalid VL for VLEN <= 128: LMUL= 2, SEW = 64, VL = 8 */
FIXTURE_VARIANT_ADD(v_csr_invalid, vl1)
{
.vstart = 0x0,
.vl = 0x8,
.vtype = 0x19,
.vcsr = 0x0,
.vlenb_mul = 0x1,
.vlenb_min = 0x0,
.vlenb_max = 0x10,
.spec = VECTOR_1_0,
};
/* XTheadVector: invalid VL for VLEN <= 128: LMUL= 2, SEW = 64, VL = 8 */
FIXTURE_VARIANT_ADD(v_csr_invalid, vl2)
{
.vstart = 0x0,
.vl = 0x8,
.vtype = 0xd,
.vcsr = 0x0,
.vlenb_mul = 0x1,
.vlenb_min = 0x0,
.vlenb_max = 0x10,
.spec = XTHEAD_VECTOR_0_7,
};
TEST_F(v_csr_invalid, ptrace_v_invalid_values)
{
unsigned long vlenb;
pid_t pid;
if (!is_vector_supported() && !is_xtheadvector_supported())
SKIP(return, "Vectors not supported");
if (is_vector_supported() && !vector_test(variant->spec))
SKIP(return, "Test not supported for Vector");
if (is_xtheadvector_supported() && !xthead_test(variant->spec))
SKIP(return, "Test not supported for XTheadVector");
vlenb = get_vr_len();
if (variant->vlenb_min) {
if (vlenb < variant->vlenb_min)
SKIP(return, "This test does not support VLEN < %lu\n",
variant->vlenb_min * 8);
}
if (variant->vlenb_max) {
if (vlenb > variant->vlenb_max)
SKIP(return, "This test does not support VLEN > %lu\n",
variant->vlenb_max * 8);
}
chld_lock = 1;
pid = fork();
ASSERT_LE(0, pid)
TH_LOG("fork: %m");
if (pid == 0) {
unsigned long vl;
while (chld_lock == 1)
asm volatile("" : : "g"(chld_lock) : "memory");
if (is_xtheadvector_supported()) {
asm volatile (
// 0 | zimm[10:0] | rs1 | 1 1 1 | rd |1010111| vsetvli
// vsetvli t4, x0, e16, m2, d1
".4byte 0b00000000010100000111111011010111\n"
"mv %[new_vl], t4\n"
: [new_vl] "=r" (vl) : : "t4");
} else {
asm volatile (
".option push\n"
".option arch, +zve32x\n"
"vsetvli %[new_vl], x0, e16, m2, tu, mu\n"
".option pop\n"
: [new_vl] "=r"(vl) : : );
}
while (1) {
asm volatile (
".option push\n"
".option norvc\n"
"ebreak\n"
"nop\n"
".option pop\n");
}
} else {
struct __riscv_v_regset_state *regset_data;
size_t regset_size;
struct iovec iov;
int status;
int ret;
/* attach */
ASSERT_EQ(0, ptrace(PTRACE_ATTACH, pid, NULL, NULL));
ASSERT_EQ(pid, waitpid(pid, &status, 0));
ASSERT_TRUE(WIFSTOPPED(status));
/* unlock */
ASSERT_EQ(0, ptrace(PTRACE_POKEDATA, pid, &chld_lock, 0));
/* resume and wait for the 1st ebreak */
ASSERT_EQ(0, ptrace(PTRACE_CONT, pid, NULL, NULL));
ASSERT_EQ(pid, waitpid(pid, &status, 0));
ASSERT_TRUE(WIFSTOPPED(status));
/* read tracee vector csr regs using ptrace GETREGSET */
regset_size = sizeof(*regset_data) + vlenb * 32;
regset_data = calloc(1, regset_size);
iov.iov_base = regset_data;
iov.iov_len = regset_size;
ASSERT_EQ(0, ptrace(PTRACE_GETREGSET, pid, NT_RISCV_VECTOR, &iov));
/* verify initial vsetvli settings */
if (is_xtheadvector_supported()) {
EXPECT_EQ(5UL, regset_data->vtype);
} else {
EXPECT_EQ(9UL, regset_data->vtype);
}
EXPECT_EQ(regset_data->vlenb, regset_data->vl);
EXPECT_EQ(vlenb, regset_data->vlenb);
EXPECT_EQ(0UL, regset_data->vstart);
EXPECT_EQ(0UL, regset_data->vcsr);
/* apply invalid settings from fixture variants */
regset_data->vlenb *= variant->vlenb_mul;
regset_data->vstart = variant->vstart;
regset_data->vtype = variant->vtype;
regset_data->vcsr = variant->vcsr;
regset_data->vl = variant->vl;
iov.iov_base = regset_data;
iov.iov_len = regset_size;
errno = 0;
ret = ptrace(PTRACE_SETREGSET, pid, NT_RISCV_VECTOR, &iov);
ASSERT_EQ(errno, EINVAL);
ASSERT_EQ(ret, -1);
/* cleanup */
free(regset_data);
ASSERT_EQ(0, kill(pid, SIGKILL));
}
}
FIXTURE(v_csr_valid)
{
};
FIXTURE_SETUP(v_csr_valid)
{
}
FIXTURE_TEARDOWN(v_csr_valid)
{
}
/* modifications of the initial vsetvli settings */
FIXTURE_VARIANT(v_csr_valid)
{
unsigned long vstart;
unsigned long vl;
unsigned long vtype;
unsigned long vcsr;
unsigned long vlenb_mul;
unsigned long vlenb_min;
unsigned long vlenb_max;
unsigned long spec;
};
/* valid for VLEN >= 128: LMUL= 1/4, SEW = 32 */
FIXTURE_VARIANT_ADD(v_csr_valid, frac_lmul1)
{
.vstart = 0x0,
.vl = 0x0,
.vtype = 0x16,
.vcsr = 0x0,
.vlenb_mul = 0x1,
.vlenb_min = 0x10,
.vlenb_max = 0x0,
.spec = VECTOR_1_0,
};
/* valid for VLEN >= 16: LMUL= 2, SEW = 32 */
FIXTURE_VARIANT_ADD(v_csr_valid, int_lmul1)
{
.vstart = 0x0,
.vl = 0x0,
.vtype = 0x11,
.vcsr = 0x0,
.vlenb_mul = 0x1,
.vlenb_min = 0x2,
.vlenb_max = 0x0,
.spec = VECTOR_1_0,
};
/* valid for XTheadVector VLEN >= 16: LMUL= 2, SEW = 32 */
FIXTURE_VARIANT_ADD(v_csr_valid, int_lmul2)
{
.vstart = 0x0,
.vl = 0x0,
.vtype = 0x9,
.vcsr = 0x0,
.vlenb_mul = 0x1,
.vlenb_min = 0x2,
.vlenb_max = 0x0,
.spec = XTHEAD_VECTOR_0_7,
};
/* valid for VLEN >= 32: LMUL= 2, SEW = 32, VL = 2 */
FIXTURE_VARIANT_ADD(v_csr_valid, int_lmul3)
{
.vstart = 0x0,
.vl = 0x2,
.vtype = 0x11,
.vcsr = 0x0,
.vlenb_mul = 0x1,
.vlenb_min = 0x4,
.vlenb_max = 0x0,
.spec = VECTOR_1_0,
};
TEST_F(v_csr_valid, ptrace_v_valid_values)
{
unsigned long vlenb;
pid_t pid;
if (!is_vector_supported() && !is_xtheadvector_supported())
SKIP(return, "Vectors not supported");
if (is_vector_supported() && !vector_test(variant->spec))
SKIP(return, "Test not supported for Vector");
if (is_xtheadvector_supported() && !xthead_test(variant->spec))
SKIP(return, "Test not supported for XTheadVector");
vlenb = get_vr_len();
if (variant->vlenb_min) {
if (vlenb < variant->vlenb_min)
SKIP(return, "This test does not support VLEN < %lu\n",
variant->vlenb_min * 8);
}
if (variant->vlenb_max) {
if (vlenb > variant->vlenb_max)
SKIP(return, "This test does not support VLEN > %lu\n",
variant->vlenb_max * 8);
}
chld_lock = 1;
pid = fork();
ASSERT_LE(0, pid)
TH_LOG("fork: %m");
if (pid == 0) {
unsigned long vl;
while (chld_lock == 1)
asm volatile("" : : "g"(chld_lock) : "memory");
if (is_xtheadvector_supported()) {
asm volatile (
// 0 | zimm[10:0] | rs1 | 1 1 1 | rd |1010111| vsetvli
// vsetvli t4, x0, e16, m2, d1
".4byte 0b00000000010100000111111011010111\n"
"mv %[new_vl], t4\n"
: [new_vl] "=r" (vl) : : "t4");
} else {
asm volatile (
".option push\n"
".option arch, +zve32x\n"
"vsetvli %[new_vl], x0, e16, m2, tu, mu\n"
".option pop\n"
: [new_vl] "=r"(vl) : : );
}
asm volatile (
".option push\n"
".option norvc\n"
".option arch, +zve32x\n"
"ebreak\n" /* breakpoint 1: apply new V state using ptrace */
"nop\n"
"ebreak\n" /* breakpoint 2: V state clean - context will not be saved */
"vmv.v.i v0, -1\n"
"ebreak\n" /* breakpoint 3: V state dirty - context will be saved */
".option pop\n");
} else {
struct __riscv_v_regset_state *regset_data;
struct user_regs_struct regs;
size_t regset_size;
struct iovec iov;
int status;
/* attach */
ASSERT_EQ(0, ptrace(PTRACE_ATTACH, pid, NULL, NULL));
ASSERT_EQ(pid, waitpid(pid, &status, 0));
ASSERT_TRUE(WIFSTOPPED(status));
/* unlock */
ASSERT_EQ(0, ptrace(PTRACE_POKEDATA, pid, &chld_lock, 0));
/* resume and wait for the 1st ebreak */
ASSERT_EQ(0, ptrace(PTRACE_CONT, pid, NULL, NULL));
ASSERT_EQ(pid, waitpid(pid, &status, 0));
ASSERT_TRUE(WIFSTOPPED(status));
/* read tracee vector csr regs using ptrace GETREGSET */
regset_size = sizeof(*regset_data) + vlenb * 32;
regset_data = calloc(1, regset_size);
iov.iov_base = regset_data;
iov.iov_len = regset_size;
ASSERT_EQ(0, ptrace(PTRACE_GETREGSET, pid, NT_RISCV_VECTOR, &iov));
/* verify initial vsetvli settings */
if (is_xtheadvector_supported()) {
EXPECT_EQ(5UL, regset_data->vtype);
} else {
EXPECT_EQ(9UL, regset_data->vtype);
}
EXPECT_EQ(regset_data->vlenb, regset_data->vl);
EXPECT_EQ(vlenb, regset_data->vlenb);
EXPECT_EQ(0UL, regset_data->vstart);
EXPECT_EQ(0UL, regset_data->vcsr);
/* apply valid settings from fixture variants */
regset_data->vlenb *= variant->vlenb_mul;
regset_data->vstart = variant->vstart;
regset_data->vtype = variant->vtype;
regset_data->vcsr = variant->vcsr;
regset_data->vl = variant->vl;
iov.iov_base = regset_data;
iov.iov_len = regset_size;
ASSERT_EQ(0, ptrace(PTRACE_SETREGSET, pid, NT_RISCV_VECTOR, &iov));
/* skip 1st ebreak, then resume and wait for the 2nd ebreak */
iov.iov_base = &regs;
iov.iov_len = sizeof(regs);
ASSERT_EQ(0, ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov));
regs.pc += 4;
ASSERT_EQ(0, ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov));
ASSERT_EQ(0, ptrace(PTRACE_CONT, pid, NULL, NULL));
ASSERT_EQ(pid, waitpid(pid, &status, 0));
ASSERT_TRUE(WIFSTOPPED(status));
/* read tracee vector csr regs using ptrace GETREGSET */
iov.iov_base = regset_data;
iov.iov_len = regset_size;
ASSERT_EQ(0, ptrace(PTRACE_GETREGSET, pid, NT_RISCV_VECTOR, &iov));
/* verify vector csr regs from tracee context */
EXPECT_EQ(regset_data->vstart, variant->vstart);
EXPECT_EQ(regset_data->vtype, variant->vtype);
EXPECT_EQ(regset_data->vcsr, variant->vcsr);
EXPECT_EQ(regset_data->vl, variant->vl);
EXPECT_EQ(regset_data->vlenb, vlenb);
/* skip 2nd ebreak, then resume and wait for the 3rd ebreak */
iov.iov_base = &regs;
iov.iov_len = sizeof(regs);
ASSERT_EQ(0, ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov));
regs.pc += 4;
ASSERT_EQ(0, ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov));
ASSERT_EQ(0, ptrace(PTRACE_CONT, pid, NULL, NULL));
ASSERT_EQ(pid, waitpid(pid, &status, 0));
ASSERT_TRUE(WIFSTOPPED(status));
/* read tracee vector csr regs using ptrace GETREGSET */
iov.iov_base = regset_data;
iov.iov_len = regset_size;
ASSERT_EQ(0, ptrace(PTRACE_GETREGSET, pid, NT_RISCV_VECTOR, &iov));
/* verify vector csr regs from tracee context */
EXPECT_EQ(regset_data->vstart, variant->vstart);
EXPECT_EQ(regset_data->vtype, variant->vtype);
EXPECT_EQ(regset_data->vcsr, variant->vcsr);
EXPECT_EQ(regset_data->vl, variant->vl);
EXPECT_EQ(regset_data->vlenb, vlenb);
/* cleanup */
free(regset_data);
ASSERT_EQ(0, kill(pid, SIGKILL));
}
}
TEST_HARNESS_MAIN
@@ -0,0 +1,118 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/wait.h>
#define THIS_PROGRAM "./vstate_exec_nolibc"
int main(int argc, char **argv)
{
int rc, pid, status, test_inherit = 0, xtheadvector = 0;
long ctrl, ctrl_c;
char *exec_argv[2], *exec_envp[2];
if (argc > 1 && strcmp(argv[1], "x"))
test_inherit = 1;
if (argc > 2 && strcmp(argv[2], "x"))
xtheadvector = 1;
ctrl = prctl(PR_RISCV_V_GET_CONTROL, 0, 0, 0, 0);
if (ctrl == -1) {
puts("PR_RISCV_V_GET_CONTROL is not supported\n");
exit(-1);
}
if (test_inherit) {
pid = fork();
if (pid == -1) {
puts("fork failed\n");
exit(-1);
}
/* child */
if (!pid) {
exec_argv[0] = THIS_PROGRAM;
exec_argv[1] = NULL;
exec_envp[0] = NULL;
exec_envp[1] = NULL;
/* launch the program again to check inherit */
rc = execve(THIS_PROGRAM, exec_argv, exec_envp);
if (rc) {
puts("child execve failed\n");
exit(-1);
}
}
} else {
pid = fork();
if (pid == -1) {
puts("fork failed\n");
exit(-1);
}
if (!pid) {
rc = prctl(PR_RISCV_V_GET_CONTROL, 0, 0, 0, 0);
if (rc != ctrl) {
puts("child's vstate_ctrl not equal to parent's\n");
exit(-1);
}
if (xtheadvector)
asm volatile (".4byte 0x00007ed7");
else
asm volatile (".option push\n\t"
".option arch, +v\n\t"
"vsetvli x0, x0, e32, m8, ta, ma\n\t"
".option pop\n\t"
);
exit(ctrl);
}
}
rc = waitpid(-1, &status, 0);
if (WIFEXITED(status) && WEXITSTATUS(status) == -1) {
puts("child exited abnormally\n");
exit(-1);
}
if (WIFSIGNALED(status)) {
if (WTERMSIG(status) != SIGILL) {
puts("child was terminated by unexpected signal\n");
exit(-1);
}
if ((ctrl & PR_RISCV_V_VSTATE_CTRL_CUR_MASK) != PR_RISCV_V_VSTATE_CTRL_OFF) {
puts("child signaled by illegal V access but vstate_ctrl is not off\n");
exit(-1);
}
/* child terminated, and its vstate_ctrl is off */
exit(ctrl);
}
ctrl_c = WEXITSTATUS(status);
if (test_inherit) {
if (ctrl & PR_RISCV_V_VSTATE_CTRL_INHERIT) {
if (!(ctrl_c & PR_RISCV_V_VSTATE_CTRL_INHERIT)) {
puts("parent has inherit bit, but child has not\n");
exit(-1);
}
}
rc = (ctrl & PR_RISCV_V_VSTATE_CTRL_NEXT_MASK) >> 2;
if (rc != PR_RISCV_V_VSTATE_CTRL_DEFAULT) {
if (rc != (ctrl_c & PR_RISCV_V_VSTATE_CTRL_CUR_MASK)) {
puts("parent's next setting does not equal to child's\n");
exit(-1);
}
if (!(ctrl & PR_RISCV_V_VSTATE_CTRL_INHERIT)) {
if ((ctrl_c & PR_RISCV_V_VSTATE_CTRL_NEXT_MASK) !=
PR_RISCV_V_VSTATE_CTRL_DEFAULT) {
puts("must clear child's next vstate_ctrl if !inherit\n");
exit(-1);
}
}
}
}
return ctrl;
}
@@ -0,0 +1,246 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <sys/prctl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <stdlib.h>
#include "kselftest_harness.h"
#include "v_helpers.h"
#define NEXT_PROGRAM "./vstate_exec_nolibc"
int test_and_compare_child(long provided, long expected, int inherit, int xtheadvector)
{
int rc;
rc = prctl(PR_RISCV_V_SET_CONTROL, provided);
if (rc != 0) {
printf("prctl with provided arg %lx failed with code %d\n",
provided, rc);
return -1;
}
rc = launch_test(NEXT_PROGRAM, inherit, xtheadvector);
if (rc != expected) {
printf("Test failed, check %d != %ld\n", rc, expected);
return -2;
}
return 0;
}
#define PR_RISCV_V_VSTATE_CTRL_CUR_SHIFT 0
#define PR_RISCV_V_VSTATE_CTRL_NEXT_SHIFT 2
TEST(get_control_no_v)
{
long rc;
if (is_vector_supported() || is_xtheadvector_supported())
SKIP(return, "Test expects vector to be not supported");
rc = prctl(PR_RISCV_V_GET_CONTROL);
EXPECT_EQ(-1, rc)
TH_LOG("GET_CONTROL should fail on kernel/hw without ZVE32X");
EXPECT_EQ(EINVAL, errno)
TH_LOG("GET_CONTROL should fail on kernel/hw without ZVE32X");
}
TEST(set_control_no_v)
{
long rc;
if (is_vector_supported() || is_xtheadvector_supported())
SKIP(return, "Test expects vector to be not supported");
rc = prctl(PR_RISCV_V_SET_CONTROL, PR_RISCV_V_VSTATE_CTRL_ON);
EXPECT_EQ(-1, rc)
TH_LOG("SET_CONTROL should fail on kernel/hw without ZVE32X");
EXPECT_EQ(EINVAL, errno)
TH_LOG("SET_CONTROL should fail on kernel/hw without ZVE32X");
}
TEST(vstate_on_current)
{
long flag;
long rc;
if (!is_vector_supported() && !is_xtheadvector_supported())
SKIP(return, "Vector not supported");
flag = PR_RISCV_V_VSTATE_CTRL_ON;
rc = prctl(PR_RISCV_V_SET_CONTROL, flag);
EXPECT_EQ(0, rc) TH_LOG("Enabling V for current should always succeed");
}
TEST(vstate_off_eperm)
{
long flag;
long rc;
if (!is_vector_supported() && !is_xtheadvector_supported())
SKIP(return, "Vector not supported");
flag = PR_RISCV_V_VSTATE_CTRL_OFF;
rc = prctl(PR_RISCV_V_SET_CONTROL, flag);
EXPECT_EQ(EPERM, errno)
TH_LOG("Disabling V in current thread with V enabled must fail with EPERM(%d)", errno);
EXPECT_EQ(-1, rc)
TH_LOG("Disabling V in current thread with V enabled must fail with EPERM(%d)", errno);
}
TEST(vstate_on_no_nesting)
{
long flag;
int xtheadvector = 0;
if (!is_vector_supported()) {
if (is_xtheadvector_supported())
xtheadvector = 1;
else
SKIP(return, "Vector not supported");
}
/* Turn on next's vector explicitly and test */
flag = PR_RISCV_V_VSTATE_CTRL_ON << PR_RISCV_V_VSTATE_CTRL_NEXT_SHIFT;
EXPECT_EQ(0, test_and_compare_child(flag, PR_RISCV_V_VSTATE_CTRL_ON, 0, xtheadvector));
}
TEST(vstate_off_nesting)
{
long flag;
int xtheadvector = 0;
if (!is_vector_supported()) {
if (is_xtheadvector_supported())
xtheadvector = 1;
else
SKIP(return, "Vector not supported");
}
/* Turn off next's vector explicitly and test */
flag = PR_RISCV_V_VSTATE_CTRL_OFF << PR_RISCV_V_VSTATE_CTRL_NEXT_SHIFT;
EXPECT_EQ(0, test_and_compare_child(flag, PR_RISCV_V_VSTATE_CTRL_OFF, 1, xtheadvector));
}
TEST(vstate_on_inherit_no_nesting)
{
long flag, expected;
int xtheadvector = 0;
if (!is_vector_supported()) {
if (is_xtheadvector_supported())
xtheadvector = 1;
else
SKIP(return, "Vector not supported");
}
/* Turn on next's vector explicitly and test no inherit */
flag = PR_RISCV_V_VSTATE_CTRL_ON << PR_RISCV_V_VSTATE_CTRL_NEXT_SHIFT;
flag |= PR_RISCV_V_VSTATE_CTRL_INHERIT;
expected = flag | PR_RISCV_V_VSTATE_CTRL_ON;
EXPECT_EQ(0, test_and_compare_child(flag, expected, 0, xtheadvector));
}
TEST(vstate_on_inherit)
{
long flag, expected;
int xtheadvector = 0;
if (!is_vector_supported()) {
if (is_xtheadvector_supported())
xtheadvector = 1;
else
SKIP(return, "Vector not supported");
}
/* Turn on next's vector explicitly and test inherit */
flag = PR_RISCV_V_VSTATE_CTRL_ON << PR_RISCV_V_VSTATE_CTRL_NEXT_SHIFT;
flag |= PR_RISCV_V_VSTATE_CTRL_INHERIT;
expected = flag | PR_RISCV_V_VSTATE_CTRL_ON;
EXPECT_EQ(0, test_and_compare_child(flag, expected, 1, xtheadvector));
}
TEST(vstate_off_inherit_no_nesting)
{
long flag, expected;
int xtheadvector = 0;
if (!is_vector_supported()) {
if (is_xtheadvector_supported())
xtheadvector = 1;
else
SKIP(return, "Vector not supported");
}
/* Turn off next's vector explicitly and test no inherit */
flag = PR_RISCV_V_VSTATE_CTRL_OFF << PR_RISCV_V_VSTATE_CTRL_NEXT_SHIFT;
flag |= PR_RISCV_V_VSTATE_CTRL_INHERIT;
expected = flag | PR_RISCV_V_VSTATE_CTRL_OFF;
EXPECT_EQ(0, test_and_compare_child(flag, expected, 0, xtheadvector));
}
TEST(vstate_off_inherit)
{
long flag, expected;
int xtheadvector = 0;
if (!is_vector_supported()) {
if (is_xtheadvector_supported())
xtheadvector = 1;
else
SKIP(return, "Vector not supported");
}
/* Turn off next's vector explicitly and test inherit */
flag = PR_RISCV_V_VSTATE_CTRL_OFF << PR_RISCV_V_VSTATE_CTRL_NEXT_SHIFT;
flag |= PR_RISCV_V_VSTATE_CTRL_INHERIT;
expected = flag | PR_RISCV_V_VSTATE_CTRL_OFF;
EXPECT_EQ(0, test_and_compare_child(flag, expected, 1, xtheadvector));
}
/* arguments should fail with EINVAL */
TEST(inval_set_control_1)
{
int rc;
if (!is_vector_supported() && !is_xtheadvector_supported())
SKIP(return, "Vector not supported");
rc = prctl(PR_RISCV_V_SET_CONTROL, 0xff0);
EXPECT_EQ(-1, rc);
EXPECT_EQ(EINVAL, errno);
}
/* arguments should fail with EINVAL */
TEST(inval_set_control_2)
{
int rc;
if (!is_vector_supported() && !is_xtheadvector_supported())
SKIP(return, "Vector not supported");
rc = prctl(PR_RISCV_V_SET_CONTROL, 0x3);
EXPECT_EQ(-1, rc);
EXPECT_EQ(EINVAL, errno);
}
/* arguments should fail with EINVAL */
TEST(inval_set_control_3)
{
int rc;
if (!is_vector_supported() && !is_xtheadvector_supported())
SKIP(return, "Vector not supported");
rc = prctl(PR_RISCV_V_SET_CONTROL, 0xc);
EXPECT_EQ(-1, rc);
EXPECT_EQ(EINVAL, errno);
}
TEST_HARNESS_MAIN
@@ -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();
}