Skip to content

Commit

Permalink
Port to ARM
Browse files Browse the repository at this point in the history
  • Loading branch information
dfaranha committed Oct 14, 2020
1 parent 1c2d60e commit d708aca
Show file tree
Hide file tree
Showing 13 changed files with 526 additions and 0 deletions.
3 changes: 3 additions & 0 deletions enable_arm_pmu/AUTHORS.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Author, target of blame:

Austin Seipp <aseipp [@at] pobox [.dot] com>
43 changes: 43 additions & 0 deletions enable_arm_pmu/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Contributing

## Commits

Rules for contribution:

* 80-character column maximum.
* The first line of a commit message should be 73 columns max.
* Try to make commits self contained. One thing at a time.
If it's a branch, squash the commits together to make one.
* Always run tests. If benchmarks regress, give OS information,
and we'll discuss.
* Always reference the issue you're working on in the bug tracker
in your commit message, and if it fixes the issue, close it.

You can use GitHub pull requests OR just email me patches directly
(see `git format-patch --help`,) whatever you are more comfortable with.

One nice aspect of submitting a pull request is that
[travis-ci.org](http://travis-ci.org) bots will automatically merge, build
and run tests against your commits, and continue as you update the request,
so you can be sure you didn't typo stuff or something before a final merge.

For multi-commit requests, I will often squash them into the smallest
possible logical changes and commit with author attribution.

### Notes on sign-offs and attributions, etc.

When you commit, **please use -s to add a Signed-off-by line**. I manage
the `Signed-off-by` line much like Git itself: by adding it, you make clear
that the contributed code abides by the source code license. I'm pretty
much always going to want you to do this.

I normally merge commits manually and give the original author attribution
via `git commit --author`. I also sign-off on it, and add an `Acked-by` field
which basically states "this commit is not totally ludicrous."

Other fields may be added in the same vein for attribution or other purposes
(`Suggested-by`, `Reviewed-by`, etc.)

## Hacker notes

N/A.
20 changes: 20 additions & 0 deletions enable_arm_pmu/LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Copyright (c) 2013 Austin Seipp

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 changes: 20 additions & 0 deletions enable_arm_pmu/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
all: ko/enable_arm_pmu.ko perf_arm_pmu perf_event_open
perf_arm_pmu: perf_arm_pmu.c
@echo CC perf_arm_pmu
@$(CC) -O3 -std=gnu99 perf_arm_pmu.c -o perf_arm_pmu
perf_event_open: perf_event_open.c
@echo CC perf_event_open
@$(CC) -O3 -std=gnu99 perf_event_open.c -o perf_event_open
ko/enable_arm_pmu.ko: ko/enable_arm_pmu.c
@echo KMOD ko/enable_arm_pmu.ko
@$(MAKE) -C ko > /dev/null
runtests: all
@echo SUDO load-module
@./load-module
@./perf_arm_pmu 128
@./perf_event_open 128
@echo SUDO unload-module
@./unload-module

clean:
@($(MAKE) -C ko clean > /dev/null) && rm -f perf_arm_pmu perf_event_open *.o *~
57 changes: 57 additions & 0 deletions enable_arm_pmu/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# User-mode access to ARM PMU cycle counters

This repository contains a kernel module and library.

ARM performance monitor units (PMUs) are only available on ARMv7 machines. In
general, this means you'll need a Cortex-A7 or better (A8, A9, A15, etc.)

More details are available in [my blog post][blog].

[blog]: http://neocontra.blogspot.com/2013/05/user-mode-performance-counters-for.html

# Testing

To compile, load, test and remove the module, you can just run:

```
$ sudo make runtests
```

## Tested on

* Samsung Chromebook
* Exynos 5 Dual, 1.7gHz Cortex-A15
* Ubuntu 13.04
* ODROID-U2
* Exynos 4 Quad, 1.7gHz Cortex-A9
* Ubuntu/Linaro 12.10 derivative

TBD: PandaBoard.

# Join in

Be sure to read the [contributing guidelines][contribute]. File bugs
in the GitHub [issue tracker][].

Master [git repository][gh]:

* `git clone https://github.com/thoughtpolice/enable_arm_pmu.git`

There's also a [BitBucket mirror][bb]:

* `git clone https://bitbucket.org/thoughtpolice/enable_arm_pmu.git`

# Authors

See [AUTHORS.txt](https://raw.github.com/thoughtpolice/enable_arm_pmu/master/AUTHORS.txt).

# License

MIT. See
[LICENSE.txt](https://raw.github.com/thoughtpolice/enable_arm_pmu/master/LICENSE.txt)
for terms of copyright and redistribution.

[contribute]: https://github.com/thoughtpolice/enable_arm_pmu/blob/master/CONTRIBUTING.md
[issue tracker]: http://github.com/thoughtpolice/enable_arm_pmu/issues
[gh]: http://github.com/thoughtpolice/enable_arm_pmu
[bb]: http://bitbucket.org/thoughtpolice/enable_arm_pmu
74 changes: 74 additions & 0 deletions enable_arm_pmu/armpmu_lib.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#ifndef ARMPMU_LIB_H
#define ARMPMU_LIB_H

static inline uint32_t
rdtsc32(void)
{
#if defined(__GNUC__)
uint32_t r = 0;
#if defined __aarch64__
asm volatile("mrs %0, pmccntr_el0" : "=r" (r));
#elif defined(__ARM_ARCH_7A__)
asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(r) );
#else
#error Unsupported architecture/compiler!
#endif
return r;
#endif
}

#define ARMV8_PMEVTYPER_P (1 << 31) /* EL1 modes filtering bit */
#define ARMV8_PMEVTYPER_U (1 << 30) /* EL0 filtering bit */
#define ARMV8_PMEVTYPER_NSK (1 << 29) /* Non-secure EL1 (kernel) modes filtering bit */
#define ARMV8_PMEVTYPER_NSU (1 << 28) /* Non-secure User mode filtering bit */
#define ARMV8_PMEVTYPER_NSH (1 << 27) /* Non-secure Hyp modes filtering bit */
#define ARMV8_PMEVTYPER_M (1 << 26) /* Secure EL3 filtering bit */
#define ARMV8_PMEVTYPER_MT (1 << 25) /* Multithreading */
#define ARMV8_PMEVTYPER_EVTCOUNT_MASK 0x3ff

static inline void
enable_pmu(uint32_t evtCount)
{
#if defined(__GNUC__) && defined __aarch64__
evtCount &= ARMV8_PMEVTYPER_EVTCOUNT_MASK;
asm volatile("isb");
/* Just use counter 0 */
asm volatile("msr pmevtyper0_el0, %0" : : "r" (evtCount));
/* Performance Monitors Count Enable Set register bit 30:1 disable, 31,1 enable */
uint32_t r = 0;

asm volatile("mrs %0, pmcntenset_el0" : "=r" (r));
asm volatile("msr pmcntenset_el0, %0" : : "r" (r|1));
#else
#error Unsupported architecture/compiler!
#endif
}

static inline uint32_t
read_pmu(void)
{
#if defined(__GNUC__) && defined __aarch64__
uint32_t r = 0;
asm volatile("mrs %0, pmevcntr0_el0" : "=r" (r));
return r;
#else
#error Unsupported architecture/compiler!
#endif
}

static inline void
disable_pmu(uint32_t evtCount)
{
#if defined(__GNUC__) && defined __aarch64__
/* Performance Monitors Count Enable Set register: clear bit 0 */
uint32_t r = 0;

asm volatile("mrs %0, pmcntenset_el0" : "=r" (r));
asm volatile("msr pmcntenset_el0, %0" : : "r" (r&&0xfffffffe));
#else
#error Unsupported architecture/compiler!
#endif
}


#endif /* ARMPMU_LIB_H */
8 changes: 8 additions & 0 deletions enable_arm_pmu/ko/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
obj-m := enable_arm_pmu.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

all:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) clean
125 changes: 125 additions & 0 deletions enable_arm_pmu/ko/enable_arm_pmu.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* Enable user-mode ARM performance counter access.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/smp.h>

/** -- Configuration stuff ------------------------------------------------- */

#define DRVR_NAME "enable_arm_pmu"

#if !defined(__arm__) && !defined(__aarch64__)
#error Module can only be compiled on ARM machines.
#endif

/** -- Initialization & boilerplate ---------------------------------------- */
#define ARMV8_PMCR_MASK 0x3f
#define ARMV8_PMCR_E (1 << 0) /* Enable all counters */
#define ARMV8_PMCR_P (1 << 1) /* Reset all counters */
#define ARMV8_PMCR_C (1 << 2) /* Cycle counter reset */
#define ARMV8_PMCR_D (1 << 3) /* CCNT counts every 64th cpu cycle */
#define ARMV8_PMCR_X (1 << 4) /* Export to ETM */
#define ARMV8_PMCR_DP (1 << 5) /* Disable CCNT if non-invasive debug*/
#define ARMV8_PMCR_N_SHIFT 11 /* Number of counters supported */
#define ARMV8_PMCR_N_MASK 0x1f

#define ARMV8_PMUSERENR_EN_EL0 (1 << 0) /* EL0 access enable */
#define ARMV8_PMUSERENR_CR (1 << 2) /* Cycle counter read enable */
#define ARMV8_PMUSERENR_ER (1 << 3) /* Event counter read enable */

#define ARMV8_PMCNTENSET_EL0_ENABLE (1<<31) /* *< Enable Perf count reg */

#define PERF_DEF_OPTS (1 | 16)
#define PERF_OPT_RESET_CYCLES (2 | 4)
#define PERF_OPT_DIV64 (8)

static inline u32 armv8pmu_pmcr_read(void)
{
u64 val=0;
asm volatile("mrs %0, pmcr_el0" : "=r" (val));
return (u32)val;
}
static inline void armv8pmu_pmcr_write(u32 val)
{
val &= ARMV8_PMCR_MASK;
isb();
asm volatile("msr pmcr_el0, %0" : : "r" ((u64)val));
}

static void
enable_cpu_counters(void* data)
{
printk(KERN_INFO "[" DRVR_NAME "] enabling user-mode PMU access on CPU #%d",
smp_processor_id());

#if __aarch64__
/* Enable user-mode access to counters. */
asm volatile("msr pmuserenr_el0, %0" : : "r"((u64)ARMV8_PMUSERENR_EN_EL0|ARMV8_PMUSERENR_ER|ARMV8_PMUSERENR_CR));
/* Initialize & Reset PMNC: C and P bits. */
armv8pmu_pmcr_write(ARMV8_PMCR_P | ARMV8_PMCR_C);
/* G4.4.11
* PMINTENSET, Performance Monitors Interrupt Enable Set register */
/* cycle counter overflow interrupt request is disabled */
asm volatile("msr pmintenset_el1, %0" : : "r" ((u64)(0 << 31)));
/* Performance Monitors Count Enable Set register bit 30:0 disable, 31 enable */
asm volatile("msr pmcntenset_el0, %0" : : "r" (ARMV8_PMCNTENSET_EL0_ENABLE));
/* start*/
armv8pmu_pmcr_write(armv8pmu_pmcr_read() | ARMV8_PMCR_E);
#elif defined(__ARM_ARCH_7A__)
/* Enable user-mode access to counters. */
asm volatile("mcr p15, 0, %0, c9, c14, 0" :: "r"(1));
/* Program PMU and enable all counters */
asm volatile("mcr p15, 0, %0, c9, c12, 0" :: "r"(PERF_DEF_OPTS));
asm volatile("mcr p15, 0, %0, c9, c12, 1" :: "r"(0x8000000f));
#else
#error Unsupported Architecture
#endif
}

static void
disable_cpu_counters(void* data)
{
printk(KERN_INFO "[" DRVR_NAME "] disabling user-mode PMU access on CPU #%d",
smp_processor_id());

#if __aarch64__
/* Performance Monitors Count Enable Set register bit 31:0 disable, 1 enable */
asm volatile("msr pmcntenset_el0, %0" : : "r" (0<<31));
/* Note above statement does not really clearing register...refer to doc */
/* Program PMU and disable all counters */
armv8pmu_pmcr_write(armv8pmu_pmcr_read() |~ARMV8_PMCR_E);
/* disable user-mode access to counters. */
asm volatile("msr pmuserenr_el0, %0" : : "r"((u64)0));
#elif defined(__ARM_ARCH_7A__)
/* Program PMU and disable all counters */
asm volatile("mcr p15, 0, %0, c9, c12, 0" :: "r"(0));
asm volatile("mcr p15, 0, %0, c9, c12, 2" :: "r"(0x8000000f));
/* Disable user-mode access to counters. */
asm volatile("mcr p15, 0, %0, c9, c14, 0" :: "r"(0));
#else
#error Unsupported Architecture
#endif
}

static int __init
init(void)
{
on_each_cpu(enable_cpu_counters, NULL, 1);
printk(KERN_INFO "[" DRVR_NAME "] initialized");
return 0;
}

static void __exit
fini(void)
{
on_each_cpu(disable_cpu_counters, NULL, 1);
printk(KERN_INFO "[" DRVR_NAME "] unloaded");
}

MODULE_AUTHOR("Austin Seipp <aseipp@pobox.com>");
MODULE_LICENSE("Dual MIT/GPL");
MODULE_DESCRIPTION("Enables user-mode access to ARMv7 PMU counters");
MODULE_VERSION("0:0.1-dev");
module_init(init);
module_exit(fini);
2 changes: 2 additions & 0 deletions enable_arm_pmu/load-module
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/sh
/sbin/insmod ./ko/enable_arm_pmu.ko || exit 1
Loading

0 comments on commit d708aca

Please sign in to comment.