title: ARM Cortex A7 returning PMCCNTR = 0 in kernel mode, and Illegal instruction in user mode (even after PMUSERENR = 1)
body: <p>I want to read the cycle count register (PMCCNTR) on a Raspberry Pi 2, which has an ARM Cortex A7 core.  I compile a kernel module for it as follows:</p>
  
  <pre><code>#include &lt;linux/module.h&gt;
  #include &lt;linux/kernel.h&gt;
  
  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");
  </code></pre>
  
  <p>and, after <code>insmod</code>, I observe the following in <code>/var/log/kern.log</code>:</p>
  
  <pre><code>PMCR = 41072000
  PMUSERENR = 0
  PMUSERENR = 1
  PMCCNTR = 0
  </code></pre>
  
  <p>When I try to read PMCCNTR from user-mode, I get illegal instruction, even after PMUSERENR has been set to 1.</p>
  
  <p>Why does PMCCNTR read as 0 in kernel mode, and an illegal instruction in user-mode?  Is there something else I need to do that I'm not doing to enable the PMCCNTR?</p>
  
  <h2>Update 1</h2>
  
  <p>Partly solved.  The solution to the multi-core issue is to call <code>on_each_cpu</code> like so:</p>
  
  <pre><code>#include &lt;linux/module.h&gt;
  #include &lt;linux/kernel.h&gt;
  
  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");
  </code></pre>
  
  <p>I can now read PMCCNTR from userland:</p>
  
  <pre><code>#include &lt;iostream&gt;
  
  unsigned ccnt_read ()
  {
    volatile unsigned cc;
    asm volatile ("mrc p15, 0, %0, c9, c13, 0" : "=r" (cc));
    return cc;
  }
  
  int main() {
    std::cout &lt;&lt; ccnt_read() &lt;&lt; std::endl;
  }
  </code></pre>
  
  <p>To run a userland program on a specific core you can use <code>taskset</code> like so (example, run on core 2):</p>
  
  <pre><code>$ taskset -c 2 ./ccnt_read
  0
  </code></pre>
  
  <p>The PMCCNTR are still not incrementing.  They need to be "switched on" somehow.</p>