cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

gigabyte-wmi.c (5766B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *  Copyright (C) 2021 Thomas Weißschuh <thomas@weissschuh.net>
      4 */
      5#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
      6
      7#include <linux/acpi.h>
      8#include <linux/dmi.h>
      9#include <linux/hwmon.h>
     10#include <linux/module.h>
     11#include <linux/wmi.h>
     12
     13#define GIGABYTE_WMI_GUID	"DEADBEEF-2001-0000-00A0-C90629100000"
     14#define NUM_TEMPERATURE_SENSORS	6
     15
     16static bool force_load;
     17module_param(force_load, bool, 0444);
     18MODULE_PARM_DESC(force_load, "Force loading on unknown platform");
     19
     20static u8 usable_sensors_mask;
     21
     22enum gigabyte_wmi_commandtype {
     23	GIGABYTE_WMI_BUILD_DATE_QUERY       =   0x1,
     24	GIGABYTE_WMI_MAINBOARD_TYPE_QUERY   =   0x2,
     25	GIGABYTE_WMI_FIRMWARE_VERSION_QUERY =   0x4,
     26	GIGABYTE_WMI_MAINBOARD_NAME_QUERY   =   0x5,
     27	GIGABYTE_WMI_TEMPERATURE_QUERY      = 0x125,
     28};
     29
     30struct gigabyte_wmi_args {
     31	u32 arg1;
     32};
     33
     34static int gigabyte_wmi_perform_query(struct wmi_device *wdev,
     35				      enum gigabyte_wmi_commandtype command,
     36				      struct gigabyte_wmi_args *args, struct acpi_buffer *out)
     37{
     38	const struct acpi_buffer in = {
     39		.length = sizeof(*args),
     40		.pointer = args,
     41	};
     42
     43	acpi_status ret = wmidev_evaluate_method(wdev, 0x0, command, &in, out);
     44
     45	if (ACPI_FAILURE(ret))
     46		return -EIO;
     47
     48	return 0;
     49}
     50
     51static int gigabyte_wmi_query_integer(struct wmi_device *wdev,
     52				      enum gigabyte_wmi_commandtype command,
     53				      struct gigabyte_wmi_args *args, u64 *res)
     54{
     55	union acpi_object *obj;
     56	struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
     57	int ret;
     58
     59	ret = gigabyte_wmi_perform_query(wdev, command, args, &result);
     60	if (ret)
     61		return ret;
     62	obj = result.pointer;
     63	if (obj && obj->type == ACPI_TYPE_INTEGER)
     64		*res = obj->integer.value;
     65	else
     66		ret = -EIO;
     67	kfree(result.pointer);
     68	return ret;
     69}
     70
     71static int gigabyte_wmi_temperature(struct wmi_device *wdev, u8 sensor, long *res)
     72{
     73	struct gigabyte_wmi_args args = {
     74		.arg1 = sensor,
     75	};
     76	u64 temp;
     77	acpi_status ret;
     78
     79	ret = gigabyte_wmi_query_integer(wdev, GIGABYTE_WMI_TEMPERATURE_QUERY, &args, &temp);
     80	if (ret == 0) {
     81		if (temp == 0)
     82			return -ENODEV;
     83		*res = (s8)temp * 1000; // value is a signed 8-bit integer
     84	}
     85	return ret;
     86}
     87
     88static int gigabyte_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
     89				   u32 attr, int channel, long *val)
     90{
     91	struct wmi_device *wdev = dev_get_drvdata(dev);
     92
     93	return gigabyte_wmi_temperature(wdev, channel, val);
     94}
     95
     96static umode_t gigabyte_wmi_hwmon_is_visible(const void *data, enum hwmon_sensor_types type,
     97					     u32 attr, int channel)
     98{
     99	return usable_sensors_mask & BIT(channel) ? 0444  : 0;
    100}
    101
    102static const struct hwmon_channel_info *gigabyte_wmi_hwmon_info[] = {
    103	HWMON_CHANNEL_INFO(temp,
    104			   HWMON_T_INPUT,
    105			   HWMON_T_INPUT,
    106			   HWMON_T_INPUT,
    107			   HWMON_T_INPUT,
    108			   HWMON_T_INPUT,
    109			   HWMON_T_INPUT),
    110	NULL
    111};
    112
    113static const struct hwmon_ops gigabyte_wmi_hwmon_ops = {
    114	.read = gigabyte_wmi_hwmon_read,
    115	.is_visible = gigabyte_wmi_hwmon_is_visible,
    116};
    117
    118static const struct hwmon_chip_info gigabyte_wmi_hwmon_chip_info = {
    119	.ops = &gigabyte_wmi_hwmon_ops,
    120	.info = gigabyte_wmi_hwmon_info,
    121};
    122
    123static u8 gigabyte_wmi_detect_sensor_usability(struct wmi_device *wdev)
    124{
    125	int i;
    126	long temp;
    127	u8 r = 0;
    128
    129	for (i = 0; i < NUM_TEMPERATURE_SENSORS; i++) {
    130		if (!gigabyte_wmi_temperature(wdev, i, &temp))
    131			r |= BIT(i);
    132	}
    133	return r;
    134}
    135
    136#define DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME(name) \
    137	{ .matches = { \
    138		DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co., Ltd."), \
    139		DMI_EXACT_MATCH(DMI_BOARD_NAME, name), \
    140	}}
    141
    142static const struct dmi_system_id gigabyte_wmi_known_working_platforms[] = {
    143	DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B450M DS3H-CF"),
    144	DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B450M S2H V2"),
    145	DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B550 AORUS ELITE AX V2"),
    146	DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B550 AORUS ELITE"),
    147	DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B550 AORUS ELITE V2"),
    148	DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B550 GAMING X V2"),
    149	DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B550I AORUS PRO AX"),
    150	DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B550M AORUS PRO-P"),
    151	DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B550M DS3H"),
    152	DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B660 GAMING X DDR4"),
    153	DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("Z390 I AORUS PRO WIFI-CF"),
    154	DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("Z490 AORUS ELITE AC"),
    155	DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("X570 AORUS ELITE"),
    156	DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("X570 AORUS ELITE WIFI"),
    157	DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("X570 GAMING X"),
    158	DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("X570 I AORUS PRO WIFI"),
    159	DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("X570 UD"),
    160	DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("Z690M AORUS ELITE AX DDR4"),
    161	{ }
    162};
    163
    164static int gigabyte_wmi_probe(struct wmi_device *wdev, const void *context)
    165{
    166	struct device *hwmon_dev;
    167
    168	if (!dmi_check_system(gigabyte_wmi_known_working_platforms)) {
    169		if (!force_load)
    170			return -ENODEV;
    171		dev_warn(&wdev->dev, "Forcing load on unknown platform");
    172	}
    173
    174	usable_sensors_mask = gigabyte_wmi_detect_sensor_usability(wdev);
    175	if (!usable_sensors_mask) {
    176		dev_info(&wdev->dev, "No temperature sensors usable");
    177		return -ENODEV;
    178	}
    179
    180	hwmon_dev = devm_hwmon_device_register_with_info(&wdev->dev, "gigabyte_wmi", wdev,
    181							 &gigabyte_wmi_hwmon_chip_info, NULL);
    182
    183	return PTR_ERR_OR_ZERO(hwmon_dev);
    184}
    185
    186static const struct wmi_device_id gigabyte_wmi_id_table[] = {
    187	{ GIGABYTE_WMI_GUID, NULL },
    188	{ }
    189};
    190
    191static struct wmi_driver gigabyte_wmi_driver = {
    192	.driver = {
    193		.name = "gigabyte-wmi",
    194	},
    195	.id_table = gigabyte_wmi_id_table,
    196	.probe = gigabyte_wmi_probe,
    197};
    198module_wmi_driver(gigabyte_wmi_driver);
    199
    200MODULE_DEVICE_TABLE(wmi, gigabyte_wmi_id_table);
    201MODULE_AUTHOR("Thomas Weißschuh <thomas@weissschuh.net>");
    202MODULE_DESCRIPTION("Gigabyte WMI temperature driver");
    203MODULE_LICENSE("GPL");