162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/* This testcase operates with the test_fpu kernel driver.
362306a36Sopenharmony_ci * It modifies the FPU control register in user mode and calls the kernel
462306a36Sopenharmony_ci * module to perform floating point operations in the kernel. The control
562306a36Sopenharmony_ci * register value should be independent between kernel and user mode.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#define _GNU_SOURCE
962306a36Sopenharmony_ci#include <stdio.h>
1062306a36Sopenharmony_ci#include <errno.h>
1162306a36Sopenharmony_ci#include <string.h>
1262306a36Sopenharmony_ci#include <fenv.h>
1362306a36Sopenharmony_ci#include <unistd.h>
1462306a36Sopenharmony_ci#include <fcntl.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ciconst char *test_fpu_path = "/sys/kernel/debug/selftest_helpers/test_fpu";
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ciint main(void)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci	char dummy[1];
2162306a36Sopenharmony_ci	int fd = open(test_fpu_path, O_RDONLY);
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	if (fd < 0) {
2462306a36Sopenharmony_ci		printf("[SKIP]\tcan't access %s: %s\n",
2562306a36Sopenharmony_ci		       test_fpu_path, strerror(errno));
2662306a36Sopenharmony_ci		return 0;
2762306a36Sopenharmony_ci	}
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	if (read(fd, dummy, 1) < 0) {
3062306a36Sopenharmony_ci		printf("[FAIL]\taccess with default rounding mode failed\n");
3162306a36Sopenharmony_ci		return 1;
3262306a36Sopenharmony_ci	}
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	fesetround(FE_DOWNWARD);
3562306a36Sopenharmony_ci	if (read(fd, dummy, 1) < 0) {
3662306a36Sopenharmony_ci		printf("[FAIL]\taccess with downward rounding mode failed\n");
3762306a36Sopenharmony_ci		return 2;
3862306a36Sopenharmony_ci	}
3962306a36Sopenharmony_ci	if (fegetround() != FE_DOWNWARD) {
4062306a36Sopenharmony_ci		printf("[FAIL]\tusermode rounding mode clobbered\n");
4162306a36Sopenharmony_ci		return 3;
4262306a36Sopenharmony_ci	}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	/* Note: the tests up to this point are quite safe and will only return
4562306a36Sopenharmony_ci	 * an error. But the exception mask setting can cause misbehaving kernel
4662306a36Sopenharmony_ci	 * to crash.
4762306a36Sopenharmony_ci	 */
4862306a36Sopenharmony_ci	feclearexcept(FE_ALL_EXCEPT);
4962306a36Sopenharmony_ci	feenableexcept(FE_ALL_EXCEPT);
5062306a36Sopenharmony_ci	if (read(fd, dummy, 1) < 0) {
5162306a36Sopenharmony_ci		printf("[FAIL]\taccess with fpu exceptions unmasked failed\n");
5262306a36Sopenharmony_ci		return 4;
5362306a36Sopenharmony_ci	}
5462306a36Sopenharmony_ci	if (fegetexcept() != FE_ALL_EXCEPT) {
5562306a36Sopenharmony_ci		printf("[FAIL]\tusermode fpu exception mask clobbered\n");
5662306a36Sopenharmony_ci		return 5;
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	printf("[OK]\ttest_fpu\n");
6062306a36Sopenharmony_ci	return 0;
6162306a36Sopenharmony_ci}
62