162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2015, Cyril Bur, IBM Corp.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * This test attempts to see if the FPU registers change across a syscall (fork).
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <stdio.h>
962306a36Sopenharmony_ci#include <unistd.h>
1062306a36Sopenharmony_ci#include <sys/syscall.h>
1162306a36Sopenharmony_ci#include <sys/time.h>
1262306a36Sopenharmony_ci#include <sys/types.h>
1362306a36Sopenharmony_ci#include <sys/wait.h>
1462306a36Sopenharmony_ci#include <stdlib.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "utils.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ciextern int test_fpu(double *darray, pid_t *pid);
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cidouble darray[] = {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0,
2162306a36Sopenharmony_ci		     1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0,
2262306a36Sopenharmony_ci		     2.1};
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ciint syscall_fpu(void)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	pid_t fork_pid;
2762306a36Sopenharmony_ci	int i;
2862306a36Sopenharmony_ci	int ret;
2962306a36Sopenharmony_ci	int child_ret;
3062306a36Sopenharmony_ci	for (i = 0; i < 1000; i++) {
3162306a36Sopenharmony_ci		/* test_fpu will fork() */
3262306a36Sopenharmony_ci		ret = test_fpu(darray, &fork_pid);
3362306a36Sopenharmony_ci		if (fork_pid == -1)
3462306a36Sopenharmony_ci			return -1;
3562306a36Sopenharmony_ci		if (fork_pid == 0)
3662306a36Sopenharmony_ci			exit(ret);
3762306a36Sopenharmony_ci		waitpid(fork_pid, &child_ret, 0);
3862306a36Sopenharmony_ci		if (ret || child_ret)
3962306a36Sopenharmony_ci			return 1;
4062306a36Sopenharmony_ci	}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	return 0;
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ciint test_syscall_fpu(void)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	/*
4862306a36Sopenharmony_ci	 * Setup an environment with much context switching
4962306a36Sopenharmony_ci	 */
5062306a36Sopenharmony_ci	pid_t pid2;
5162306a36Sopenharmony_ci	pid_t pid = fork();
5262306a36Sopenharmony_ci	int ret;
5362306a36Sopenharmony_ci	int child_ret;
5462306a36Sopenharmony_ci	FAIL_IF(pid == -1);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	pid2 = fork();
5762306a36Sopenharmony_ci	/* Can't FAIL_IF(pid2 == -1); because already forked once */
5862306a36Sopenharmony_ci	if (pid2 == -1) {
5962306a36Sopenharmony_ci		/*
6062306a36Sopenharmony_ci		 * Couldn't fork, ensure test is a fail
6162306a36Sopenharmony_ci		 */
6262306a36Sopenharmony_ci		child_ret = ret = 1;
6362306a36Sopenharmony_ci	} else {
6462306a36Sopenharmony_ci		ret = syscall_fpu();
6562306a36Sopenharmony_ci		if (pid2)
6662306a36Sopenharmony_ci			waitpid(pid2, &child_ret, 0);
6762306a36Sopenharmony_ci		else
6862306a36Sopenharmony_ci			exit(ret);
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	ret |= child_ret;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	if (pid)
7462306a36Sopenharmony_ci		waitpid(pid, &child_ret, 0);
7562306a36Sopenharmony_ci	else
7662306a36Sopenharmony_ci		exit(ret);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	FAIL_IF(ret || child_ret);
7962306a36Sopenharmony_ci	return 0;
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ciint main(int argc, char *argv[])
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	return test_harness(test_syscall_fpu, "syscall_fpu");
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci}
87