Line 0
Link Here
|
|
|
1 |
// SPDX-License-Identifier: GPL-2.0+ |
2 |
/* |
3 |
* Test cases for using floating point operations inside a kernel module. |
4 |
* This tests kernel_fpu_begin() and kernel_fpu_end() functions, especially |
5 |
* when userland has modified the floating point control registers. |
6 |
* |
7 |
* To facilitate the test, this module registers file /proc/sys/debug/test_fpu, |
8 |
* which when read causes a sequence of floating point operations. If the |
9 |
* operations fail, either the read returns error status or the kernel crashes. |
10 |
* If the operations succeed, the read returns 0 bytes. |
11 |
*/ |
12 |
|
13 |
#include <linux/module.h> |
14 |
#include <linux/kernel.h> |
15 |
#include <asm/fpu/api.h> |
16 |
|
17 |
static int test_fpu(void) |
18 |
{ |
19 |
/* This sequence of operations tests that rounding mode is |
20 |
* to nearest and that denormal numbers are supported. |
21 |
* Volatile variables are used to avoid compiler optimizing |
22 |
* the calculations away. |
23 |
*/ |
24 |
volatile double a, b, c, d, e, f, g; |
25 |
|
26 |
a = 4.0; |
27 |
b = 1e-15; |
28 |
c = 1e-310; |
29 |
d = a + b; /* Sets precision flag */ |
30 |
e = a + b / 2; /* Result depends on rounding mode */ |
31 |
f = b / c; /* Denormal and very large values */ |
32 |
g = a + c * f; /* Depends on denormal support */ |
33 |
|
34 |
if (d > a && e > a && g > a) |
35 |
return 0; |
36 |
else |
37 |
return -EINVAL; |
38 |
} |
39 |
|
40 |
static int test_fpu_sysctl(struct ctl_table *ctl, int write, |
41 |
void __user *buffer, size_t *lenp, loff_t *ppos) |
42 |
{ |
43 |
int status = -EINVAL; |
44 |
|
45 |
kernel_fpu_begin(); |
46 |
status = test_fpu(); |
47 |
kernel_fpu_end(); |
48 |
|
49 |
*lenp = 0; |
50 |
return status; |
51 |
} |
52 |
|
53 |
static struct ctl_table test_fpu_table[] = { |
54 |
{ |
55 |
.procname = "test_fpu", |
56 |
.data = NULL, |
57 |
.maxlen = 0, |
58 |
.mode = 0444, |
59 |
.proc_handler = test_fpu_sysctl, |
60 |
}, |
61 |
{ } |
62 |
}; |
63 |
|
64 |
static struct ctl_table test_fpu_root_table[] = { |
65 |
{ |
66 |
.procname = "debug", |
67 |
.maxlen = 0, |
68 |
.mode = 0555, |
69 |
.child = test_fpu_table, |
70 |
}, |
71 |
{ } |
72 |
}; |
73 |
|
74 |
static struct ctl_table_header *test_fpu_header; |
75 |
|
76 |
static int __init test_fpu_init(void) |
77 |
{ |
78 |
int status = -EINVAL; |
79 |
|
80 |
test_fpu_header = register_sysctl_table(test_fpu_root_table); |
81 |
if (!test_fpu_header) |
82 |
return -ENOMEM; |
83 |
|
84 |
/* FPU operations are tested both during the module loader and |
85 |
* later from syscalls. |
86 |
*/ |
87 |
kernel_fpu_begin(); |
88 |
status = test_fpu(); |
89 |
kernel_fpu_end(); |
90 |
|
91 |
return status; |
92 |
} |
93 |
|
94 |
|
95 |
static void __exit test_fpu_exit(void) |
96 |
{ |
97 |
if (test_fpu_header) |
98 |
unregister_sysctl_table(test_fpu_header); |
99 |
} |
100 |
|
101 |
module_init(test_fpu_init); |
102 |
module_exit(test_fpu_exit); |
103 |
|
104 |
MODULE_LICENSE("GPL"); |