非法指令-内核模块

时间:2018-07-23 17:04:27

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

我正在尝试遵循以下说明:

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 / dev /控制台 5 / dev / ptmx 10杂项 13输入 14声音 29磅 90吨 108 ppp 116 alsa 128点 136分 180 USB 189 usb_device 第204章 226台 249 roccat 250人 251桶 252三通 253 rtc 254 gpiochip

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

猫/ proc / sys / kernel /污染的

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 Edge eth0

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

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 -内核模块应启用用户空间访问,并且不起作用。寻找帮助以了解原因。

谢谢, 路易斯


编辑:格式化,使我的问题更清楚。

1 个答案:

答案 0 :(得分:0)

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

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

但是,我已经在运行Debian 83映像的Dragonboard 410c上成功使用了内核模块,该模块运行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要求您重建内核-仅加载新模块是不够的。