diff options
Diffstat (limited to 'drivers/hwmon/dell-smm-hwmon.c')
-rw-r--r-- | drivers/hwmon/dell-smm-hwmon.c | 85 |
1 files changed, 62 insertions, 23 deletions
diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c index c43318d3416e..2ac87d553e22 100644 --- a/drivers/hwmon/dell-smm-hwmon.c +++ b/drivers/hwmon/dell-smm-hwmon.c | |||
@@ -35,6 +35,7 @@ | |||
35 | #include <linux/uaccess.h> | 35 | #include <linux/uaccess.h> |
36 | #include <linux/io.h> | 36 | #include <linux/io.h> |
37 | #include <linux/sched.h> | 37 | #include <linux/sched.h> |
38 | #include <linux/ctype.h> | ||
38 | 39 | ||
39 | #include <linux/i8k.h> | 40 | #include <linux/i8k.h> |
40 | 41 | ||
@@ -66,11 +67,13 @@ | |||
66 | 67 | ||
67 | static DEFINE_MUTEX(i8k_mutex); | 68 | static DEFINE_MUTEX(i8k_mutex); |
68 | static char bios_version[4]; | 69 | static char bios_version[4]; |
70 | static char bios_machineid[16]; | ||
69 | static struct device *i8k_hwmon_dev; | 71 | static struct device *i8k_hwmon_dev; |
70 | static u32 i8k_hwmon_flags; | 72 | static u32 i8k_hwmon_flags; |
71 | static uint i8k_fan_mult = I8K_FAN_MULT; | 73 | static uint i8k_fan_mult = I8K_FAN_MULT; |
72 | static uint i8k_pwm_mult; | 74 | static uint i8k_pwm_mult; |
73 | static uint i8k_fan_max = I8K_FAN_HIGH; | 75 | static uint i8k_fan_max = I8K_FAN_HIGH; |
76 | static bool disallow_fan_type_call; | ||
74 | 77 | ||
75 | #define I8K_HWMON_HAVE_TEMP1 (1 << 0) | 78 | #define I8K_HWMON_HAVE_TEMP1 (1 << 0) |
76 | #define I8K_HWMON_HAVE_TEMP2 (1 << 1) | 79 | #define I8K_HWMON_HAVE_TEMP2 (1 << 1) |
@@ -94,13 +97,13 @@ module_param(ignore_dmi, bool, 0); | |||
94 | MODULE_PARM_DESC(ignore_dmi, "Continue probing hardware even if DMI data does not match"); | 97 | MODULE_PARM_DESC(ignore_dmi, "Continue probing hardware even if DMI data does not match"); |
95 | 98 | ||
96 | #if IS_ENABLED(CONFIG_I8K) | 99 | #if IS_ENABLED(CONFIG_I8K) |
97 | static bool restricted; | 100 | static bool restricted = true; |
98 | module_param(restricted, bool, 0); | 101 | module_param(restricted, bool, 0); |
99 | MODULE_PARM_DESC(restricted, "Allow fan control if SYS_ADMIN capability set"); | 102 | MODULE_PARM_DESC(restricted, "Restrict fan control and serial number to CAP_SYS_ADMIN (default: 1)"); |
100 | 103 | ||
101 | static bool power_status; | 104 | static bool power_status; |
102 | module_param(power_status, bool, 0600); | 105 | module_param(power_status, bool, 0600); |
103 | MODULE_PARM_DESC(power_status, "Report power status in /proc/i8k"); | 106 | MODULE_PARM_DESC(power_status, "Report power status in /proc/i8k (default: 0)"); |
104 | #endif | 107 | #endif |
105 | 108 | ||
106 | static uint fan_mult; | 109 | static uint fan_mult; |
@@ -235,14 +238,28 @@ static int i8k_get_fan_speed(int fan) | |||
235 | /* | 238 | /* |
236 | * Read the fan type. | 239 | * Read the fan type. |
237 | */ | 240 | */ |
238 | static int i8k_get_fan_type(int fan) | 241 | static int _i8k_get_fan_type(int fan) |
239 | { | 242 | { |
240 | struct smm_regs regs = { .eax = I8K_SMM_GET_FAN_TYPE, }; | 243 | struct smm_regs regs = { .eax = I8K_SMM_GET_FAN_TYPE, }; |
241 | 244 | ||
245 | if (disallow_fan_type_call) | ||
246 | return -EINVAL; | ||
247 | |||
242 | regs.ebx = fan & 0xff; | 248 | regs.ebx = fan & 0xff; |
243 | return i8k_smm(®s) ? : regs.eax & 0xff; | 249 | return i8k_smm(®s) ? : regs.eax & 0xff; |
244 | } | 250 | } |
245 | 251 | ||
252 | static int i8k_get_fan_type(int fan) | ||
253 | { | ||
254 | /* I8K_SMM_GET_FAN_TYPE SMM call is expensive, so cache values */ | ||
255 | static int types[2] = { INT_MIN, INT_MIN }; | ||
256 | |||
257 | if (types[fan] == INT_MIN) | ||
258 | types[fan] = _i8k_get_fan_type(fan); | ||
259 | |||
260 | return types[fan]; | ||
261 | } | ||
262 | |||
246 | /* | 263 | /* |
247 | * Read the fan nominal rpm for specific fan speed. | 264 | * Read the fan nominal rpm for specific fan speed. |
248 | */ | 265 | */ |
@@ -387,14 +404,20 @@ i8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg) | |||
387 | 404 | ||
388 | switch (cmd) { | 405 | switch (cmd) { |
389 | case I8K_BIOS_VERSION: | 406 | case I8K_BIOS_VERSION: |
407 | if (!isdigit(bios_version[0]) || !isdigit(bios_version[1]) || | ||
408 | !isdigit(bios_version[2])) | ||
409 | return -EINVAL; | ||
410 | |||
390 | val = (bios_version[0] << 16) | | 411 | val = (bios_version[0] << 16) | |
391 | (bios_version[1] << 8) | bios_version[2]; | 412 | (bios_version[1] << 8) | bios_version[2]; |
392 | break; | 413 | break; |
393 | 414 | ||
394 | case I8K_MACHINE_ID: | 415 | case I8K_MACHINE_ID: |
395 | memset(buff, 0, 16); | 416 | if (restricted && !capable(CAP_SYS_ADMIN)) |
396 | strlcpy(buff, i8k_get_dmi_data(DMI_PRODUCT_SERIAL), | 417 | return -EPERM; |
397 | sizeof(buff)); | 418 | |
419 | memset(buff, 0, sizeof(buff)); | ||
420 | strlcpy(buff, bios_machineid, sizeof(buff)); | ||
398 | break; | 421 | break; |
399 | 422 | ||
400 | case I8K_FN_STATUS: | 423 | case I8K_FN_STATUS: |
@@ -511,7 +534,7 @@ static int i8k_proc_show(struct seq_file *seq, void *offset) | |||
511 | seq_printf(seq, "%s %s %s %d %d %d %d %d %d %d\n", | 534 | seq_printf(seq, "%s %s %s %d %d %d %d %d %d %d\n", |
512 | I8K_PROC_FMT, | 535 | I8K_PROC_FMT, |
513 | bios_version, | 536 | bios_version, |
514 | i8k_get_dmi_data(DMI_PRODUCT_SERIAL), | 537 | (restricted && !capable(CAP_SYS_ADMIN)) ? "-1" : bios_machineid, |
515 | cpu_temp, | 538 | cpu_temp, |
516 | left_fan, right_fan, left_speed, right_speed, | 539 | left_fan, right_fan, left_speed, right_speed, |
517 | ac_power, fn_key); | 540 | ac_power, fn_key); |
@@ -718,6 +741,9 @@ static struct attribute *i8k_attrs[] = { | |||
718 | static umode_t i8k_is_visible(struct kobject *kobj, struct attribute *attr, | 741 | static umode_t i8k_is_visible(struct kobject *kobj, struct attribute *attr, |
719 | int index) | 742 | int index) |
720 | { | 743 | { |
744 | if (disallow_fan_type_call && | ||
745 | (index == 9 || index == 12)) | ||
746 | return 0; | ||
721 | if (index >= 0 && index <= 1 && | 747 | if (index >= 0 && index <= 1 && |
722 | !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP1)) | 748 | !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP1)) |
723 | return 0; | 749 | return 0; |
@@ -767,13 +793,17 @@ static int __init i8k_init_hwmon(void) | |||
767 | if (err >= 0) | 793 | if (err >= 0) |
768 | i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP4; | 794 | i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP4; |
769 | 795 | ||
770 | /* First fan attributes, if fan type is OK */ | 796 | /* First fan attributes, if fan status or type is OK */ |
771 | err = i8k_get_fan_type(0); | 797 | err = i8k_get_fan_status(0); |
798 | if (err < 0) | ||
799 | err = i8k_get_fan_type(0); | ||
772 | if (err >= 0) | 800 | if (err >= 0) |
773 | i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN1; | 801 | i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN1; |
774 | 802 | ||
775 | /* Second fan attributes, if fan type is OK */ | 803 | /* Second fan attributes, if fan status or type is OK */ |
776 | err = i8k_get_fan_type(1); | 804 | err = i8k_get_fan_status(1); |
805 | if (err < 0) | ||
806 | err = i8k_get_fan_type(1); | ||
777 | if (err >= 0) | 807 | if (err >= 0) |
778 | i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN2; | 808 | i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN2; |
779 | 809 | ||
@@ -929,12 +959,14 @@ static struct dmi_system_id i8k_dmi_table[] __initdata = { | |||
929 | 959 | ||
930 | MODULE_DEVICE_TABLE(dmi, i8k_dmi_table); | 960 | MODULE_DEVICE_TABLE(dmi, i8k_dmi_table); |
931 | 961 | ||
932 | static struct dmi_system_id i8k_blacklist_dmi_table[] __initdata = { | 962 | /* |
963 | * On some machines once I8K_SMM_GET_FAN_TYPE is issued then CPU fan speed | ||
964 | * randomly going up and down due to bug in Dell SMM or BIOS. Here is blacklist | ||
965 | * of affected Dell machines for which we disallow I8K_SMM_GET_FAN_TYPE call. | ||
966 | * See bug: https://bugzilla.kernel.org/show_bug.cgi?id=100121 | ||
967 | */ | ||
968 | static struct dmi_system_id i8k_blacklist_fan_type_dmi_table[] __initdata = { | ||
933 | { | 969 | { |
934 | /* | ||
935 | * CPU fan speed going up and down on Dell Studio XPS 8000 | ||
936 | * for unknown reasons. | ||
937 | */ | ||
938 | .ident = "Dell Studio XPS 8000", | 970 | .ident = "Dell Studio XPS 8000", |
939 | .matches = { | 971 | .matches = { |
940 | DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | 972 | DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), |
@@ -942,16 +974,19 @@ static struct dmi_system_id i8k_blacklist_dmi_table[] __initdata = { | |||
942 | }, | 974 | }, |
943 | }, | 975 | }, |
944 | { | 976 | { |
945 | /* | ||
946 | * CPU fan speed going up and down on Dell Studio XPS 8100 | ||
947 | * for unknown reasons. | ||
948 | */ | ||
949 | .ident = "Dell Studio XPS 8100", | 977 | .ident = "Dell Studio XPS 8100", |
950 | .matches = { | 978 | .matches = { |
951 | DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | 979 | DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), |
952 | DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Studio XPS 8100"), | 980 | DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Studio XPS 8100"), |
953 | }, | 981 | }, |
954 | }, | 982 | }, |
983 | { | ||
984 | .ident = "Dell Inspiron 580", | ||
985 | .matches = { | ||
986 | DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
987 | DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Inspiron 580 "), | ||
988 | }, | ||
989 | }, | ||
955 | { } | 990 | { } |
956 | }; | 991 | }; |
957 | 992 | ||
@@ -966,8 +1001,7 @@ static int __init i8k_probe(void) | |||
966 | /* | 1001 | /* |
967 | * Get DMI information | 1002 | * Get DMI information |
968 | */ | 1003 | */ |
969 | if (!dmi_check_system(i8k_dmi_table) || | 1004 | if (!dmi_check_system(i8k_dmi_table)) { |
970 | dmi_check_system(i8k_blacklist_dmi_table)) { | ||
971 | if (!ignore_dmi && !force) | 1005 | if (!ignore_dmi && !force) |
972 | return -ENODEV; | 1006 | return -ENODEV; |
973 | 1007 | ||
@@ -978,8 +1012,13 @@ static int __init i8k_probe(void) | |||
978 | i8k_get_dmi_data(DMI_BIOS_VERSION)); | 1012 | i8k_get_dmi_data(DMI_BIOS_VERSION)); |
979 | } | 1013 | } |
980 | 1014 | ||
1015 | if (dmi_check_system(i8k_blacklist_fan_type_dmi_table)) | ||
1016 | disallow_fan_type_call = true; | ||
1017 | |||
981 | strlcpy(bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION), | 1018 | strlcpy(bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION), |
982 | sizeof(bios_version)); | 1019 | sizeof(bios_version)); |
1020 | strlcpy(bios_machineid, i8k_get_dmi_data(DMI_PRODUCT_SERIAL), | ||
1021 | sizeof(bios_machineid)); | ||
983 | 1022 | ||
984 | /* | 1023 | /* |
985 | * Get SMM Dell signature | 1024 | * Get SMM Dell signature |