ARM Cortex-A53下如何设置PMUSERENR_EL0.EN以读取PMCR_EL0?
解决ARM Cortex-A53用户态访问PMCR_EL0的问题
核心操作逻辑
要让EL0(用户态)正常访问PMCR_EL0,必须在EL1(内核态)完成两项关键配置:
- 将
PMUSERENR_EL0的EN位(第0位)置1,开放用户态对性能监控寄存器的访问权限 - 将
PMCR_EL0的E位(第0位)置1,启用性能监控单元(PMU);同时确保DP位(第3位)为0,避免深度睡眠时PMU被禁用
正确的内核模块实现
以下是可直接编译使用的内核模块代码,完成寄存器的配置与验证:
#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> static int __init pmu_enable_init(void) { unsigned long pmuserenr; unsigned long pmcr; // 配置PMUSERENR_EL0:置位EN位(bit0) asm volatile("mrs %0, pmuserenr_el0" : "=r" (pmuserenr)); pmuserenr |= 0x1; asm volatile("msr pmuserenr_el0, %0" : : "r" (pmuserenr)); // 配置PMCR_EL0:置位E位启用PMU,清除DP位避免睡眠禁用 asm volatile("mrs %0, pmcr_el0" : "=r" (pmcr)); pmcr |= 0x1; pmcr &= ~(0x8); asm volatile("msr pmcr_el0, %0" : : "r" (pmcr)); // 输出配置结果到内核日志 asm volatile("mrs %0, pmuserenr_el0" : "=r" (pmuserenr)); pr_info("PMUSERENR_EL0 after setup: 0x%lx\n", pmuserenr); asm volatile("mrs %0, pmcr_el0" : "=r" (pmcr)); pr_info("PMCR_EL0 after setup: 0x%lx\n", pmcr); return 0; } static void __exit pmu_enable_exit(void) { // 可选:卸载模块时恢复寄存器默认状态 unsigned long pmuserenr; asm volatile("mrs %0, pmuserenr_el0" : "=r" (pmuserenr)); pmuserenr &= ~0x1; asm volatile("msr pmuserenr_el0, %0" : : "r" (pmuserenr)); pr_info("PMUSERENR_EL0 restored to default\n"); } module_init(pmu_enable_init); module_exit(pmu_enable_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Enable user-mode access to PMCR_EL0 on Cortex-A53");
编译与加载步骤
- 创建Makefile:
obj-m += pmu_enable.o KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: $(MAKE) -C $(KDIR) M=$(PWD) modules clean: $(MAKE) -C $(KDIR) M=$(PWD) clean
- 编译模块:执行
make命令 - 加载模块:
sudo insmod pmu_enable.ko - 验证配置:
dmesg | tail,查看内核日志中输出的寄存器值,确认PMUSERENR_EL0的第0位已置1
用户态测试程序
使用以下代码验证用户态是否能正常读取PMCR_EL0:
#include <stdio.h> int main() { unsigned long pmcr; asm volatile("mrs %0, pmcr_el0" : "=r" (pmcr)); printf("PMCR_EL0 value: 0x%lx\n", pmcr); return 0; }
编译:gcc -o test_pmcr test_pmcr.c,运行:./test_pmcr,此时应能正常输出寄存器值,无非法指令异常。
常见问题排查
- 若模块加载后寄存器值仍为0:检查内核是否启用
CONFIG_PERF_EVENTS,树莓派3B的64位Raspbian内核默认启用该选项,若未开启需重新编译内核配置。 - 注意寄存器名称拼写:你之前提到的
PMUSERENR_EN0是拼写错误,正确名称为PMUSERENR_EL0,拼写错误会导致读取无效值。 - 内核模块默认运行在EL1,直接使用
msr/mrs指令修改寄存器即可,无需额外权限切换。
内容的提问来源于stack exchange,提问作者WSWS
相关产品推荐
相关产品推荐

