18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: MIT
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright © 2021 Intel Corporation
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/kernel.h>
78c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
88c2ecf20Sopenharmony_ci#include <linux/slab.h>
98c2ecf20Sopenharmony_ci#include <linux/string.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include "i915_drv.h"
128c2ecf20Sopenharmony_ci#include "i915_mitigations.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_cistatic unsigned long mitigations __read_mostly = ~0UL;
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_cienum {
178c2ecf20Sopenharmony_ci	CLEAR_RESIDUALS = 0,
188c2ecf20Sopenharmony_ci};
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic const char * const names[] = {
218c2ecf20Sopenharmony_ci	[CLEAR_RESIDUALS] = "residuals",
228c2ecf20Sopenharmony_ci};
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cibool i915_mitigate_clear_residuals(void)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	return READ_ONCE(mitigations) & BIT(CLEAR_RESIDUALS);
278c2ecf20Sopenharmony_ci}
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic int mitigations_set(const char *val, const struct kernel_param *kp)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	unsigned long new = ~0UL;
328c2ecf20Sopenharmony_ci	char *str, *sep, *tok;
338c2ecf20Sopenharmony_ci	bool first = true;
348c2ecf20Sopenharmony_ci	int err = 0;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	BUILD_BUG_ON(ARRAY_SIZE(names) >= BITS_PER_TYPE(mitigations));
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	str = kstrdup(val, GFP_KERNEL);
398c2ecf20Sopenharmony_ci	if (!str)
408c2ecf20Sopenharmony_ci		return -ENOMEM;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	for (sep = str; (tok = strsep(&sep, ","));) {
438c2ecf20Sopenharmony_ci		bool enable = true;
448c2ecf20Sopenharmony_ci		int i;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci		/* Be tolerant of leading/trailing whitespace */
478c2ecf20Sopenharmony_ci		tok = strim(tok);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci		if (first) {
508c2ecf20Sopenharmony_ci			first = false;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci			if (!strcmp(tok, "auto"))
538c2ecf20Sopenharmony_ci				continue;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci			new = 0;
568c2ecf20Sopenharmony_ci			if (!strcmp(tok, "off"))
578c2ecf20Sopenharmony_ci				continue;
588c2ecf20Sopenharmony_ci		}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci		if (*tok == '!') {
618c2ecf20Sopenharmony_ci			enable = !enable;
628c2ecf20Sopenharmony_ci			tok++;
638c2ecf20Sopenharmony_ci		}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci		if (!strncmp(tok, "no", 2)) {
668c2ecf20Sopenharmony_ci			enable = !enable;
678c2ecf20Sopenharmony_ci			tok += 2;
688c2ecf20Sopenharmony_ci		}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci		if (*tok == '\0')
718c2ecf20Sopenharmony_ci			continue;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(names); i++) {
748c2ecf20Sopenharmony_ci			if (!strcmp(tok, names[i])) {
758c2ecf20Sopenharmony_ci				if (enable)
768c2ecf20Sopenharmony_ci					new |= BIT(i);
778c2ecf20Sopenharmony_ci				else
788c2ecf20Sopenharmony_ci					new &= ~BIT(i);
798c2ecf20Sopenharmony_ci				break;
808c2ecf20Sopenharmony_ci			}
818c2ecf20Sopenharmony_ci		}
828c2ecf20Sopenharmony_ci		if (i == ARRAY_SIZE(names)) {
838c2ecf20Sopenharmony_ci			pr_err("Bad \"%s.mitigations=%s\", '%s' is unknown\n",
848c2ecf20Sopenharmony_ci			       DRIVER_NAME, val, tok);
858c2ecf20Sopenharmony_ci			err = -EINVAL;
868c2ecf20Sopenharmony_ci			break;
878c2ecf20Sopenharmony_ci		}
888c2ecf20Sopenharmony_ci	}
898c2ecf20Sopenharmony_ci	kfree(str);
908c2ecf20Sopenharmony_ci	if (err)
918c2ecf20Sopenharmony_ci		return err;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	WRITE_ONCE(mitigations, new);
948c2ecf20Sopenharmony_ci	return 0;
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic int mitigations_get(char *buffer, const struct kernel_param *kp)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	unsigned long local = READ_ONCE(mitigations);
1008c2ecf20Sopenharmony_ci	int count, i;
1018c2ecf20Sopenharmony_ci	bool enable;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	if (!local)
1048c2ecf20Sopenharmony_ci		return scnprintf(buffer, PAGE_SIZE, "%s\n", "off");
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	if (local & BIT(BITS_PER_LONG - 1)) {
1078c2ecf20Sopenharmony_ci		count = scnprintf(buffer, PAGE_SIZE, "%s,", "auto");
1088c2ecf20Sopenharmony_ci		enable = false;
1098c2ecf20Sopenharmony_ci	} else {
1108c2ecf20Sopenharmony_ci		enable = true;
1118c2ecf20Sopenharmony_ci		count = 0;
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(names); i++) {
1158c2ecf20Sopenharmony_ci		if ((local & BIT(i)) != enable)
1168c2ecf20Sopenharmony_ci			continue;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci		count += scnprintf(buffer + count, PAGE_SIZE - count,
1198c2ecf20Sopenharmony_ci				   "%s%s,", enable ? "" : "!", names[i]);
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	buffer[count - 1] = '\n';
1238c2ecf20Sopenharmony_ci	return count;
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic const struct kernel_param_ops ops = {
1278c2ecf20Sopenharmony_ci	.set = mitigations_set,
1288c2ecf20Sopenharmony_ci	.get = mitigations_get,
1298c2ecf20Sopenharmony_ci};
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cimodule_param_cb_unsafe(mitigations, &ops, NULL, 0600);
1328c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mitigations,
1338c2ecf20Sopenharmony_ci"Selectively enable security mitigations for all Intel® GPUs in the system.\n"
1348c2ecf20Sopenharmony_ci"\n"
1358c2ecf20Sopenharmony_ci"  auto -- enables all mitigations required for the platform [default]\n"
1368c2ecf20Sopenharmony_ci"  off  -- disables all mitigations\n"
1378c2ecf20Sopenharmony_ci"\n"
1388c2ecf20Sopenharmony_ci"Individual mitigations can be enabled by passing a comma-separated string,\n"
1398c2ecf20Sopenharmony_ci"e.g. mitigations=residuals to enable only clearing residuals or\n"
1408c2ecf20Sopenharmony_ci"mitigations=auto,noresiduals to disable only the clear residual mitigation.\n"
1418c2ecf20Sopenharmony_ci"Either '!' or 'no' may be used to switch from enabling the mitigation to\n"
1428c2ecf20Sopenharmony_ci"disabling it.\n"
1438c2ecf20Sopenharmony_ci"\n"
1448c2ecf20Sopenharmony_ci"Active mitigations for Ivybridge, Baytrail, Haswell:\n"
1458c2ecf20Sopenharmony_ci"  residuals -- clear all thread-local registers between contexts"
1468c2ecf20Sopenharmony_ci);
147