hwmon fixes for v7.3-rc4

* cgbc-hwmon: Add missing sensors, and fix current sensors ID lookup
 
 * gpioufan: Return IRQ_HANDLED from the shared alarm IRQ handler to fix
   possible interrupt storm
 
 * hp-wmi-sensors: Improve raw WMI string handling, and fix UaF in show
   function
 
 * k10temp: Fix model id range of Zen5 Turin to stop reporting temperature
   data for non-existing CCDs
 
 * pmbus
 
   - core: Increase number of phases to fix UaF problems
 
   - tps53679: Fix TPS53676 phase page decoding, and select page 0
     for single-page applications
 
 * pwm-fan: Stop RPM timer before freeing tach data to fix UaF problem
 
 * w83793: Release probe data through kref to fix UaF problem
 
 * w83791d: Remove fan/pwm 4-5 sysfs group on remove to fix UaF problem
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEiHPvMQj9QTOCiqgVyx8mb86fmYEFAmqtY4YACgkQyx8mb86f
 mYHX0Q//UmlkmqkHH3XdKi1QX/RV60f82HfzXRkMq98FYocEYv4TKnru3U5pRLY3
 Vs/h4GcDWT16ApqKbciBxgoUj72XBohm/T0gv8rhC/gkx4w8F7/CPuCB6vq9kIAj
 EIOHM3KoulfwUN6K1JeFdo5sIrTMxYGnQoJjS31/HfExEGbcBMZjA4eWLkqA0s18
 zmtYKbCJbG2WFRkI8/HKeQ2MwFE3RLNrxPVNvWJNzwIPjMvaAD7eDSsRjgXpaH6s
 A2OHpnGkDLE1qeyuYYx+nFYmMQDGDxnt6QvEjX5cVUYE+jDIXuzF5HJVfLCaXoSJ
 cFJmyNVTNE6Is1g6qeBRwObSq/NJH3O6p7kXCf5qwO27XBXgt/7K4q751tMq8oEE
 kXiYn3WfBL4WJjYqQw8kEh2/D18fyj9XmoS7iBXzf1qXgRSROm/9irMBjsWiw0WF
 92iE9U7UaE/a5fGX+dbRnB7QmLZ33U5TzA3ERb+MwJQj6o8Llvb5gmJOmlYebpa1
 zRiu285Y3oGnjjkFgzvpps9hsVw3kb2Xs2utMbTPrJ2z4SUO9KInl+ifOr5w9VI+
 sNU0Rn3eQ6IT40NMO9FhHPciZygKmob6hNDYh/6e+OulMDW7XW46ES2e8jjwME30
 fjDpfKDxoAU/xQ548XQJBRcUNjUhV8S6NYZr3H4PpyWlPVmSkOY=
 =lG35
 -----END PGP SIGNATURE-----

Merge tag 'hwmon-for-v7.3-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging

Pull hwmon fixes from Guenter Roeck:

 - Add missing sensors, and fix current sensors ID lookup (cgbc-hwmon)

 - Return IRQ_HANDLED from the shared alarm IRQ handler to fix possible
   interrupt storm (gpioufan)

 - Improve raw WMI string handling, and fix UaF in show function
   (hp-wmi-sensors)

 - Fix k10temp model id range of Zen5 Turin to stop reporting
   temperature data for non-existing CCDs

 - pmbus:
     - Increase number of phases to fix UaF problems
     - Fix TPS53676 phase page decoding, and select page 0 for
       single-page applications

 - Stop pwm-fan RPM timer before freeing tach data to fix UaF

 - Release w83793 probe data through kref to fix UaF

 - Remove w83791d fan/pwm 4-5 sysfs group on remove to fix UaF

* tag 'hwmon-for-v7.3-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging:
  hwmon: (hp-wmi-sensors) Improve raw WMI string handling
  hwmon: (pmbus/tps53679) Select page 0 for single-page TPS53676
  hwmon: (pmbus/tps53679) Fix TPS53676 phase page decoding
  hwmon: (hp-wmi-sensors) Fix use-after-free in fungible_show()
  hwmon: (w83793) release probe data through kref
  hwmon: (w83791d) remove fan/pwm 4-5 sysfs group on remove
  hwmon: (gpio-fan) return IRQ_HANDLED from the shared alarm IRQ handler
  hwmon: (pmbus/core) increase number of phases and add new mask
  hwmon: (cgbc-hwmon) Add missing sensors
  hwmon: (cgbc-hwmon) Fix current sensors ID lookup
  hwmon: (pwm-fan) Stop RPM timer before freeing tach data
  hwmon: (k10temp) Fix model id range of Zen5 Turin
