brintos

brintos / linux-shallow public Read only

0
0
Text · 18.2 KiB · ea9c7d4 Raw
798 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2021 ARM Limited.4 * Original author: Mark Brown <broonie@kernel.org>5 */6#include <assert.h>7#include <errno.h>8#include <fcntl.h>9#include <stdbool.h>10#include <stddef.h>11#include <stdio.h>12#include <stdlib.h>13#include <string.h>14#include <unistd.h>15#include <sys/auxv.h>16#include <sys/prctl.h>17#include <sys/types.h>18#include <sys/wait.h>19#include <asm/sigcontext.h>20#include <asm/hwcap.h>21 22#include "../../kselftest.h"23#include "rdvl.h"24 25#define ARCH_MIN_VL SVE_VL_MIN26 27struct vec_data {28	const char *name;29	unsigned long hwcap_type;30	unsigned long hwcap;31	const char *rdvl_binary;32	int (*rdvl)(void);33 34	int prctl_get;35	int prctl_set;36	const char *default_vl_file;37 38	int default_vl;39	int min_vl;40	int max_vl;41};42 43#define VEC_SVE 044#define VEC_SME 145 46static struct vec_data vec_data[] = {47	[VEC_SVE] = {48		.name = "SVE",49		.hwcap_type = AT_HWCAP,50		.hwcap = HWCAP_SVE,51		.rdvl = rdvl_sve,52		.rdvl_binary = "./rdvl-sve",53		.prctl_get = PR_SVE_GET_VL,54		.prctl_set = PR_SVE_SET_VL,55		.default_vl_file = "/proc/sys/abi/sve_default_vector_length",56	},57	[VEC_SME] = {58		.name = "SME",59		.hwcap_type = AT_HWCAP2,60		.hwcap = HWCAP2_SME,61		.rdvl = rdvl_sme,62		.rdvl_binary = "./rdvl-sme",63		.prctl_get = PR_SME_GET_VL,64		.prctl_set = PR_SME_SET_VL,65		.default_vl_file = "/proc/sys/abi/sme_default_vector_length",66	},67};68 69static bool vec_type_supported(struct vec_data *data)70{71	return getauxval(data->hwcap_type) & data->hwcap;72}73 74static int stdio_read_integer(FILE *f, const char *what, int *val)75{76	int n = 0;77	int ret;78 79	ret = fscanf(f, "%d%*1[\n]%n", val, &n);80	if (ret < 1 || n < 1) {81		ksft_print_msg("failed to parse integer from %s\n", what);82		return -1;83	}84 85	return 0;86}87 88/* Start a new process and return the vector length it sees */89static int get_child_rdvl(struct vec_data *data)90{91	FILE *out;92	int pipefd[2];93	pid_t pid, child;94	int read_vl, ret;95 96	ret = pipe(pipefd);97	if (ret == -1) {98		ksft_print_msg("pipe() failed: %d (%s)\n",99			       errno, strerror(errno));100		return -1;101	}102 103	fflush(stdout);104 105	child = fork();106	if (child == -1) {107		ksft_print_msg("fork() failed: %d (%s)\n",108			       errno, strerror(errno));109		close(pipefd[0]);110		close(pipefd[1]);111		return -1;112	}113 114	/* Child: put vector length on the pipe */115	if (child == 0) {116		/*117		 * Replace stdout with the pipe, errors to stderr from118		 * here as kselftest prints to stdout.119		 */120		ret = dup2(pipefd[1], 1);121		if (ret == -1) {122			fprintf(stderr, "dup2() %d\n", errno);123			exit(EXIT_FAILURE);124		}125 126		/* exec() a new binary which puts the VL on stdout */127		ret = execl(data->rdvl_binary, data->rdvl_binary, NULL);128		fprintf(stderr, "execl(%s) failed: %d (%s)\n",129			data->rdvl_binary, errno, strerror(errno));130 131		exit(EXIT_FAILURE);132	}133 134	close(pipefd[1]);135 136	/* Parent; wait for the exit status from the child & verify it */137	do {138		pid = wait(&ret);139		if (pid == -1) {140			ksft_print_msg("wait() failed: %d (%s)\n",141				       errno, strerror(errno));142			close(pipefd[0]);143			return -1;144		}145	} while (pid != child);146 147	assert(pid == child);148 149	if (!WIFEXITED(ret)) {150		ksft_print_msg("child exited abnormally\n");151		close(pipefd[0]);152		return -1;153	}154 155	if (WEXITSTATUS(ret) != 0) {156		ksft_print_msg("child returned error %d\n",157			       WEXITSTATUS(ret));158		close(pipefd[0]);159		return -1;160	}161 162	out = fdopen(pipefd[0], "r");163	if (!out) {164		ksft_print_msg("failed to open child stdout\n");165		close(pipefd[0]);166		return -1;167	}168 169	ret = stdio_read_integer(out, "child", &read_vl);170	fclose(out);171	if (ret != 0)172		return ret;173 174	return read_vl;175}176 177static int file_read_integer(const char *name, int *val)178{179	FILE *f;180	int ret;181 182	f = fopen(name, "r");183	if (!f) {184		ksft_test_result_fail("Unable to open %s: %d (%s)\n",185				      name, errno,186				      strerror(errno));187		return -1;188	}189 190	ret = stdio_read_integer(f, name, val);191	fclose(f);192 193	return ret;194}195 196static int file_write_integer(const char *name, int val)197{198	FILE *f;199 200	f = fopen(name, "w");201	if (!f) {202		ksft_test_result_fail("Unable to open %s: %d (%s)\n",203				      name, errno,204				      strerror(errno));205		return -1;206	}207 208	fprintf(f, "%d", val);209	fclose(f);210 211	return 0;212}213 214/*215 * Verify that we can read the default VL via proc, checking that it216 * is set in a freshly spawned child.217 */218static void proc_read_default(struct vec_data *data)219{220	int default_vl, child_vl, ret;221 222	ret = file_read_integer(data->default_vl_file, &default_vl);223	if (ret != 0)224		return;225 226	/* Is this the actual default seen by new processes? */227	child_vl = get_child_rdvl(data);228	if (child_vl != default_vl) {229		ksft_test_result_fail("%s is %d but child VL is %d\n",230				      data->default_vl_file,231				      default_vl, child_vl);232		return;233	}234 235	ksft_test_result_pass("%s default vector length %d\n", data->name,236			      default_vl);237	data->default_vl = default_vl;238}239 240/* Verify that we can write a minimum value and have it take effect */241static void proc_write_min(struct vec_data *data)242{243	int ret, new_default, child_vl;244 245	if (geteuid() != 0) {246		ksft_test_result_skip("Need to be root to write to /proc\n");247		return;248	}249 250	ret = file_write_integer(data->default_vl_file, ARCH_MIN_VL);251	if (ret != 0)252		return;253 254	/* What was the new value? */255	ret = file_read_integer(data->default_vl_file, &new_default);256	if (ret != 0)257		return;258 259	/* Did it take effect in a new process? */260	child_vl = get_child_rdvl(data);261	if (child_vl != new_default) {262		ksft_test_result_fail("%s is %d but child VL is %d\n",263				      data->default_vl_file,264				      new_default, child_vl);265		return;266	}267 268	ksft_test_result_pass("%s minimum vector length %d\n", data->name,269			      new_default);270	data->min_vl = new_default;271 272	file_write_integer(data->default_vl_file, data->default_vl);273}274 275/* Verify that we can write a maximum value and have it take effect */276static void proc_write_max(struct vec_data *data)277{278	int ret, new_default, child_vl;279 280	if (geteuid() != 0) {281		ksft_test_result_skip("Need to be root to write to /proc\n");282		return;283	}284 285	/* -1 is accepted by the /proc interface as the maximum VL */286	ret = file_write_integer(data->default_vl_file, -1);287	if (ret != 0)288		return;289 290	/* What was the new value? */291	ret = file_read_integer(data->default_vl_file, &new_default);292	if (ret != 0)293		return;294 295	/* Did it take effect in a new process? */296	child_vl = get_child_rdvl(data);297	if (child_vl != new_default) {298		ksft_test_result_fail("%s is %d but child VL is %d\n",299				      data->default_vl_file,300				      new_default, child_vl);301		return;302	}303 304	ksft_test_result_pass("%s maximum vector length %d\n", data->name,305			      new_default);306	data->max_vl = new_default;307 308	file_write_integer(data->default_vl_file, data->default_vl);309}310 311/* Can we read back a VL from prctl? */312static void prctl_get(struct vec_data *data)313{314	int ret;315 316	ret = prctl(data->prctl_get);317	if (ret == -1) {318		ksft_test_result_fail("%s prctl() read failed: %d (%s)\n",319				      data->name, errno, strerror(errno));320		return;321	}322 323	/* Mask out any flags */324	ret &= PR_SVE_VL_LEN_MASK;325 326	/* Is that what we can read back directly? */327	if (ret == data->rdvl())328		ksft_test_result_pass("%s current VL is %d\n",329				      data->name, ret);330	else331		ksft_test_result_fail("%s prctl() VL %d but RDVL is %d\n",332				      data->name, ret, data->rdvl());333}334 335/* Does the prctl let us set the VL we already have? */336static void prctl_set_same(struct vec_data *data)337{338	int cur_vl = data->rdvl();339	int ret;340 341	ret = prctl(data->prctl_set, cur_vl);342	if (ret < 0) {343		ksft_test_result_fail("%s prctl set failed: %d (%s)\n",344				      data->name, errno, strerror(errno));345		return;346	}347 348	ksft_test_result(cur_vl == data->rdvl(),349			 "%s set VL %d and have VL %d\n",350			 data->name, cur_vl, data->rdvl());351}352 353/* Can we set a new VL for this process? */354static void prctl_set(struct vec_data *data)355{356	int ret;357 358	if (data->min_vl == data->max_vl) {359		ksft_test_result_skip("%s only one VL supported\n",360				      data->name);361		return;362	}363 364	/* Try to set the minimum VL */365	ret = prctl(data->prctl_set, data->min_vl);366	if (ret < 0) {367		ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",368				      data->name, data->min_vl,369				      errno, strerror(errno));370		return;371	}372 373	if ((ret & PR_SVE_VL_LEN_MASK) != data->min_vl) {374		ksft_test_result_fail("%s prctl set %d but return value is %d\n",375				      data->name, data->min_vl, data->rdvl());376		return;377	}378 379	if (data->rdvl() != data->min_vl) {380		ksft_test_result_fail("%s set %d but RDVL is %d\n",381				      data->name, data->min_vl, data->rdvl());382		return;383	}384 385	/* Try to set the maximum VL */386	ret = prctl(data->prctl_set, data->max_vl);387	if (ret < 0) {388		ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",389				      data->name, data->max_vl,390				      errno, strerror(errno));391		return;392	}393 394	if ((ret & PR_SVE_VL_LEN_MASK) != data->max_vl) {395		ksft_test_result_fail("%s prctl() set %d but return value is %d\n",396				      data->name, data->max_vl, data->rdvl());397		return;398	}399 400	/* The _INHERIT flag should not be present when we read the VL */401	ret = prctl(data->prctl_get);402	if (ret == -1) {403		ksft_test_result_fail("%s prctl() read failed: %d (%s)\n",404				      data->name, errno, strerror(errno));405		return;406	}407 408	if (ret & PR_SVE_VL_INHERIT) {409		ksft_test_result_fail("%s prctl() reports _INHERIT\n",410				      data->name);411		return;412	}413 414	ksft_test_result_pass("%s prctl() set min/max\n", data->name);415}416 417/* If we didn't request it a new VL shouldn't affect the child */418static void prctl_set_no_child(struct vec_data *data)419{420	int ret, child_vl;421 422	if (data->min_vl == data->max_vl) {423		ksft_test_result_skip("%s only one VL supported\n",424				      data->name);425		return;426	}427 428	ret = prctl(data->prctl_set, data->min_vl);429	if (ret < 0) {430		ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",431				      data->name, data->min_vl,432				      errno, strerror(errno));433		return;434	}435 436	/* Ensure the default VL is different */437	ret = file_write_integer(data->default_vl_file, data->max_vl);438	if (ret != 0)439		return;440 441	/* Check that the child has the default we just set */442	child_vl = get_child_rdvl(data);443	if (child_vl != data->max_vl) {444		ksft_test_result_fail("%s is %d but child VL is %d\n",445				      data->default_vl_file,446				      data->max_vl, child_vl);447		return;448	}449 450	ksft_test_result_pass("%s vector length used default\n", data->name);451 452	file_write_integer(data->default_vl_file, data->default_vl);453}454 455/* If we didn't request it a new VL shouldn't affect the child */456static void prctl_set_for_child(struct vec_data *data)457{458	int ret, child_vl;459 460	if (data->min_vl == data->max_vl) {461		ksft_test_result_skip("%s only one VL supported\n",462				      data->name);463		return;464	}465 466	ret = prctl(data->prctl_set, data->min_vl | PR_SVE_VL_INHERIT);467	if (ret < 0) {468		ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",469				      data->name, data->min_vl,470				      errno, strerror(errno));471		return;472	}473 474	/* The _INHERIT flag should be present when we read the VL */475	ret = prctl(data->prctl_get);476	if (ret == -1) {477		ksft_test_result_fail("%s prctl() read failed: %d (%s)\n",478				      data->name, errno, strerror(errno));479		return;480	}481	if (!(ret & PR_SVE_VL_INHERIT)) {482		ksft_test_result_fail("%s prctl() does not report _INHERIT\n",483				      data->name);484		return;485	}486 487	/* Ensure the default VL is different */488	ret = file_write_integer(data->default_vl_file, data->max_vl);489	if (ret != 0)490		return;491 492	/* Check that the child inherited our VL */493	child_vl = get_child_rdvl(data);494	if (child_vl != data->min_vl) {495		ksft_test_result_fail("%s is %d but child VL is %d\n",496				      data->default_vl_file,497				      data->min_vl, child_vl);498		return;499	}500 501	ksft_test_result_pass("%s vector length was inherited\n", data->name);502 503	file_write_integer(data->default_vl_file, data->default_vl);504}505 506/* _ONEXEC takes effect only in the child process */507static void prctl_set_onexec(struct vec_data *data)508{509	int ret, child_vl;510 511	if (data->min_vl == data->max_vl) {512		ksft_test_result_skip("%s only one VL supported\n",513				      data->name);514		return;515	}516 517	/* Set a known value for the default and our current VL */518	ret = file_write_integer(data->default_vl_file, data->max_vl);519	if (ret != 0)520		return;521 522	ret = prctl(data->prctl_set, data->max_vl);523	if (ret < 0) {524		ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",525				      data->name, data->min_vl,526				      errno, strerror(errno));527		return;528	}529 530	/* Set a different value for the child to have on exec */531	ret = prctl(data->prctl_set, data->min_vl | PR_SVE_SET_VL_ONEXEC);532	if (ret < 0) {533		ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",534				      data->name, data->min_vl,535				      errno, strerror(errno));536		return;537	}538 539	/* Our current VL should stay the same */540	if (data->rdvl() != data->max_vl) {541		ksft_test_result_fail("%s VL changed by _ONEXEC prctl()\n",542				      data->name);543		return;544	}545 546	/* Check that the child inherited our VL */547	child_vl = get_child_rdvl(data);548	if (child_vl != data->min_vl) {549		ksft_test_result_fail("Set %d _ONEXEC but child VL is %d\n",550				      data->min_vl, child_vl);551		return;552	}553 554	ksft_test_result_pass("%s vector length set on exec\n", data->name);555 556	file_write_integer(data->default_vl_file, data->default_vl);557}558 559/* For each VQ verify that setting via prctl() does the right thing */560static void prctl_set_all_vqs(struct vec_data *data)561{562	int ret, vq, vl, new_vl, i;563	int orig_vls[ARRAY_SIZE(vec_data)];564	int errors = 0;565 566	if (!data->min_vl || !data->max_vl) {567		ksft_test_result_skip("%s Failed to enumerate VLs, not testing VL setting\n",568				      data->name);569		return;570	}571 572	for (i = 0; i < ARRAY_SIZE(vec_data); i++) {573		if (!vec_type_supported(&vec_data[i]))574			continue;575		orig_vls[i] = vec_data[i].rdvl();576	}577 578	for (vq = SVE_VQ_MIN; vq <= SVE_VQ_MAX; vq++) {579		vl = sve_vl_from_vq(vq);580 581		/* Attempt to set the VL */582		ret = prctl(data->prctl_set, vl);583		if (ret < 0) {584			errors++;585			ksft_print_msg("%s prctl set failed for %d: %d (%s)\n",586				       data->name, vl,587				       errno, strerror(errno));588			continue;589		}590 591		new_vl = ret & PR_SVE_VL_LEN_MASK;592 593		/* Check that we actually have the reported new VL */594		if (data->rdvl() != new_vl) {595			ksft_print_msg("Set %s VL %d but RDVL reports %d\n",596				       data->name, new_vl, data->rdvl());597			errors++;598		}599 600		/* Did any other VLs change? */601		for (i = 0; i < ARRAY_SIZE(vec_data); i++) {602			if (&vec_data[i] == data)603				continue;604 605			if (!vec_type_supported(&vec_data[i]))606				continue;607 608			if (vec_data[i].rdvl() != orig_vls[i]) {609				ksft_print_msg("%s VL changed from %d to %d\n",610					       vec_data[i].name, orig_vls[i],611					       vec_data[i].rdvl());612				errors++;613			}614		}615 616		/* Was that the VL we asked for? */617		if (new_vl == vl)618			continue;619 620		/* Should round up to the minimum VL if below it */621		if (vl < data->min_vl) {622			if (new_vl != data->min_vl) {623				ksft_print_msg("%s VL %d returned %d not minimum %d\n",624					       data->name, vl, new_vl,625					       data->min_vl);626				errors++;627			}628 629			continue;630		}631 632		/* Should round down to maximum VL if above it */633		if (vl > data->max_vl) {634			if (new_vl != data->max_vl) {635				ksft_print_msg("%s VL %d returned %d not maximum %d\n",636					       data->name, vl, new_vl,637					       data->max_vl);638				errors++;639			}640 641			continue;642		}643 644		/* Otherwise we should've rounded down */645		if (!(new_vl < vl)) {646			ksft_print_msg("%s VL %d returned %d, did not round down\n",647				       data->name, vl, new_vl);648			errors++;649 650			continue;651		}652	}653 654	ksft_test_result(errors == 0, "%s prctl() set all VLs, %d errors\n",655			 data->name, errors);656}657 658typedef void (*test_type)(struct vec_data *);659 660static const test_type tests[] = {661	/*662	 * The default/min/max tests must be first and in this order663	 * to provide data for other tests.664	 */665	proc_read_default,666	proc_write_min,667	proc_write_max,668 669	prctl_get,670	prctl_set_same,671	prctl_set,672	prctl_set_no_child,673	prctl_set_for_child,674	prctl_set_onexec,675	prctl_set_all_vqs,676};677 678static inline void smstart(void)679{680	asm volatile("msr S0_3_C4_C7_3, xzr");681}682 683static inline void smstart_sm(void)684{685	asm volatile("msr S0_3_C4_C3_3, xzr");686}687 688static inline void smstop(void)689{690	asm volatile("msr S0_3_C4_C6_3, xzr");691}692 693 694/*695 * Verify we can change the SVE vector length while SME is active and696 * continue to use SME afterwards.697 */698static void change_sve_with_za(void)699{700	struct vec_data *sve_data = &vec_data[VEC_SVE];701	bool pass = true;702	int ret, i;703 704	if (sve_data->min_vl == sve_data->max_vl) {705		ksft_print_msg("Only one SVE VL supported, can't change\n");706		ksft_test_result_skip("change_sve_while_sme\n");707		return;708	}709 710	/* Ensure we will trigger a change when we set the maximum */711	ret = prctl(sve_data->prctl_set, sve_data->min_vl);712	if (ret != sve_data->min_vl) {713		ksft_print_msg("Failed to set SVE VL %d: %d\n",714			       sve_data->min_vl, ret);715		pass = false;716	}717 718	/* Enable SM and ZA */719	smstart();720 721	/* Trigger another VL change */722	ret = prctl(sve_data->prctl_set, sve_data->max_vl);723	if (ret != sve_data->max_vl) {724		ksft_print_msg("Failed to set SVE VL %d: %d\n",725			       sve_data->max_vl, ret);726		pass = false;727	}728 729	/*730	 * Spin for a bit with SM enabled to try to trigger another731	 * save/restore.  We can't use syscalls without exiting732	 * streaming mode.733	 */734	for (i = 0; i < 100000000; i++)735		smstart_sm();736 737	/*738	 * TODO: Verify that ZA was preserved over the VL change and739	 * spin.740	 */741 742	/* Clean up after ourselves */743	smstop();744	ret = prctl(sve_data->prctl_set, sve_data->default_vl);745	if (ret != sve_data->default_vl) {746	        ksft_print_msg("Failed to restore SVE VL %d: %d\n",747			       sve_data->default_vl, ret);748		pass = false;749	}750 751	ksft_test_result(pass, "change_sve_with_za\n");752}753 754typedef void (*test_all_type)(void);755 756static const struct {757	const char *name;758	test_all_type test;759}  all_types_tests[] = {760	{ "change_sve_with_za", change_sve_with_za },761};762 763int main(void)764{765	bool all_supported = true;766	int i, j;767 768	ksft_print_header();769	ksft_set_plan(ARRAY_SIZE(tests) * ARRAY_SIZE(vec_data) +770		      ARRAY_SIZE(all_types_tests));771 772	for (i = 0; i < ARRAY_SIZE(vec_data); i++) {773		struct vec_data *data = &vec_data[i];774		unsigned long supported;775 776		supported = vec_type_supported(data);777		if (!supported)778			all_supported = false;779 780		for (j = 0; j < ARRAY_SIZE(tests); j++) {781			if (supported)782				tests[j](data);783			else784				ksft_test_result_skip("%s not supported\n",785						      data->name);786		}787	}788 789	for (i = 0; i < ARRAY_SIZE(all_types_tests); i++) {790		if (all_supported)791			all_types_tests[i].test();792		else793			ksft_test_result_skip("%s\n", all_types_tests[i].name);794	}795 796	ksft_exit_pass();797}798