Skip to content

Commit 60949b7

Browse files
clementlegerrafaeljw
authored andcommitted
ACPI: CPPC: Fix MASK_VAL() usage
MASK_VAL() was added as a way to handle bit_offset and bit_width for registers located in system memory address space. However, while suited for reading, it does not work for writing and result in corrupted registers when writing values with bit_offset > 0. Moreover, when a register is collocated with another one at the same address but with a different mask, the current code results in the other registers being overwritten with 0s. The write procedure for SYSTEM_MEMORY registers should actually read the value, mask it, update it and write it with the updated value. Moreover, since registers can be located in the same word, we must take care of locking the access before doing it. We should potentially use a global lock since we don't know in if register addresses aren't shared with another _CPC package but better not encourage vendors to do so. Assume that registers can use the same word inside a _CPC package and thus, use a per _CPC package lock. Fixes: 2f4a4d6 ("ACPI: CPPC: Use access_width over bit_width for system memory accesses") Signed-off-by: Clément Léger <[email protected]> Link: https://patch.msgid.link/[email protected] [ rjw: Dropped redundant semicolon ] Signed-off-by: Rafael J. Wysocki <[email protected]>
1 parent 5be63fc commit 60949b7

File tree

2 files changed

+41
-4
lines changed

2 files changed

+41
-4
lines changed

drivers/acpi/cppc_acpi.c

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,11 @@ show_cppc_data(cppc_get_perf_ctrs, cppc_perf_fb_ctrs, wraparound_time);
171171
#define GET_BIT_WIDTH(reg) ((reg)->access_width ? (8 << ((reg)->access_width - 1)) : (reg)->bit_width)
172172

173173
/* Shift and apply the mask for CPC reads/writes */
174-
#define MASK_VAL(reg, val) (((val) >> (reg)->bit_offset) & \
174+
#define MASK_VAL_READ(reg, val) (((val) >> (reg)->bit_offset) & \
175175
GENMASK(((reg)->bit_width) - 1, 0))
176+
#define MASK_VAL_WRITE(reg, prev_val, val) \
177+
((((val) & GENMASK(((reg)->bit_width) - 1, 0)) << (reg)->bit_offset) | \
178+
((prev_val) & ~(GENMASK(((reg)->bit_width) - 1, 0) << (reg)->bit_offset))) \
176179

177180
static ssize_t show_feedback_ctrs(struct kobject *kobj,
178181
struct kobj_attribute *attr, char *buf)
@@ -859,6 +862,7 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
859862

860863
/* Store CPU Logical ID */
861864
cpc_ptr->cpu_id = pr->id;
865+
spin_lock_init(&cpc_ptr->rmw_lock);
862866

863867
/* Parse PSD data for this CPU */
864868
ret = acpi_get_psd(cpc_ptr, handle);
@@ -1064,7 +1068,7 @@ static int cpc_read(int cpu, struct cpc_register_resource *reg_res, u64 *val)
10641068
}
10651069

10661070
if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
1067-
*val = MASK_VAL(reg, *val);
1071+
*val = MASK_VAL_READ(reg, *val);
10681072

10691073
return 0;
10701074
}
@@ -1073,9 +1077,11 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val)
10731077
{
10741078
int ret_val = 0;
10751079
int size;
1080+
u64 prev_val;
10761081
void __iomem *vaddr = NULL;
10771082
int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
10781083
struct cpc_reg *reg = &reg_res->cpc_entry.reg;
1084+
struct cpc_desc *cpc_desc;
10791085

10801086
size = GET_BIT_WIDTH(reg);
10811087

@@ -1108,8 +1114,34 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val)
11081114
return acpi_os_write_memory((acpi_physical_address)reg->address,
11091115
val, size);
11101116

1111-
if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
1112-
val = MASK_VAL(reg, val);
1117+
if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
1118+
cpc_desc = per_cpu(cpc_desc_ptr, cpu);
1119+
if (!cpc_desc) {
1120+
pr_debug("No CPC descriptor for CPU:%d\n", cpu);
1121+
return -ENODEV;
1122+
}
1123+
1124+
spin_lock(&cpc_desc->rmw_lock);
1125+
switch (size) {
1126+
case 8:
1127+
prev_val = readb_relaxed(vaddr);
1128+
break;
1129+
case 16:
1130+
prev_val = readw_relaxed(vaddr);
1131+
break;
1132+
case 32:
1133+
prev_val = readl_relaxed(vaddr);
1134+
break;
1135+
case 64:
1136+
prev_val = readq_relaxed(vaddr);
1137+
break;
1138+
default:
1139+
spin_unlock(&cpc_desc->rmw_lock);
1140+
return -EFAULT;
1141+
}
1142+
val = MASK_VAL_WRITE(reg, prev_val, val);
1143+
val |= prev_val;
1144+
}
11131145

11141146
switch (size) {
11151147
case 8:
@@ -1136,6 +1168,9 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val)
11361168
break;
11371169
}
11381170

1171+
if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
1172+
spin_unlock(&cpc_desc->rmw_lock);
1173+
11391174
return ret_val;
11401175
}
11411176

include/acpi/cppc_acpi.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ struct cpc_desc {
6464
int cpu_id;
6565
int write_cmd_status;
6666
int write_cmd_id;
67+
/* Lock used for RMW operations in cpc_write() */
68+
spinlock_t rmw_lock;
6769
struct cpc_register_resource cpc_regs[MAX_CPC_REG_ENT];
6870
struct acpi_psd_package domain_info;
6971
struct kobject kobj;

0 commit comments

Comments
 (0)