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 | |
66 | DEFINE_DEBUGFS_ATTRIBUTE(test_fpu_fops, test_fpu_get, NULL, "%lld\n" ); |
67 | static struct dentry *selftest_dir; |
68 | |
69 | static int __init test_fpu_init(void) |
70 | { |
71 | selftest_dir = debugfs_create_dir(name: "selftest_helpers" , NULL); |
72 | if (!selftest_dir) |
73 | return -ENOMEM; |
74 | |
75 | debugfs_create_file_unsafe(name: "test_fpu" , mode: 0444, parent: selftest_dir, NULL, |
76 | fops: &test_fpu_fops); |
77 | |
78 | return 0; |
79 | } |
80 | |
81 | static void __exit test_fpu_exit(void) |
82 | { |
83 | debugfs_remove(dentry: selftest_dir); |
84 | } |
85 | |
86 | module_init(test_fpu_init); |
87 | module_exit(test_fpu_exit); |
88 | |
89 | MODULE_LICENSE("GPL" ); |
90 | |