summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/amd/pm/amdgpu_pm.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/amd/pm/amdgpu_pm.c')
-rw-r--r--drivers/gpu/drm/amd/pm/amdgpu_pm.c103
1 files changed, 76 insertions, 27 deletions
diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c
index 736304e73ca4..97da01aff76c 100644
--- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c
+++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c
@@ -41,6 +41,8 @@
#define DEVICE_ATTR_IS(_name) (attr_id == device_attr_id__##_name)
+#define power_2_mwatt(power) (((power) >> 8) * 1000 + ((power) & 0xff))
+
struct od_attribute {
struct kobj_attribute attribute;
struct list_head entry;
@@ -244,6 +246,10 @@ static ssize_t amdgpu_set_power_dpm_state(struct device *dev,
enum amd_pm_state_type state;
int ret;
+ /* Reject empty/whitespace strings - fuzzing found this is not validated */
+ if (count == 0 || sysfs_streq(buf, ""))
+ return -EINVAL;
+
if (sysfs_streq(buf, "battery"))
state = POWER_STATE_TYPE_BATTERY;
else if (sysfs_streq(buf, "balanced"))
@@ -364,6 +370,10 @@ static ssize_t amdgpu_set_power_dpm_force_performance_level(struct device *dev,
enum amd_dpm_forced_level level;
int ret = 0;
+ /* Reject empty/whitespace strings - fuzzing found this is not validated */
+ if (count == 0 || sysfs_streq(buf, ""))
+ return -EINVAL;
+
if (sysfs_streq(buf, "low"))
level = AMD_DPM_FORCED_LEVEL_LOW;
else if (sysfs_streq(buf, "high"))
@@ -798,6 +808,8 @@ static ssize_t amdgpu_set_pp_od_clk_voltage(struct device *dev,
while ((sub_str = strsep(&tmp_str, delimiter)) != NULL) {
if (strlen(sub_str) == 0)
continue;
+ if (parameter_size >= ARRAY_SIZE(parameter))
+ return -EINVAL;
ret = kstrtol(sub_str, 0, &parameter[parameter_size]);
if (ret)
return -EINVAL;
@@ -866,6 +878,8 @@ static ssize_t amdgpu_get_pp_od_clk_voltage(struct device *dev,
for (clk_index = 0 ; clk_index < ARRAY_SIZE(od_clocks) ; clk_index++) {
amdgpu_dpm_emit_clock_levels(adev, od_clocks[clk_index], buf, &size);
+ if (unlikely(size >= (PAGE_SIZE - 1)))
+ break;
}
if (size == 0)
@@ -902,6 +916,10 @@ static ssize_t amdgpu_set_pp_features(struct device *dev,
uint64_t featuremask;
int ret;
+ /* Reject empty/whitespace strings - fuzzing found kstrtou64 accepts "" as 0 */
+ if (count == 0 || sysfs_streq(buf, ""))
+ return -EINVAL;
+
ret = kstrtou64(buf, 0, &featuremask);
if (ret)
return -EINVAL;
@@ -1027,6 +1045,10 @@ static ssize_t amdgpu_read_mask(const char *buf, size_t count, uint32_t *mask)
*mask = 0;
+ /* Reject empty/whitespace strings - fuzzing found this is not validated */
+ if (count == 0 || sysfs_streq(buf, ""))
+ return -EINVAL;
+
bytes = min(count, sizeof(buf_cpy) - 1);
memcpy(buf_cpy, buf, bytes);
buf_cpy[bytes] = '\0';
@@ -1373,23 +1395,26 @@ static ssize_t amdgpu_set_pp_power_profile_mode(struct device *dev,
long parameter[64];
char *sub_str, buf_cpy[128];
char *tmp_str;
- uint32_t i = 0;
char tmp[2];
long int profile_mode = 0;
const char delimiter[3] = {' ', '\n', '\0'};
- tmp[0] = *(buf);
+ /* Reject empty/whitespace strings - fuzzing found this is not validated */
+ if (count == 0 || sysfs_streq(buf, ""))
+ return -EINVAL;
+
+ tmp[0] = *(buf++);
tmp[1] = '\0';
ret = kstrtol(tmp, 0, &profile_mode);
if (ret)
return -EINVAL;
if (profile_mode == PP_SMC_POWER_PROFILE_CUSTOM) {
- if (count < 2 || count > 127)
+ if (count < 2 || count > sizeof(buf_cpy))
return -EINVAL;
- while (isspace(*++buf))
- i++;
- memcpy(buf_cpy, buf, count-i);
+ while (isspace(*buf))
+ buf++;
+ strscpy(buf_cpy, buf, sizeof(buf_cpy));
tmp_str = buf_cpy;
while ((sub_str = strsep(&tmp_str, delimiter)) != NULL) {
if (strlen(sub_str) == 0)
@@ -1614,6 +1639,10 @@ static ssize_t amdgpu_set_thermal_throttling_logging(struct device *dev,
if (ret)
return ret;
+ /* Reject negative values - only 0 (disable) or 1-3600 (seconds) are valid */
+ if (throttling_logging_interval < 0)
+ return -EINVAL;
+
if (throttling_logging_interval > 3600)
return -EINVAL;
@@ -1862,12 +1891,12 @@ static ssize_t amdgpu_set_smartshift_bias(struct device *dev,
{
struct drm_device *ddev = dev_get_drvdata(dev);
struct amdgpu_device *adev = drm_to_adev(ddev);
- int r = 0;
+ int r;
int bias = 0;
r = kstrtoint(buf, 10, &bias);
if (r)
- goto out;
+ return r;
r = amdgpu_pm_get_access(adev);
if (r < 0)
@@ -1879,14 +1908,12 @@ static ssize_t amdgpu_set_smartshift_bias(struct device *dev,
bias = AMDGPU_SMARTSHIFT_MIN_BIAS;
amdgpu_smartshift_bias = bias;
- r = count;
/* TODO: update bias level with SMU message */
-out:
amdgpu_pm_put_access(adev);
- return r;
+ return count;
}
static int ss_power_attr_update(struct amdgpu_device *adev, struct amdgpu_device_attr *attr,
@@ -2034,15 +2061,13 @@ static int pp_dpm_clk_default_attr_update(struct amdgpu_device *adev, struct amd
gc_ver == IP_VERSION(11, 0, 3)) && adev->vcn.num_vcn_inst >= 2))
*states = ATTR_STATE_UNSUPPORTED;
} else if (DEVICE_ATTR_IS(pp_dpm_pcie)) {
- if (gc_ver == IP_VERSION(9, 4, 2) ||
- amdgpu_is_multi_aid(adev))
+ if (amdgpu_is_multi_aid(adev))
*states = ATTR_STATE_UNSUPPORTED;
}
switch (gc_ver) {
case IP_VERSION(9, 4, 1):
- case IP_VERSION(9, 4, 2):
- /* the Mi series card does not support standalone mclk/socclk/fclk level setting */
+ /* Arcturus does not support standalone mclk/socclk/fclk level setting */
if (DEVICE_ATTR_IS(pp_dpm_mclk) ||
DEVICE_ATTR_IS(pp_dpm_socclk) ||
DEVICE_ATTR_IS(pp_dpm_fclk)) {
@@ -2050,6 +2075,19 @@ static int pp_dpm_clk_default_attr_update(struct amdgpu_device *adev, struct amd
dev_attr->store = NULL;
}
break;
+ case IP_VERSION(9, 4, 2):
+ if (DEVICE_ATTR_IS(pp_dpm_mclk) ||
+ DEVICE_ATTR_IS(pp_dpm_socclk)) {
+ /* Aldebaran mclk/socclk DPM only supports voltage control,
+ * not allow to set dpm level directly */
+ dev_attr->attr.mode &= ~S_IWUGO;
+ dev_attr->store = NULL;
+ } else if (DEVICE_ATTR_IS(pp_dpm_fclk) ||
+ DEVICE_ATTR_IS(pp_dpm_pcie)) {
+ /* Aldebaran does not support fclk/pcie dpm */
+ *states = ATTR_STATE_UNSUPPORTED;
+ }
+ break;
default:
break;
}
@@ -2505,12 +2543,12 @@ static ssize_t amdgpu_set_pm_policy_attr(struct device *dev,
.dev_attr = __ATTR(_name, 0644, amdgpu_get_pm_policy_attr, \
amdgpu_set_pm_policy_attr), \
.id = PP_PM_POLICY_##_id, \
- };
+ }
#define AMDGPU_PM_POLICY_ATTR_VAR(_name) pm_policy_attr_##_name.dev_attr.attr
-AMDGPU_PM_POLICY_ATTR(soc_pstate, SOC_PSTATE)
-AMDGPU_PM_POLICY_ATTR(xgmi_plpd, XGMI_PLPD)
+AMDGPU_PM_POLICY_ATTR(soc_pstate, SOC_PSTATE);
+AMDGPU_PM_POLICY_ATTR(xgmi_plpd, XGMI_PLPD);
static struct attribute *pm_policy_attrs[] = {
&AMDGPU_PM_POLICY_ATTR_VAR(soc_pstate),
@@ -2660,6 +2698,11 @@ static int default_attr_update(struct amdgpu_device *adev, struct amdgpu_device_
gc_ver != IP_VERSION(9, 4, 3)) ||
gc_ver < IP_VERSION(9, 0, 0))
*states = ATTR_STATE_UNSUPPORTED;
+
+ if (adev->scpm_enabled) {
+ dev_attr->attr.mode &= ~S_IWUGO;
+ dev_attr->store = NULL;
+ }
} else if (DEVICE_ATTR_IS(gpu_metrics)) {
if (gc_ver < IP_VERSION(9, 1, 0))
*states = ATTR_STATE_UNSUPPORTED;
@@ -3313,7 +3356,6 @@ static int amdgpu_hwmon_get_power(struct device *dev,
enum amd_pp_sensors sensor)
{
struct amdgpu_device *adev = dev_get_drvdata(dev);
- unsigned int uw;
u32 query = 0;
int r;
@@ -3322,9 +3364,7 @@ static int amdgpu_hwmon_get_power(struct device *dev,
return r;
/* convert to microwatts */
- uw = (query >> 8) * 1000000 + (query & 0xff) * 1000;
-
- return uw;
+ return power_2_mwatt(query) * 1000;
}
static ssize_t amdgpu_hwmon_show_power_avg(struct device *dev,
@@ -3922,6 +3962,7 @@ static int parse_input_od_command_lines(const char *buf,
size_t count,
u32 *type,
long *params,
+ size_t params_max,
uint32_t *num_of_params)
{
const char delimiter[3] = {' ', '\n', '\0'};
@@ -3957,6 +3998,9 @@ static int parse_input_od_command_lines(const char *buf,
if (strlen(sub_str) == 0)
continue;
+ if (parameter_size >= params_max)
+ return -EINVAL;
+
ret = kstrtol(sub_str, 0, &params[parameter_size]);
if (ret)
return -EINVAL;
@@ -3988,6 +4032,7 @@ amdgpu_distribute_custom_od_settings(struct amdgpu_device *adev,
count,
&cmd_type,
parameter,
+ ARRAY_SIZE(parameter),
&parameter_size);
if (ret)
return ret;
@@ -4862,7 +4907,7 @@ static int amdgpu_debugfs_pm_info_pp(struct seq_file *m, struct amdgpu_device *a
{
uint32_t mp1_ver = amdgpu_ip_version(adev, MP1_HWIP, 0);
uint32_t gc_ver = amdgpu_ip_version(adev, GC_HWIP, 0);
- uint32_t value;
+ uint32_t value, mwatt, centiwatt;
uint64_t value64 = 0;
uint32_t query = 0;
int size;
@@ -4887,17 +4932,21 @@ static int amdgpu_debugfs_pm_info_pp(struct seq_file *m, struct amdgpu_device *a
seq_printf(m, "\t%u mV (VDDNB)\n", value);
size = sizeof(uint32_t);
if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_AVG_POWER, (void *)&query, &size)) {
+ mwatt = power_2_mwatt(query);
+ centiwatt = DIV_ROUND_CLOSEST(mwatt, 10);
if (adev->flags & AMD_IS_APU)
- seq_printf(m, "\t%u.%02u W (average SoC including CPU)\n", query >> 8, query & 0xff);
+ seq_printf(m, "\t%u.%02u W (average SoC including CPU)\n", centiwatt / 100, centiwatt % 100);
else
- seq_printf(m, "\t%u.%02u W (average SoC)\n", query >> 8, query & 0xff);
+ seq_printf(m, "\t%u.%02u W (average SoC)\n", centiwatt / 100, centiwatt % 100);
}
size = sizeof(uint32_t);
if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_INPUT_POWER, (void *)&query, &size)) {
+ mwatt = power_2_mwatt(query);
+ centiwatt = DIV_ROUND_CLOSEST(mwatt, 10);
if (adev->flags & AMD_IS_APU)
- seq_printf(m, "\t%u.%02u W (current SoC including CPU)\n", query >> 8, query & 0xff);
+ seq_printf(m, "\t%u.%02u W (current SoC including CPU)\n", centiwatt / 100, centiwatt % 100);
else
- seq_printf(m, "\t%u.%02u W (current SoC)\n", query >> 8, query & 0xff);
+ seq_printf(m, "\t%u.%02u W (current SoC)\n", centiwatt / 100, centiwatt % 100);
}
size = sizeof(value);
seq_printf(m, "\n");