You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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");

编译与加载步骤

  1. 创建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
  1. 编译模块:执行make命令
  2. 加载模块:sudo insmod pmu_enable.ko
  3. 验证配置: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 07:10:31