162306a36Sopenharmony_ci// SPDX-License-Identifier: MIT
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright © 2021 Intel Corporation
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/kernel.h>
762306a36Sopenharmony_ci#include <linux/moduleparam.h>
862306a36Sopenharmony_ci#include <linux/slab.h>
962306a36Sopenharmony_ci#include <linux/string.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "i915_driver.h"
1262306a36Sopenharmony_ci#include "i915_drv.h"
1362306a36Sopenharmony_ci#include "i915_mitigations.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistatic unsigned long mitigations __read_mostly = ~0UL;
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cienum {
1862306a36Sopenharmony_ci	CLEAR_RESIDUALS = 0,
1962306a36Sopenharmony_ci};
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic const char * const names[] = {
2262306a36Sopenharmony_ci	[CLEAR_RESIDUALS] = "residuals",
2362306a36Sopenharmony_ci};
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cibool i915_mitigate_clear_residuals(void)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	return READ_ONCE(mitigations) & BIT(CLEAR_RESIDUALS);
2862306a36Sopenharmony_ci}
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic int mitigations_set(const char *val, const struct kernel_param *kp)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	unsigned long new = ~0UL;
3362306a36Sopenharmony_ci	char *str, *sep, *tok;
3462306a36Sopenharmony_ci	bool first = true;
3562306a36Sopenharmony_ci	int err = 0;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	BUILD_BUG_ON(ARRAY_SIZE(names) >= BITS_PER_TYPE(mitigations));
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	str = kstrdup(val, GFP_KERNEL);
4062306a36Sopenharmony_ci	if (!str)
4162306a36Sopenharmony_ci		return -ENOMEM;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	for (sep = str; (tok = strsep(&sep, ","));) {
4462306a36Sopenharmony_ci		bool enable = true;
4562306a36Sopenharmony_ci		int i;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci		/* Be tolerant of leading/trailing whitespace */
4862306a36Sopenharmony_ci		tok = strim(tok);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci		if (first) {
5162306a36Sopenharmony_ci			first = false;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci			if (!strcmp(tok, "auto"))
5462306a36Sopenharmony_ci				continue;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci			new = 0;
5762306a36Sopenharmony_ci			if (!strcmp(tok, "off"))
5862306a36Sopenharmony_ci				continue;
5962306a36Sopenharmony_ci		}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci		if (*tok == '!') {
6262306a36Sopenharmony_ci			enable = !enable;
6362306a36Sopenharmony_ci			tok++;
6462306a36Sopenharmony_ci		}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci		if (!strncmp(tok, "no", 2)) {
6762306a36Sopenharmony_ci			enable = !enable;
6862306a36Sopenharmony_ci			tok += 2;
6962306a36Sopenharmony_ci		}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci		if (*tok == '\0')
7262306a36Sopenharmony_ci			continue;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(names); i++) {
7562306a36Sopenharmony_ci			if (!strcmp(tok, names[i])) {
7662306a36Sopenharmony_ci				if (enable)
7762306a36Sopenharmony_ci					new |= BIT(i);
7862306a36Sopenharmony_ci				else
7962306a36Sopenharmony_ci					new &= ~BIT(i);
8062306a36Sopenharmony_ci				break;
8162306a36Sopenharmony_ci			}
8262306a36Sopenharmony_ci		}
8362306a36Sopenharmony_ci		if (i == ARRAY_SIZE(names)) {
8462306a36Sopenharmony_ci			pr_err("Bad \"%s.mitigations=%s\", '%s' is unknown\n",
8562306a36Sopenharmony_ci			       DRIVER_NAME, val, tok);
8662306a36Sopenharmony_ci			err = -EINVAL;
8762306a36Sopenharmony_ci			break;
8862306a36Sopenharmony_ci		}
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci	kfree(str);
9162306a36Sopenharmony_ci	if (err)
9262306a36Sopenharmony_ci		return err;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	WRITE_ONCE(mitigations, new);
9562306a36Sopenharmony_ci	return 0;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic int mitigations_get(char *buffer, const struct kernel_param *kp)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	unsigned long local = READ_ONCE(mitigations);
10162306a36Sopenharmony_ci	int count, i;
10262306a36Sopenharmony_ci	bool enable;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	if (!local)
10562306a36Sopenharmony_ci		return scnprintf(buffer, PAGE_SIZE, "%s\n", "off");
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	if (local & BIT(BITS_PER_LONG - 1)) {
10862306a36Sopenharmony_ci		count = scnprintf(buffer, PAGE_SIZE, "%s,", "auto");
10962306a36Sopenharmony_ci		enable = false;
11062306a36Sopenharmony_ci	} else {
11162306a36Sopenharmony_ci		enable = true;
11262306a36Sopenharmony_ci		count = 0;
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(names); i++) {
11662306a36Sopenharmony_ci		if ((local & BIT(i)) != enable)
11762306a36Sopenharmony_ci			continue;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci		count += scnprintf(buffer + count, PAGE_SIZE - count,
12062306a36Sopenharmony_ci				   "%s%s,", enable ? "" : "!", names[i]);
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	buffer[count - 1] = '\n';
12462306a36Sopenharmony_ci	return count;
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic const struct kernel_param_ops ops = {
12862306a36Sopenharmony_ci	.set = mitigations_set,
12962306a36Sopenharmony_ci	.get = mitigations_get,
13062306a36Sopenharmony_ci};
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cimodule_param_cb_unsafe(mitigations, &ops, NULL, 0600);
13362306a36Sopenharmony_ciMODULE_PARM_DESC(mitigations,
13462306a36Sopenharmony_ci"Selectively enable security mitigations for all Intel® GPUs in the system.\n"
13562306a36Sopenharmony_ci"\n"
13662306a36Sopenharmony_ci"  auto -- enables all mitigations required for the platform [default]\n"
13762306a36Sopenharmony_ci"  off  -- disables all mitigations\n"
13862306a36Sopenharmony_ci"\n"
13962306a36Sopenharmony_ci"Individual mitigations can be enabled by passing a comma-separated string,\n"
14062306a36Sopenharmony_ci"e.g. mitigations=residuals to enable only clearing residuals or\n"
14162306a36Sopenharmony_ci"mitigations=auto,noresiduals to disable only the clear residual mitigation.\n"
14262306a36Sopenharmony_ci"Either '!' or 'no' may be used to switch from enabling the mitigation to\n"
14362306a36Sopenharmony_ci"disabling it.\n"
14462306a36Sopenharmony_ci"\n"
14562306a36Sopenharmony_ci"Active mitigations for Ivybridge, Baytrail, Haswell:\n"
14662306a36Sopenharmony_ci"  residuals -- clear all thread-local registers between contexts"
14762306a36Sopenharmony_ci);
148