Skip to content

Commit 59af95e

Browse files
Dapeng MiPeter Zijlstra
authored andcommitted
perf/x86/intel: Add support for rdpmc user disable feature
Starting with Panther Cove, the rdpmc user disable feature is supported. This feature allows the perf system to disable user space rdpmc reads at the counter level. Currently, when a global counter is active, any user with rdpmc rights can read it, even if perf access permissions forbid it (e.g., disallow reading ring 0 counters). The rdpmc user disable feature mitigates this security concern. Details: - A new RDPMC_USR_DISABLE bit (bit 37) in each EVNTSELx MSR indicates that the GP counter cannot be read by RDPMC in ring 3. - New RDPMC_USR_DISABLE bits in IA32_FIXED_CTR_CTRL MSR (bits 33, 37, 41, 45, etc.) for fixed counters 0, 1, 2, 3, etc. - When calling rdpmc instruction for counter x, the following pseudo code demonstrates how the counter value is obtained: If (!CPL0 && RDPMC_USR_DISABLE[x] == 1) ? 0 : counter_value; - RDPMC_USR_DISABLE is enumerated by CPUID.0x23.0.EBX[2]. This patch extends the current global user space rdpmc control logic via the sysfs interface (/sys/devices/cpu/rdpmc) as follows: - rdpmc = 0: Global user space rdpmc and counter-level user space rdpmc for all counters are both disabled. - rdpmc = 1: Global user space rdpmc is enabled during the mmap-enabled time window, and counter-level user space rdpmc is enabled only for non-system-wide events. This prevents counter data leaks as count data is cleared during context switches. - rdpmc = 2: Global user space rdpmc and counter-level user space rdpmc for all counters are enabled unconditionally. The new rdpmc settings only affect newly activated perf events; currently active perf events remain unaffected. This simplifies and cleans up the code. The default value of rdpmc remains unchanged at 1. For more details about rdpmc user disable, please refer to chapter 15 "RDPMC USER DISABLE" in ISE documentation. Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Link: https://patch.msgid.link/20260114011750.350569-8-dapeng1.mi@linux.intel.com
1 parent 8c74e4e commit 59af95e

5 files changed

Lines changed: 104 additions & 2 deletions

File tree

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
What: /sys/bus/event_source/devices/cpu.../rdpmc
2+
Date: November 2011
3+
KernelVersion: 3.10
4+
Contact: Linux kernel mailing list linux-kernel@vger.kernel.org
5+
Description: The /sys/bus/event_source/devices/cpu.../rdpmc attribute
6+
is used to show/manage if rdpmc instruction can be
7+
executed in user space. This attribute supports 3 numbers.
8+
- rdpmc = 0
9+
user space rdpmc is globally disabled for all PMU
10+
counters.
11+
- rdpmc = 1
12+
user space rdpmc is globally enabled only in event mmap
13+
ioctl called time window. If the mmap region is unmapped,
14+
user space rdpmc is disabled again.
15+
- rdpmc = 2
16+
user space rdpmc is globally enabled for all PMU
17+
counters.
18+
19+
In the Intel platforms supporting counter level's user
20+
space rdpmc disable feature (CPUID.23H.EBX[2] = 1), the
21+
meaning of 3 numbers is extended to
22+
- rdpmc = 0
23+
global user space rdpmc and counter level's user space
24+
rdpmc of all counters are both disabled.
25+
- rdpmc = 1
26+
No changes on behavior of global user space rdpmc.
27+
counter level's rdpmc of system-wide events is disabled
28+
but counter level's rdpmc of non-system-wide events is
29+
enabled.
30+
- rdpmc = 2
31+
global user space rdpmc and counter level's user space
32+
rdpmc of all counters are both enabled unconditionally.
33+
34+
The default value of rdpmc is 1.
35+
36+
Please notice:
37+
- global user space rdpmc's behavior would change
38+
immediately along with the rdpmc value's change,
39+
but the behavior of counter level's user space rdpmc
40+
won't take effect immediately until the event is
41+
reactivated or recreated.
42+
- The rdpmc attribute is global, even for x86 hybrid
43+
platforms. For example, changing cpu_core/rdpmc will
44+
also change cpu_atom/rdpmc.

