【问题标题】:Illegal Instruction - Kernel Modules非法指令 - 内核模块
【发布时间】:2018-07-23 17:04:27
【问题描述】:

我正在尝试遵循这些说明:

https://developer.arm.com/products/system-design/cycle-models/knowledge-articles/system-performance-analysis-and-the-arm-performance-monitor-unit

我的目标是为 PMU 启用用户空间访问。这些是我正在使用的模块的一些说明。

static void enable_cpu_counters(void* data)                                                                         
{                                                                                                       
    asm volatile("msr pmuserenr_el0, %0" :: "r"(0xf));
    armv8pmu_pmcr_write (ARMV8_PMCR_LC|ARMV8_PMCR_E);                                                      
    asm volatile("msr PMCNTENSET_EL0, %0" :: "r" ((u32)(1<<31)));
    armv8pmu_pmcr_write(armv8pmu_pmcr_read() | ARMV8_PMCR_E|ARMV8_PMCR_LC);   
    printk("\nCPU:%d \n", smp_processor_id());
}                                                                                                       

static void disable_cpu_counters (void* data)                                                                        
{                                                                                                                                                                                                   
    printk(KERN_INFO "\ndisabling user-mode PMU access on CPU #%d \n",                       
    smp_processor_id());
    /* Program PMU and disable all counters */                                                            
    armv8pmu_pmcr_write(armv8pmu_pmcr_read() |~ARMV8_PMCR_E);                                              
    asm volatile("msr pmuserenr_el0, %0" ::"r"((u64)0));                                                 
}                                                                                                       

static int __init init(void)                                                                                              
{
    unsigned int reguser=0;     

    isb();

    asm volatile("mrs %0, pmcr_el0" : "=r" (reguser));
    printk(KERN_INFO "\nPMCR_EL0 register before : %x\n", reguser);
    asm volatile("mrs %0, pmuserenr_el0" : "=r" (reguser));
    printk(KERN_INFO "\nPMUSERENR register before : %x\n", reguser);

on_each_cpu (enable_cpu_counters, NULL, 1);                                                             
    printk(KERN_INFO "\nEnable Access PMU Initialized\n");

    /* Enable counters */
    asm volatile("mrs %0, pmcr_el0" : "=r" (reguser));
    printk(KERN_INFO "\nPMCR_EL0 register after : %x\n", reguser);
    asm volatile("mrs %0, pmuserenr_el0" : "=r" (reguser));
    printk(KERN_INFO "\nPMUSERENR register after : %x\n", reguser);

    return 0;                                                                                              
}                                                                                                       

static void __exit fini(void)                                                                                              
{                                                                                                       
    on_each_cpu(disable_cpu_counters, NULL, 1);                                                            
    printk(KERN_INFO "\nAccess PMU Disabled\n");                                                          
}                                                                                                       

module_init (init);                                                                                      
module_exit (fini);

输出(dmesg):

[79371.445026] \x0aPMCR_EL0 register before : 41023040
[79371.450997] \x0aPMUSERENR register before : 0
[79371.456511] \x0aCPU:3
[79371.456546] \x0aCPU:2
[79371.456614] \x0aCPU:0
[79371.456631] \x0aCPU:4
[79371.456652] \x0aCPU:5
[79371.473784] \x0aCPU:1
[79371.477254] \x0aEnable Access PMU Initialized
[79371.482693] \x0aPMCR_EL0 register after : 41023001
[79371.488560] \x0aPMUSERENR register after : f

接着是第二个模块,这次只从寄存器中读取值。

模块(测试它是否保留寄存器中的值):

static int __init init(void)                                                                                              
{        
    unsigned int reguser=0;                                                               
    isb();

    /* Enable counters */
    asm volatile("mrs %0, pmcr_el0" : "=r" (reguser));
    printk(KERN_INFO "\nPMCR_EL0 register : %x\n", reguser);
    asm volatile("mrs %0, pmuserenr_el0" : "=r" (reguser));
    printk(KERN_INFO "\nPMUSERENR register : %x\n", reguser);

    return 0;                                                                                              
}                                                                                                       

static void __exit fini(void)                                                                                              
{                                                                                                                                                         
    printk(KERN_INFO "\nDisabling read_arm_pmu.ko\n");                                                          
}                                                                                                       

module_init (init);                                                                                      
module_exit (fini);

输出(dmesg):

[79385.429198] \x0aPMCR_EL0 register : 41023040
[79385.434584] \x0aPMUSERENR register : 0

用一个简单的程序在用户空间测试功能给出:

juno:/data/data/papi/workplace # ./monitoring 2 2 2 3
[1359092.706711] monitoring[11095]: undefined instruction: pc=000000000040065c
[1359092.713652] Code: 00000000 00000000 00000000 d10043ff (d53b9c00)
Illegal instruction

注意:我知道这可能是因为 PMCR_EL0 的第一位是 0 而 PMUSERENR 不是 f。不知道如何做到这一点,以便模块不会从寄存器中更改回值...


可能有助于调试的有用命令:

juno:/ # cat /proc/modules

read_arm_pmu 16384 0 - 实时 0x0000000000000000 (PO)

enable_arm_pmu 16384 0 - 实时 0x0000000000000000 (PO)

juno:/ # cat /proc/devices

