671 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			671 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: GPL-2.0-only
 | 
						|
/*
 | 
						|
 * Copyright (C) 2021 ARM Limited.
 | 
						|
 * Original author: Mark Brown <broonie@kernel.org>
 | 
						|
 */
 | 
						|
#include <assert.h>
 | 
						|
#include <errno.h>
 | 
						|
#include <fcntl.h>
 | 
						|
#include <stddef.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <sys/auxv.h>
 | 
						|
#include <sys/prctl.h>
 | 
						|
#include <sys/types.h>
 | 
						|
#include <sys/wait.h>
 | 
						|
#include <asm/sigcontext.h>
 | 
						|
#include <asm/hwcap.h>
 | 
						|
 | 
						|
#include "../../kselftest.h"
 | 
						|
#include "rdvl.h"
 | 
						|
 | 
						|
#define ARCH_MIN_VL SVE_VL_MIN
 | 
						|
 | 
						|
struct vec_data {
 | 
						|
	const char *name;
 | 
						|
	unsigned long hwcap_type;
 | 
						|
	unsigned long hwcap;
 | 
						|
	const char *rdvl_binary;
 | 
						|
	int (*rdvl)(void);
 | 
						|
 | 
						|
	int prctl_get;
 | 
						|
	int prctl_set;
 | 
						|
	const char *default_vl_file;
 | 
						|
 | 
						|
	int default_vl;
 | 
						|
	int min_vl;
 | 
						|
	int max_vl;
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
static struct vec_data vec_data[] = {
 | 
						|
	{
 | 
						|
		.name = "SVE",
 | 
						|
		.hwcap_type = AT_HWCAP,
 | 
						|
		.hwcap = HWCAP_SVE,
 | 
						|
		.rdvl = rdvl_sve,
 | 
						|
		.rdvl_binary = "./rdvl-sve",
 | 
						|
		.prctl_get = PR_SVE_GET_VL,
 | 
						|
		.prctl_set = PR_SVE_SET_VL,
 | 
						|
		.default_vl_file = "/proc/sys/abi/sve_default_vector_length",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		.name = "SME",
 | 
						|
		.hwcap_type = AT_HWCAP2,
 | 
						|
		.hwcap = HWCAP2_SME,
 | 
						|
		.rdvl = rdvl_sme,
 | 
						|
		.rdvl_binary = "./rdvl-sme",
 | 
						|
		.prctl_get = PR_SME_GET_VL,
 | 
						|
		.prctl_set = PR_SME_SET_VL,
 | 
						|
		.default_vl_file = "/proc/sys/abi/sme_default_vector_length",
 | 
						|
	},
 | 
						|
};
 | 
						|
 | 
						|
static int stdio_read_integer(FILE *f, const char *what, int *val)
 | 
						|
{
 | 
						|
	int n = 0;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	ret = fscanf(f, "%d%*1[\n]%n", val, &n);
 | 
						|
	if (ret < 1 || n < 1) {
 | 
						|
		ksft_print_msg("failed to parse integer from %s\n", what);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* Start a new process and return the vector length it sees */
 | 
						|
static int get_child_rdvl(struct vec_data *data)
 | 
						|
{
 | 
						|
	FILE *out;
 | 
						|
	int pipefd[2];
 | 
						|
	pid_t pid, child;
 | 
						|
	int read_vl, ret;
 | 
						|
 | 
						|
	ret = pipe(pipefd);
 | 
						|
	if (ret == -1) {
 | 
						|
		ksft_print_msg("pipe() failed: %d (%s)\n",
 | 
						|
			       errno, strerror(errno));
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	fflush(stdout);
 | 
						|
 | 
						|
	child = fork();
 | 
						|
	if (child == -1) {
 | 
						|
		ksft_print_msg("fork() failed: %d (%s)\n",
 | 
						|
			       errno, strerror(errno));
 | 
						|
		close(pipefd[0]);
 | 
						|
		close(pipefd[1]);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Child: put vector length on the pipe */
 | 
						|
	if (child == 0) {
 | 
						|
		/*
 | 
						|
		 * Replace stdout with the pipe, errors to stderr from
 | 
						|
		 * here as kselftest prints to stdout.
 | 
						|
		 */
 | 
						|
		ret = dup2(pipefd[1], 1);
 | 
						|
		if (ret == -1) {
 | 
						|
			fprintf(stderr, "dup2() %d\n", errno);
 | 
						|
			exit(EXIT_FAILURE);
 | 
						|
		}
 | 
						|
 | 
						|
		/* exec() a new binary which puts the VL on stdout */
 | 
						|
		ret = execl(data->rdvl_binary, data->rdvl_binary, NULL);
 | 
						|
		fprintf(stderr, "execl(%s) failed: %d (%s)\n",
 | 
						|
			data->rdvl_binary, errno, strerror(errno));
 | 
						|
 | 
						|
		exit(EXIT_FAILURE);
 | 
						|
	}
 | 
						|
 | 
						|
	close(pipefd[1]);
 | 
						|
 | 
						|
	/* Parent; wait for the exit status from the child & verify it */
 | 
						|
	do {
 | 
						|
		pid = wait(&ret);
 | 
						|
		if (pid == -1) {
 | 
						|
			ksft_print_msg("wait() failed: %d (%s)\n",
 | 
						|
				       errno, strerror(errno));
 | 
						|
			close(pipefd[0]);
 | 
						|
			return -1;
 | 
						|
		}
 | 
						|
	} while (pid != child);
 | 
						|
 | 
						|
	assert(pid == child);
 | 
						|
 | 
						|
	if (!WIFEXITED(ret)) {
 | 
						|
		ksft_print_msg("child exited abnormally\n");
 | 
						|
		close(pipefd[0]);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	if (WEXITSTATUS(ret) != 0) {
 | 
						|
		ksft_print_msg("child returned error %d\n",
 | 
						|
			       WEXITSTATUS(ret));
 | 
						|
		close(pipefd[0]);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	out = fdopen(pipefd[0], "r");
 | 
						|
	if (!out) {
 | 
						|
		ksft_print_msg("failed to open child stdout\n");
 | 
						|
		close(pipefd[0]);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = stdio_read_integer(out, "child", &read_vl);
 | 
						|
	fclose(out);
 | 
						|
	if (ret != 0)
 | 
						|
		return ret;
 | 
						|
 | 
						|
	return read_vl;
 | 
						|
}
 | 
						|
 | 
						|
static int file_read_integer(const char *name, int *val)
 | 
						|
{
 | 
						|
	FILE *f;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	f = fopen(name, "r");
 | 
						|
	if (!f) {
 | 
						|
		ksft_test_result_fail("Unable to open %s: %d (%s)\n",
 | 
						|
				      name, errno,
 | 
						|
				      strerror(errno));
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = stdio_read_integer(f, name, val);
 | 
						|
	fclose(f);
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int file_write_integer(const char *name, int val)
 | 
						|
{
 | 
						|
	FILE *f;
 | 
						|
 | 
						|
	f = fopen(name, "w");
 | 
						|
	if (!f) {
 | 
						|
		ksft_test_result_fail("Unable to open %s: %d (%s)\n",
 | 
						|
				      name, errno,
 | 
						|
				      strerror(errno));
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	fprintf(f, "%d", val);
 | 
						|
	fclose(f);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Verify that we can read the default VL via proc, checking that it
 | 
						|
 * is set in a freshly spawned child.
 | 
						|
 */
 | 
						|
static void proc_read_default(struct vec_data *data)
 | 
						|
{
 | 
						|
	int default_vl, child_vl, ret;
 | 
						|
 | 
						|
	ret = file_read_integer(data->default_vl_file, &default_vl);
 | 
						|
	if (ret != 0)
 | 
						|
		return;
 | 
						|
 | 
						|
	/* Is this the actual default seen by new processes? */
 | 
						|
	child_vl = get_child_rdvl(data);
 | 
						|
	if (child_vl != default_vl) {
 | 
						|
		ksft_test_result_fail("%s is %d but child VL is %d\n",
 | 
						|
				      data->default_vl_file,
 | 
						|
				      default_vl, child_vl);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	ksft_test_result_pass("%s default vector length %d\n", data->name,
 | 
						|
			      default_vl);
 | 
						|
	data->default_vl = default_vl;
 | 
						|
}
 | 
						|
 | 
						|
/* Verify that we can write a minimum value and have it take effect */
 | 
						|
static void proc_write_min(struct vec_data *data)
 | 
						|
{
 | 
						|
	int ret, new_default, child_vl;
 | 
						|
 | 
						|
	if (geteuid() != 0) {
 | 
						|
		ksft_test_result_skip("Need to be root to write to /proc\n");
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = file_write_integer(data->default_vl_file, ARCH_MIN_VL);
 | 
						|
	if (ret != 0)
 | 
						|
		return;
 | 
						|
 | 
						|
	/* What was the new value? */
 | 
						|
	ret = file_read_integer(data->default_vl_file, &new_default);
 | 
						|
	if (ret != 0)
 | 
						|
		return;
 | 
						|
 | 
						|
	/* Did it take effect in a new process? */
 | 
						|
	child_vl = get_child_rdvl(data);
 | 
						|
	if (child_vl != new_default) {
 | 
						|
		ksft_test_result_fail("%s is %d but child VL is %d\n",
 | 
						|
				      data->default_vl_file,
 | 
						|
				      new_default, child_vl);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	ksft_test_result_pass("%s minimum vector length %d\n", data->name,
 | 
						|
			      new_default);
 | 
						|
	data->min_vl = new_default;
 | 
						|
 | 
						|
	file_write_integer(data->default_vl_file, data->default_vl);
 | 
						|
}
 | 
						|
 | 
						|
/* Verify that we can write a maximum value and have it take effect */
 | 
						|
static void proc_write_max(struct vec_data *data)
 | 
						|
{
 | 
						|
	int ret, new_default, child_vl;
 | 
						|
 | 
						|
	if (geteuid() != 0) {
 | 
						|
		ksft_test_result_skip("Need to be root to write to /proc\n");
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* -1 is accepted by the /proc interface as the maximum VL */
 | 
						|
	ret = file_write_integer(data->default_vl_file, -1);
 | 
						|
	if (ret != 0)
 | 
						|
		return;
 | 
						|
 | 
						|
	/* What was the new value? */
 | 
						|
	ret = file_read_integer(data->default_vl_file, &new_default);
 | 
						|
	if (ret != 0)
 | 
						|
		return;
 | 
						|
 | 
						|
	/* Did it take effect in a new process? */
 | 
						|
	child_vl = get_child_rdvl(data);
 | 
						|
	if (child_vl != new_default) {
 | 
						|
		ksft_test_result_fail("%s is %d but child VL is %d\n",
 | 
						|
				      data->default_vl_file,
 | 
						|
				      new_default, child_vl);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	ksft_test_result_pass("%s maximum vector length %d\n", data->name,
 | 
						|
			      new_default);
 | 
						|
	data->max_vl = new_default;
 | 
						|
 | 
						|
	file_write_integer(data->default_vl_file, data->default_vl);
 | 
						|
}
 | 
						|
 | 
						|
/* Can we read back a VL from prctl? */
 | 
						|
static void prctl_get(struct vec_data *data)
 | 
						|
{
 | 
						|
	int ret;
 | 
						|
 | 
						|
	ret = prctl(data->prctl_get);
 | 
						|
	if (ret == -1) {
 | 
						|
		ksft_test_result_fail("%s prctl() read failed: %d (%s)\n",
 | 
						|
				      data->name, errno, strerror(errno));
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Mask out any flags */
 | 
						|
	ret &= PR_SVE_VL_LEN_MASK;
 | 
						|
 | 
						|
	/* Is that what we can read back directly? */
 | 
						|
	if (ret == data->rdvl())
 | 
						|
		ksft_test_result_pass("%s current VL is %d\n",
 | 
						|
				      data->name, ret);
 | 
						|
	else
 | 
						|
		ksft_test_result_fail("%s prctl() VL %d but RDVL is %d\n",
 | 
						|
				      data->name, ret, data->rdvl());
 | 
						|
}
 | 
						|
 | 
						|
/* Does the prctl let us set the VL we already have? */
 | 
						|
static void prctl_set_same(struct vec_data *data)
 | 
						|
{
 | 
						|
	int cur_vl = data->rdvl();
 | 
						|
	int ret;
 | 
						|
 | 
						|
	ret = prctl(data->prctl_set, cur_vl);
 | 
						|
	if (ret < 0) {
 | 
						|
		ksft_test_result_fail("%s prctl set failed: %d (%s)\n",
 | 
						|
				      data->name, errno, strerror(errno));
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	ksft_test_result(cur_vl == data->rdvl(),
 | 
						|
			 "%s set VL %d and have VL %d\n",
 | 
						|
			 data->name, cur_vl, data->rdvl());
 | 
						|
}
 | 
						|
 | 
						|
/* Can we set a new VL for this process? */
 | 
						|
static void prctl_set(struct vec_data *data)
 | 
						|
{
 | 
						|
	int ret;
 | 
						|
 | 
						|
	if (data->min_vl == data->max_vl) {
 | 
						|
		ksft_test_result_skip("%s only one VL supported\n",
 | 
						|
				      data->name);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Try to set the minimum VL */
 | 
						|
	ret = prctl(data->prctl_set, data->min_vl);
 | 
						|
	if (ret < 0) {
 | 
						|
		ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
 | 
						|
				      data->name, data->min_vl,
 | 
						|
				      errno, strerror(errno));
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	if ((ret & PR_SVE_VL_LEN_MASK) != data->min_vl) {
 | 
						|
		ksft_test_result_fail("%s prctl set %d but return value is %d\n",
 | 
						|
				      data->name, data->min_vl, data->rdvl());
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	if (data->rdvl() != data->min_vl) {
 | 
						|
		ksft_test_result_fail("%s set %d but RDVL is %d\n",
 | 
						|
				      data->name, data->min_vl, data->rdvl());
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Try to set the maximum VL */
 | 
						|
	ret = prctl(data->prctl_set, data->max_vl);
 | 
						|
	if (ret < 0) {
 | 
						|
		ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
 | 
						|
				      data->name, data->max_vl,
 | 
						|
				      errno, strerror(errno));
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	if ((ret & PR_SVE_VL_LEN_MASK) != data->max_vl) {
 | 
						|
		ksft_test_result_fail("%s prctl() set %d but return value is %d\n",
 | 
						|
				      data->name, data->max_vl, data->rdvl());
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* The _INHERIT flag should not be present when we read the VL */
 | 
						|
	ret = prctl(data->prctl_get);
 | 
						|
	if (ret == -1) {
 | 
						|
		ksft_test_result_fail("%s prctl() read failed: %d (%s)\n",
 | 
						|
				      data->name, errno, strerror(errno));
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	if (ret & PR_SVE_VL_INHERIT) {
 | 
						|
		ksft_test_result_fail("%s prctl() reports _INHERIT\n",
 | 
						|
				      data->name);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	ksft_test_result_pass("%s prctl() set min/max\n", data->name);
 | 
						|
}
 | 
						|
 | 
						|
/* If we didn't request it a new VL shouldn't affect the child */
 | 
						|
static void prctl_set_no_child(struct vec_data *data)
 | 
						|
{
 | 
						|
	int ret, child_vl;
 | 
						|
 | 
						|
	if (data->min_vl == data->max_vl) {
 | 
						|
		ksft_test_result_skip("%s only one VL supported\n",
 | 
						|
				      data->name);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = prctl(data->prctl_set, data->min_vl);
 | 
						|
	if (ret < 0) {
 | 
						|
		ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
 | 
						|
				      data->name, data->min_vl,
 | 
						|
				      errno, strerror(errno));
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Ensure the default VL is different */
 | 
						|
	ret = file_write_integer(data->default_vl_file, data->max_vl);
 | 
						|
	if (ret != 0)
 | 
						|
		return;
 | 
						|
 | 
						|
	/* Check that the child has the default we just set */
 | 
						|
	child_vl = get_child_rdvl(data);
 | 
						|
	if (child_vl != data->max_vl) {
 | 
						|
		ksft_test_result_fail("%s is %d but child VL is %d\n",
 | 
						|
				      data->default_vl_file,
 | 
						|
				      data->max_vl, child_vl);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	ksft_test_result_pass("%s vector length used default\n", data->name);
 | 
						|
 | 
						|
	file_write_integer(data->default_vl_file, data->default_vl);
 | 
						|
}
 | 
						|
 | 
						|
/* If we didn't request it a new VL shouldn't affect the child */
 | 
						|
static void prctl_set_for_child(struct vec_data *data)
 | 
						|
{
 | 
						|
	int ret, child_vl;
 | 
						|
 | 
						|
	if (data->min_vl == data->max_vl) {
 | 
						|
		ksft_test_result_skip("%s only one VL supported\n",
 | 
						|
				      data->name);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = prctl(data->prctl_set, data->min_vl | PR_SVE_VL_INHERIT);
 | 
						|
	if (ret < 0) {
 | 
						|
		ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
 | 
						|
				      data->name, data->min_vl,
 | 
						|
				      errno, strerror(errno));
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* The _INHERIT flag should be present when we read the VL */
 | 
						|
	ret = prctl(data->prctl_get);
 | 
						|
	if (ret == -1) {
 | 
						|
		ksft_test_result_fail("%s prctl() read failed: %d (%s)\n",
 | 
						|
				      data->name, errno, strerror(errno));
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	if (!(ret & PR_SVE_VL_INHERIT)) {
 | 
						|
		ksft_test_result_fail("%s prctl() does not report _INHERIT\n",
 | 
						|
				      data->name);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Ensure the default VL is different */
 | 
						|
	ret = file_write_integer(data->default_vl_file, data->max_vl);
 | 
						|
	if (ret != 0)
 | 
						|
		return;
 | 
						|
 | 
						|
	/* Check that the child inherited our VL */
 | 
						|
	child_vl = get_child_rdvl(data);
 | 
						|
	if (child_vl != data->min_vl) {
 | 
						|
		ksft_test_result_fail("%s is %d but child VL is %d\n",
 | 
						|
				      data->default_vl_file,
 | 
						|
				      data->min_vl, child_vl);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	ksft_test_result_pass("%s vector length was inherited\n", data->name);
 | 
						|
 | 
						|
	file_write_integer(data->default_vl_file, data->default_vl);
 | 
						|
}
 | 
						|
 | 
						|
/* _ONEXEC takes effect only in the child process */
 | 
						|
static void prctl_set_onexec(struct vec_data *data)
 | 
						|
{
 | 
						|
	int ret, child_vl;
 | 
						|
 | 
						|
	if (data->min_vl == data->max_vl) {
 | 
						|
		ksft_test_result_skip("%s only one VL supported\n",
 | 
						|
				      data->name);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Set a known value for the default and our current VL */
 | 
						|
	ret = file_write_integer(data->default_vl_file, data->max_vl);
 | 
						|
	if (ret != 0)
 | 
						|
		return;
 | 
						|
 | 
						|
	ret = prctl(data->prctl_set, data->max_vl);
 | 
						|
	if (ret < 0) {
 | 
						|
		ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
 | 
						|
				      data->name, data->min_vl,
 | 
						|
				      errno, strerror(errno));
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Set a different value for the child to have on exec */
 | 
						|
	ret = prctl(data->prctl_set, data->min_vl | PR_SVE_SET_VL_ONEXEC);
 | 
						|
	if (ret < 0) {
 | 
						|
		ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
 | 
						|
				      data->name, data->min_vl,
 | 
						|
				      errno, strerror(errno));
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Our current VL should stay the same */
 | 
						|
	if (data->rdvl() != data->max_vl) {
 | 
						|
		ksft_test_result_fail("%s VL changed by _ONEXEC prctl()\n",
 | 
						|
				      data->name);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Check that the child inherited our VL */
 | 
						|
	child_vl = get_child_rdvl(data);
 | 
						|
	if (child_vl != data->min_vl) {
 | 
						|
		ksft_test_result_fail("Set %d _ONEXEC but child VL is %d\n",
 | 
						|
				      data->min_vl, child_vl);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	ksft_test_result_pass("%s vector length set on exec\n", data->name);
 | 
						|
 | 
						|
	file_write_integer(data->default_vl_file, data->default_vl);
 | 
						|
}
 | 
						|
 | 
						|
/* For each VQ verify that setting via prctl() does the right thing */
 | 
						|
static void prctl_set_all_vqs(struct vec_data *data)
 | 
						|
{
 | 
						|
	int ret, vq, vl, new_vl;
 | 
						|
	int errors = 0;
 | 
						|
 | 
						|
	if (!data->min_vl || !data->max_vl) {
 | 
						|
		ksft_test_result_skip("%s Failed to enumerate VLs, not testing VL setting\n",
 | 
						|
				      data->name);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	for (vq = SVE_VQ_MIN; vq <= SVE_VQ_MAX; vq++) {
 | 
						|
		vl = sve_vl_from_vq(vq);
 | 
						|
 | 
						|
		/* Attempt to set the VL */
 | 
						|
		ret = prctl(data->prctl_set, vl);
 | 
						|
		if (ret < 0) {
 | 
						|
			errors++;
 | 
						|
			ksft_print_msg("%s prctl set failed for %d: %d (%s)\n",
 | 
						|
				       data->name, vl,
 | 
						|
				       errno, strerror(errno));
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		new_vl = ret & PR_SVE_VL_LEN_MASK;
 | 
						|
 | 
						|
		/* Check that we actually have the reported new VL */
 | 
						|
		if (data->rdvl() != new_vl) {
 | 
						|
			ksft_print_msg("Set %s VL %d but RDVL reports %d\n",
 | 
						|
				       data->name, new_vl, data->rdvl());
 | 
						|
			errors++;
 | 
						|
		}
 | 
						|
 | 
						|
		/* Was that the VL we asked for? */
 | 
						|
		if (new_vl == vl)
 | 
						|
			continue;
 | 
						|
 | 
						|
		/* Should round up to the minimum VL if below it */
 | 
						|
		if (vl < data->min_vl) {
 | 
						|
			if (new_vl != data->min_vl) {
 | 
						|
				ksft_print_msg("%s VL %d returned %d not minimum %d\n",
 | 
						|
					       data->name, vl, new_vl,
 | 
						|
					       data->min_vl);
 | 
						|
				errors++;
 | 
						|
			}
 | 
						|
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		/* Should round down to maximum VL if above it */
 | 
						|
		if (vl > data->max_vl) {
 | 
						|
			if (new_vl != data->max_vl) {
 | 
						|
				ksft_print_msg("%s VL %d returned %d not maximum %d\n",
 | 
						|
					       data->name, vl, new_vl,
 | 
						|
					       data->max_vl);
 | 
						|
				errors++;
 | 
						|
			}
 | 
						|
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		/* Otherwise we should've rounded down */
 | 
						|
		if (!(new_vl < vl)) {
 | 
						|
			ksft_print_msg("%s VL %d returned %d, did not round down\n",
 | 
						|
				       data->name, vl, new_vl);
 | 
						|
			errors++;
 | 
						|
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	ksft_test_result(errors == 0, "%s prctl() set all VLs, %d errors\n",
 | 
						|
			 data->name, errors);
 | 
						|
}
 | 
						|
 | 
						|
typedef void (*test_type)(struct vec_data *);
 | 
						|
 | 
						|
static const test_type tests[] = {
 | 
						|
	/*
 | 
						|
	 * The default/min/max tests must be first and in this order
 | 
						|
	 * to provide data for other tests.
 | 
						|
	 */
 | 
						|
	proc_read_default,
 | 
						|
	proc_write_min,
 | 
						|
	proc_write_max,
 | 
						|
 | 
						|
	prctl_get,
 | 
						|
	prctl_set_same,
 | 
						|
	prctl_set,
 | 
						|
	prctl_set_no_child,
 | 
						|
	prctl_set_for_child,
 | 
						|
	prctl_set_onexec,
 | 
						|
	prctl_set_all_vqs,
 | 
						|
};
 | 
						|
 | 
						|
int main(void)
 | 
						|
{
 | 
						|
	int i, j;
 | 
						|
 | 
						|
	ksft_print_header();
 | 
						|
	ksft_set_plan(ARRAY_SIZE(tests) * ARRAY_SIZE(vec_data));
 | 
						|
 | 
						|
	for (i = 0; i < ARRAY_SIZE(vec_data); i++) {
 | 
						|
		struct vec_data *data = &vec_data[i];
 | 
						|
		unsigned long supported;
 | 
						|
 | 
						|
		supported = getauxval(data->hwcap_type) & data->hwcap;
 | 
						|
 | 
						|
		for (j = 0; j < ARRAY_SIZE(tests); j++) {
 | 
						|
			if (supported)
 | 
						|
				tests[j](data);
 | 
						|
			else
 | 
						|
				ksft_test_result_skip("%s not supported\n",
 | 
						|
						      data->name);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	ksft_exit_pass();
 | 
						|
}
 |