This commit is contained in:
Linus Torvalds 2026-09-18 10:27:23 -07:00
commit d24e3bf4c5
10 changed files with 161 additions and 80 deletions

View File

@ -28,34 +28,44 @@ system.
Name Description
============= ======================
temp1_input CPU temperature
temp2_input Box temperature
temp2_input Case temperature
temp3_input Ambient temperature
temp4_input Board temperature
temp5_input Carrier temperature
temp6_input Chipset temperature
temp7_input Video temperature
temp4_input CPU Board temperature
temp5_input Carrier Board temperature
temp6_input System Chipset temperature
temp7_input Video Controller/Board temperature
temp8_input Other temperature
temp9_input TOPDIM temperature
temp10_input BOTTOMDIM temperature
in0_input CPU voltage
temp9_input Top DIMM 0 temperature
temp10_input Bottom DIMM 0 temperature
temp11_input Alternate Board temperature
temp12_input Top DIMM 1 temperature
temp13_input Top DIMM 2 temperature
temp14_input Top DIMM 3 temperature
temp15_input Top DIMM 4 temperature
temp16_input Top DIMM 5 temperature
temp17_input Top DIMM 6 temperature
temp18_input Top DIMM 7 temperature
temp19_input Bottom DIMM 1 temperature
in0_input CPU Core voltage
in1_input DC Runtime voltage
in2_input DC Standby voltage
in3_input CMOS Battery voltage
in4_input Battery voltage
in4_input Battery Supply voltage
in5_input AC voltage
in6_input Other voltage
in7_input 5V voltage
in7_input 5V Runtime voltage
in8_input 5V Standby voltage
in9_input 3V3 voltage
in9_input 3V3 Runtime voltage
in10_input 3V3 Standby voltage
in11_input VCore A voltage
in12_input VCore B voltage
in13_input 12V voltage
curr1_input DC current
curr2_input 5V current
curr3_input 12V current
in13_input 12V Runtime voltage
in14_input 12V Standby voltage
curr1_input DC Runtime current
curr2_input 5V Runtime current
curr3_input 12V Runtime current
fan1_input CPU fan
fan2_input Box fan
fan2_input Case fan
fan3_input Ambient fan
fan4_input Chiptset fan
fan5_input Video fan

View File

