1 | // SPDX-License-Identifier: MIT |
2 | /* |
3 | * Copyright © 2021 Intel Corporation |
4 | */ |
5 | |
6 | #include <linux/kernel.h> |
7 | #include <linux/moduleparam.h> |
8 | #include <linux/slab.h> |
9 | #include <linux/string.h> |
10 | |
11 | #include "i915_driver.h" |
12 | #include "i915_drv.h" |
13 | #include "i915_mitigations.h" |
14 | |
15 | static unsigned long mitigations __read_mostly = ~0UL; |
16 | |
17 | enum { |
18 | CLEAR_RESIDUALS = 0, |
19 | }; |
20 | |
21 | static const char * const names[] = { |
22 | [CLEAR_RESIDUALS] = "residuals" , |
23 | }; |
24 | |
25 | bool i915_mitigate_clear_residuals(void) |
26 | { |
27 | return READ_ONCE(mitigations) & BIT(CLEAR_RESIDUALS); |
28 | } |
29 | |
30 | static int mitigations_set(const char *val, const struct kernel_param *kp) |
31 | { |
32 | unsigned long new = ~0UL; |
33 | char *str, *sep, *tok; |
34 | bool first = true; |
35 | int err = 0; |
36 | |
37 | BUILD_BUG_ON(ARRAY_SIZE(names) >= BITS_PER_TYPE(mitigations)); |
38 | |
39 | str = kstrdup(s: val, GFP_KERNEL); |
40 | if (!str) |
41 | return -ENOMEM; |
42 | |
43 | for (sep = str; (tok = strsep(&sep, "," ));) { |
44 | bool enable = true; |
45 | int i; |
46 | |
47 | /* Be tolerant of leading/trailing whitespace */ |
48 | tok = strim(tok); |
49 | |
50 | if (first) { |
51 | first = false; |
52 | |
53 | if (!strcmp(tok, "auto" )) |
54 | continue; |
55 | |
56 | new = 0; |
57 | if (!strcmp(tok, "off" )) |
58 | continue; |
59 | } |
60 | |
61 | if (*tok == '!') { |
62 | enable = !enable; |
63 | tok++; |
64 | } |
65 | |
66 | if (!strncmp(tok, "no" , 2)) { |
67 | enable = !enable; |
68 | tok += 2; |
69 | } |
70 | |
71 | if (*tok == '\0') |
72 | continue; |
73 | |
74 | for (i = 0; i < ARRAY_SIZE(names); i++) { |
75 | if (!strcmp(tok, names[i])) { |
76 | if (enable) |
77 | new |= BIT(i); |
78 | else |
79 | new &= ~BIT(i); |
80 | break; |
81 | } |
82 | } |
83 | if (i == ARRAY_SIZE(names)) { |
84 | pr_err("Bad \"%s.mitigations=%s\", '%s' is unknown\n" , |
85 | DRIVER_NAME, val, tok); |
86 | err = -EINVAL; |
87 | break; |
88 | } |
89 | } |
90 | kfree(objp: str); |
91 | if (err) |
92 | return err; |
93 | |
94 | WRITE_ONCE(mitigations, new); |
95 | return 0; |
96 | } |
97 | |
98 | static int mitigations_get(char *buffer, const struct kernel_param *kp) |
99 | { |
100 | unsigned long local = READ_ONCE(mitigations); |
101 | int count, i; |
102 | bool enable; |
103 | |
104 | if (!local) |
105 | return scnprintf(buf: buffer, PAGE_SIZE, fmt: "%s\n" , "off" ); |
106 | |
107 | if (local & BIT(BITS_PER_LONG - 1)) { |
108 | count = scnprintf(buf: buffer, PAGE_SIZE, fmt: "%s," , "auto" ); |
109 | enable = false; |
110 | } else { |
111 | enable = true; |
112 | count = 0; |
113 | } |
114 | |
115 | for (i = 0; i < ARRAY_SIZE(names); i++) { |
116 | if ((local & BIT(i)) != enable) |
117 | continue; |
118 | |
119 | count += scnprintf(buf: buffer + count, PAGE_SIZE - count, |
120 | fmt: "%s%s," , enable ? "" : "!" , names[i]); |
121 | } |
122 | |
123 | buffer[count - 1] = '\n'; |
124 | return count; |
125 | } |
126 | |
127 | static const struct kernel_param_ops ops = { |
128 | .set = mitigations_set, |
129 | .get = mitigations_get, |
130 | }; |
131 | |
132 | module_param_cb_unsafe(mitigations, &ops, NULL, 0600); |
133 | MODULE_PARM_DESC(mitigations, |
134 | "Selectively enable security mitigations for all Intel® GPUs in the system.\n" |
135 | "\n" |
136 | " auto -- enables all mitigations required for the platform [default]\n" |
137 | " off -- disables all mitigations\n" |
138 | "\n" |
139 | "Individual mitigations can be enabled by passing a comma-separated string,\n" |
140 | "e.g. mitigations=residuals to enable only clearing residuals or\n" |
141 | "mitigations=auto,noresiduals to disable only the clear residual mitigation.\n" |
142 | "Either '!' or 'no' may be used to switch from enabling the mitigation to\n" |
143 | "disabling it.\n" |
144 | "\n" |
145 | "Active mitigations for Ivybridge, Baytrail, Haswell:\n" |
146 | " residuals -- clear all thread-local registers between contexts" |
147 | ); |
148 | |