字符设备: 1 个内存 5 /dev/tty 5 /开发/控制台 5 /dev/ptmx 10 杂项 13输入 14声 29 脸书 90 吨 108 pp 第116章 128 分 136 分 180个USB 第189章 第204章 226 平方米 第249章 250 抽头 第251话 252 三通 253 实时时钟 254gpio芯片

块设备: 1 个虚拟磁盘 第259章 7 循环 8 标准差 31 mtdblock 65 标准差 66 标准差 67 标准差 68 标准差 69 标准差 70 标准差 71 标准差 128 标准差 129 标准差 130 标准差 131 标准差 132 标准差 133 标准差 134 标准差 135 标准差 179毫米 253 设备映射器 第254章

cat /proc/sys/kernel/tainted

4097

juno:/ # cat /proc/interrupts

CPU0 CPU1 CPU2 CPU3 CPU4 CPU5

2:1844410 1197983 1353732 16779 3199 5419 GIC v2 92级arch_mem_timer

3: 0 0 0 0 0 0 GIC v2 29 级arch_timer

4:894927 272142 288023 18616 39333 15390 GIC v2 30级arch_timer

7: 0 0 0 0 0 0 GIC v2 198 级别计时器

14: 419017 0 0 0 0 0 GIC v2 68 级 mhu_link

15: 0 0 0 0 0 0 GIC v2 67 级别 mhu_link

16: 0 0 0 0 0 0 GIC v2 120 级别 7ff00000.dma

17: 0 0 0 0 0 0 GIC v2 121 级别 7ff00000.dma

18: 0 0 0 0 0 0 GIC v2 122 级别 7ff00000.dma

19: 0 0 0 0 0 0 GIC v2 123 级别 7ff00000.dma

20: 0 0 0 0 0 0 GIC v2 124 级别 7ff00000.dma

21: 0 0 0 0 0 0 GIC v2 140 级别 7ff00000.dma

22: 0 0 0 0 0 0 GIC v2 141 级别 7ff00000.dma

23: 0 0 0 0 0 0 GIC v2 142 级别 7ff00000.dma

24: 0 0 0 0 0 0 GIC v2 143 级别 7ff00000.dma

25: 0 0 0 0 0 0 GIC v2 125 级 hdlcd

26: 0 0 0 0 0 0 GIC v2 117 级 hdlcd

27: 6336 0 0 0 0 0 GIC v2 115 级 uart-pl011

28: 53666 0 0 0 0 0 GIC v2 136 级别 7ffa0000.i2c

30: 47345 0 0 0 0 0 GIC v2 149 级别 ehci_hcd:usb1

33: 14370 0 0 0 0 0 GIC v2 65 级别 2d000000.gpu

34: 0 0 0 0 0 0 GIC v2 66 级别 2d000000.gpu

35: 39222 0 0 0 0 0 GIC v2 64 级别 2d000000.gpu

37: 52 0 0 0 0 0 GIC v2 194 级别 mmci-pl18x (cmd)

40: 0 0 0 0 0 0 GIC v2 100 级别 rtc-pl031

43: 0 0 0 0 0 0 GIC v2 169 级别 sata_sil24[0000:03:00.0]

45: 0 0 0 0 0 0 M SI 0 边缘 PCIe PME, aerdrv

52: 1049234 0 0 0 0 0 M SI 4194304 边缘 eth0

IPI0: 62569 1835646 1888285 43688 29194 29798 重新调度中断

IPI1:315 1276 725 386 307 207 函数调用中断

IPI2: 0 0 0 0 0 0 C PU 停止中断

IPI3:829962 24938 62500 1274 498 1029 定时器广播中断

IPI4:800250 925302 1266671 10821 9264 7192 I RQ 工作中断

IPI5: 0 0 0 0 0 0 C PU 唤醒中断

错误:0

我希望你们能帮助我,感觉我在一些可能很简单的事情上浪费了很多时间。

TLDR - 内核模块应该启用用户空间访问,但它不工作。寻求帮助以了解原因。

谢谢, 路易斯


编辑:格式化,让我的问题更清晰。

【问题讨论】:

    标签: module linux-kernel arm arm64 illegal-instruction


    【解决方案1】:

    FWIW,我已经成功地在旧开发板上使用了内核模块技巧,但在新开发板上我遇到了完全相同的问题。

    https://devtalk.nvidia.com/default/topic/955554/jetson-tx1/performance-counters-reset-itself/ 中建议禁用CONFIG_CPU_IDLE 可能会有所帮助,这对我确实有帮助。禁用该功能后,我可以通过内核模块在pmuserenr_el0 中设置位,并通过用户空间启用/使用计数器。

    但是,我已经成功地在 Dragonboard 410c 上使用了内核模块,该内核模块运行 Debian 83 映像,它运行 debian-qcom-dragonboard410c-16.04 内核。该特定内核还启用了CONFIG_CPU_IDLE,因此显然某些地方发生了变化,使得CONFIG_CPU_IDLE 中断了PMU 访问。

    出于好奇,我还测试了调整reset_pmuserenr_el0 宏以将pmuserenr_el0 设置为0xf 而不是清除它,看看我是否可以保持CONFIG_CPU_IDLE 启用。这确实允许我从用户空间访问 PMU 寄存器,但任何基准测试都是不可能的,因为计数器最终会一直停止。

    不幸的是,禁用 CONFIG_CPU_IDLE 需要您重新构建内核 - 仅加载一个新模块是不够的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-10-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多