@ -41,45 +41,60 @@ enum cgbc_sensor_types {
static const char * const cgbc_hwmon_labels_temp[] = {
"CPU Temperature",
"Box Temperature",
"Case Temperature",
"Ambient Temperature",
"Board Temperature",
"Carrier Temperature",
"Chipset Temperature",
"Video Temperature",
"CPU Board Temperature",
"Carrier Board Temperature",
"System Chipset Temperature",
"Video Controller/Board Temperature",
"Other Temperature",
"TOPDIM Temperature",
"BOTTOMDIM Temperature",
"Top DIMM 0 Temperature",
"Bottom DIMM 0 Temperature",
"Alternate Board Temperature",
"Top DIMM 1 Temperature",
"Top DIMM 2 Temperature",
"Top DIMM 3 Temperature",
"Top DIMM 4 Temperature",
"Top DIMM 5 Temperature",
"Top DIMM 6 Temperature",
"Top DIMM 7 Temperature",
"Bottom DIMM 1 Temperature",
};
static const char * const cgbc_hwmon_labels_in[] = {
"CPU Core Voltage",
"DC Runtime Voltage",
"DC Standby Voltage",
"CMOS Battery Voltage",
"Battery Supply Voltage",
"AC Voltage",
"Other Voltage",
"5V Runtime Voltage",
"5V Standby Voltage",
"3V3 Runtime Voltage",
"3V3 Standby Voltage",
"VCore A Voltage",
"VCore B Voltage",
"12V Runtime Voltage",
"12V Standby Voltage",
};
/*
* Current sensors are a bit special, they don't have consecutive IDs like
* other types of sensors. So they need to be defined explicitly.
*/
static const struct {
enum hwmon_sensor_types type;
const char *label;
} cgbc_hwmon_labels_in[] = {
{ hwmon_in, "CPU Voltage" },
{ hwmon_in, "DC Runtime Voltage" },
{ hwmon_in, "DC Standby Voltage" },
{ hwmon_in, "CMOS Battery Voltage" },
{ hwmon_in, "Battery Voltage" },
{ hwmon_in, "AC Voltage" },
{ hwmon_in, "Other Voltage" },
{ hwmon_in, "5V Voltage" },
{ hwmon_in, "5V Standby Voltage" },
{ hwmon_in, "3V3 Voltage" },
{ hwmon_in, "3V3 Standby Voltage" },
{ hwmon_in, "VCore A Voltage" },
{ hwmon_in, "VCore B Voltage" },
{ hwmon_in, "12V Voltage" },
{ hwmon_curr, "DC Current" },
{ hwmon_curr, "5V Current" },
{ hwmon_curr, "12V Current" },
int id;
} cgbc_hwmon_labels_curr[] = {
{ "DC Runtime Current", 0x12 },
{ "5V Runtime Current", 0x18 },
{ "12V Runtime Current", 0x1E },
};
#define CGBC_HWMON_NB_IN_SENSORS 14
static const char * const cgbc_hwmon_labels_fan[] = {
"CPU Fan",
"Box Fan",
"Case Fan",
"Ambient Fan",
"Chipset Fan",
"Video Fan",
@ -114,7 +129,8 @@ static int cgbc_hwmon_probe_sensors(struct device *dev, struct cgbc_hwmon_data *
for (i = 0; i < nb_sensors; i++) {
enum cgbc_sensor_types type;
unsigned int channel;
unsigned int channel, id;
int j;
/*
* No need to request data for the first sensor.
@ -128,32 +144,49 @@ static int cgbc_hwmon_probe_sensors(struct device *dev, struct cgbc_hwmon_data *
}
type = FIELD_GET(CGBC_HWMON_TYPE_MASK, data[1]);
channel = FIELD_GET(CGBC_HWMON_ID_MASK, data[1]) - 1;
id = FIELD_GET(CGBC_HWMON_ID_MASK, data[1]);
channel = id - 1;
if (type == CGBC_HWMON_TYPE_TEMP && channel < ARRAY_SIZE(cgbc_hwmon_labels_temp)) {
sensor->type = hwmon_temp;
sensor->label = cgbc_hwmon_labels_temp[channel];
} else if (type == CGBC_HWMON_TYPE_IN &&
channel < ARRAY_SIZE(cgbc_hwmon_labels_in)) {
} else if (type == CGBC_HWMON_TYPE_IN) {
/*
* The Board Controller doesn't differentiate current and voltage sensors.
* Get the sensor type from cgbc_hwmon_labels_in[channel].type instead.
* First check if it is a current sensor.
*/
sensor->type = cgbc_hwmon_labels_in[channel].type;
sensor->label = cgbc_hwmon_labels_in[channel].label;
for (j = 0; j < ARRAY_SIZE(cgbc_hwmon_labels_curr); j++) {
if (id == cgbc_hwmon_labels_curr[j].id) {
sensor->type = hwmon_curr;
sensor->label = cgbc_hwmon_labels_curr[j].label;
channel = j;
}
}
/* If it's not a current sensor, it may be a voltage sensor. */
if (!sensor->label && channel < ARRAY_SIZE(cgbc_hwmon_labels_in)) {
sensor->type = hwmon_in;
sensor->label = cgbc_hwmon_labels_in[channel];
}
} else if (type == CGBC_HWMON_TYPE_FAN &&
channel < ARRAY_SIZE(cgbc_hwmon_labels_fan)) {
sensor->type = hwmon_fan;
sensor->label = cgbc_hwmon_labels_fan[channel];
} else {
dev_warn(dev, "Board Controller returned an unknown sensor (type=%d, channel=%d), ignore it",
type, channel);
}
if (!sensor->label) {
dev_warn(dev, "Board Controller returned an unknown sensor (bc_type=%d, bc_id=%d), ignore it",
type, id);
continue;
}
sensor->active = FIELD_GET(CGBC_HWMON_ACTIVE_BIT, data[1]);
sensor->channel = channel;
sensor->index = i;
dev_dbg(dev, "Found sensor: bc_type=%d, bc_id=%d, hwmon_type=%d, hwmon_channel=%d, hwmon_label='%s', active=%d\n",
type, id, sensor->type, sensor->channel, sensor->label, sensor->active);
sensor++;
hwmon->nb_sensors++;
}
@ -167,14 +200,6 @@ static struct cgbc_hwmon_sensor *cgbc_hwmon_find_sensor(struct cgbc_hwmon_data *
struct cgbc_hwmon_sensor *sensor = NULL;
int i;
/*
* The Board Controller doesn't differentiate current and voltage sensors.
* The channel value (from the Board Controller point of view) shall be computed for current
* sensors.
*/
if (type == hwmon_curr)
channel += CGBC_HWMON_NB_IN_SENSORS;
for (i = 0; i < hwmon->nb_sensors; i++) {
if (hwmon->sensors[i].type == type && hwmon->sensors[i].channel == channel) {
sensor = &hwmon->sensors[i];
@ -240,7 +265,12 @@ static const struct hwmon_channel_info * const cgbc_hwmon_info[] = {
HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL),
HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL),
HWMON_CHANNEL_INFO(in,
HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL,
@ -248,7 +278,8 @@ static const struct hwmon_channel_info * const cgbc_hwmon_info[] = {
HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL),
HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL),
HWMON_CHANNEL_INFO(curr,
HWMON_C_INPUT | HWMON_C_LABEL, HWMON_C_INPUT | HWMON_C_LABEL,
HWMON_C_INPUT | HWMON_C_LABEL),

View File

@ -68,7 +68,7 @@ static irqreturn_t fan_alarm_irq_handler(int irq, void *dev_id)
schedule_work(&fan_data->alarm_work);
return IRQ_NONE;
return IRQ_HANDLED;
}
static ssize_t fan1_alarm_show(struct device *dev,
@ -103,7 +103,7 @@ static int fan_alarm_init(struct gpio_fan_data *fan_data)
irq_set_irq_type(alarm_irq, IRQ_TYPE_EDGE_BOTH);
return devm_request_irq(dev, alarm_irq, fan_alarm_irq_handler,
IRQF_SHARED, "GPIO fan alarm", fan_data);
0, "GPIO fan alarm", fan_data);
}
/*

View File

@ -526,14 +526,12 @@ static int check_wobj(const union acpi_object *wobj,
for (prop = 0; prop <= last_prop; prop++) {
type = elements[prop].type;
valid_type = property_map[prop];
if (type != valid_type) {
if (type == ACPI_TYPE_BUFFER &&
valid_type == ACPI_TYPE_STRING &&
is_raw_wmi_string(elements[prop].buffer.pointer,
elements[prop].buffer.length))
continue;
if (type == ACPI_TYPE_BUFFER &&
is_raw_wmi_string(elements[prop].buffer.pointer,
elements[prop].buffer.length))
type = ACPI_TYPE_STRING;
if (type != valid_type)
return -EINVAL;
}
}
return 0;
@ -579,6 +577,7 @@ static int check_numeric_sensor_wobj(const union acpi_object *wobj,
int prop = HP_WMI_PROPERTY_NAME;
acpi_object_type valid_type;
union acpi_object *elements;
union acpi_object *element;
u32 elem_count;
int last_prop;
bool is_new;
@ -602,13 +601,20 @@ static int check_numeric_sensor_wobj(const union acpi_object *wobj,
elem_count > HP_WMI_MAX_PROPERTIES)
return -EINVAL;
type = elements[HP_WMI_PROPERTY_SIZE].type;
element = &elements[HP_WMI_PROPERTY_SIZE];
type = element->type;
switch (type) {
case ACPI_TYPE_INTEGER:
is_new = true;
last_prop = HP_WMI_PROPERTY_RATE_UNITS;
break;
case ACPI_TYPE_BUFFER:
if (!is_raw_wmi_string(element->buffer.pointer,
element->buffer.length))
return -EINVAL;
fallthrough;
case ACPI_TYPE_STRING:
is_new = false;
last_prop = HP_WMI_PROPERTY_CURRENT_READING;
@ -631,6 +637,10 @@ static int check_numeric_sensor_wobj(const union acpi_object *wobj,
for (i = 0; i < elem_count && prop <= last_prop; i++, prop++) {
type = elements[i].type;
valid_type = hp_wmi_property_map[prop];
if (type == ACPI_TYPE_BUFFER &&
is_raw_wmi_string(elements[i].buffer.pointer,
elements[i].buffer.length))
type = ACPI_TYPE_STRING;
if (type != valid_type)
return -EINVAL;
@ -651,6 +661,10 @@ static int check_numeric_sensor_wobj(const union acpi_object *wobj,
/* PossibleStates[0] has already been type-checked. */
for (j = 0; i + 1 < elem_count && j + 1 < count; j++) {
type = elements[++i].type;
if (type == ACPI_TYPE_BUFFER &&
is_raw_wmi_string(elements[i].buffer.pointer,
elements[i].buffer.length))
type = ACPI_TYPE_STRING;
if (type != valid_type)
return -EINVAL;
}
@ -1247,7 +1261,9 @@ static int fungible_show(struct seq_file *seqf, enum hp_wmi_property prop)
break;
case HP_WMI_PROPERTY_CURRENT_STATE:
mutex_lock(&state->lock);
seq_printf(seqf, "%s\n", nsensor->current_state);
mutex_unlock(&state->lock);
break;
case HP_WMI_PROPERTY_UNIT_MODIFIER:

View File

@ -523,7 +523,7 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
}
} else if (boot_cpu_data.x86 == 0x1a) {
switch (boot_cpu_data.x86_model) {
case 0x00 ... 0x2f: /* Zen5 Turin */
case 0x00 ... 0x1f: /* Zen5 Turin */
data->ccd_offset = 0x1F0;
k10temp_get_ccd_support(data, 16);
break;

View File

@ -242,6 +242,7 @@ enum pmbus_regs {
/*
* OPERATION
*/
#define PB_OPERATION_CONTROL_V_SRC GENMASK(5, 4)
#define PB_OPERATION_CONTROL_ON BIT(7)
/*
@ -386,7 +387,7 @@ enum pmbus_sensor_classes {
};
#define PMBUS_PAGES 32 /* Per PMBus specification */
#define PMBUS_PHASES 10 /* Maximum number of phases per page */
#define PMBUS_PHASES 16 /* Maximum number of phases per page */
/* Functionality bit mask */
#define PMBUS_HAVE_VIN BIT(0)

View File

@ -187,7 +187,7 @@ static int tps53676_identify(struct i2c_client *client,
return -EIO;
for (i = 0; i < 2 * TPS53676_MAX_PHASES; i += 2) {
if (buf[i + 1] & 0x80) {
if (buf[i] & 0x08)
if (buf[i] & BIT(4))
phases_b++;
else
phases_a++;
@ -200,6 +200,15 @@ static int tps53676_identify(struct i2c_client *client,
if (phases_b > 0) {
info->pages = 2;
info->phases[1] = phases_b;
} else {
/*
* pmbus_set_page() does not update the PAGE register on
* single-page devices, so select page 0 explicitly in case
* the boot firmware left the device on another page.
*/
ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0);
if (ret < 0)
return ret;
}
return 0;
}

View File

@ -483,7 +483,6 @@ static void pwm_fan_cleanup(void *__ctx)
{
struct pwm_fan_ctx *ctx = __ctx;
timer_delete_sync(&ctx->rpm_timer);
if (ctx->pwm_shutdown) {
ctx->enable_mode = pwm_enable_reg_enable;
__set_pwm(ctx, ctx->pwm_shutdown);
@ -494,6 +493,13 @@ static void pwm_fan_cleanup(void *__ctx)
}
}
static void pwm_fan_timer_cleanup(void *__ctx)
{
struct pwm_fan_ctx *ctx = __ctx;
timer_shutdown_sync(&ctx->rpm_timer);
}
static int pwm_fan_probe(struct platform_device *pdev)
{
struct thermal_cooling_device *cdev;
@ -644,6 +650,10 @@ static int pwm_fan_probe(struct platform_device *pdev)
}
if (ctx->tach_count > 0) {
ret = devm_add_action_or_reset(dev, pwm_fan_timer_cleanup, ctx);
if (ret)
return ret;
ctx->sample_start = ktime_get();
mod_timer(&ctx->rpm_timer, jiffies + HZ);
@ -700,6 +710,7 @@ static void pwm_fan_shutdown(struct platform_device *pdev)
{
struct pwm_fan_ctx *ctx = platform_get_drvdata(pdev);
pwm_fan_timer_cleanup(ctx);
pwm_fan_cleanup(ctx);
}

View File

@ -1415,6 +1415,7 @@ static void w83791d_remove(struct i2c_client *client)
struct w83791d_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &w83791d_group_fanpwm45);
sysfs_remove_group(&client->dev.kobj, &w83791d_group);
}

View File

@ -1928,7 +1928,9 @@ static int w83793_probe(struct i2c_client *client)
for (i = 0; i < ARRAY_SIZE(w83793_temp); i++)
device_remove_file(dev, &w83793_temp[i].dev_attr);
free_mem:
kfree(data);
mutex_lock(&watchdog_data_mutex);
kref_put(&data->kref, w83793_release_resources);
mutex_unlock(&watchdog_data_mutex);
exit:
return err;
}