arm32 arm64 读取PMCCNTR cpu cycle counter

ARM 的时钟周期计数保存在PMCCNTR 寄存器,不像x86用户态可以直接读取,需内核态使能,一种是在内核中使能,比如init,比较简单的是在模块中使能。

本来写了两个,arm32一个,arm64一个,方便对比合在了一起。
只测试了32位cortex-a9双核, 还有 个64位a76 a55。
enpmu.c

#include 
#include 
#include 

MODULE_AUTHOR("cn");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.0");

#if !defined(__arm__) && !defined(__aarch64__)
#error module only support arm32 arm64.
#endif


#ifdef __aarch64__
typedef unsigned long ulint;  //64
#elif defined __arm__
typedef unsigned int ulint;  //32
#endif

static void en_access(void*)
{
    ulint i=0,tmpvar=0;

#ifdef __aarch64__
    asm volatile("mrs %0, mpidr_el1 ":"=r"(i));
    i =  (i >>8) &0xff;
#else
    asm volatile("mrc p15,0,%0,c0,c0,5 ":"=r"(i));
    i =  i & 3;
#endif  
    asm volatile (  
#ifdef __aarch64__
            "mrs %0,pmuserenr_el0 \n"
            "orr %0, %0,%1 \n"
            "msr pmuserenr_el0,%0"
#else
            "mrc p15, 0, %0, c9, c14, 0 \n"
            "orr %0, %0,%1 \n"
            "mcr p15, 0, %0, c9, c14, 0 \n"
#endif
            :"+r"(tmpvar):"r"(0xf));
    asm volatile(   
#ifdef __aarch64__
            "mrs %0, pmcr_el0 \n"
            "orr %0, %0, %1 \n" //32  0x41
            "bic %0, %0, %2 \n"
            "msr pmcr_el0,%0 \n"
#else
            "mrc p15, 0, %0, c9, c12, 0 \n"
            "orr %0, %0,%1 \n"
            "bic %0, %0, %2 \n"
            "mcr p15, 0, %0, c9, c12, 0 \n"
#endif
            :"+r"(tmpvar):"r"(0x81),"r"(0x28));
    asm volatile(   
#ifdef __aarch64__
            "msr pmcntenset_el0,%1 \n"
            "mrs %0, cntvct_el0 \n"
#else
            "mcr p15, 0, %1, c9, c12, 1 \n"
            "mrc p15, 0, %0, c9, c13, 0 \n"
#endif
            :"=r"(tmpvar) :"r"(0xffffffff));
    printk("core %lu tsc = %lx",(unsigned long)i, (unsigned long)tmpvar );
}

static void restore_access(void*) {
    ulint i,tmpvar=0;
#ifdef __aarch64__
    asm volatile( "mrs %0, mpidr_el1": "=r"(i));
    i =  (i >> 8)&0xff;
#else
    asm volatile("mrc p15,0,%0,c0,c0,5 \n" : "=r"(i));
    i =  i & 3;
#endif

    asm volatile (
#ifdef __aarch64__
            "mrs %0,pmcr_el0 \n"
            "bic %0,%0, %2 \n"
            "msr pmcr_el0,%0\n"
            "msr pmuserenr_el0,%1\n"
            "mrs %0, cntvct_el0 \n"
#else
            "mrc p15, 0, %0, c9, c14, 0 \n"
            "bic %0,%0, %2 \n"
            "mcr p15, 0, %0, c9, c14, 0 \n"
            "mcr p15, 0, %1, c9, c12, 1 \n"
            "mrc p15, 0, %0, c9, c13, 0 \n"
#endif
            :"+r" (tmpvar):"r"(0),"r"(1));
    printk("un core %lx tsc = %lx",(unsigned long)i, (unsigned long)tmpvar );

}
static int __init  start(void) 
{ 
    on_each_cpu(en_access, NULL, 1);
    printk(KERN_INFO "pmu access enabled\n"); 
    return 0; 
} 

static void __exit stop(void) 
{ 
    on_each_cpu(restore_access, NULL, 1);
    printk(KERN_INFO "pmu access disabled\n"); 
} 

module_init(start); 
module_exit(stop); 

Makefile

obj-m = enpmu.o
all:
	make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules
clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

直接

make
insmod enpmu.ko
rmmod enpmu

然后就可以读取了 以下arm32 arm64 x86_64读取方法
test.c

#include 
#ifndef __arm__
typedef unsigned long ulint;
#else
typedef unsigned int ulint;
#endif
int main()
{
				ulint ct = 0;
#ifdef __aarch64__
                asm volatile("mrs %0, cntvct_el0" :"=r"(ct));
#elif defined __arm__
				asm volatile("mrc p15,0,%0, c9, c13, 0":"=r"(ct));
#elif defined __x86_64__
                asm volatile("rdtsc ; shl $32, %%rdx ; or %%rdx, %0": "=a"(ct));
#endif
				printf("%lx\n",(unsigned long)ct);
}
gcc test.c
./a.out
taskset -c 1 ./a.out

taskset -c 选择在哪个核上运行。

附录

CNTVCT_EL0

pmcntenset_el0  Performance Monitors Count Enable Set register
 purpose Enables the Cycle Count Register·
C [31]·
0x1 »   PMCCNTR_EL0 enable·
P<m>»   PMEVCNTR<n>_EL0 enable
0xFFFFFFFF

pmuserenr_el0
Performance Monitors User Enable Register
Enable or disables EL0 access to the performance Monitors;
ER [3]                                                                                    
»   Event counters Read enable,
»   1 en rw
CR [2] 
»   Cycle counter Read enable·
»   32 MRC read PMCCNTR  MRRC read PMCCNTR
SW[1]  software increment register Write enable
»   1
En [0] Enable
»   Enables EL0 read/write access to PMU registers
0xF

PMCR_EL0
bit[9] Freeze-on-overflow                                                                 
0
LC [6]  1   aarch32  supported long cycle
»   0x1
DP [5] Disable cycle counter when event counting is prohibited·
	0x0 not affected

D [3] clock divider··
	0 pmccntr_el0 counts every clock cycle
E  [0] enable
	1 Affected counters are enabled by pmcntenset_el0
	
mrc/mcr  Op1 CRm Op2 Name Type Reset Description
0 	c12 0 PMCR RW 0x41093000 Performance Monitor Control Register
		1 PMCNTENSET RW 0x00000000 Count Enable Set Register
		2 PMCNTENCLR RW 0x00000000 Count Enable Clear Register
		3 PMOVSR RW - Overflow Flag Status Register
		4 PMSWINC WO - Software Increment Register
		5 PMSELR RW 0x00000000 Event Counter Selection Register
	c13 0 PMCCNTR RW - Cycle Count Register
		1 PMXEVTYPER RW - Event Type Selection Register
		2 PMXEVCNTR RW - Event Count Registers
	c14 0 PMUSERENR RWa 0x00000000 User Enable Register
		1 PMINTENSET RW 0x00000000 Interrupt Enable Set Register
		2 PMINTENCLR RW 0x00000000 Interrupt Enable Clear Register



你可能感兴趣的:(linux,arm开发)