]>
Commit | Line | Data |
---|---|---|
4185b3b9 PA |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Test cases for using floating point operations inside a kernel module. | |
4 | * | |
5 | * This tests kernel_fpu_begin() and kernel_fpu_end() functions, especially | |
6 | * when userland has modified the floating point control registers. The kernel | |
7 | * state might depend on the state set by the userland thread that was active | |
8 | * before a syscall. | |
9 | * | |
10 | * To facilitate the test, this module registers file | |
11 | * /sys/kernel/debug/selftest_helpers/test_fpu, which when read causes a | |
12 | * sequence of floating point operations. If the operations fail, either the | |
13 | * read returns error status or the kernel crashes. | |
14 | * If the operations succeed, the read returns "1\n". | |
15 | */ | |
16 | ||
17 | #include <linux/module.h> | |
18 | #include <linux/kernel.h> | |
19 | #include <linux/debugfs.h> | |
20 | #include <asm/fpu/api.h> | |
21 | ||
22 | static int test_fpu(void) | |
23 | { | |
24 | /* | |
25 | * This sequence of operations tests that rounding mode is | |
26 | * to nearest and that denormal numbers are supported. | |
27 | * Volatile variables are used to avoid compiler optimizing | |
28 | * the calculations away. | |
29 | */ | |
30 | volatile double a, b, c, d, e, f, g; | |
31 | ||
32 | a = 4.0; | |
33 | b = 1e-15; | |
34 | c = 1e-310; | |
35 | ||
36 | /* Sets precision flag */ | |
37 | d = a + b; | |
38 | ||
39 | /* Result depends on rounding mode */ | |
40 | e = a + b / 2; | |
41 | ||
42 | /* Denormal and very large values */ | |
43 | f = b / c; | |
44 | ||
45 | /* Depends on denormal support */ | |
46 | g = a + c * f; | |
47 | ||
48 | if (d > a && e > a && g > a) | |
49 | return 0; | |
50 | else | |
51 | return -EINVAL; | |
52 | } | |
53 | ||
54 | static int test_fpu_get(void *data, u64 *val) | |
55 | { | |
56 | int status = -EINVAL; | |
57 | ||
58 | kernel_fpu_begin(); | |
59 | status = test_fpu(); | |
60 | kernel_fpu_end(); | |
61 | ||
62 | *val = 1; | |
63 | return status; | |
64 | } | |
65 | ||
443121b3 | 66 | DEFINE_DEBUGFS_ATTRIBUTE(test_fpu_fops, test_fpu_get, NULL, "%lld\n"); |
4185b3b9 PA |
67 | static struct dentry *selftest_dir; |
68 | ||
69 | static int __init test_fpu_init(void) | |
70 | { | |
71 | selftest_dir = debugfs_create_dir("selftest_helpers", NULL); | |
72 | if (!selftest_dir) | |
73 | return -ENOMEM; | |
74 | ||
443121b3 | 75 | debugfs_create_file_unsafe("test_fpu", 0444, selftest_dir, NULL, |
76 | &test_fpu_fops); | |
4185b3b9 PA |
77 | |
78 | return 0; | |
79 | } | |
80 | ||
81 | static void __exit test_fpu_exit(void) | |
82 | { | |
83 | debugfs_remove(selftest_dir); | |
84 | } | |
85 | ||
86 | module_init(test_fpu_init); | |
87 | module_exit(test_fpu_exit); | |
88 | ||
89 | MODULE_LICENSE("GPL"); |