| // SPDX-License-Identifier: MIT |
| /* |
| * Copyright © 2021 Intel Corporation |
| */ |
| |
| #include <linux/kernel.h> |
| #include <linux/moduleparam.h> |
| #include <linux/slab.h> |
| #include <linux/string.h> |
| |
| #include "i915_drv.h" |
| #include "i915_mitigations.h" |
| |
| static unsigned long mitigations __read_mostly = ~0UL; |
| |
| enum { |
| CLEAR_RESIDUALS = 0, |
| }; |
| |
| static const char * const names[] = { |
| [CLEAR_RESIDUALS] = "residuals", |
| }; |
| |
| bool i915_mitigate_clear_residuals(void) |
| { |
| return READ_ONCE(mitigations) & BIT(CLEAR_RESIDUALS); |
| } |
| |
| static int mitigations_set(const char *val, const struct kernel_param *kp) |
| { |
| unsigned long new = ~0UL; |
| char *str, *sep, *tok; |
| bool first = true; |
| int err = 0; |
| |
| BUILD_BUG_ON(ARRAY_SIZE(names) >= BITS_PER_TYPE(mitigations)); |
| |
| str = kstrdup(val, GFP_KERNEL); |
| if (!str) |
| return -ENOMEM; |
| |
| for (sep = str; (tok = strsep(&sep, ","));) { |
| bool enable = true; |
| int i; |
| |
| /* Be tolerant of leading/trailing whitespace */ |
| tok = strim(tok); |
| |
| if (first) { |
| first = false; |
| |
| if (!strcmp(tok, "auto")) |
| continue; |
| |
| new = 0; |
| if (!strcmp(tok, "off")) |
| continue; |
| } |
| |
| if (*tok == '!') { |
| enable = !enable; |
| tok++; |
| } |
| |
| if (!strncmp(tok, "no", 2)) { |
| enable = !enable; |
| tok += 2; |
| } |
| |
| if (*tok == '\0') |
| continue; |
| |
| for (i = 0; i < ARRAY_SIZE(names); i++) { |
| if (!strcmp(tok, names[i])) { |
| if (enable) |
| new |= BIT(i); |
| else |
| new &= ~BIT(i); |
| break; |
| } |
| } |
| if (i == ARRAY_SIZE(names)) { |
| pr_err("Bad \"%s.mitigations=%s\", '%s' is unknown\n", |
| DRIVER_NAME, val, tok); |
| err = -EINVAL; |
| break; |
| } |
| } |
| kfree(str); |
| if (err) |
| return err; |
| |
| WRITE_ONCE(mitigations, new); |
| return 0; |
| } |
| |
| static int mitigations_get(char *buffer, const struct kernel_param *kp) |
| { |
| unsigned long local = READ_ONCE(mitigations); |
| int count, i; |
| bool enable; |
| |
| if (!local) |
| return scnprintf(buffer, PAGE_SIZE, "%s\n", "off"); |
| |
| if (local & BIT(BITS_PER_LONG - 1)) { |
| count = scnprintf(buffer, PAGE_SIZE, "%s,", "auto"); |
| enable = false; |
| } else { |
| enable = true; |
| count = 0; |
| } |
| |
| for (i = 0; i < ARRAY_SIZE(names); i++) { |
| if ((local & BIT(i)) != enable) |
| continue; |
| |
| count += scnprintf(buffer + count, PAGE_SIZE - count, |
| "%s%s,", enable ? "" : "!", names[i]); |
| } |
| |
| buffer[count - 1] = '\n'; |
| return count; |
| } |
| |
| static const struct kernel_param_ops ops = { |
| .set = mitigations_set, |
| .get = mitigations_get, |
| }; |
| |
| module_param_cb_unsafe(mitigations, &ops, NULL, 0600); |
| MODULE_PARM_DESC(mitigations, |
| "Selectively enable security mitigations for all Intel® GPUs in the system.\n" |
| "\n" |
| " auto -- enables all mitigations required for the platform [default]\n" |
| " off -- disables all mitigations\n" |
| "\n" |
| "Individual mitigations can be enabled by passing a comma-separated string,\n" |
| "e.g. mitigations=residuals to enable only clearing residuals or\n" |
| "mitigations=auto,noresiduals to disable only the clear residual mitigation.\n" |
| "Either '!' or 'no' may be used to switch from enabling the mitigation to\n" |
| "disabling it.\n" |
| "\n" |
| "Active mitigations for Ivybridge, Baytrail, Haswell:\n" |
| " residuals -- clear all thread-local registers between contexts" |
| ); |