arch/x86/events/core.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2616,6 +2616,27 @@ static ssize_t get_attr_rdpmc(struct device *cdev,
26162616
return snprintf(buf, 40, "%d\n", x86_pmu.attr_rdpmc);
26172617
}
26182618

2619+
/*
2620+
* Behaviors of rdpmc value:
2621+
* - rdpmc = 0
2622+
* global user space rdpmc and counter level's user space rdpmc of all
2623+
* counters are both disabled.
2624+
* - rdpmc = 1
2625+
* global user space rdpmc is enabled in mmap enabled time window and
2626+
* counter level's user space rdpmc is enabled for only non system-wide
2627+
* events. Counter level's user space rdpmc of system-wide events is
2628+
* still disabled by default. This won't introduce counter data leak for
2629+
* non system-wide events since their count data would be cleared when
2630+
* context switches.
2631+
* - rdpmc = 2
2632+
* global user space rdpmc and counter level's user space rdpmc of all
2633+
* counters are enabled unconditionally.
2634+
*
2635+
* Suppose the rdpmc value won't be changed frequently, don't dynamically
2636+
* reschedule events to make the new rpdmc value take effect on active perf
2637+
* events immediately, the new rdpmc value would only impact the new
2638+
* activated perf events. This makes code simpler and cleaner.
2639+
*/
26192640
static ssize_t set_attr_rdpmc(struct device *cdev,
26202641
struct device_attribute *attr,
26212642
const char *buf, size_t count)

arch/x86/events/intel/core.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3128,6 +3128,8 @@ static void intel_pmu_enable_fixed(struct perf_event *event)
31283128
bits |= INTEL_FIXED_0_USER;
31293129
if (hwc->config & ARCH_PERFMON_EVENTSEL_OS)
31303130
bits |= INTEL_FIXED_0_KERNEL;
3131+
if (hwc->config & ARCH_PERFMON_EVENTSEL_RDPMC_USER_DISABLE)
3132+
bits |= INTEL_FIXED_0_RDPMC_USER_DISABLE;
31313133

31323134
/*
31333135
* ANY bit is supported in v3 and up
@@ -3263,6 +3265,27 @@ static void intel_pmu_enable_event_ext(struct perf_event *event)
32633265
__intel_pmu_update_event_ext(hwc->idx, ext);
32643266
}
32653267

3268+
static void intel_pmu_update_rdpmc_user_disable(struct perf_event *event)
3269+
{
3270+
if (!x86_pmu_has_rdpmc_user_disable(event->pmu))
3271+
return;
3272+
3273+
/*
3274+
* Counter scope's user-space rdpmc is disabled by default
3275+
* except two cases.
3276+
* a. rdpmc = 2 (user space rdpmc enabled unconditionally)
3277+
* b. rdpmc = 1 and the event is not a system-wide event.
3278+
* The count of non-system-wide events would be cleared when
3279+
* context switches, so no count data is leaked.
3280+
*/
3281+
if (x86_pmu.attr_rdpmc == X86_USER_RDPMC_ALWAYS_ENABLE ||
3282+
(x86_pmu.attr_rdpmc == X86_USER_RDPMC_CONDITIONAL_ENABLE &&
3283+
event->ctx->task))
3284+
event->hw.config &= ~ARCH_PERFMON_EVENTSEL_RDPMC_USER_DISABLE;
3285+
else
3286+
event->hw.config |= ARCH_PERFMON_EVENTSEL_RDPMC_USER_DISABLE;
3287+
}
3288+
32663289
DEFINE_STATIC_CALL_NULL(intel_pmu_enable_event_ext, intel_pmu_enable_event_ext);
32673290

32683291
static void intel_pmu_enable_event(struct perf_event *event)
@@ -3271,6 +3294,8 @@ static void intel_pmu_enable_event(struct perf_event *event)
32713294
struct hw_perf_event *hwc = &event->hw;
32723295
int idx = hwc->idx;
32733296

