ARM Cortex A7在内核模式下返回PMCCNTR = 0,在用户模式下返回非法指令(即使在PMUSERENR = 1之后)

时间:2015-07-24 21:53:42

标签: linux arm raspberry-pi raspberry-pi2

我想在具有ARM Cortex A7内核的Raspberry Pi 2上读取循环计数寄存器(PMCCNTR)。我为它编译了一个内核模块,如下所示:

#include <linux/module.h>
#include <linux/kernel.h>

int init_module()
{
  volatile u32 PMCR, PMUSERENR, PMCCNTR;

  // READ PMCR
  PMCR = 0xDEADBEEF;
  asm volatile ("mrc p15, 0, %0, c9, c12, 0\n\t" : "=r" (PMCR));
  printk (KERN_INFO "PMCR = %x\n", PMCR);

  // READ PMUSERENR 
  PMUSERENR = 0xDEADBEEF;
  asm volatile ("mrc p15, 0, %0, c9, c14, 0\n\t" : "=r" (PMUSERENR));
  printk (KERN_INFO "PMUSERENR = %x\n", PMUSERENR);

  // WRITE PMUSERENR = 1
  asm volatile ("mcr p15, 0, %0, c9, c14, 0\n\t" : : "r" (1));

  // READ PWMUSERENR AGAIN
  asm volatile ("mrc p15, 0, %0, c9, c14, 0\n\t" : "=r" (PMUSERENR));
  printk (KERN_INFO "PMUSERENR = %x\n", PMUSERENR);

  // READ PMCCNTR
  PMCCNTR = 0xDEADBEEF;
  asm volatile ("mrc p15, 0, %0, c9, c13, 0\n\t" : "=r" (PMCCNTR));
  printk (KERN_ALERT "PMCCNTR = %x\n", PMCCNTR);
  return 0;
}

void cleanup_module()
{
}

MODULE_LICENSE("GPL");

,在insmod之后,我会在/var/log/kern.log中注意以下内容:

PMCR = 41072000
PMUSERENR = 0
PMUSERENR = 1
PMCCNTR = 0

当我尝试从用户模式读取PMCCNTR时,即使PMUSERENR设置为1,我也会收到非法指令。

为什么PMCCNTR在内核模式下读为0,在用户模式下读取为非法指令?还有其他我需要做的事情,我没有做到启用PMCCNTR吗?

更新1

部分解决了。解决多核问题的方法是像这样调用on_each_cpu

#include <linux/module.h>
#include <linux/kernel.h>

static void enable_ccnt_read(void* data)
{
  // WRITE PMUSERENR = 1
  asm volatile ("mcr p15, 0, %0, c9, c14, 0\n\t" : : "r" (1));
}

int init_module()
{
  on_each_cpu(enable_ccnt_read, NULL, 1);
  return 0;
}

void cleanup_module()
{
}

MODULE_LICENSE("GPL");

我现在可以从用户区读取PMCCNTR:

#include <iostream>

unsigned ccnt_read ()
{
  volatile unsigned cc;
  asm volatile ("mrc p15, 0, %0, c9, c13, 0" : "=r" (cc));
  return cc;
}

int main() {
  std::cout << ccnt_read() << std::endl;
}

要在特定核心上运行用户态程序,您可以像taskset一样使用(例如,在核心2上运行):

$ taskset -c 2 ./ccnt_read
0

PMCCNTR仍未递增。他们需要开启&#34;不知。

3 个答案:

答案 0 :(得分:6)

以下是后人的工作解决方案:

内核模块:

#include <linux/module.h>
#include <linux/kernel.h>

static void enable_ccnt_read(void* data)
{
  // PMUSERENR = 1
  asm volatile ("mcr p15, 0, %0, c9, c14, 0" :: "r"(1));

  // PMCR.E (bit 0) = 1
  asm volatile ("mcr p15, 0, %0, c9, c12, 0" :: "r"(1));

  // PMCNTENSET.C (bit 31) = 1
  asm volatile ("mcr p15, 0, %0, c9, c12, 1" :: "r"(1 << 31));
}

int init_module()
{
  on_each_cpu(enable_ccnt_read, NULL, 1);
  return 0;
}

void cleanup_module()
{
}

MODULE_LICENSE("GPL");

客户端程序:

#include <iostream>

unsigned ccnt_read ()
{

  volatile unsigned cc;
  asm volatile ("mrc p15, 0, %0, c9, c13, 0" : "=r" (cc));
  return cc;
}

int main() {
  std::cout << ccnt_read() << std::endl;
}

答案 1 :(得分:1)

您所做的是启用计数器的用户级访问权限。您尚未启用此计数器。除了允许访问之外,还必须对PMCNTENSET的第31位(C位)进行编程以启用计数。这与on_each_cpu()更改一起应该启用您要查找的功能。

请注意:如果进程在CCNT读取之间迁移到不同的核心,那么您的测量结果将会混乱。

答案 2 :(得分:0)

我在模拟中运行这个芯片,并发现了上述问题的另一个问题。启用时必须重置性能计数器,否则将根据未定义的值生成断言。这意味着PMCR寄存器应设置如下:

// PMCR.E (bit 0) = 1, PMCR.C (bit 2) = 1
asm volatile ("mcr p15, 0, %0, c9, c12, 0" :: "r"(5));