3297+
intel_pmu_update_rdpmc_user_disable(event);
3298+
32743299
if (unlikely(event->attr.precise_ip))
32753300
static_call(x86_pmu_pebs_enable)(event);
32763301

@@ -5869,6 +5894,8 @@ static void update_pmu_cap(struct pmu *pmu)
58695894
hybrid(pmu, config_mask) |= ARCH_PERFMON_EVENTSEL_UMASK2;
58705895
if (ebx_0.split.eq)
58715896
hybrid(pmu, config_mask) |= ARCH_PERFMON_EVENTSEL_EQ;
5897+
if (ebx_0.split.rdpmc_user_disable)
5898+
hybrid(pmu, config_mask) |= ARCH_PERFMON_EVENTSEL_RDPMC_USER_DISABLE;
58725899

58735900
if (eax_0.split.cntr_subleaf) {
58745901
cpuid_count(ARCH_PERFMON_EXT_LEAF, ARCH_PERFMON_NUM_COUNTER_LEAF,

arch/x86/events/perf_event.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1333,6 +1333,12 @@ static inline u64 x86_pmu_get_event_config(struct perf_event *event)
13331333
return event->attr.config & hybrid(event->pmu, config_mask);
13341334
}
13351335

1336+
static inline bool x86_pmu_has_rdpmc_user_disable(struct pmu *pmu)
1337+
{
1338+
return !!(hybrid(pmu, config_mask) &
1339+
ARCH_PERFMON_EVENTSEL_RDPMC_USER_DISABLE);
1340+
}
1341+
13361342
extern struct event_constraint emptyconstraint;
13371343

13381344
extern struct event_constraint unconstrained;

arch/x86/include/asm/perf_event.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,15 @@
3333
#define ARCH_PERFMON_EVENTSEL_CMASK 0xFF000000ULL
3434
#define ARCH_PERFMON_EVENTSEL_BR_CNTR (1ULL << 35)
3535
#define ARCH_PERFMON_EVENTSEL_EQ (1ULL << 36)
36+
#define ARCH_PERFMON_EVENTSEL_RDPMC_USER_DISABLE (1ULL << 37)
3637
#define ARCH_PERFMON_EVENTSEL_UMASK2 (0xFFULL << 40)
3738

3839
#define INTEL_FIXED_BITS_STRIDE 4
3940
#define INTEL_FIXED_0_KERNEL (1ULL << 0)
4041
#define INTEL_FIXED_0_USER (1ULL << 1)
4142
#define INTEL_FIXED_0_ANYTHREAD (1ULL << 2)
4243
#define INTEL_FIXED_0_ENABLE_PMI (1ULL << 3)
44+
#define INTEL_FIXED_0_RDPMC_USER_DISABLE (1ULL << 33)
4345
#define INTEL_FIXED_3_METRICS_CLEAR (1ULL << 2)
4446

4547
#define HSW_IN_TX (1ULL << 32)
@@ -50,7 +52,7 @@
5052
#define INTEL_FIXED_BITS_MASK \
5153
(INTEL_FIXED_0_KERNEL | INTEL_FIXED_0_USER | \
5254
INTEL_FIXED_0_ANYTHREAD | INTEL_FIXED_0_ENABLE_PMI | \
53-
ICL_FIXED_0_ADAPTIVE)
55+
ICL_FIXED_0_ADAPTIVE | INTEL_FIXED_0_RDPMC_USER_DISABLE)
5456

5557
#define intel_fixed_bits_by_idx(_idx, _bits) \
5658
((_bits) << ((_idx) * INTEL_FIXED_BITS_STRIDE))
@@ -226,7 +228,9 @@ union cpuid35_ebx {
226228
unsigned int umask2:1;
227229
/* EQ-bit Supported */
228230
unsigned int eq:1;
229-
unsigned int reserved:30;
231+
/* rdpmc user disable Supported */
232+
unsigned int rdpmc_user_disable:1;
233+
unsigned int reserved:29;
230234
} split;
231235
unsigned int full;
232236
};

0 commit comments

Comments
 (0)