* [Qemu-devel] RFC: ARM Semihosting, PMU, and BBV Changes
@ 2015-08-05 16:51 Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 01/14] Make unknown semihosting calls non-fatal Christopher Covington
` (14 more replies)
0 siblings, 15 replies; 25+ messages in thread
From: Christopher Covington @ 2015-08-05 16:51 UTC (permalink / raw)
To: qemu-devel
Hi,
This series is a jumble of changes that I have found useful for
creating samples of long-running applications. I do not expect any of
these patches to be merged upstream as-is but I'm publishing them as a
way to ask for high-level feedback, as there may very well be much
better ways to achieve the same goal. These changes are based on
commit 1fd1a2cc182d4e331acd92c2cdbb52b3e78c040e.
While the patches are in chronological order, their functionality can
roughly be put in the following categories:
A) Functional fixes
B) Guest->emulator/host communication
C) Instrumentation/profiling capabilities
A) Patches 3, 4, 5, and 13 can perhaps be categorized as functional
fixes. They may be fixed on the current tip. As a write this, I'm
thinking this category may be the best one to target for initial
upstreaming.
B) Patches 1, 2, and 11 implement communications mechanisms. We have
used Angel semihosting and "magic exceptions" to perform various kinds
of guest to emulator and guest to host communication. Since these
patches were originally developed, I've been able to reduce or remove
our need for them, but if anyone has suggestions on better ways to not
need to communicate in the first place or use existing mechanisms to
communicate, that'd be appreciated. As an example, we previously used
semihosting open(), read(), and write() calls for host filesystem
access, but have been able to replace those by mounting a VirtIO-MMIO
9P filesystem. Another example is using poweroff, which ends up making
a PSCI call, to end the run, rather than a custom executable that
makes a semihosting exit call.
C) The instrumentation implemented in patches 6, 7, 8, 9, 10, 12, and
14 allow us to measure instruction counts and "block vectors", with
the primary application of running the offline SimPoint algorithm [1]
on the collected block vectors and dumping application level
checkpoints using CRIU [2] in a second pass.
Thanks,
Christopher Covington
1. http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.58.4012
2. http://criu.org/Main_Page
^ permalink raw reply [flat|nested] 25+ messages in thread
* [Qemu-devel] [RFC 01/14] Make unknown semihosting calls non-fatal
2015-08-05 16:51 [Qemu-devel] RFC: ARM Semihosting, PMU, and BBV Changes Christopher Covington
@ 2015-08-05 16:51 ` Christopher Covington
2015-08-06 9:11 ` Alex Bennée
2015-08-05 16:51 ` [Qemu-devel] [RFC 02/14] Added semihosting support for A64 in full-system mode Christopher Covington
` (13 subsequent siblings)
14 siblings, 1 reply; 25+ messages in thread
From: Christopher Covington @ 2015-08-05 16:51 UTC (permalink / raw)
To: qemu-devel; +Cc: Christopher Covington
Signed-off-by: Christopher Covington <cov@codeaurora.org>
---
target-arm/arm-semi.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/target-arm/arm-semi.c b/target-arm/arm-semi.c
index a8b83e6..bcc70ec 100644
--- a/target-arm/arm-semi.c
+++ b/target-arm/arm-semi.c
@@ -186,8 +186,6 @@ static void arm_semi_flen_cb(CPUState *cs, target_ulong ret, target_ulong err)
#define SET_ARG(n, val) put_user_ual(val, args + (n) * 4)
uint32_t do_arm_semihosting(CPUARMState *env)
{
- ARMCPU *cpu = arm_env_get_cpu(env);
- CPUState *cs = CPU(cpu);
target_ulong args;
target_ulong arg0, arg1, arg2, arg3;
char * s;
@@ -195,6 +193,8 @@ uint32_t do_arm_semihosting(CPUARMState *env)
uint32_t ret;
uint32_t len;
#ifdef CONFIG_USER_ONLY
+ ARMCPU *cpu = arm_env_get_cpu(env);
+ CPUState *cs = CPU(cpu);
TaskState *ts = cs->opaque;
#else
CPUARMState *ts = env;
@@ -562,7 +562,6 @@ uint32_t do_arm_semihosting(CPUARMState *env)
exit(ret);
default:
fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr);
- cpu_dump_state(cs, stderr, fprintf, 0);
- abort();
+ return -1;
}
}
--
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [Qemu-devel] [RFC 02/14] Added semihosting support for A64 in full-system mode
2015-08-05 16:51 [Qemu-devel] RFC: ARM Semihosting, PMU, and BBV Changes Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 01/14] Make unknown semihosting calls non-fatal Christopher Covington
@ 2015-08-05 16:51 ` Christopher Covington
2015-08-11 18:16 ` Peter Maydell
2015-08-05 16:51 ` [Qemu-devel] [RFC 03/14] Fix makefile Christopher Covington
` (12 subsequent siblings)
14 siblings, 1 reply; 25+ messages in thread
From: Christopher Covington @ 2015-08-05 16:51 UTC (permalink / raw)
To: qemu-devel; +Cc: Christopher Covington
This is for full-system only; not implemented in user mode
Written by Derek Hower.
Signed-off-by: Christopher Covington <cov@codeaurora.org>
---
include/exec/softmmu-semi.h | 21 ++++++-
target-arm/arm-semi.c | 142 ++++++++++++++++++++++++++++++++++++--------
target-arm/cpu.h | 3 +-
target-arm/helper-a64.c | 28 ++++++++-
target-arm/helper.c | 1 +
target-arm/internals.h | 8 +++
target-arm/translate-a64.c | 2 +-
7 files changed, 174 insertions(+), 31 deletions(-)
diff --git a/include/exec/softmmu-semi.h b/include/exec/softmmu-semi.h
index 8401f7d..9ab8353 100644
--- a/include/exec/softmmu-semi.h
+++ b/include/exec/softmmu-semi.h
@@ -9,6 +9,13 @@
#ifndef SOFTMMU_SEMI_H
#define SOFTMMU_SEMI_H 1
+static inline uint64_t softmmu_tget64(CPUArchState *env, uint64_t addr)
+{
+ uint64_t val;
+
+ cpu_memory_rw_debug(ENV_GET_CPU(env), addr, (uint8_t *)&val, 8, 0);
+ return tswap64(val);
+}
static inline uint32_t softmmu_tget32(CPUArchState *env, uint32_t addr)
{
uint32_t val;
@@ -24,19 +31,27 @@ static inline uint32_t softmmu_tget8(CPUArchState *env, uint32_t addr)
return val;
}
+#define get_user_u64(arg, p) ({ arg = softmmu_tget64(env, p) ; 0; })
#define get_user_u32(arg, p) ({ arg = softmmu_tget32(env, p) ; 0; })
#define get_user_u8(arg, p) ({ arg = softmmu_tget8(env, p) ; 0; })
#define get_user_ual(arg, p) get_user_u32(arg, p)
+static inline void softmmu_tput64(CPUArchState *env, uint64_t addr, uint64_t val)
+{
+ val = tswap64(val);
+ cpu_memory_rw_debug(ENV_GET_CPU(env), addr, (uint8_t *)&val, 8, 1);
+}
+
static inline void softmmu_tput32(CPUArchState *env, uint32_t addr, uint32_t val)
{
val = tswap32(val);
cpu_memory_rw_debug(ENV_GET_CPU(env), addr, (uint8_t *)&val, 4, 1);
}
+#define put_user_u64(arg, p) ({ softmmu_tput64(env, p, arg) ; 0; })
#define put_user_u32(arg, p) ({ softmmu_tput32(env, p, arg) ; 0; })
#define put_user_ual(arg, p) put_user_u32(arg, p)
-static void *softmmu_lock_user(CPUArchState *env, uint32_t addr, uint32_t len,
+static void *softmmu_lock_user(CPUArchState *env, target_ulong addr, uint32_t len,
int copy)
{
uint8_t *p;
@@ -48,11 +63,11 @@ static void *softmmu_lock_user(CPUArchState *env, uint32_t addr, uint32_t len,
return p;
}
#define lock_user(type, p, len, copy) softmmu_lock_user(env, p, len, copy)
-static char *softmmu_lock_user_string(CPUArchState *env, uint32_t addr)
+static char *softmmu_lock_user_string(CPUArchState *env, target_ulong addr)
{
char *p;
char *s;
- uint8_t c;
+ uint8_t c = 0;
/* TODO: Make this something that isn't fixed size. */
s = p = malloc(1024);
if (!s) {
diff --git a/target-arm/arm-semi.c b/target-arm/arm-semi.c
index bcc70ec..b89ea8f 100644
--- a/target-arm/arm-semi.c
+++ b/target-arm/arm-semi.c
@@ -4,6 +4,9 @@
* Copyright (c) 2005, 2007 CodeSourcery.
* Written by Paul Brook.
*
+ * Copyright (c) 2015 the Linux Foundation.
+ * Written by Derek Hower.
+ *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@@ -140,19 +143,35 @@ static void arm_semi_cb(CPUState *cs, target_ulong ret, target_ulong err)
#else
syscall_err = err;
#endif
- env->regs[0] = ret;
+ if (env->aarch64) {
+ env->xregs[0] = ret;
+ } else {
+ env->regs[0] = ret;
+ }
} else {
/* Fixup syscalls that use nonstardard return conventions. */
switch (env->regs[0]) {
case TARGET_SYS_WRITE:
case TARGET_SYS_READ:
+ if (env->aarch64) {
+ env->xregs[0] = arm_semi_syscall_len - ret;
+ } else {
env->regs[0] = arm_semi_syscall_len - ret;
+ }
break;
case TARGET_SYS_SEEK:
+ if (env->aarch64) {
+ env->xregs[0] = 0;
+ } else {
env->regs[0] = 0;
+ }
break;
default:
+ if (env->aarch64) {
+ env->xregs[0] = ret;
+ } else {
env->regs[0] = ret;
+ }
break;
}
}
@@ -165,8 +184,13 @@ static void arm_semi_flen_cb(CPUState *cs, target_ulong ret, target_ulong err)
/* The size is always stored in big-endian order, extract
the value. We assume the size always fit in 32 bits. */
uint32_t size;
- cpu_memory_rw_debug(cs, env->regs[13]-64+32, (uint8_t *)&size, 4, 0);
- env->regs[0] = be32_to_cpu(size);
+ if (env->aarch64) {
+ cpu_memory_rw_debug(cs, env->pc-64+32, (uint8_t *)&size, 4, 0);
+ env->xregs[0] = be32_to_cpu(size);
+ } else {
+ cpu_memory_rw_debug(cs, env->regs[13]-64+32, (uint8_t *)&size, 4, 0);
+ env->regs[0] = be32_to_cpu(size);
+ }
#ifdef CONFIG_USER_ONLY
((TaskState *)cs->opaque)->swi_errno = err;
#else
@@ -177,14 +201,28 @@ static void arm_semi_flen_cb(CPUState *cs, target_ulong ret, target_ulong err)
/* Read the input value from the argument block; fail the semihosting
* call if the memory read fails.
*/
-#define GET_ARG(n) do { \
- if (get_user_ual(arg ## n, args + (n) * 4)) { \
- return (uint32_t)-1; \
+#define GET_ARG(n) do { \
+ if (env->aarch64) { \
+ if (get_user_u64(arg ## n, args + (n) * 8)) { \
+ return (target_ulong)-1; \
+ } \
+ } else { \
+ if (get_user_ual(arg ## n, (uint32_t) args + (n) * 4)) { \
+ return (target_ulong)-1; \
+ } \
+ } \
+ } while (0)
+
+#define SET_ARG(n, val) \
+ ({ \
+ if (env->aarch64) { \
+ ret = put_user_u64(val, args + (n) * 8); \
+ } else { \
+ ret = put_user_ual(val, args + (n) * 4); \
} \
-} while (0)
-
-#define SET_ARG(n, val) put_user_ual(val, args + (n) * 4)
-uint32_t do_arm_semihosting(CPUARMState *env)
+ 0; \
+ })
+target_ulong do_arm_semihosting(CPUARMState *env)
{
target_ulong args;
target_ulong arg0, arg1, arg2, arg3;
@@ -200,8 +238,13 @@ uint32_t do_arm_semihosting(CPUARMState *env)
CPUARMState *ts = env;
#endif
- nr = env->regs[0];
- args = env->regs[1];
+ if (env->aarch64) {
+ nr = env->xregs[0];
+ args = env->xregs[1];
+ } else {
+ nr = env->regs[0];
+ args = env->regs[1];
+ }
switch (nr) {
case TARGET_SYS_OPEN:
GET_ARG(0);
@@ -224,9 +267,13 @@ uint32_t do_arm_semihosting(CPUARMState *env)
if (use_gdb_syscalls()) {
gdb_do_syscall(arm_semi_cb, "open,%s,%x,1a4", arg0,
(int)arg2+1, gdb_open_modeflags[arg1]);
- ret = env->regs[0];
+ if (env->aarch64) {
+ ret = env->xregs[0];
+ } else {
+ ret = env->regs[0];
+ }
} else {
- ret = set_swi_errno(ts, open(s, open_modeflags[arg1], 0644));
+ ret = set_swi_errno(ts, open(s, open_modeflags[arg1], 0644));
}
unlock_user(s, arg0, 0);
return ret;
@@ -234,7 +281,11 @@ uint32_t do_arm_semihosting(CPUARMState *env)
GET_ARG(0);
if (use_gdb_syscalls()) {
gdb_do_syscall(arm_semi_cb, "close,%x", arg0);
- return env->regs[0];
+ if (env->aarch64) {
+ return env->xregs[0];
+ } else {
+ return env->regs[0];
+ }
} else {
return set_swi_errno(ts, close(arg0));
}
@@ -248,7 +299,11 @@ uint32_t do_arm_semihosting(CPUARMState *env)
/* Write to debug console. stderr is near enough. */
if (use_gdb_syscalls()) {
gdb_do_syscall(arm_semi_cb, "write,2,%x,1", args);
- return env->regs[0];
+ if (env->aarch64) {
+ return env->xregs[0];
+ } else {
+ return env->regs[0];
+ }
} else {
return write(STDERR_FILENO, &c, 1);
}
@@ -260,7 +315,11 @@ uint32_t do_arm_semihosting(CPUARMState *env)
len = strlen(s);
if (use_gdb_syscalls()) {
gdb_do_syscall(arm_semi_cb, "write,2,%x,%x\n", args, len);
- ret = env->regs[0];
+ if (env->aarch64) {
+ ret = env->xregs[0];
+ } else {
+ ret = env->regs[0];
+ }
} else {
ret = write(STDERR_FILENO, s, len);
}
@@ -274,7 +333,11 @@ uint32_t do_arm_semihosting(CPUARMState *env)
if (use_gdb_syscalls()) {
arm_semi_syscall_len = len;
gdb_do_syscall(arm_semi_cb, "write,%x,%x,%x", arg0, arg1, len);
- return env->regs[0];
+ if (env->aarch64) {
+ return env->xregs[0];
+ } else {
+ return env->regs[0];
+ }
} else {
s = lock_user(VERIFY_READ, arg1, len, 1);
if (!s) {
@@ -295,7 +358,11 @@ uint32_t do_arm_semihosting(CPUARMState *env)
if (use_gdb_syscalls()) {
arm_semi_syscall_len = len;
gdb_do_syscall(arm_semi_cb, "read,%x,%x,%x", arg0, arg1, len);
- return env->regs[0];
+ if (env->aarch64) {
+ return env->xregs[0];
+ } else {
+ return env->regs[0];
+ }
} else {
s = lock_user(VERIFY_WRITE, arg1, len, 0);
if (!s) {
@@ -317,7 +384,11 @@ uint32_t do_arm_semihosting(CPUARMState *env)
GET_ARG(0);
if (use_gdb_syscalls()) {
gdb_do_syscall(arm_semi_cb, "isatty,%x", arg0);
- return env->regs[0];
+ if (env->aarch64) {
+ return env->xregs[0];
+ } else {
+ return env->regs[0];
+ }
} else {
return isatty(arg0);
}
@@ -326,7 +397,11 @@ uint32_t do_arm_semihosting(CPUARMState *env)
GET_ARG(1);
if (use_gdb_syscalls()) {
gdb_do_syscall(arm_semi_cb, "lseek,%x,%x,0", arg0, arg1);
- return env->regs[0];
+ if (env->aarch64) {
+ return env->xregs[0];
+ } else {
+ return env->regs[0];
+ }
} else {
ret = set_swi_errno(ts, lseek(arg0, arg1, SEEK_SET));
if (ret == (uint32_t)-1)
@@ -336,9 +411,16 @@ uint32_t do_arm_semihosting(CPUARMState *env)
case TARGET_SYS_FLEN:
GET_ARG(0);
if (use_gdb_syscalls()) {
+ if (env->aarch64) {
+ gdb_do_syscall(arm_semi_flen_cb, "fstat,%x,%x",
+ arg0, env->pc-64);
+ return env->xregs[0];
+
+ } else {
gdb_do_syscall(arm_semi_flen_cb, "fstat,%x,%x",
arg0, env->regs[13]-64);
return env->regs[0];
+ }
} else {
struct stat buf;
ret = set_swi_errno(ts, fstat(arg0, &buf));
@@ -354,7 +436,11 @@ uint32_t do_arm_semihosting(CPUARMState *env)
GET_ARG(1);
if (use_gdb_syscalls()) {
gdb_do_syscall(arm_semi_cb, "unlink,%s", arg0, (int)arg1+1);
- ret = env->regs[0];
+ if (env->aarch64) {
+ ret = env->xregs[0];
+ } else {
+ ret = env->regs[0];
+ }
} else {
s = lock_user_string(arg0);
if (!s) {
@@ -373,7 +459,11 @@ uint32_t do_arm_semihosting(CPUARMState *env)
if (use_gdb_syscalls()) {
gdb_do_syscall(arm_semi_cb, "rename,%s,%s",
arg0, (int)arg1+1, arg2, (int)arg3+1);
- return env->regs[0];
+ if (env->aarch64) {
+ return env->xregs[0];
+ } else {
+ return env->regs[0];
+ }
} else {
char *s2;
s = lock_user_string(arg0);
@@ -398,7 +488,11 @@ uint32_t do_arm_semihosting(CPUARMState *env)
GET_ARG(1);
if (use_gdb_syscalls()) {
gdb_do_syscall(arm_semi_cb, "system,%s", arg0, (int)arg1+1);
- return env->regs[0];
+ if (env->aarch64) {
+ return env->xregs[0];
+ } else {
+ return env->regs[0];
+ }
} else {
s = lock_user_string(arg0);
if (!s) {
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index d4a5899..2525569 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -56,6 +56,7 @@
#define EXCP_SMC 13 /* Secure Monitor Call */
#define EXCP_VIRQ 14
#define EXCP_VFIQ 15
+#define EXCP_ARMV8_HLT 16 /* avoid conflict with cpu-defs.h:EXCP_HLT */
#define ARMV7M_EXCP_RESET 1
#define ARMV7M_EXCP_NMI 2
@@ -489,7 +490,7 @@ typedef struct CPUARMState {
ARMCPU *cpu_arm_init(const char *cpu_model);
int cpu_arm_exec(CPUARMState *s);
-uint32_t do_arm_semihosting(CPUARMState *env);
+target_ulong do_arm_semihosting(CPUARMState *env);
void aarch64_sync_32_to_64(CPUARMState *env);
void aarch64_sync_64_to_32(CPUARMState *env);
diff --git a/target-arm/helper-a64.c b/target-arm/helper-a64.c
index 861f6fa..8803293 100644
--- a/target-arm/helper-a64.c
+++ b/target-arm/helper-a64.c
@@ -466,6 +466,14 @@ void aarch64_cpu_do_interrupt(CPUState *cs)
unsigned int new_el = arm_excp_target_el(cs, cs->exception_index);
target_ulong addr = env->cp15.vbar_el[new_el];
unsigned int new_mode = aarch64_pstate_mode(new_el, true);
+#ifndef CONFIG_USER_ONLY
+ uint64_t mask;
+#endif
+
+ uint32_t syndrome =
+ cs->exception_index == EXCP_ARMV8_HLT ?
+ env->exception.syndrome & ~0xffff :
+ env->exception.syndrome;
if (arm_current_el(env) < new_el) {
if (env->aarch64) {
@@ -482,7 +490,7 @@ void aarch64_cpu_do_interrupt(CPUState *cs)
if (qemu_loglevel_mask(CPU_LOG_INT)
&& !excp_is_internal(cs->exception_index)) {
qemu_log_mask(CPU_LOG_INT, "...with ESR 0x%" PRIx32 "\n",
- env->exception.syndrome);
+ syndrome);
}
if (arm_is_psci_call(cpu, cs->exception_index)) {
@@ -504,8 +512,24 @@ void aarch64_cpu_do_interrupt(CPUState *cs)
case EXCP_HVC:
case EXCP_HYP_TRAP:
case EXCP_SMC:
- env->cp15.esr_el[new_el] = env->exception.syndrome;
+ env->cp15.esr_el[new_el] = syndrome;
break;
+#ifndef CONFIG_USER_ONLY
+ case EXCP_ARMV8_HLT:
+ if (env->aarch64) {
+ if (semihosting_enabled) {
+ mask = env->exception.syndrome & 0xffff;
+ if (mask == 0xf000 ) {
+ env->xregs[0] = do_arm_semihosting(env);
+ qemu_log_mask(CPU_LOG_INT, "...handled a semihosting call\n");
+ return;
+ }
+ } else {
+ cpu_abort(cs, "Skipping semihosting call!\n");
+ }
+ }
+ break;
+#endif
case EXCP_IRQ:
case EXCP_VIRQ:
addr += 0x80;
diff --git a/target-arm/helper.c b/target-arm/helper.c
index e4e4931..4491b05 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -12,6 +12,7 @@
#include <zlib.h> /* For crc32 */
#ifndef CONFIG_USER_ONLY
+
static inline int get_phys_addr(CPUARMState *env, target_ulong address,
int access_type, ARMMMUIdx mmu_idx,
hwaddr *phys_ptr, MemTxAttrs *attrs, int *prot,
diff --git a/target-arm/internals.h b/target-arm/internals.h
index 2cc3017..ddbbc0f 100644
--- a/target-arm/internals.h
+++ b/target-arm/internals.h
@@ -50,6 +50,7 @@ static const char * const excnames[] = {
[EXCP_IRQ] = "IRQ",
[EXCP_FIQ] = "FIQ",
[EXCP_BKPT] = "Breakpoint",
+ [EXCP_ARMV8_HLT] = "HLT",
[EXCP_EXCEPTION_EXIT] = "QEMU v7M exception exit",
[EXCP_KERNEL_TRAP] = "QEMU intercept of kernel commpage",
[EXCP_STREX] = "QEMU intercept of STREX",
@@ -260,6 +261,13 @@ static inline uint32_t syn_aa32_bkpt(uint32_t imm16, bool is_thumb)
| (is_thumb ? 0 : ARM_EL_IL);
}
+static inline uint32_t syn_aa64_hlt(uint32_t imm16)
+{
+ // architecturally, the syndrome is uncategorized. We add the imm16 field so
+ // that it can be accessed later for semihosting
+ return (EC_UNCATEGORIZED << ARM_EL_EC_SHIFT) | ARM_EL_IL | (imm16 & 0xffff);
+}
+
static inline uint32_t syn_aa64_sysregtrap(int op0, int op1, int op2,
int crn, int crm, int rt,
int isread)
diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c
index 0b192a1..14a501c 100644
--- a/target-arm/translate-a64.c
+++ b/target-arm/translate-a64.c
@@ -1544,7 +1544,7 @@ static void disas_exc(DisasContext *s, uint32_t insn)
break;
}
/* HLT */
- unsupported_encoding(s, insn);
+ gen_exception_insn(s, 0, EXCP_ARMV8_HLT, syn_aa64_hlt(imm16));
break;
case 5:
if (op2_ll < 1 || op2_ll > 3) {
--
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [Qemu-devel] [RFC 03/14] Fix makefile
2015-08-05 16:51 [Qemu-devel] RFC: ARM Semihosting, PMU, and BBV Changes Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 01/14] Make unknown semihosting calls non-fatal Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 02/14] Added semihosting support for A64 in full-system mode Christopher Covington
@ 2015-08-05 16:51 ` Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 04/14] Modify load exclusive/store exclusive to use physical addresses with the monitor Christopher Covington
` (11 subsequent siblings)
14 siblings, 0 replies; 25+ messages in thread
From: Christopher Covington @ 2015-08-05 16:51 UTC (permalink / raw)
To: qemu-devel; +Cc: Christopher Covington
Fixes https://bugs.launchpad.net/qemu/+bug/1319493/
Signed-off-by: Christopher Covington <cov@codeaurora.org>
---
Makefile | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/Makefile b/Makefile
index 93af871..fcc6314 100644
--- a/Makefile
+++ b/Makefile
@@ -408,7 +408,11 @@ ifneq ($(CONFIG_MODULES),)
done
endif
ifneq ($(HELPERS-y),)
- $(call install-prog,$(HELPERS-y),$(DESTDIR)$(libexecdir))
+ $(INSTALL_DIR) "$(DESTDIR)$(libexecdir)"
+ $(INSTALL_PROG) $(HELPERS-y) "$(DESTDIR)$(libexecdir)"
+ifneq ($(STRIP),)
+ $(STRIP) $(addprefix "$(DESTDIR)$(libexecdir)/",$(notdir $(HELPERS-y)))
+endif
endif
ifneq ($(BLOBS),)
set -e; for x in $(BLOBS); do \
--
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [Qemu-devel] [RFC 04/14] Modify load exclusive/store exclusive to use physical addresses with the monitor
2015-08-05 16:51 [Qemu-devel] RFC: ARM Semihosting, PMU, and BBV Changes Christopher Covington
` (2 preceding siblings ...)
2015-08-05 16:51 ` [Qemu-devel] [RFC 03/14] Fix makefile Christopher Covington
@ 2015-08-05 16:51 ` Christopher Covington
2015-09-23 17:19 ` [Qemu-devel] [PATCHv2] target-arm: Use physical addresses for ldrex/strex Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 05/14] Fixed TLB invalidate ops Christopher Covington
` (10 subsequent siblings)
14 siblings, 1 reply; 25+ messages in thread
From: Christopher Covington @ 2015-08-05 16:51 UTC (permalink / raw)
To: qemu-devel; +Cc: Christopher Covington
Written by Derek Hower.
Signed-off-by: Christopher Covington <cov@codeaurora.org>
---
target-arm/helper-a64.h | 2 ++
target-arm/helper.c | 22 ++++++++++++++++++++++
target-arm/translate-a64.c | 25 +++++++++++++++++++++++--
3 files changed, 47 insertions(+), 2 deletions(-)
diff --git a/target-arm/helper-a64.h b/target-arm/helper-a64.h
index 1d3d10f..a713d29 100644
--- a/target-arm/helper-a64.h
+++ b/target-arm/helper-a64.h
@@ -46,3 +46,5 @@ DEF_HELPER_FLAGS_2(frecpx_f32, TCG_CALL_NO_RWG, f32, f32, ptr)
DEF_HELPER_FLAGS_2(fcvtx_f64_to_f32, TCG_CALL_NO_RWG, f32, f64, env)
DEF_HELPER_FLAGS_3(crc32_64, TCG_CALL_NO_RWG_SE, i64, i64, i64, i32)
DEF_HELPER_FLAGS_3(crc32c_64, TCG_CALL_NO_RWG_SE, i64, i64, i64, i32)
+
+DEF_HELPER_3(get_phys_addr64, i64, env, i64, i32)
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 4491b05..be564b2 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -24,6 +24,28 @@ static inline int get_phys_addr(CPUARMState *env, target_ulong address,
#define PMCRE 0x1
#endif
+#ifdef TARGET_AARCH64
+
+uint64_t HELPER(get_phys_addr64)(CPUARMState *env,
+ uint64_t vaddr, uint32_t memidx)
+{
+#ifdef CONFIG_USER_ONLY
+ return vaddr;
+#else
+ hwaddr phys_addr;
+ int prot; // ignored
+ target_ulong page_size; // ignored
+ MemTxAttrs attrs = {}; // ignored
+
+ // we just want the address from this function and don't care about faults.
+ // therefore, we always assume the operation is a load
+ get_phys_addr(env, vaddr, 0, memidx == 0, &phys_addr, &attrs, &prot, &page_size);
+ return phys_addr;
+#endif
+}
+
+#endif
+
static int vfp_gdb_get_reg(CPUARMState *env, uint8_t *buf, int reg)
{
int nregs;
diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c
index 14a501c..20d1d3c 100644
--- a/target-arm/translate-a64.c
+++ b/target-arm/translate-a64.c
@@ -1683,7 +1683,17 @@ static void gen_load_exclusive(DisasContext *s, int rt, int rt2,
tcg_gen_mov_i64(cpu_reg(s, rt), tmp);
tcg_temp_free_i64(tmp);
- tcg_gen_mov_i64(cpu_exclusive_addr, addr);
+
+ // the monitor must be set on the physical address
+ // we've already read the address at this point, so we know
+ // the translation won't fault
+ TCGv_i64 physaddr = tcg_temp_new_i64();
+ TCGv_i32 idx = tcg_temp_new_i32();
+ tcg_gen_movi_i32(idx, get_mem_index(s));
+ gen_helper_get_phys_addr64(physaddr, cpu_env, addr, idx);
+ tcg_gen_mov_i64(cpu_exclusive_addr, physaddr);
+ tcg_temp_free_i64(physaddr);
+ tcg_temp_free_i32(idx);
}
#ifdef CONFIG_USER_ONLY
@@ -1720,13 +1730,24 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2,
* basic block ends at the branch insn.
*/
tcg_gen_mov_i64(addr, inaddr);
- tcg_gen_brcond_i64(TCG_COND_NE, addr, cpu_exclusive_addr, fail_label);
tmp = tcg_temp_new_i64();
tcg_gen_qemu_ld_i64(tmp, addr, get_mem_index(s), MO_TE + size);
tcg_gen_brcond_i64(TCG_COND_NE, tmp, cpu_exclusive_val, fail_label);
tcg_temp_free_i64(tmp);
+ // the monitor must be checked on the physical address.
+ // We've alredy loaded this address, so we don't need to check for
+ // a fault condition
+ TCGv_i64 physaddr = tcg_temp_new_i64();
+ TCGv_i32 idx = tcg_temp_new_i32();
+ tcg_gen_movi_i32(idx, get_mem_index(s));
+ gen_helper_get_phys_addr64(physaddr, cpu_env, addr, idx);
+
+ tcg_gen_brcond_i64(TCG_COND_NE, physaddr, cpu_exclusive_addr, fail_label);
+ tcg_temp_free_i64(physaddr);
+ tcg_temp_free_i32(idx);
+
if (is_pair) {
TCGv_i64 addrhi = tcg_temp_new_i64();
TCGv_i64 tmphi = tcg_temp_new_i64();
--
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [Qemu-devel] [RFC 05/14] Fixed TLB invalidate ops.
2015-08-05 16:51 [Qemu-devel] RFC: ARM Semihosting, PMU, and BBV Changes Christopher Covington
` (3 preceding siblings ...)
2015-08-05 16:51 ` [Qemu-devel] [RFC 04/14] Modify load exclusive/store exclusive to use physical addresses with the monitor Christopher Covington
@ 2015-08-05 16:51 ` Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 06/14] Added support for block profiling for AArch32 and Aarch64 Christopher Covington
` (9 subsequent siblings)
14 siblings, 0 replies; 25+ messages in thread
From: Christopher Covington @ 2015-08-05 16:51 UTC (permalink / raw)
To: qemu-devel; +Cc: Christopher Covington
Prior to this patch, QEMU was only invalidating the TLB for the local
processor on a TLB flush event, causing unstable behavoir in smp
mode. This patch corrects the behavoir so that all TLBs are
invalidated across the system.
Written by Derek Hower.
Signed-off-by: Christopher Covington <cov@codeaurora.org>
---
target-arm/helper.c | 68 +++++++++++++++++++++++------------------------------
1 file changed, 30 insertions(+), 38 deletions(-)
diff --git a/target-arm/helper.c b/target-arm/helper.c
index be564b2..ff3c8f7 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -355,23 +355,35 @@ void init_cpreg_list(ARMCPU *cpu)
g_list_free(keys);
}
-static void dacr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
+static void arm_tlb_flush(CPUARMState *env, int flush_global)
{
- ARMCPU *cpu = arm_env_get_cpu(env);
+ CPUState* cpu;
+ CPU_FOREACH(cpu) {
+ tlb_flush(cpu, flush_global);
+ }
+}
+static void arm_tlb_flush_page(CPUARMState *env, target_ulong addr)
+{
+ CPUState* cpu;
+ CPU_FOREACH(cpu) {
+ tlb_flush_page(cpu, addr);
+ }
+}
+
+static void dacr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
+{
raw_write(env, ri, value);
- tlb_flush(CPU(cpu), 1); /* Flush TLB as domain not tracked in TLB */
+ arm_tlb_flush(env, 1);/* Flush TLB as domain not tracked in TLB */
}
static void fcse_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
{
- ARMCPU *cpu = arm_env_get_cpu(env);
-
if (raw_read(env, ri) != value) {
/* Unlike real hardware the qemu TLB uses virtual addresses,
* not modified virtual addresses, so this causes a TLB flush.
*/
- tlb_flush(CPU(cpu), 1);
+ arm_tlb_flush(env, 1);
raw_write(env, ri, value);
}
}
@@ -379,15 +391,13 @@ static void fcse_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
static void contextidr_write(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
- ARMCPU *cpu = arm_env_get_cpu(env);
-
if (raw_read(env, ri) != value && !arm_feature(env, ARM_FEATURE_MPU)
&& !extended_addresses_enabled(env)) {
/* For VMSA (when not using the LPAE long descriptor page table
* format) this register includes the ASID, so do a TLB flush.
* For PMSA it is purely a process ID and no action is needed.
*/
- tlb_flush(CPU(cpu), 1);
+ arm_tlb_flush(env, 1);
}
raw_write(env, ri, value);
}
@@ -396,36 +406,28 @@ static void tlbiall_write(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
/* Invalidate all (TLBIALL) */
- ARMCPU *cpu = arm_env_get_cpu(env);
-
- tlb_flush(CPU(cpu), 1);
+ arm_tlb_flush(env, 1);
}
static void tlbimva_write(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
/* Invalidate single TLB entry by MVA and ASID (TLBIMVA) */
- ARMCPU *cpu = arm_env_get_cpu(env);
-
- tlb_flush_page(CPU(cpu), value & TARGET_PAGE_MASK);
+ arm_tlb_flush(env, value & TARGET_PAGE_MASK);
}
static void tlbiasid_write(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
/* Invalidate by ASID (TLBIASID) */
- ARMCPU *cpu = arm_env_get_cpu(env);
-
- tlb_flush(CPU(cpu), value == 0);
+ arm_tlb_flush(env, value == 0);
}
static void tlbimvaa_write(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
/* Invalidate single entry by MVA, all ASIDs (TLBIMVAA) */
- ARMCPU *cpu = arm_env_get_cpu(env);
-
- tlb_flush_page(CPU(cpu), value & TARGET_PAGE_MASK);
+ arm_tlb_flush_page(env, value & TARGET_PAGE_MASK);
}
/* IS variants of TLB operations must affect all cores */
@@ -1792,13 +1794,11 @@ static void vmsa_ttbcr_raw_write(CPUARMState *env, const ARMCPRegInfo *ri,
static void vmsa_ttbcr_write(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
- ARMCPU *cpu = arm_env_get_cpu(env);
-
if (arm_feature(env, ARM_FEATURE_LPAE)) {
/* With LPAE the TTBCR could result in a change of ASID
* via the TTBCR.A1 bit, so do a TLB flush.
*/
- tlb_flush(CPU(cpu), 1);
+ arm_tlb_flush(env, 1);
}
vmsa_ttbcr_raw_write(env, ri, value);
}
@@ -1818,11 +1818,10 @@ static void vmsa_ttbcr_reset(CPUARMState *env, const ARMCPRegInfo *ri)
static void vmsa_tcr_el1_write(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
- ARMCPU *cpu = arm_env_get_cpu(env);
TCR *tcr = raw_ptr(env, ri);
/* For AArch64 the A1 bit could result in a change of ASID, so TLB flush. */
- tlb_flush(CPU(cpu), 1);
+ arm_tlb_flush(env, 1);
tcr->raw_tcr = value;
}
@@ -1833,9 +1832,7 @@ static void vmsa_ttbr_write(CPUARMState *env, const ARMCPRegInfo *ri,
* must flush the TLB.
*/
if (cpreg_field_is_64bit(ri)) {
- ARMCPU *cpu = arm_env_get_cpu(env);
-
- tlb_flush(CPU(cpu), 1);
+ arm_tlb_flush(env, 1);
}
raw_write(env, ri, value);
}
@@ -2166,29 +2163,26 @@ static void tlbi_aa64_va_write(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
/* Invalidate by VA (AArch64 version) */
- ARMCPU *cpu = arm_env_get_cpu(env);
uint64_t pageaddr = sextract64(value << 12, 0, 56);
- tlb_flush_page(CPU(cpu), pageaddr);
+ arm_tlb_flush_page(env, pageaddr);
}
static void tlbi_aa64_vaa_write(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
/* Invalidate by VA, all ASIDs (AArch64 version) */
- ARMCPU *cpu = arm_env_get_cpu(env);
uint64_t pageaddr = sextract64(value << 12, 0, 56);
- tlb_flush_page(CPU(cpu), pageaddr);
+ arm_tlb_flush_page(env, pageaddr);
}
static void tlbi_aa64_asid_write(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
/* Invalidate by ASID (AArch64 version) */
- ARMCPU *cpu = arm_env_get_cpu(env);
int asid = extract64(value, 48, 16);
- tlb_flush(CPU(cpu), asid == 0);
+ arm_tlb_flush(env, asid == 0);
}
static void tlbi_aa64_va_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
@@ -2271,8 +2265,6 @@ static void spsel_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t val)
static void sctlr_write(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
- ARMCPU *cpu = arm_env_get_cpu(env);
-
if (raw_read(env, ri) == value) {
/* Skip the TLB flush if nothing actually changed; Linux likes
* to do a lot of pointless SCTLR writes.
@@ -2283,7 +2275,7 @@ static void sctlr_write(CPUARMState *env, const ARMCPRegInfo *ri,
raw_write(env, ri, value);
/* ??? Lots of these bits are not implemented. */
/* This may enable/disable the MMU, so do a TLB flush. */
- tlb_flush(CPU(cpu), 1);
+ arm_tlb_flush(env, 1);
}
static const ARMCPRegInfo v8_cp_reginfo[] = {
--
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [Qemu-devel] [RFC 06/14] Added support for block profiling for AArch32 and Aarch64
2015-08-05 16:51 [Qemu-devel] RFC: ARM Semihosting, PMU, and BBV Changes Christopher Covington
` (4 preceding siblings ...)
2015-08-05 16:51 ` [Qemu-devel] [RFC 05/14] Fixed TLB invalidate ops Christopher Covington
@ 2015-08-05 16:51 ` Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 07/14] Add PMU to ARM virt platform Christopher Covington
` (8 subsequent siblings)
14 siblings, 0 replies; 25+ messages in thread
From: Christopher Covington @ 2015-08-05 16:51 UTC (permalink / raw)
To: qemu-devel; +Cc: Christopher Covington
Blocks are split on all branch instructions. Code assumes a translation
block does not end in a branch. When a branch instruction is identified,
gen_store_is_jmp(1) is called to indicate that a split should happen on
this instruction. The exit notifier is used to finalize the block
profiler, ensuring all block vector files are closed and the block stats
are printed to stdout when QEMU exits.
Written by Gideon Billings and Aaron Lindsay.
Signed-off-by: Christopher Covington <cov@codeaurora.org>
---
Makefile.objs | 1 +
bbv_profiler.c | 77 +++++++
bbv_profiler.h | 35 ++++
configure | 19 ++
include/exec/cpu-defs.h | 6 +
qemu-options.hx | 10 +
rules.mak | 3 +-
target-arm/helper.c | 57 +++++
target-arm/helper.h | 6 +
target-arm/translate-a64.c | 80 +++++++
target-arm/translate.c | 503 ++++++++++++++++++++++++++++++++++++++++++++-
vl.c | 58 ++++++
12 files changed, 850 insertions(+), 5 deletions(-)
create mode 100644 bbv_profiler.c
create mode 100644 bbv_profiler.h
diff --git a/Makefile.objs b/Makefile.objs
index 28999d3..7da955a 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -2,6 +2,7 @@
# Common libraries for tools and emulators
stub-obj-y = stubs/
util-obj-y = util/ qobject/ qapi/ qapi-types.o qapi-visit.o qapi-event.o
+util-obj-$(CONFIG_BBVEC) += bbv_profiler.o
#######################################################################
# block-obj-y is code used by both qemu system emulation and qemu-img
diff --git a/bbv_profiler.c b/bbv_profiler.c
new file mode 100644
index 0000000..51e8060
--- /dev/null
+++ b/bbv_profiler.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2015 the Linux Foundation.
+ * Written by Gideon Billings and Aaron Lindsay.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include "bbv_profiler.h"
+
+static BasicBlockTraceHandle trace = NULL;
+static uint32_t mode = 0;
+static uint64_t pid = 0;
+static Notifier exit_notifier;
+
+static void bbtrace_exit_notifier(Notifier *notifier, void *data)
+{
+ assert(trace != NULL);
+
+ // In addition to freeing memory, bbvec_free also closes open files and
+ // prints statistics.
+ bbvec_free(trace);
+}
+
+void bbtrace_init(uint64_t interval_size, const char *filename_base, bool trace_userspace_only, bool combine_pids)
+{
+ assert(trace == NULL);
+ trace = bbvec_create(interval_size, filename_base, trace_userspace_only, combine_pids);
+}
+
+Notifier* get_bbtrace_exit_notifier(void) {
+ exit_notifier.notify = &bbtrace_exit_notifier;
+ return &exit_notifier;
+}
+
+int bbtrace_initialized(void) {
+ return (trace != 0);
+}
+
+void bb_process(uint64_t PC, uint64_t IC)
+{
+ assert(trace != NULL);
+
+ bbvec_insert_bb(trace, PC, IC);
+}
+
+void bb_context_check_mode(uint64_t IC, uint32_t new_mode)
+{
+ assert(trace != NULL);
+
+ if (mode != new_mode) {
+ mode = new_mode;
+ bool usrmode = !new_mode;
+ bbvec_mode_change(trace, usrmode, IC);
+ }
+}
+
+void bb_context_check_pid(uint64_t IC, uint64_t new_pid)
+{
+ assert(trace != NULL);
+
+ if (pid != new_pid) {
+ pid = new_pid;
+ bbvec_pid_change(trace, new_pid, IC);
+ }
+}
diff --git a/bbv_profiler.h b/bbv_profiler.h
new file mode 100644
index 0000000..26dfa1f
--- /dev/null
+++ b/bbv_profiler.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015 the Linux Foundation.
+ * Written by Gideon Billings and Aaron Lindsay.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef BBV_PROFILER
+#define BBV_PROFILER
+
+#include "bbvec_trace.h"
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "sysemu/sysemu.h"
+
+void bbtrace_init(uint64_t interval_size, const char *filename_base, bool trace_userspace_only, bool combine_pids);
+Notifier* get_bbtrace_exit_notifier(void);
+int bbtrace_initialized(void);
+void bb_process(uint64_t PC, uint64_t IC);
+void bb_context_check_mode(uint64_t IC, uint32_t mode);
+void bb_context_check_pid(uint64_t IC, uint64_t tpid);
+
+#endif
diff --git a/configure b/configure
index 255d85b..5ddb2a6 100755
--- a/configure
+++ b/configure
@@ -29,6 +29,8 @@ TMPL="${TMPDIR1}/${TMPB}.lo"
TMPA="${TMPDIR1}/lib${TMPB}.la"
TMPE="${TMPDIR1}/${TMPB}.exe"
+LBBVEC_FLAG=""
+
rm -f config.log
# Print a helpful header at the top of config.log
@@ -337,6 +339,9 @@ vhdx=""
quorum=""
numa=""
tcmalloc="no"
+libbbvec_prefix=""
+bbvec_lib=""
+bbvec_include=""
# parse CC options first
for opt do
@@ -774,6 +779,15 @@ for opt do
;;
--target-list=*) target_list="$optarg"
;;
+ --enable-bb-profile)
+ bbvec="yes"
+ libbbvec_prefix="/prj/qct/qctps/modeling/ral_armv8/workloads/libbbvec/latest"
+ bbvec_lib="$libbbvec_prefix/lib"
+ bbvec_include="$libbbvec_prefix/include"
+ LDFLAGS="-L$bbvec_lib $LDFLAGS"
+ LBBVEC_FLAG="-lbbvec"
+ QEMU_INCLUDES="-I$bbvec_include $QEMU_INCLUDES"
+ ;;
--enable-trace-backends=*) trace_backends="$optarg"
;;
# XXX: backwards compatibility
@@ -4980,6 +4994,10 @@ if test "$rdma" = "yes" ; then
echo "CONFIG_RDMA=y" >> $config_host_mak
fi
+if test "$bbvec" = "yes" ; then
+ echo "CONFIG_BBVEC=y" >> $config_host_mak
+fi
+
# Hold two types of flag:
# CONFIG_THREAD_SETNAME_BYTHREAD - we've got a way of setting the name on
# a thread we have a handle to
@@ -5040,6 +5058,7 @@ echo "CFLAGS=$CFLAGS" >> $config_host_mak
echo "CFLAGS_NOPIE=$CFLAGS_NOPIE" >> $config_host_mak
echo "QEMU_CFLAGS=$QEMU_CFLAGS" >> $config_host_mak
echo "QEMU_INCLUDES=$QEMU_INCLUDES" >> $config_host_mak
+echo "LBBVEC_FLAG=$LBBVEC_FLAG" >> $config_host_mak
if test "$sparse" = "yes" ; then
echo "CC := REAL_CC=\"\$(CC)\" cgcc" >> $config_host_mak
echo "CPP := REAL_CC=\"\$(CPP)\" cgcc" >> $config_host_mak
diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h
index 3f56546..1eed930 100644
--- a/include/exec/cpu-defs.h
+++ b/include/exec/cpu-defs.h
@@ -134,5 +134,11 @@ typedef struct CPUIOTLBEntry {
#define CPU_COMMON \
/* soft mmu support */ \
CPU_COMMON_TLB \
+ /* Instruction Count (for profiling) */ \
+ uint64_t prof_ic; \
+ /* PC (for profiling) */ \
+ uint64_t prof_pc; \
+ /* next page start (for profiling) */ \
+ uint64_t prof_is_jmp; \
#endif
diff --git a/qemu-options.hx b/qemu-options.hx
index ec356f6..e3b1a6a 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -913,6 +913,16 @@ with a serial console. Use @key{C-a h} for help on switching between
the console and monitor.
ETEXI
+DEF("bbvec", HAS_ARG, QEMU_OPTION_bbvec,
+ "-bbvec [intervalSize=i][,fileBase=file][,userspaceOnly=i][,combinePIDs=i]\n"
+ " enable basic block vector profiling\n", QEMU_ARCH_ALL)
+STEXI
+@item -bbvec @var{option}[,@var{option}[,@var{option}[,...]]]
+@findex -bbvec
+This option enables basic block vector profiling. Blocks are split on
+any branch instruction.
+ETEXI
+
DEF("curses", 0, QEMU_OPTION_curses,
"-curses use a curses/ncurses interface instead of SDL\n",
QEMU_ARCH_ALL)
diff --git a/rules.mak b/rules.mak
index 3a05627..9830f71 100644
--- a/rules.mak
+++ b/rules.mak
@@ -65,7 +65,7 @@ LINKPROG = $(or $(CXX),$(CC))
ifeq ($(LIBTOOL),)
LINK = $(call quiet-command, $(LINKPROG) $(QEMU_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ \
$(call process-archive-undefs, $1) \
- $(version-obj-y) $(call extract-libs,$1) $(LIBS)," LINK $(TARGET_DIR)$@")
+ $(LBBVEC_FLAG) $(version-obj-y) $(call extract-libs,$1) $(LIBS)," LINK $(TARGET_DIR)$@")
else
LIBTOOL += $(if $(V),,--quiet)
%.lo: %.c
@@ -79,6 +79,7 @@ LINK = $(call quiet-command,\
$(if $(filter %.lo %.la,$1),$(LIBTOOL) --mode=link --tag=CC \
)$(LINKPROG) $(QEMU_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ \
$(call process-archive-undefs, $1)\
+ $(LBBVEC_FLAG) \
$(if $(filter %.lo %.la,$1),$(version-lobj-y),$(version-obj-y)) \
$(if $(filter %.lo %.la,$1),$(LIBTOOLFLAGS)) \
$(call extract-libs,$(1:.lo=.o)) $(LIBS),$(if $(filter %.lo %.la,$1),"lt LINK ", " LINK ")"$(TARGET_DIR)$@")
diff --git a/target-arm/helper.c b/target-arm/helper.c
index ff3c8f7..d2c02be 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -11,6 +11,10 @@
#include "arm_ldst.h"
#include <zlib.h> /* For crc32 */
+#ifdef CONFIG_BBVEC
+#include "bbv_profiler.h"
+#endif // CONFIG_BBVEC
+
#ifndef CONFIG_USER_ONLY
static inline int get_phys_addr(CPUARMState *env, target_ulong address,
@@ -4005,6 +4009,59 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
env->uncached_cpsr = (env->uncached_cpsr & ~mask) | (val & mask);
}
+#ifdef CONFIG_BBVEC
+void HELPER(bbv_profile)(CPUARMState *env)
+{
+ /* Helper profiles previous tb, so value of PC will be 0 on first call.
+ * Also make sure basic block split is on a branch. Otherwise, join with
+ * next basic block.
+ */
+ if (env->prof_pc && env->prof_is_jmp) {
+ bb_process(env->prof_pc, env->prof_ic);
+ env->prof_ic = 0;
+ }
+}
+
+void HELPER(context_check_mode)(CPUARMState *env)
+{
+ uint32_t mode;
+
+ /* Get current mode: userspace or privileged */
+ if (env->aarch64) {
+ mode = extract32(env->pstate, 2, 2);
+ }
+ else if ((env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_USR) {
+ mode = 0;
+ }
+ /* We don't currently implement the Virtualization or TrustZone
+ * extensions, so PL2 and PL3 don't exist for us.
+ */
+ else mode = 1;
+
+ bb_context_check_mode(env->prof_ic, mode);
+}
+
+void HELPER(context_check_pid)(CPUARMState *env)
+{
+ uint64_t pid;
+
+ /* Read pid from CONTEXTIDR register. In aarch32, if EL1 is not in AArch64
+ * mode, we need to shift out the address space identifier in the first 8 bits.
+ *
+ * FIXME: according to the Arm instruction reference, CONTEXTIDR only contains
+ * the address identifier if TTBVR.EAE = 1. We need to check if this is
+ * the same in QEMU.
+ */
+ if (arm_el_is_aa64(env, 1)) {
+ pid = env->cp15.contextidr_el[1];
+ }
+ else
+ pid = env->cp15.contextidr_el[1] >> 8;
+
+ bb_context_check_pid(env->prof_ic, pid);
+}
+#endif //CONFIG_BBVEC
+
/* Sign/zero extend */
uint32_t HELPER(sxtb16)(uint32_t x)
{
diff --git a/target-arm/helper.h b/target-arm/helper.h
index dec3728..41291c9 100644
--- a/target-arm/helper.h
+++ b/target-arm/helper.h
@@ -37,6 +37,12 @@ PAS_OP(uq)
PAS_OP(uh)
#undef PAS_OP
+#ifdef CONFIG_BBVEC
+DEF_HELPER_1(bbv_profile, void, env)
+DEF_HELPER_1(context_check_mode, void, env)
+DEF_HELPER_1(context_check_pid, void, env)
+#endif // CONFIG_BBVEC
+
DEF_HELPER_3(ssat, i32, env, i32, i32)
DEF_HELPER_3(usat, i32, env, i32, i32)
DEF_HELPER_3(ssat16, i32, env, i32, i32)
diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c
index 20d1d3c..80b27ed 100644
--- a/target-arm/translate-a64.c
+++ b/target-arm/translate-a64.c
@@ -37,6 +37,10 @@
#include "trace-tcg.h"
+#ifdef CONFIG_BBVEC
+#include "bbv_profiler.h"
+#endif // CONFIG_BBVEC
+
static TCGv_i64 cpu_X[32];
static TCGv_i64 cpu_pc;
static TCGv_i32 cpu_NF, cpu_ZF, cpu_CF, cpu_VF;
@@ -45,6 +49,15 @@ static TCGv_i32 cpu_NF, cpu_ZF, cpu_CF, cpu_VF;
static TCGv_i64 cpu_exclusive_addr;
static TCGv_i64 cpu_exclusive_val;
static TCGv_i64 cpu_exclusive_high;
+#ifdef CONFIG_BBVEC
+/* cpu_pc is used for generating jumps and does not always track the
+ * current pc. cpu_prof_pc is used by the basic block profiling code to
+ * keep track of the current pc, so the last pc in the block can be
+ * captured. */
+static TCGv_i64 cpu_prof_pc;
+static TCGv_i64 cpu_prof_ic;
+static TCGv_i64 cpu_prof_is_jmp;
+#endif // CONFIG_BBVEC
#ifdef CONFIG_USER_ONLY
static TCGv_i64 cpu_exclusive_test;
static TCGv_i32 cpu_exclusive_info;
@@ -109,6 +122,13 @@ void a64_translate_init(void)
cpu_CF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, CF), "CF");
cpu_VF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, VF), "VF");
+#ifdef CONFIG_BBVEC
+ // bbvec profiling globals
+ cpu_prof_ic = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUARMState, prof_ic), "prof_ic");
+ cpu_prof_pc = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUARMState, prof_pc), "prof_pc");
+ cpu_prof_is_jmp = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUARMState, prof_is_jmp), "prof_is_jmp");
+#endif // CONFIG_BBVEC
+
cpu_exclusive_addr = tcg_global_mem_new_i64(TCG_AREG0,
offsetof(CPUARMState, exclusive_addr), "exclusive_addr");
cpu_exclusive_val = tcg_global_mem_new_i64(TCG_AREG0,
@@ -188,6 +208,24 @@ void gen_a64_set_pc_im(uint64_t val)
tcg_gen_movi_i64(cpu_pc, val);
}
+#ifdef CONFIG_BBVEC
+/* Basic block profiling functions */
+static void gen_insn_cnt_incr(CPUARMState * env, DisasContext *s)
+{
+ tcg_gen_addi_i64(cpu_prof_ic, cpu_prof_ic, 1);
+}
+
+static void gen_pc_incr(CPUARMState * env, DisasContext *s)
+{
+ tcg_gen_movi_i64(cpu_prof_pc, s->pc);
+}
+
+static void gen_store_is_jmp(uint32_t jmp)
+{
+ tcg_gen_movi_i64(cpu_prof_is_jmp, jmp);
+}
+#endif // CONFIG_BBVEC
+
static void gen_exception_internal(int excp)
{
TCGv_i32 tcg_excp = tcg_const_i32(excp);
@@ -1584,9 +1622,17 @@ static void disas_uncond_b_reg(DisasContext *s, uint32_t insn)
switch (opc) {
case 0: /* BR */
case 2: /* RET */
+#ifdef CONFIG_BBVEC
+ if (bbtrace_initialized())
+ gen_store_is_jmp(1);
+#endif // CONFIG_BBVEC
tcg_gen_mov_i64(cpu_pc, cpu_reg(s, rn));
break;
case 1: /* BLR */
+#ifdef CONFIG_BBVEC
+ if (bbtrace_initialized())
+ gen_store_is_jmp(1);
+#endif // CONFIG_BBVEC
tcg_gen_mov_i64(cpu_pc, cpu_reg(s, rn));
tcg_gen_movi_i64(cpu_reg(s, 30), s->pc);
break;
@@ -1619,15 +1665,31 @@ static void disas_b_exc_sys(DisasContext *s, uint32_t insn)
switch (extract32(insn, 25, 7)) {
case 0x0a: case 0x0b:
case 0x4a: case 0x4b: /* Unconditional branch (immediate) */
+#ifdef CONFIG_BBVEC
+ if (bbtrace_initialized())
+ gen_store_is_jmp(1);
+#endif // CONFIG_BBVEC
disas_uncond_b_imm(s, insn);
break;
case 0x1a: case 0x5a: /* Compare & branch (immediate) */
+#ifdef CONFIG_BBVEC
+ if (bbtrace_initialized())
+ gen_store_is_jmp(1);
+#endif // CONFIG_BBVEC
disas_comp_b_imm(s, insn);
break;
case 0x1b: case 0x5b: /* Test & branch (immediate) */
+#ifdef CONFIG_BBVEC
+ if (bbtrace_initialized())
+ gen_store_is_jmp(1);
+#endif // CONFIG_BBVEC
disas_test_b_imm(s, insn);
break;
case 0x2a: /* Conditional branch (immediate) */
+#ifdef CONFIG_BBVEC
+ if (bbtrace_initialized())
+ gen_store_is_jmp(1);
+#endif // CONFIG_BBVEC
disas_cond_b_imm(s, insn);
break;
case 0x6a: /* Exception generation / System */
@@ -11006,7 +11068,25 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu,
tcg_clear_temp_count();
+#ifdef CONFIG_BBVEC
+ /* Profile previously run block, check for PID change, and initialize
+ * prof_is_jmp flag. */
+ if (bbtrace_initialized()) {
+ gen_helper_bbv_profile(cpu_env);
+ gen_store_is_jmp(0);
+ gen_helper_context_check_pid(cpu_env);
+ }
+#endif // CONFIG_BBVEC
+
do {
+#ifdef CONFIG_BBVEC
+ if (bbtrace_initialized()) {
+ gen_helper_context_check_mode(cpu_env);
+ gen_insn_cnt_incr(env, dc);
+ gen_pc_incr(env, dc);
+ }
+#endif // CONFIG_BBVEC
+
if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) {
QTAILQ_FOREACH(bp, &cs->breakpoints, entry) {
if (bp->pc == dc->pc) {
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 9116529..7a19a8b 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -37,6 +37,9 @@
#include "trace-tcg.h"
+#ifdef CONFIG_BBVEC
+#include "bbv_profiler.h"
+#endif
#define ENABLE_ARCH_4T arm_dc_feature(s, ARM_FEATURE_V4T)
#define ENABLE_ARCH_5 arm_dc_feature(s, ARM_FEATURE_V5)
@@ -67,6 +70,11 @@ static TCGv_i32 cpu_R[16];
static TCGv_i32 cpu_CF, cpu_NF, cpu_VF, cpu_ZF;
static TCGv_i64 cpu_exclusive_addr;
static TCGv_i64 cpu_exclusive_val;
+#ifdef CONFIG_BBVEC
+static TCGv_i64 cpu_prof_ic;
+static TCGv_i64 cpu_prof_pc;
+static TCGv_i64 cpu_prof_is_jmp;
+#endif
#ifdef CONFIG_USER_ONLY
static TCGv_i64 cpu_exclusive_test;
static TCGv_i32 cpu_exclusive_info;
@@ -99,6 +107,13 @@ void arm_translate_init(void)
cpu_VF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, VF), "VF");
cpu_ZF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, ZF), "ZF");
+#ifdef CONFIG_BBVEC
+ // bbvec profiling globals
+ cpu_prof_ic = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUARMState, prof_ic), "prof_ic");
+ cpu_prof_pc = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUARMState, prof_pc), "prof_pc");
+ cpu_prof_is_jmp = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUARMState, prof_is_jmp), "prof_is_jmp");
+#endif
+
cpu_exclusive_addr = tcg_global_mem_new_i64(TCG_AREG0,
offsetof(CPUARMState, exclusive_addr), "exclusive_addr");
cpu_exclusive_val = tcg_global_mem_new_i64(TCG_AREG0,
@@ -177,11 +192,33 @@ static inline TCGv_i32 load_reg(DisasContext *s, int reg)
return tmp;
}
+#ifdef CONFIG_BBVEC
+/* Basic block profiling functions */
+static void gen_insn_cnt_incr(CPUARMState * env, DisasContext *s)
+{
+ tcg_gen_addi_i64(cpu_prof_ic, cpu_prof_ic, 1);
+}
+
+static void gen_pc_incr(CPUARMState * env, DisasContext *s)
+{
+ tcg_gen_movi_i64(cpu_prof_pc, s->pc);
+}
+
+static void gen_store_is_jmp(uint32_t jmp)
+{
+ tcg_gen_movi_i64(cpu_prof_is_jmp, jmp);
+}
+#endif
+
/* Set a CPU register. The source must be a temporary and will be
marked as dead. */
static void store_reg(DisasContext *s, int reg, TCGv_i32 var)
{
if (reg == 15) {
+#ifdef CONFIG_BBVEC
+ if (bbtrace_initialized())
+ gen_store_is_jmp(1);
+#endif
tcg_gen_andi_i32(var, var, ~1);
s->is_jmp = DISAS_JUMP;
}
@@ -834,6 +871,10 @@ static inline void gen_bx_im(DisasContext *s, uint32_t addr)
{
TCGv_i32 tmp;
+#ifdef CONFIG_BBVEC
+ if (bbtrace_initialized())
+ gen_store_is_jmp(1);
+#endif
s->is_jmp = DISAS_UPDATE;
if (s->thumb != (addr & 1)) {
tmp = tcg_temp_new_i32();
@@ -847,6 +888,10 @@ static inline void gen_bx_im(DisasContext *s, uint32_t addr)
/* Set PC and Thumb state from var. var is marked as dead. */
static inline void gen_bx(DisasContext *s, TCGv_i32 var)
{
+#ifdef CONFIG_BBVEC
+ if (bbtrace_initialized())
+ gen_store_is_jmp(1);
+#endif
s->is_jmp = DISAS_UPDATE;
tcg_gen_andi_i32(cpu_R[15], var, ~1);
tcg_gen_andi_i32(var, var, 1);
@@ -3963,6 +4008,10 @@ static inline void gen_jmp (DisasContext *s, uint32_t dest)
dest |= 1;
gen_bx_im(s, dest);
} else {
+#ifdef CONFIG_BBVEC
+ if (bbtrace_initialized())
+ gen_store_is_jmp(1);
+#endif
gen_goto_tb(s, 0, dest);
s->is_jmp = DISAS_TB_JUMP;
}
@@ -7795,6 +7844,257 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
goto illegal_op;
}
if (cond != 0xe) {
+#ifdef CONFIG_BBVEC
+ /* For conditional arm instructions, checks for branching instructions
+ * need to happen here before the conditional is evaluated for basic
+ * block profiling. This is because if the conditional is false, a direct
+ * jump to the next instruction is generated and any code after this point
+ * will be skipped. We only want to split blocks on conditional instructions
+ * that are branches, or instructions that set the pc. The code below is
+ * taken from the code for decoding the instruction directly after this
+ * conditional check. Only the cases that lead to the generation of a jump or
+ * pc write are checked. Instructions that can write the pc are identified by
+ * calls to store_reg(), store_reg_bx(), and store_reg_from_load() where the
+ * value of reg can be 15. Instructions that generate a branch are identified
+ * by calls to gen_bx_im(), gen_bx(), and gen_jmp().
+ */
+ if (bbtrace_initialized()) {
+ if ((insn & 0x0f900000) == 0x03000000) {
+ if ((insn & (1 << 21)) == 0) {
+ rd = (insn >> 12) & 0xf;
+ /* possible write to pc register */
+ if (rd == 15)
+ gen_store_is_jmp(1);
+ }
+ }
+ else if ((insn & 0x0f900000) == 0x01000000
+ && (insn & 0x00000090) != 0x00000090) {
+ /* miscellaneous instructions */
+ op1 = (insn >> 21) & 3;
+ sh = (insn >> 4) & 0xf;
+ rm = insn & 0xf;
+ switch (sh) {
+ case 0x0:
+ if (!(op1 & 1)) {
+ rd = (insn >> 12) & 0xf;
+ /* possible write to pc register */
+ if (rd == 15)
+ gen_store_is_jmp(1);
+ }
+ break;
+ case 0x1:
+ if (op1 == 1)
+ gen_store_is_jmp(1);
+ else if (op1 == 3) {
+ /* clz */
+ rd = (insn >> 12) & 0xf;
+ if (rd == 15)
+ gen_store_is_jmp(1);
+ }
+ break;
+ case 0x2:
+ if (op1 == 1)
+ gen_store_is_jmp(1);
+ break;
+ case 0x3:
+ if (op1 == 1)
+ gen_store_is_jmp(1);
+ break;
+ case 0x4:
+ rd = extract32(insn, 12, 4);
+ if (rd == 15)
+ gen_store_is_jmp(1);
+ break;
+ case 0x5:
+ rd = (insn >> 12) & 0xf;
+ if (rd == 15)
+ gen_store_is_jmp(1);
+ break;
+ case 0x8: case 0xa: case 0xc: case 0xe:
+ rd = (insn >> 16) & 0xf;
+ if (rd == 15) {
+ if (op1 != 2)
+ gen_store_is_jmp(1);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ else if (((insn & 0x0e000000) == 0 &&
+ (insn & 0x00000090) != 0x90) ||
+ ((insn & 0x0e000000) == (1 << 25))) {
+ int set_cc, logic_cc;
+ op1 = (insn >> 21) & 0xf;
+ set_cc = (insn >> 20) & 1;
+ logic_cc = table_logic_cc[op1] & set_cc;
+ rd = (insn >> 12) & 0xf;
+ if (rd == 15) {
+ if (op1 == 0x00 || op1 == 0x01 || op1 == 0x03 ||
+ op1 == 0x05 || op1 == 0x06 || op1 == 0x07 ||
+ op1 == 0x0c || op1 == 0x0e || op1 == 0x0f)
+ gen_store_is_jmp(1);
+ else if (op1 == 0x02 && !set_cc)
+ gen_store_is_jmp(1);
+ else if (op1 == 0x0d && !logic_cc)
+ gen_store_is_jmp(1);
+ }
+ }
+ else {
+ /* other instructions */
+ op1 = (insn >> 24) & 0xf;
+ switch(op1) {
+ case 0x0:
+ case 0x1:
+ /* multiplies, extra load/stores */
+ sh = (insn >> 5) & 3;
+ if (sh == 0) {
+ if (op1 == 0x0) {
+ rd = (insn >> 16) & 0xf;
+ rn = (insn >> 12) & 0xf;
+ op1 = (insn >> 20) & 0xf;
+ switch (op1) {
+ case 0: case 1: case 2: case 3: case 6:
+ if (rd == 15)
+ gen_store_is_jmp(1);
+ break;
+ case 8: case 9: case 10: case 11:
+ case 12: case 13: case 14: case 15:
+ if (rn == 15 || rd == 15)
+ gen_store_is_jmp(1);
+ break;
+ }
+ } else {
+ rd = (insn >> 12) & 0xf;
+ if (rd == 15) {
+ if (insn & (1 << 23)) {
+ /* load/store exclusive */
+ int op2 = (insn >> 8) & 3;
+ if (op2 == 0) {
+ if (insn & (1 << 20))
+ gen_store_is_jmp(1);
+ }
+ } else {
+ gen_store_is_jmp(1);
+ }
+ }
+ }
+ } else {
+ /* Misc load/store */
+ int load = 0;
+ rd = (insn >> 12) & 0xf;
+ rn = (insn >> 16) & 0xf;
+ if (insn & (1 << 20))
+ load = 1;
+ else if (sh & 2) {
+ if (sh & 1)
+ load = 0;
+ else if (rd == 15) {
+ gen_store_is_jmp(1);
+ load = 1;
+ }
+ }
+ if (!(insn & (1 << 24)) && rn == 15)
+ gen_store_is_jmp(1);
+ else if ((insn & (1 << 21)) && rn == 15)
+ gen_store_is_jmp(1);
+ if (load && rd == 15)
+ gen_store_is_jmp(1);
+ break;
+ }
+ break;
+ case 0x4:
+ case 0x5:
+ goto prof_do_ldst;
+ case 0x6:
+ case 0x7:
+ if (insn & (1 << 4)) {
+ rd = (insn >> 12) & 0xf;
+ rn = (insn >> 16) & 0xf;
+ switch ((insn >> 23) & 3) {
+ case 0: /* Parallel add/subtract. */
+ if (rd == 15)
+ gen_store_is_jmp(1);
+ break;
+ case 1:
+ if (rd == 15)
+ gen_store_is_jmp(1);
+ break;
+ case 2: /* Multiplies (Type 3). */
+ switch ((insn >> 20) & 0x7) {
+ case 5:
+ if (rn == 15)
+ gen_store_is_jmp(1);
+ break;
+ case 0:
+ case 4:
+ if (!(insn & (1 << 22)) && rn == 15)
+ gen_store_is_jmp(1);
+ break;
+ case 1:
+ case 3:
+ if (rn == 15)
+ gen_store_is_jmp(1);
+ break;
+ }
+ break;
+ case 3:
+ op1 = ((insn >> 17) & 0x38) | ((insn >> 5) & 7);
+ if (op1 == 0 && rn == 15)
+ gen_store_is_jmp(1);
+ else if (rd == 15)
+ gen_store_is_jmp(1);
+ break;
+ }
+ break;
+ }
+ prof_do_ldst:
+ rn = (insn >> 16) & 0xf;
+ rd = (insn >> 12) & 0xf;
+ if (!(insn & (1 << 24)) && rn == 15)
+ gen_store_is_jmp(1);
+ else if ((insn & (1 << 21)) && rn == 15)
+ gen_store_is_jmp(1);
+ if ((insn & (1 << 20)) && rd == 15)
+ gen_store_is_jmp(1);
+ break;
+ case 0x08:
+ case 0x09:
+ {
+ int i, user, loaded_base;
+ loaded_base = 0;
+ rn = (insn >> 16) & 0xf;
+ user = 0;
+ if (insn & (1 << 22)) {
+ if ((insn & (1 << 15)) == 0)
+ user = 1;
+ }
+ if (!user) {
+ for(i=0;i<16;i++) {
+ if ((insn & (1 << i)) && (insn & (1 << 20))) {
+ if (i != rn)
+ gen_store_is_jmp(1);
+ else
+ loaded_base = 1;
+ }
+ }
+ }
+ if ((insn & (1 << 21)) && rn == 15)
+ gen_store_is_jmp(1);
+ if (loaded_base && rn == 15)
+ gen_store_is_jmp(1);
+ break;
+ }
+ case 0xa:
+ case 0xb:
+ gen_store_is_jmp(1);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+#endif // CONFIG_BBVEC
/* if not always execute, we generate a conditional jump to
next instruction */
s->condlabel = gen_new_label();
@@ -9944,6 +10244,10 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
}
}
} else {
+#ifdef CONFIG_BBVEC
+ if (bbtrace_initialized())
+ gen_store_is_jmp(1);
+#endif
/* Conditional branch. */
op = (insn >> 22) & 0xf;
/* Generate a conditional jump to next instruction. */
@@ -10302,16 +10606,177 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s)
TCGv_i32 tmp2;
TCGv_i32 addr;
+ insn = arm_lduw_code(env, s->pc, s->bswap_code);
+
if (s->condexec_mask) {
cond = s->condexec_cond;
if (cond != 0x0e) { /* Skip conditional when condition is AL. */
- s->condlabel = gen_new_label();
- arm_gen_test_cc(cond ^ 1, s->condlabel);
- s->condjmp = 1;
+#ifdef CONFIG_BBVEC
+ /* For conditional arm instructions, checks for branching instructions
+ * need to happen here before the conditional is evaluated for basic
+ * block profiling. This is because if the conditional is false, a direct
+ * jump to the next instruction is generated and any code after this point
+ * will be skipped. We only want to split blocks on conditional instructions
+ * that are branches, or instructions that set the pc. The code below is
+ * taken from the code for decoding the instruction directly after this
+ * conditional check. Only the cases that lead to the generation of a jump or
+ * pc write are checked. Instructions that can write the pc are identified by
+ * calls to store_reg(), store_reg_bx(), and store_reg_from_load() where the
+ * value of reg can be 15. Instructions that generate a branch are identified
+ * by calls to gen_bx_im(), gen_bx(), and gen_jmp().
+ */
+ if (bbtrace_initialized()) {
+ switch (insn >> 12) {
+ case 0: case 1:
+ rd = insn & 7;
+ /* possible write to pc */
+ if (rd == 15)
+ gen_store_is_jmp(1);
+ break;
+ case 2: case 3:
+ /* arithmetic large immediate */
+ /* possible write to pc */
+ op = (insn >> 11) & 3;
+ rd = (insn >> 8) & 0x7;
+ if (rd == 15) {
+ if (op == 0 || op == 2 || op == 3) /* mov, add, sub */
+ gen_store_is_jmp(1);
+ }
+ break;
+ case 4:
+ if (insn & (1 << 11)) {
+ rd = (insn >> 8) & 7;
+ /* possible write to pc */
+ if (rd == 15)
+ gen_store_is_jmp(1);
+ break;
+ }
+ if (insn & (1 << 10)) {
+ rd = (insn & 7) | ((insn >> 4) & 8);
+ op = (insn >> 8) & 3;
+ /* possible write to pc */
+ if (rd == 15) {
+ if (op == 0 || op == 2) /* cmp, mov/cpy */
+ gen_store_is_jmp(1);
+ break;
+ }
+ /* branch instruction */
+ if (op == 3) /* branch [and link] exchange thumb register */
+ gen_store_is_jmp(1);
+ break;
+ }
+ /* data processing register */
+ rd = insn & 7;
+ rm = (insn >> 3) & 7;
+ op = (insn >> 6) & 0xf;
+ if (op == 2 || op == 3 || op == 4 || op == 7) {
+ /* the shift/rotate ops want the operands backwards */
+ val = rm;
+ rm = rd;
+ rd = val;
+ val = 1;
+ } else {
+ val = 0;
+ }
+ if (op == 0xf) { /* mvn */
+ val = 1;
+ rm = rd;
+ }
+ if (rd != 16) {
+ if (val) {
+ /* possible write to pc */
+ if (rm == 15)
+ gen_store_is_jmp(1);
+ } else {
+ /* possible write to pc */
+ if (rd == 15)
+ gen_store_is_jmp(1);
+ }
+ }
+ break;
+ case 5:
+ /* load/store register offset. */
+ rd = insn & 7;
+ op = (insn >> 9) & 7;
+ /* load to pc register */
+ if (op >= 3 && rd == 15)
+ gen_store_is_jmp(1);
+ break;
+ case 6: case 7: case 8:
+ rd = insn & 7;
+ /* load to pc register */
+ if ((insn & (1 << 11)) && rd == 15)
+ gen_store_is_jmp(1);
+ break;
+ case 9:
+ rd = (insn >> 8) & 7;
+ /* load to pc register */
+ if ((insn & (1 << 11)) && rd == 15)
+ gen_store_is_jmp(1);
+ break;
+ case 10:
+ rd = (insn >> 8) & 7;
+ /* possible write to pc register */
+ if (rd == 15)
+ gen_store_is_jmp(1);
+ break;
+ case 11:
+ op = (insn >> 8) & 0xf;
+ switch (op) {
+ case 2:
+ rd = insn & 7;
+ /* possible write to pc */
+ if (rd == 15)
+ gen_store_is_jmp(1);
+ case 4: case 5: case 0xc: case 0xd:
+ /* write to pc register */
+ if ((insn & 0x0900) == 0x0900)
+ gen_store_is_jmp(1);
+ break;
+ case 1: case 3: case 9: case 11: /* czb */
+ /* branch instruction */
+ gen_store_is_jmp(1);
+ break;
+ case 0xa:
+ rd = insn & 0x7;
+ /* possible write to pc register */
+ if (rd == 15)
+ gen_store_is_jmp(1);
+ break;
+ default:
+ break;
+ }
+ case 12:
+ /* load/store multiple */
+ rn = (insn >> 8) & 0x7;
+ /* possible write to pc */
+ if (rn == 15) {
+ if (((insn & (1 << rn)) == 0) || (insn & (1 << 11)))
+ gen_store_is_jmp(1);
+ }
+ break;
+ case 13:
+ cond = (insn >> 8) & 0xf;
+ /* branch instruction */
+ if (cond != 0xe && cond != 0xf)
+ gen_store_is_jmp(1);
+ break;
+ case 14:
+ /* branch instruction */
+ if (!(insn & (1 << 11)))
+ gen_store_is_jmp(1);
+ break;
+ default:
+ break;
+ }
+ }
+#endif // CONFIG_BBVEC
+ s->condlabel = gen_new_label();
+ arm_gen_test_cc(cond ^ 1, s->condlabel);
+ s->condjmp = 1;
}
}
- insn = arm_lduw_code(env, s->pc, s->bswap_code);
s->pc += 2;
switch (insn >> 12) {
@@ -10828,6 +11293,10 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s)
break;
case 1: case 3: case 9: case 11: /* czb */
+#ifdef CONFIG_BBVEC
+ if (bbtrace_initialized())
+ gen_store_is_jmp(1);
+#endif
rm = insn & 7;
tmp = load_reg(s, rm);
s->condlabel = gen_new_label();
@@ -10982,6 +11451,10 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s)
break;
}
/* generate a conditional jump to next instruction */
+#ifdef CONFIG_BBVEC
+ if (bbtrace_initialized())
+ gen_store_is_jmp(1);
+#endif
s->condlabel = gen_new_label();
arm_gen_test_cc(cond ^ 1, s->condlabel);
s->condjmp = 1;
@@ -11153,7 +11626,29 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu,
tcg_gen_movi_i32(tmp, 0);
store_cpu_field(tmp, condexec_bits);
}
+
+#ifdef CONFIG_BBVEC
+ /* Profile previously run block, check for PID change, and initialize
+ * prof_is_jmp flag. */
+ if (bbtrace_initialized()) {
+ gen_helper_bbv_profile(cpu_env);
+ gen_store_is_jmp(0);
+ gen_helper_context_check_pid(cpu_env);
+ }
+#endif
+
do {
+#ifdef CONFIG_BBVEC
+ if (bbtrace_initialized()) {
+ gen_helper_context_check_mode(cpu_env);
+ gen_insn_cnt_incr(env, dc);
+ gen_pc_incr(env, dc);
+ /* FIXME: this call should not be necessary if all the cases
+ where the prof_is_jmp flag gets set are correct. */
+ gen_store_is_jmp(0);
+ }
+#endif
+
#ifdef CONFIG_USER_ONLY
/* Intercept jump to the magic kernel page. */
if (dc->pc >= 0xffff0000) {
diff --git a/vl.c b/vl.c
index 74c2681..d7f31cf 100644
--- a/vl.c
+++ b/vl.c
@@ -30,6 +30,10 @@
#include "config-host.h"
+#ifdef CONFIG_BBVEC
+#include "bbv_profiler.h"
+#endif
+
#ifdef CONFIG_SECCOMP
#include "sysemu/seccomp.h"
#endif
@@ -490,6 +494,35 @@ static QemuOptsList qemu_semihosting_config_opts = {
},
};
+#ifdef CONFIG_BBVEC
+static QemuOptsList qemu_bbvec_opts = {
+ .name = "bbvec",
+ .implied_opt_name = "bbvec",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_bbvec_opts.head),
+ .merge_lists = true,
+ .desc = {
+ {
+ .name = "intervalSize",
+ .type = QEMU_OPT_NUMBER,
+ },
+ {
+ .name = "fileBase",
+ .type = QEMU_OPT_STRING,
+ .help = "Sets the prefix for the output basic block vector files",
+ },
+ {
+ .name = "userspaceOnly",
+ .type = QEMU_OPT_BOOL,
+ },
+ {
+ .name = "combinePIDs",
+ .type = QEMU_OPT_BOOL,
+ },
+ { /* end of list */ }
+ },
+};
+#endif
+
/**
* Get machine options
*
@@ -2794,6 +2827,9 @@ int main(int argc, char **argv, char **envp)
qemu_add_opts(&qemu_option_rom_opts);
qemu_add_opts(&qemu_machine_opts);
qemu_add_opts(&qemu_mem_opts);
+#ifdef CONFIG_BBVEC
+ qemu_add_opts(&qemu_bbvec_opts);
+#endif
qemu_add_opts(&qemu_smp_opts);
qemu_add_opts(&qemu_boot_opts);
qemu_add_opts(&qemu_sandbox_opts);
@@ -3002,6 +3038,28 @@ int main(int argc, char **argv, char **envp)
case QEMU_OPTION_nographic:
display_type = DT_NOGRAPHIC;
break;
+#ifdef CONFIG_BBVEC
+ case QEMU_OPTION_bbvec:
+ opts = qemu_opts_parse(qemu_find_opts("bbvec"), optarg, 0);
+
+ bool userspaceonly, combinepids;
+ uint64_t intervalsize;
+ const char *filebase = NULL;
+
+ intervalsize = qemu_opt_get_number(opts, "intervalSize", 100);
+ intervalsize *= 1000000;
+ userspaceonly = qemu_opt_get_bool(opts, "userspaceOnly", 1);
+ combinepids = qemu_opt_get_bool(opts, "combinePIDs", 0);
+ filebase = qemu_opt_get(opts, "fileBase");
+ if (!filebase)
+ bbtrace_init(intervalsize, "bbvec", userspaceonly, combinepids);
+ else
+ bbtrace_init(intervalsize, filebase, userspaceonly, combinepids);
+
+ qemu_add_exit_notifier(get_bbtrace_exit_notifier());
+
+ break;
+#endif
case QEMU_OPTION_curses:
#ifdef CONFIG_CURSES
display_type = DT_CURSES;
--
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [Qemu-devel] [RFC 07/14] Add PMU to ARM virt platform
2015-08-05 16:51 [Qemu-devel] RFC: ARM Semihosting, PMU, and BBV Changes Christopher Covington
` (5 preceding siblings ...)
2015-08-05 16:51 ` [Qemu-devel] [RFC 06/14] Added support for block profiling for AArch32 and Aarch64 Christopher Covington
@ 2015-08-05 16:51 ` Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 08/14] Add instruction-counting infrastructure to target-arm Christopher Covington
` (7 subsequent siblings)
14 siblings, 0 replies; 25+ messages in thread
From: Christopher Covington @ 2015-08-05 16:51 UTC (permalink / raw)
To: qemu-devel; +Cc: Christopher Covington
This reserves one PPI (private peripheral interrupt) for the PMU and
creates a corresponding entry in the device tree.
Writteb by Aaron Lindsay.
Signed-off-by: Christopher Covington <cov@codeaurora.org>
---
hw/arm/virt.c | 21 +++++++++++++++++++++
target-arm/cpu-qom.h | 4 ++--
target-arm/cpu.c | 8 ++++----
target-arm/cpu.h | 3 ++-
target-arm/helper.c | 6 +++---
5 files changed, 32 insertions(+), 10 deletions(-)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 565f573..bd1fbb6 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -295,6 +295,23 @@ static void fdt_add_timer_nodes(const VirtBoardInfo *vbi)
GIC_FDT_IRQ_TYPE_PPI, 10, irqflags);
}
+static void fdt_add_pmu_nodes(const VirtBoardInfo *vbi)
+{
+ uint32_t irqflags = GIC_FDT_IRQ_FLAGS_EDGE_LO_HI;
+ const char nodename[] = "/pmu";
+ const char compat[] = "arm,armv8-pmuv3\0arm,cortex-a15-pmu";
+
+ irqflags = deposit32(irqflags, GIC_FDT_IRQ_PPI_CPU_START,
+ GIC_FDT_IRQ_PPI_CPU_WIDTH, (1 << vbi->smp_cpus) - 1);
+
+
+ qemu_fdt_add_subnode(vbi->fdt, nodename);
+ qemu_fdt_setprop(vbi->fdt, nodename, "compatible", compat, sizeof(compat));
+
+ qemu_fdt_setprop_cells(vbi->fdt, nodename, "interrupts",
+ GIC_FDT_IRQ_TYPE_PPI, 7, irqflags);
+}
+
static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi)
{
int cpu;
@@ -384,6 +401,9 @@ static uint32_t create_gic(const VirtBoardInfo *vbi, qemu_irq *pic)
/* virtual timer */
qdev_connect_gpio_out(cpudev, 1,
qdev_get_gpio_in(gicdev, ppibase + 27));
+ /* PMU */
+ qdev_connect_gpio_out(cpudev, 2,
+ qdev_get_gpio_in(gicdev, ppibase + 23));
sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ));
}
@@ -802,6 +822,7 @@ static void machvirt_init(MachineState *machine)
}
g_strfreev(cpustr);
fdt_add_timer_nodes(vbi);
+ fdt_add_pmu_nodes(vbi);
fdt_add_cpu_nodes(vbi);
fdt_add_psci_node(vbi);
diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h
index ed5a644..bb6722f 100644
--- a/target-arm/cpu-qom.h
+++ b/target-arm/cpu-qom.h
@@ -84,8 +84,8 @@ typedef struct ARMCPU {
/* Timers used by the generic (architected) timer */
QEMUTimer *gt_timer[NUM_GTIMERS];
- /* GPIO outputs for generic timer */
- qemu_irq gt_timer_outputs[NUM_GTIMERS];
+ /* GPIO outputs for generic timer and PMU */
+ qemu_irq ppi_outputs[NUM_GTIMERS + 1];
/* 'compatible' string for this CPU for Linux device trees */
const char *dtb_compatible;
diff --git a/target-arm/cpu.c b/target-arm/cpu.c
index 3ca3fa8..87d0772 100644
--- a/target-arm/cpu.c
+++ b/target-arm/cpu.c
@@ -379,17 +379,17 @@ static void arm_cpu_initfn(Object *obj)
/* VIRQ and VFIQ are unused with KVM but we add them to maintain
* the same interface as non-KVM CPUs.
*/
- qdev_init_gpio_in(DEVICE(cpu), arm_cpu_kvm_set_irq, 4);
+ qdev_init_gpio_in(DEVICE(cpu), arm_cpu_kvm_set_irq, 5);
} else {
- qdev_init_gpio_in(DEVICE(cpu), arm_cpu_set_irq, 4);
+ qdev_init_gpio_in(DEVICE(cpu), arm_cpu_set_irq, 5);
}
cpu->gt_timer[GTIMER_PHYS] = timer_new(QEMU_CLOCK_VIRTUAL, GTIMER_SCALE,
arm_gt_ptimer_cb, cpu);
cpu->gt_timer[GTIMER_VIRT] = timer_new(QEMU_CLOCK_VIRTUAL, GTIMER_SCALE,
arm_gt_vtimer_cb, cpu);
- qdev_init_gpio_out(DEVICE(cpu), cpu->gt_timer_outputs,
- ARRAY_SIZE(cpu->gt_timer_outputs));
+ qdev_init_gpio_out(DEVICE(cpu), cpu->ppi_outputs,
+ ARRAY_SIZE(cpu->ppi_outputs));
#endif
/* DTB consumers generally don't in fact care what the 'compatible'
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 2525569..44084a5 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -112,9 +112,10 @@ typedef struct ARMGenericTimer {
uint64_t ctl; /* Timer Control register */
} ARMGenericTimer;
+#define NUM_GTIMERS 2
#define GTIMER_PHYS 0
#define GTIMER_VIRT 1
-#define NUM_GTIMERS 2
+#define PMU_IDX 2
typedef struct {
uint64_t raw_tcr;
diff --git a/target-arm/helper.c b/target-arm/helper.c
index d2c02be..1843ec5 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -1216,7 +1216,7 @@ static void gt_recalc_timer(ARMCPU *cpu, int timeridx)
uint64_t nexttick;
gt->ctl = deposit32(gt->ctl, 2, 1, istatus);
- qemu_set_irq(cpu->gt_timer_outputs[timeridx],
+ qemu_set_irq(cpu->ppi_outputs[timeridx],
(istatus && !(gt->ctl & 2)));
if (istatus) {
/* Next transition is when count rolls back over to zero */
@@ -1237,7 +1237,7 @@ static void gt_recalc_timer(ARMCPU *cpu, int timeridx)
} else {
/* Timer disabled: ISTATUS and timer output always clear */
gt->ctl &= ~4;
- qemu_set_irq(cpu->gt_timer_outputs[timeridx], 0);
+ qemu_set_irq(cpu->ppi_outputs[timeridx], 0);
timer_del(cpu->gt_timer[timeridx]);
}
}
@@ -1297,7 +1297,7 @@ static void gt_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri,
/* IMASK toggled: don't need to recalculate,
* just set the interrupt line based on ISTATUS
*/
- qemu_set_irq(cpu->gt_timer_outputs[timeridx],
+ qemu_set_irq(cpu->ppi_outputs[timeridx],
(oldval & 4) && !(value & 2));
}
}
--
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [Qemu-devel] [RFC 08/14] Add instruction-counting infrastructure to target-arm
2015-08-05 16:51 [Qemu-devel] RFC: ARM Semihosting, PMU, and BBV Changes Christopher Covington
` (6 preceding siblings ...)
2015-08-05 16:51 ` [Qemu-devel] [RFC 07/14] Add PMU to ARM virt platform Christopher Covington
@ 2015-08-05 16:51 ` Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 09/14] Implement remaining PMU functionality Christopher Covington
` (6 subsequent siblings)
14 siblings, 0 replies; 25+ messages in thread
From: Christopher Covington @ 2015-08-05 16:51 UTC (permalink / raw)
To: qemu-devel; +Cc: Christopher Covington
This (partially) divorces counting instructions from basic block
collection so instructions can be counted without the bbv plugin being
enabled.
Written by Aaron Lindsay.
Signed-off-by: Christopher Covington <cov@codeaurora.org>
---
include/exec/cpu-defs.h | 2 ++
target-arm/helper.c | 43 +++++++++++++++++++++++++++++++++++++++++++
target-arm/helper.h | 2 ++
target-arm/translate-a64.c | 18 ++++++++++--------
target-arm/translate.c | 18 ++++++++++--------
5 files changed, 67 insertions(+), 16 deletions(-)
diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h
index 1eed930..9af080f 100644
--- a/include/exec/cpu-defs.h
+++ b/include/exec/cpu-defs.h
@@ -136,6 +136,8 @@ typedef struct CPUIOTLBEntry {
CPU_COMMON_TLB \
/* Instruction Count (for profiling) */ \
uint64_t prof_ic; \
+ /* How much of prof_ic's value have we processed? */ \
+ uint64_t prof_ic_last; \
/* PC (for profiling) */ \
uint64_t prof_pc; \
/* next page start (for profiling) */ \
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 1843ec5..be3ad01 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -673,6 +673,15 @@ static inline bool arm_ccnt_enabled(CPUARMState *env)
return true;
}
+/* Called by anything that wants to be an input for event counts to the PMU
+ * (except for SWINC, event 0x000, since its events can target specific
+ * counters)
+ */
+static void pmevcntr_increment(CPUARMState *env, uint8_t event_type,
+ uint64_t increment_by)
+{
+}
+
void pmccntr_sync(CPUARMState *env)
{
uint64_t temp_ticks;
@@ -4060,6 +4069,40 @@ void HELPER(context_check_pid)(CPUARMState *env)
bb_context_check_pid(env->prof_ic, pid);
}
+
+void HELPER(update_instruction_count)(CPUARMState *env)
+{
+ if (bbtrace_initialized()) {
+ /*
+ * If the bbv plugin is compiled in and enabled, we must account for the
+ * fact that bbv_profile needs to see prof_ic before we clear it.
+ * However, it doesn't always clear the counter every time this gets
+ * called, so we must keep track of the last value seen to ensure we
+ * update the instruction counter correctly in that case.
+ */
+ increment_instruction_counters(env->prof_ic - env->prof_ic_last);
+ if (env->prof_pc && env->prof_is_jmp) {
+ // If this is the end of a basic block, zero out last_seen counter too
+ env->prof_ic_last = 0;
+ } else {
+ env->prof_ic_last = env->prof_ic;
+ }
+ } else {
+ pmevcntr_increment(env, PMU_COUNTER_TYPE_INSTRUCTIONS, env->prof_ic);
+ pmevcntr_increment(env, PMU_COUNTER_TYPE_CYCLES, env->prof_ic);
+ env->prof_ic = 0;
+ }
+}
+
+#else //!CONFIG_BBVEC
+
+void HELPER(update_instruction_count)(CPUARMState *env)
+{
+ pmevcntr_increment(env, PMU_COUNTER_TYPE_INSTRUCTIONS, env->prof_ic);
+ pmevcntr_increment(env, PMU_COUNTER_TYPE_CYCLES, env->prof_ic);
+ env->prof_ic = 0;
+}
+
#endif //CONFIG_BBVEC
/* Sign/zero extend */
diff --git a/target-arm/helper.h b/target-arm/helper.h
index 41291c9..02edf3a 100644
--- a/target-arm/helper.h
+++ b/target-arm/helper.h
@@ -43,6 +43,8 @@ DEF_HELPER_1(context_check_mode, void, env)
DEF_HELPER_1(context_check_pid, void, env)
#endif // CONFIG_BBVEC
+DEF_HELPER_1(update_instruction_count, void, env)
+
DEF_HELPER_3(ssat, i32, env, i32, i32)
DEF_HELPER_3(usat, i32, env, i32, i32)
DEF_HELPER_3(ssat16, i32, env, i32, i32)
diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c
index 80b27ed..f6f8832 100644
--- a/target-arm/translate-a64.c
+++ b/target-arm/translate-a64.c
@@ -55,9 +55,9 @@ static TCGv_i64 cpu_exclusive_high;
* keep track of the current pc, so the last pc in the block can be
* captured. */
static TCGv_i64 cpu_prof_pc;
-static TCGv_i64 cpu_prof_ic;
static TCGv_i64 cpu_prof_is_jmp;
#endif // CONFIG_BBVEC
+static TCGv_i64 cpu_prof_ic;
#ifdef CONFIG_USER_ONLY
static TCGv_i64 cpu_exclusive_test;
static TCGv_i32 cpu_exclusive_info;
@@ -124,10 +124,11 @@ void a64_translate_init(void)
#ifdef CONFIG_BBVEC
// bbvec profiling globals
- cpu_prof_ic = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUARMState, prof_ic), "prof_ic");
cpu_prof_pc = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUARMState, prof_pc), "prof_pc");
cpu_prof_is_jmp = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUARMState, prof_is_jmp), "prof_is_jmp");
#endif // CONFIG_BBVEC
+ cpu_prof_ic = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUARMState, prof_ic), "prof_ic");
cpu_exclusive_addr = tcg_global_mem_new_i64(TCG_AREG0,
offsetof(CPUARMState, exclusive_addr), "exclusive_addr");
@@ -210,11 +211,6 @@ void gen_a64_set_pc_im(uint64_t val)
#ifdef CONFIG_BBVEC
/* Basic block profiling functions */
-static void gen_insn_cnt_incr(CPUARMState * env, DisasContext *s)
-{
- tcg_gen_addi_i64(cpu_prof_ic, cpu_prof_ic, 1);
-}
-
static void gen_pc_incr(CPUARMState * env, DisasContext *s)
{
tcg_gen_movi_i64(cpu_prof_pc, s->pc);
@@ -226,6 +222,11 @@ static void gen_store_is_jmp(uint32_t jmp)
}
#endif // CONFIG_BBVEC
+static void gen_insn_cnt_incr(CPUARMState * env, DisasContext *s)
+{
+ tcg_gen_addi_i64(cpu_prof_ic, cpu_prof_ic, 1);
+}
+
static void gen_exception_internal(int excp)
{
TCGv_i32 tcg_excp = tcg_const_i32(excp);
@@ -11068,6 +11069,7 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu,
tcg_clear_temp_count();
+ gen_helper_update_instruction_count(cpu_env);
#ifdef CONFIG_BBVEC
/* Profile previously run block, check for PID change, and initialize
* prof_is_jmp flag. */
@@ -11082,10 +11084,10 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu,
#ifdef CONFIG_BBVEC
if (bbtrace_initialized()) {
gen_helper_context_check_mode(cpu_env);
- gen_insn_cnt_incr(env, dc);
gen_pc_incr(env, dc);
}
#endif // CONFIG_BBVEC
+ gen_insn_cnt_incr(env, dc);
if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) {
QTAILQ_FOREACH(bp, &cs->breakpoints, entry) {
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 7a19a8b..113e3b6 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -71,10 +71,10 @@ static TCGv_i32 cpu_CF, cpu_NF, cpu_VF, cpu_ZF;
static TCGv_i64 cpu_exclusive_addr;
static TCGv_i64 cpu_exclusive_val;
#ifdef CONFIG_BBVEC
-static TCGv_i64 cpu_prof_ic;
static TCGv_i64 cpu_prof_pc;
static TCGv_i64 cpu_prof_is_jmp;
#endif
+static TCGv_i64 cpu_prof_ic;
#ifdef CONFIG_USER_ONLY
static TCGv_i64 cpu_exclusive_test;
static TCGv_i32 cpu_exclusive_info;
@@ -109,10 +109,11 @@ void arm_translate_init(void)
#ifdef CONFIG_BBVEC
// bbvec profiling globals
- cpu_prof_ic = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUARMState, prof_ic), "prof_ic");
cpu_prof_pc = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUARMState, prof_pc), "prof_pc");
cpu_prof_is_jmp = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUARMState, prof_is_jmp), "prof_is_jmp");
#endif
+ cpu_prof_ic = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUARMState, prof_ic), "prof_ic");
cpu_exclusive_addr = tcg_global_mem_new_i64(TCG_AREG0,
offsetof(CPUARMState, exclusive_addr), "exclusive_addr");
@@ -194,11 +195,6 @@ static inline TCGv_i32 load_reg(DisasContext *s, int reg)
#ifdef CONFIG_BBVEC
/* Basic block profiling functions */
-static void gen_insn_cnt_incr(CPUARMState * env, DisasContext *s)
-{
- tcg_gen_addi_i64(cpu_prof_ic, cpu_prof_ic, 1);
-}
-
static void gen_pc_incr(CPUARMState * env, DisasContext *s)
{
tcg_gen_movi_i64(cpu_prof_pc, s->pc);
@@ -210,6 +206,11 @@ static void gen_store_is_jmp(uint32_t jmp)
}
#endif
+static void gen_insn_cnt_incr(CPUARMState * env, DisasContext *s)
+{
+ tcg_gen_addi_i64(cpu_prof_ic, cpu_prof_ic, 1);
+}
+
/* Set a CPU register. The source must be a temporary and will be
marked as dead. */
static void store_reg(DisasContext *s, int reg, TCGv_i32 var)
@@ -11627,6 +11628,7 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu,
store_cpu_field(tmp, condexec_bits);
}
+ gen_helper_update_instruction_count(cpu_env);
#ifdef CONFIG_BBVEC
/* Profile previously run block, check for PID change, and initialize
* prof_is_jmp flag. */
@@ -11641,13 +11643,13 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu,
#ifdef CONFIG_BBVEC
if (bbtrace_initialized()) {
gen_helper_context_check_mode(cpu_env);
- gen_insn_cnt_incr(env, dc);
gen_pc_incr(env, dc);
/* FIXME: this call should not be necessary if all the cases
where the prof_is_jmp flag gets set are correct. */
gen_store_is_jmp(0);
}
#endif
+ gen_insn_cnt_incr(env, dc);
#ifdef CONFIG_USER_ONLY
/* Intercept jump to the magic kernel page. */
--
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [Qemu-devel] [RFC 09/14] Implement remaining PMU functionality
2015-08-05 16:51 [Qemu-devel] RFC: ARM Semihosting, PMU, and BBV Changes Christopher Covington
` (7 preceding siblings ...)
2015-08-05 16:51 ` [Qemu-devel] [RFC 08/14] Add instruction-counting infrastructure to target-arm Christopher Covington
@ 2015-08-05 16:51 ` Christopher Covington
2016-02-02 21:22 ` Alistair Francis
2015-08-05 16:51 ` [Qemu-devel] [RFC 10/14] bbvec: Move mode/PID change detection to register writes Christopher Covington
` (5 subsequent siblings)
14 siblings, 1 reply; 25+ messages in thread
From: Christopher Covington @ 2015-08-05 16:51 UTC (permalink / raw)
To: qemu-devel; +Cc: Christopher Covington
This adds logic to increment PMEVCNTR's based on different event inputs,
implements all remaining CP registers, and triggers an interrupt on
event overflow.
Written by Aaron Lindsay.
Signed-off-by: Christopher Covington <cov@codeaurora.org>
---
target-arm/cpu-qom.h | 2 +
target-arm/cpu.c | 2 +
target-arm/cpu.h | 37 ++--
target-arm/cpu64.c | 2 +
target-arm/helper.c | 538 ++++++++++++++++++++++++++++++++++++++-------------
5 files changed, 425 insertions(+), 156 deletions(-)
diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h
index bb6722f..2a0f3f4 100644
--- a/target-arm/cpu-qom.h
+++ b/target-arm/cpu-qom.h
@@ -136,6 +136,8 @@ typedef struct ARMCPU {
uint32_t id_pfr0;
uint32_t id_pfr1;
uint32_t id_dfr0;
+ uint32_t pmceid0;
+ uint32_t pmceid1;
uint32_t id_afr0;
uint32_t id_mmfr0;
uint32_t id_mmfr1;
diff --git a/target-arm/cpu.c b/target-arm/cpu.c
index 87d0772..6a728d9 100644
--- a/target-arm/cpu.c
+++ b/target-arm/cpu.c
@@ -938,6 +938,8 @@ static void cortex_a15_initfn(Object *obj)
cpu->id_pfr0 = 0x00001131;
cpu->id_pfr1 = 0x00011011;
cpu->id_dfr0 = 0x02010555;
+ cpu->pmceid0 = 0x00000481; /* PMUv3 events 0x0, 0x8, and 0x11 */
+ cpu->pmceid1 = 0x00000000;
cpu->id_afr0 = 0x00000000;
cpu->id_mmfr0 = 0x10201105;
cpu->id_mmfr1 = 0x20000000;
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 44084a5..f6857fa 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -112,11 +112,22 @@ typedef struct ARMGenericTimer {
uint64_t ctl; /* Timer Control register */
} ARMGenericTimer;
+/* Indices into ARMCPU.ppi_outputs (and .gt_timer for timers) */
#define NUM_GTIMERS 2
#define GTIMER_PHYS 0
#define GTIMER_VIRT 1
#define PMU_IDX 2
+enum pmu_counter_type {
+ PMU_COUNTER_TYPE_SWINC = 0x000,
+ PMU_COUNTER_TYPE_INSTRUCTIONS = 0x008,
+ PMU_COUNTER_TYPE_CYCLES = 0x011
+};
+
+/* Performance monitor event counter state */
+#define NUM_PMU_COUNTERS 4 /* 0-30, inclusive, doesn't count cycle counter */
+#define PMU_COUNTER_MASK 0x8000000F /* Mask of bits allowed for PMINTEN{SET|CLR}*/
+
typedef struct {
uint64_t raw_tcr;
uint32_t mask;
@@ -287,12 +298,13 @@ typedef struct CPUARMState {
};
uint32_t c9_insn; /* Cache lockdown registers. */
uint32_t c9_data;
- uint64_t c9_pmcr; /* performance monitor control register */
- uint64_t c9_pmcnten; /* perf monitor counter enables */
+ uint32_t c9_pmcr; /* performance monitor control register */
+ uint64_t c9_pmccntr;
+ uint32_t c9_pmcnten; /* perf monitor counter enables */
uint32_t c9_pmovsr; /* perf monitor overflow status */
- uint32_t c9_pmxevtyper; /* perf monitor event type */
uint32_t c9_pmuserenr; /* perf monitor user enable */
uint32_t c9_pminten; /* perf monitor interrupt enables */
+ uint32_t c9_pmselr; /* perf monitor event counter selection */
union { /* Memory attribute redirection */
struct {
#ifdef HOST_WORDS_BIGENDIAN
@@ -354,6 +366,9 @@ typedef struct CPUARMState {
uint64_t tpidruro_ns;
uint64_t tpidrro_el[1];
};
+ uint32_t c14_pmccfiltr; /* Performance Monitor Filter Register */
+ uint32_t c14_pmevcntr[NUM_PMU_COUNTERS];
+ uint32_t c14_pmevtyper[NUM_PMU_COUNTERS];
uint64_t c14_cntfrq; /* Counter Frequency register */
uint64_t c14_cntkctl; /* Timer Control register */
ARMGenericTimer c14_timer[NUM_GTIMERS];
@@ -371,11 +386,6 @@ typedef struct CPUARMState {
uint64_t dbgwvr[16]; /* watchpoint value registers */
uint64_t dbgwcr[16]; /* watchpoint control registers */
uint64_t mdscr_el1;
- /* If the counter is enabled, this stores the last time the counter
- * was reset. Otherwise it stores the counter value
- */
- uint64_t c15_ccnt;
- uint64_t pmccfiltr_el0; /* Performance Monitor Filter Register */
} cp15;
struct {
@@ -508,17 +518,6 @@ int cpu_arm_signal_handler(int host_signum, void *pinfo,
int arm_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int rw,
int mmu_idx);
-/**
- * pmccntr_sync
- * @env: CPUARMState
- *
- * Synchronises the counter in the PMCCNTR. This must always be called twice,
- * once before any action that might affect the timer and again afterwards.
- * The function is used to swap the state of the register if required.
- * This only happens when not in user mode (!CONFIG_USER_ONLY)
- */
-void pmccntr_sync(CPUARMState *env);
-
/* SCTLR bit meanings. Several bits have been reused in newer
* versions of the architecture; in that case we define constants
* for both old and new bit meanings. Code which tests against those
diff --git a/target-arm/cpu64.c b/target-arm/cpu64.c
index 270bc2f..b2a3a74 100644
--- a/target-arm/cpu64.c
+++ b/target-arm/cpu64.c
@@ -132,6 +132,8 @@ static void aarch64_a57_initfn(Object *obj)
cpu->id_isar5 = 0x00011121;
cpu->id_aa64pfr0 = 0x00002222;
cpu->id_aa64dfr0 = 0x10305106;
+ cpu->pmceid0 = 0x00000481; /* PMUv3 events 0x0, 0x8, and 0x11 */
+ cpu->pmceid1 = 0x00000000;
cpu->id_aa64isar0 = 0x00011120;
cpu->id_aa64mmfr0 = 0x00001124;
cpu->dbgdidr = 0x3516d000;
diff --git a/target-arm/helper.c b/target-arm/helper.c
index be3ad01..a659e67 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -21,11 +21,6 @@ static inline int get_phys_addr(CPUARMState *env, target_ulong address,
int access_type, ARMMMUIdx mmu_idx,
hwaddr *phys_ptr, MemTxAttrs *attrs, int *prot,
target_ulong *page_size);
-
-/* Definitions for the PMCCNTR and PMCR registers */
-#define PMCRD 0x8
-#define PMCRC 0x4
-#define PMCRE 0x1
#endif
#ifdef TARGET_AARCH64
@@ -660,150 +655,337 @@ static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri)
return CP_ACCESS_OK;
}
-#ifndef CONFIG_USER_ONLY
+/* Definitions for the PMU CP registers */
+#define PMCR_LC 0x40
+#define PMCR_D 0x8
+#define PMCR_C 0x4
+#define PMCR_E 0x1
+
+#define PMCNTEN_C 0x80000000
+
+#define PMCCFILTR_NSH 0x08000000
+#define PMCCFILTR_P 0x80000000
+#define PMCCFILTR_U 0x40000000
-static inline bool arm_ccnt_enabled(CPUARMState *env)
+#define PMEVTYPER_NSH 0x08000000
+#define PMEVTYPER_P 0x80000000
+#define PMEVTYPER_U 0x40000000
+#define PMEVTYPER_EVTCOUNT 0x000003ff
+
+#define PMXEVTYPER_P 0x80000000
+#define PMXEVTYPER_U 0x40000000
+
+static void pmccfiltr_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
{
- /* This does not support checking PMCCFILTR_EL0 register */
+ env->cp15.c14_pmccfiltr = value & 0xfc000000;
+}
- if (!(env->cp15.c9_pmcr & PMCRE)) {
- return false;
+static uint64_t pmevcntr_read(CPUARMState *env, const ARMCPRegInfo *ri,
+ const uint8_t idx)
+{
+ if (idx >= NUM_PMU_COUNTERS) {
+ return arm_cp_read_zero(env, ri);
}
+ return env->cp15.c14_pmevcntr[idx];
+}
- return true;
+static uint64_t pmevcntr_readfn(CPUARMState *env, const ARMCPRegInfo *ri)
+{
+ uint8_t idx = ((ri->crm & 3) << 3) | (ri->opc2 & 7);
+ return pmevcntr_read(env, ri, idx);
}
-/* Called by anything that wants to be an input for event counts to the PMU
- * (except for SWINC, event 0x000, since its events can target specific
- * counters)
- */
-static void pmevcntr_increment(CPUARMState *env, uint8_t event_type,
- uint64_t increment_by)
+static void pmevcntr_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value, const uint8_t idx)
{
+ if (idx >= NUM_PMU_COUNTERS) {
+ arm_cp_write_ignore(env, ri, value);
+ } else {
+ env->cp15.c14_pmevcntr[idx] = value;
+ }
}
-void pmccntr_sync(CPUARMState *env)
+static void pmevcntr_writefn(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
{
- uint64_t temp_ticks;
+ uint8_t idx = ((ri->crm & 3) << 3) | (ri->opc2 & 7);
+ pmevcntr_write(env, ri, value, idx);
- temp_ticks = muldiv64(qemu_clock_get_us(QEMU_CLOCK_VIRTUAL),
- get_ticks_per_sec(), 1000000);
+}
- if (env->cp15.c9_pmcr & PMCRD) {
- /* Increment once every 64 processor clock cycles */
- temp_ticks /= 64;
+static uint64_t pmevtyper_read(CPUARMState *env, const ARMCPRegInfo *ri,
+ const uint8_t idx)
+{
+ if (idx >= NUM_PMU_COUNTERS) {
+ return arm_cp_read_zero(env, ri);
}
+ return env->cp15.c14_pmevtyper[idx];
+}
+
+static uint64_t pmevtyper_readfn(CPUARMState *env, const ARMCPRegInfo *ri)
+{
+ uint8_t idx = ((ri->crm & 3) << 3) | (ri->opc2 & 7);
+ return pmevtyper_read(env, ri, idx);
+}
- if (arm_ccnt_enabled(env)) {
- env->cp15.c15_ccnt = temp_ticks - env->cp15.c15_ccnt;
+static void pmevtyper_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value, const uint8_t idx)
+{
+ if (idx >= NUM_PMU_COUNTERS) {
+ arm_cp_write_ignore(env, ri, value);
+ } else {
+ env->cp15.c14_pmevtyper[idx] = value & 0xf80003ff;
}
}
-static void pmcr_write(CPUARMState *env, const ARMCPRegInfo *ri,
- uint64_t value)
+static void pmevtyper_writefn(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
{
- pmccntr_sync(env);
+ uint8_t idx = ((ri->crm & 3) << 3) | (ri->opc2 & 7);
+ pmevtyper_write(env, ri, value, idx);
+}
- if (value & PMCRC) {
- /* The counter has been reset */
- env->cp15.c15_ccnt = 0;
+static void pmselr_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ env->cp15.c9_pmselr = value & 31;
+}
+
+static uint64_t pmxevcntr_read(CPUARMState *env, const ARMCPRegInfo *ri)
+{
+ return pmevcntr_read(env, ri, env->cp15.c9_pmselr & 31);
+}
+
+static void pmxevcntr_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ pmevcntr_write(env, ri, value, env->cp15.c9_pmselr & 31);
+}
+
+static uint64_t pmxevtyper_read(CPUARMState *env, const ARMCPRegInfo *ri)
+{
+ uint8_t idx = env->cp15.c9_pmselr & 31;
+ if (idx == 31)
+ return env->cp15.c14_pmccfiltr;
+ return pmevtyper_read(env, ri, env->cp15.c9_pmselr & 31);
+}
+
+static void pmxevtyper_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ uint8_t idx = env->cp15.c9_pmselr & 31;
+ if (idx == 31)
+ pmccfiltr_write(env, ri, value);
+ else
+ pmevtyper_write(env, ri, value, env->cp15.c9_pmselr & 31);
+}
+
+static inline bool pmccntr_enabled(CPUARMState *env)
+{
+ /* This does not check PMCR.E to see if all events are disabled, it is
+ * assumed that is being checked externally, and doesn't support checking
+ * for the secure/non-secure components of the PMCCFILTR_EL0 register.
+ */
+
+ if (!(env->cp15.c9_pmcnten & PMCNTEN_C)) {
+ return false;
}
- /* only the DP, X, D and E bits are writable */
- env->cp15.c9_pmcr &= ~0x39;
- env->cp15.c9_pmcr |= (value & 0x39);
+ switch (arm_current_el(env)) {
+ case 2:
+ if (!(env->cp15.c14_pmccfiltr & PMCCFILTR_NSH)) {
+ return false;
+ } else {
+ break;
+ }
+ case 1:
+ if (env->cp15.c14_pmccfiltr & PMCCFILTR_P) {
+ return false;
+ } else {
+ break;
+ }
+ case 0:
+ if (env->cp15.c14_pmccfiltr & PMCCFILTR_U) {
+ return false;
+ } else {
+ break;
+ }
+ }
- pmccntr_sync(env);
+ return true;
}
-static uint64_t pmccntr_read(CPUARMState *env, const ARMCPRegInfo *ri)
+static inline bool pmevcntr_enabled(CPUARMState *env, uint8_t idx)
{
- uint64_t total_ticks;
+ /* This does not check PMCR.E to see if all events are disabled, it is
+ * assumed that is being checked externally, and doesn't support checking
+ * for the secure/non-secure components of the PMEVTYPER<n>_EL0 registers
+ */
+
+ if (!(env->cp15.c9_pmcnten & (1U << idx))) {
+ return false;
+ }
- if (!arm_ccnt_enabled(env)) {
- /* Counter is disabled, do not change value */
- return env->cp15.c15_ccnt;
+ switch (arm_current_el(env)) {
+ case 2:
+ if (!(env->cp15.c14_pmevtyper[idx] & PMEVTYPER_NSH)) {
+ return false;
+ } else {
+ break;
+ }
+ case 1:
+ if (env->cp15.c14_pmevtyper[idx] & PMEVTYPER_P) {
+ return false;
+ } else {
+ break;
+ }
+ case 0:
+ if (env->cp15.c14_pmevtyper[idx] & PMEVTYPER_U) {
+ return false;
+ } else {
+ break;
+ }
}
- total_ticks = muldiv64(qemu_clock_get_us(QEMU_CLOCK_VIRTUAL),
- get_ticks_per_sec(), 1000000);
+ return true;
+}
+
+static void pmu_update_irq(CPUARMState *env)
+{
+ ARMCPU *cpu = arm_env_get_cpu(env);
+ qemu_set_irq(cpu->ppi_outputs[PMU_IDX], (env->cp15.c9_pmcr & PMCR_E) &&
+ (env->cp15.c9_pminten & env->cp15.c9_pmovsr));
+}
+
+static void pmccntr_increment(CPUARMState *env, uint64_t increment_by)
+{
+ if (pmccntr_enabled(env)) {
+ uint64_t new_pmccntr = env->cp15.c9_pmccntr + increment_by;
+ unsigned int overflow_bit = (env->cp15.c9_pmcr & PMCR_LC) ? 63 : 31;
- if (env->cp15.c9_pmcr & PMCRD) {
- /* Increment once every 64 processor clock cycles */
- total_ticks /= 64;
+ if (!(new_pmccntr & (1 << overflow_bit)) &&
+ env->cp15.c9_pmccntr & (1 << overflow_bit)) {
+ env->cp15.c9_pmovsr |= (1 << 31);
+ pmu_update_irq(env);
+ }
+ env->cp15.c9_pmccntr = new_pmccntr;
}
- return total_ticks - env->cp15.c15_ccnt;
}
-static void pmccntr_write(CPUARMState *env, const ARMCPRegInfo *ri,
- uint64_t value)
+/* Called by anything that wants to be an input for event counts to the PMU
+ * (except for SWINC, event 0x000 since its events can target specific counters
+ */
+static void pmevcntr_increment(CPUARMState *env, uint8_t event_type,
+ uint64_t increment_by)
{
- uint64_t total_ticks;
+ unsigned int i;
- if (!arm_ccnt_enabled(env)) {
- /* Counter is disabled, set the absolute value */
- env->cp15.c15_ccnt = value;
+ //early out if no counters are enabled
+ if (!(env->cp15.c9_pmcr & PMCR_E) || !env->cp15.c9_pmcnten)
return;
- }
- total_ticks = muldiv64(qemu_clock_get_us(QEMU_CLOCK_VIRTUAL),
- get_ticks_per_sec(), 1000000);
+ if (event_type == PMU_COUNTER_TYPE_CYCLES)
+ pmccntr_increment(env, increment_by);
+
+ for (i = 0; i < NUM_PMU_COUNTERS; i++) {
+ if (pmevcntr_enabled(env, i) &&
+ (env->cp15.c14_pmevtyper[i] & PMEVTYPER_EVTCOUNT) == event_type) {
+ uint32_t new_pmevcntr = env->cp15.c14_pmevcntr[i] + increment_by;
- if (env->cp15.c9_pmcr & PMCRD) {
- /* Increment once every 64 processor clock cycles */
- total_ticks /= 64;
+ if (!(new_pmevcntr & (1 << 31)) &&
+ (env->cp15.c14_pmevcntr[i] & (1 << 31))) {
+ env->cp15.c9_pmovsr |= (1 << i);
+ pmu_update_irq(env);
+ }
+ env->cp15.c14_pmevcntr[i] = new_pmevcntr;
+ }
}
- env->cp15.c15_ccnt = total_ticks - value;
}
-static void pmccntr_write32(CPUARMState *env, const ARMCPRegInfo *ri,
- uint64_t value)
+static void pmswinc_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
{
- uint64_t cur_val = pmccntr_read(env, NULL);
+ unsigned int i;
- pmccntr_write(env, ri, deposit64(cur_val, 0, 32, value));
+ for (i = 0; i < NUM_PMU_COUNTERS; i++) {
+ if (value & (1 << i) &&
+ pmevcntr_enabled(env, i) &&
+ (env->cp15.c14_pmevtyper[i] & PMEVTYPER_EVTCOUNT) == PMU_COUNTER_TYPE_SWINC) {
+ uint32_t new_pmevcntr = env->cp15.c14_pmevcntr[i] + 1;
+
+ if (!(new_pmevcntr & (1 << 31)) &&
+ (env->cp15.c14_pmevcntr[i] & (1 << 31))) {
+ env->cp15.c9_pmovsr |= (1 << i);
+ pmu_update_irq(env);
+ }
+ env->cp15.c14_pmevcntr[i] = new_pmevcntr;
+ }
+ }
}
-#else /* CONFIG_USER_ONLY */
+static void pmcr_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ if (value & PMCR_C) {
+ /* The counter has been reset */
+ env->cp15.c9_pmccntr = 0;
+ }
+
+ /* only the DP, X, D and E bits are writable */
+ env->cp15.c9_pmcr &= ~0x39;
+ env->cp15.c9_pmcr |= (value & 0x39);
+}
-void pmccntr_sync(CPUARMState *env)
+static uint64_t pmccntr_read(CPUARMState *env, const ARMCPRegInfo *ri)
{
+ return env->cp15.c9_pmccntr;
}
-#endif
+static void pmccntr_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ env->cp15.c9_pmccntr = value;
+}
-static void pmccfiltr_write(CPUARMState *env, const ARMCPRegInfo *ri,
+static void pmccntr_write32(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
- pmccntr_sync(env);
- env->cp15.pmccfiltr_el0 = value & 0x7E000000;
- pmccntr_sync(env);
+ uint64_t cur_val = pmccntr_read(env, NULL);
+
+ pmccntr_write(env, ri, deposit64(cur_val, 0, 32, value));
}
static void pmcntenset_write(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
- value &= (1 << 31);
+ value &= PMU_COUNTER_MASK;
env->cp15.c9_pmcnten |= value;
+ pmu_update_irq(env);
}
static void pmcntenclr_write(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
- value &= (1 << 31);
+ value &= PMU_COUNTER_MASK;
env->cp15.c9_pmcnten &= ~value;
+ pmu_update_irq(env);
}
-static void pmovsr_write(CPUARMState *env, const ARMCPRegInfo *ri,
+static void pmovsset_write(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
- env->cp15.c9_pmovsr &= ~value;
+ value &= PMU_COUNTER_MASK;
+ env->cp15.c9_pmovsr |= value;
+ pmu_update_irq(env);
}
-static void pmxevtyper_write(CPUARMState *env, const ARMCPRegInfo *ri,
- uint64_t value)
+static void pmovsr_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
{
- env->cp15.c9_pmxevtyper = value & 0xff;
+ value &= PMU_COUNTER_MASK;
+ env->cp15.c9_pmovsr &= ~value;
+ pmu_update_irq(env);
}
static void pmuserenr_write(CPUARMState *env, const ARMCPRegInfo *ri,
@@ -815,16 +997,17 @@ static void pmuserenr_write(CPUARMState *env, const ARMCPRegInfo *ri,
static void pmintenset_write(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
- /* We have no event counters so only the C bit can be changed */
- value &= (1 << 31);
+ value &= PMU_COUNTER_MASK;
env->cp15.c9_pminten |= value;
+ pmu_update_irq(env);
}
static void pmintenclr_write(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
- value &= (1 << 31);
+ value &= PMU_COUNTER_MASK;
env->cp15.c9_pminten &= ~value;
+ pmu_update_irq(env);
}
static void vbar_write(CPUARMState *env, const ARMCPRegInfo *ri,
@@ -905,11 +1088,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
/* the old v6 WFI, UNPREDICTABLE in v7 but we choose to NOP */
{ .name = "NOP", .cp = 15, .crn = 7, .crm = 0, .opc1 = 0, .opc2 = 4,
.access = PL1_W, .type = ARM_CP_NOP },
- /* Performance monitors are implementation defined in v7,
- * but with an ARM recommended set of registers, which we
- * follow (although we don't actually implement any counters)
- *
- * Performance registers fall into three categories:
+ /* Performance monitor registers fall into three categories:
* (a) always UNDEF in PL0, RW in PL1 (PMINTENSET, PMINTENCLR)
* (b) RO in PL0 (ie UNDEF on write), RW in PL1 (PMUSERENR)
* (c) UNDEF in PL0 if PMUSERENR.EN==0, otherwise accessible (all others)
@@ -918,7 +1097,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
*/
{ .name = "PMCNTENSET", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 1,
.access = PL0_RW, .type = ARM_CP_ALIAS,
- .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcnten),
+ .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten),
.writefn = pmcntenset_write,
.accessfn = pmreg_access,
.raw_writefn = raw_write },
@@ -929,71 +1108,117 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
.writefn = pmcntenset_write, .raw_writefn = raw_write },
{ .name = "PMCNTENCLR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 2,
.access = PL0_RW,
- .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcnten),
+ .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten),
.accessfn = pmreg_access,
- .writefn = pmcntenclr_write,
+ .writefn = pmcntenclr_write, .raw_writefn = raw_write,
.type = ARM_CP_ALIAS },
{ .name = "PMCNTENCLR_EL0", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 2,
.access = PL0_RW, .accessfn = pmreg_access,
.type = ARM_CP_ALIAS,
.fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten),
- .writefn = pmcntenclr_write },
+ .writefn = pmcntenclr_write, .raw_writefn = raw_write },
+ { .name = "PMOVSSET_EL0", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 14, .opc2 = 3,
+ .access = PL0_RW, .accessfn = pmreg_access,
+ .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr), .resetvalue = 0,
+ .writefn = pmovsset_write, .raw_writefn = raw_write },
{ .name = "PMOVSR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 3,
.access = PL0_RW, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr),
- .accessfn = pmreg_access,
+ .accessfn = pmreg_access, .type = ARM_CP_ALIAS,
+ .writefn = pmovsr_write,
+ .raw_writefn = raw_write },
+ { .name = "PMOVSCLR_EL0", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 3,
+ .access = PL0_RW, .accessfn = pmreg_access,
+ .type = ARM_CP_ALIAS,
+ .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr),
.writefn = pmovsr_write,
.raw_writefn = raw_write },
/* Unimplemented so WI. */
{ .name = "PMSWINC", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 4,
- .access = PL0_W, .accessfn = pmreg_access, .type = ARM_CP_NOP },
- /* Since we don't implement any events, writing to PMSELR is UNPREDICTABLE.
- * We choose to RAZ/WI.
- */
+ .access = PL0_W, .accessfn = pmreg_access, .type = ARM_CP_NO_RAW,
+ .writefn = pmswinc_write },
+ { .name = "PMSWINC_EL0", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 4,
+ .access = PL0_W, .accessfn = pmreg_access, .type = ARM_CP_NO_RAW,
+ .writefn = pmswinc_write },
{ .name = "PMSELR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 5,
- .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0,
- .accessfn = pmreg_access },
-#ifndef CONFIG_USER_ONLY
+ .access = PL0_RW, .type = ARM_CP_ALIAS,
+ .accessfn = pmreg_access, .writefn = pmselr_write,
+ .fieldoffset = offsetof(CPUARMState, cp15.c9_pmselr) },
+ { .name = "PMSELR_EL0", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 5,
+ .access = PL0_RW, .accessfn = pmreg_access,
+ .fieldoffset = offsetof(CPUARMState, cp15.c9_pmselr),
+ .writefn = pmselr_write, .resetvalue = 0 },
+ { .name = "PMXEVCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 2,
+ .access = PL0_RW, .type = ARM_CP_ALIAS,
+ .accessfn = pmreg_access, .writefn = pmxevcntr_write,
+ .readfn = pmxevcntr_read },
+ { .name = "PMXEVCNTR_EL0", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 2,
+ .access = PL0_RW, .type = ARM_CP_ALIAS,
+ .accessfn = pmreg_access, .writefn = pmxevcntr_write,
+ .readfn = pmxevcntr_read },
+ { .name = "PMXEVTYPER", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 1,
+ .access = PL0_RW, .type = ARM_CP_ALIAS,
+ .accessfn = pmreg_access, .writefn = pmxevtyper_write,
+ .readfn = pmxevtyper_read },
+ { .name = "PMXEVTYPER_EL0", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 1,
+ .access = PL0_RW, .type = ARM_CP_ALIAS,
+ .accessfn = pmreg_access, .writefn = pmxevtyper_write,
+ .readfn = pmxevtyper_read },
{ .name = "PMCCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 0,
- .access = PL0_RW, .resetvalue = 0, .type = ARM_CP_IO,
+ .access = PL0_RW, .resetvalue = 0,
.readfn = pmccntr_read, .writefn = pmccntr_write32,
.accessfn = pmreg_access },
{ .name = "PMCCNTR_EL0", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 0,
.access = PL0_RW, .accessfn = pmreg_access,
- .type = ARM_CP_IO,
- .readfn = pmccntr_read, .writefn = pmccntr_write, },
-#endif
+ .type = ARM_CP_ALIAS,
+ .readfn = pmccntr_read, .writefn = pmccntr_write },
+ { .name = "PMCCFILTR", .cp = 15, .crn = 14, .crm = 15, .opc1 = 0, .opc2 = 7,
+ .access = PL0_RW, .accessfn = pmreg_access,
+ .writefn = pmccfiltr_write,
+ .fieldoffset = offsetof(CPUARMState, cp15.c14_pmccfiltr),
+ .resetvalue = 0 },
{ .name = "PMCCFILTR_EL0", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 3, .crn = 14, .crm = 15, .opc2 = 7,
.writefn = pmccfiltr_write,
.access = PL0_RW, .accessfn = pmreg_access,
- .type = ARM_CP_IO,
- .fieldoffset = offsetof(CPUARMState, cp15.pmccfiltr_el0),
- .resetvalue = 0, },
- { .name = "PMXEVTYPER", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 1,
- .access = PL0_RW,
- .fieldoffset = offsetof(CPUARMState, cp15.c9_pmxevtyper),
- .accessfn = pmreg_access, .writefn = pmxevtyper_write,
- .raw_writefn = raw_write },
- /* Unimplemented, RAZ/WI. */
- { .name = "PMXEVCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 2,
- .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0,
- .accessfn = pmreg_access },
+ .type = ARM_CP_ALIAS,
+ .fieldoffset = offsetof(CPUARMState, cp15.c14_pmccfiltr) },
{ .name = "PMUSERENR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 0,
+ .access = PL0_R | PL1_RW, .type = ARM_CP_ALIAS,
+ .fieldoffset = offsetof(CPUARMState, cp15.c9_pmuserenr),
+ .writefn = pmuserenr_write, .raw_writefn = raw_write },
+ { .name = "PMUSERENR_EL0", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 14, .opc2 = 0,
.access = PL0_R | PL1_RW,
.fieldoffset = offsetof(CPUARMState, cp15.c9_pmuserenr),
.resetvalue = 0,
.writefn = pmuserenr_write, .raw_writefn = raw_write },
{ .name = "PMINTENSET", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 1,
- .access = PL1_RW,
+ .access = PL1_RW, .type = ARM_CP_ALIAS,
.fieldoffset = offsetof(CPUARMState, cp15.c9_pminten),
.resetvalue = 0,
.writefn = pmintenset_write, .raw_writefn = raw_write },
+ { .name = "PMINTENSET_EL1", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 1,
+ .access = PL1_RW,
+ .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), .resetvalue = 0,
+ .writefn = pmintenset_write, .raw_writefn = raw_write },
{ .name = "PMINTENCLR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 2,
.access = PL1_RW, .type = ARM_CP_ALIAS,
.fieldoffset = offsetof(CPUARMState, cp15.c9_pminten),
.resetvalue = 0, .writefn = pmintenclr_write, },
+ { .name = "PMINTENCLR_EL1", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 2,
+ .access = PL1_RW, .type = ARM_CP_ALIAS,
+ .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten),
+ .writefn = pmintenclr_write },
{ .name = "VBAR", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .crn = 12, .crm = 0, .opc1 = 0, .opc2 = 0,
.access = PL1_RW, .writefn = vbar_write,
@@ -3024,6 +3249,7 @@ static void define_debug_regs(ARMCPU *cpu)
void register_cp_regs_for_features(ARMCPU *cpu)
{
+ unsigned int i;
/* Register all the coprocessor registers based on feature bits */
CPUARMState *env = &cpu->env;
if (arm_feature(env, ARM_FEATURE_M)) {
@@ -3120,15 +3346,14 @@ void register_cp_regs_for_features(ARMCPU *cpu)
}
if (arm_feature(env, ARM_FEATURE_V7)) {
/* v7 performance monitor control register: same implementor
- * field as main ID register, and we implement only the cycle
- * count register.
+ * field as main ID register.
*/
-#ifndef CONFIG_USER_ONLY
ARMCPRegInfo pmcr = {
.name = "PMCR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 0,
.access = PL0_RW,
- .type = ARM_CP_IO | ARM_CP_ALIAS,
- .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcr),
+ .type = ARM_CP_ALIAS,
+ .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcr),
+ .resetvalue = (cpu->midr & 0xff000000) | (NUM_PMU_COUNTERS << 11),
.accessfn = pmreg_access, .writefn = pmcr_write,
.raw_writefn = raw_write,
};
@@ -3136,14 +3361,47 @@ void register_cp_regs_for_features(ARMCPU *cpu)
.name = "PMCR_EL0", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 0,
.access = PL0_RW, .accessfn = pmreg_access,
- .type = ARM_CP_IO,
.fieldoffset = offsetof(CPUARMState, cp15.c9_pmcr),
- .resetvalue = cpu->midr & 0xff000000,
+ .resetvalue = (cpu->midr & 0xff000000) | (NUM_PMU_COUNTERS << 11),
.writefn = pmcr_write, .raw_writefn = raw_write,
};
define_one_arm_cp_reg(cpu, &pmcr);
define_one_arm_cp_reg(cpu, &pmcr64);
-#endif
+
+ for (i = 0; i < 31; i++) {
+ char *pmevcntr_name = g_strdup_printf("PMEVCNTR%d", i);
+ char *pmevcntr_el0_name = g_strdup_printf("PMEVCNTR%d_EL0", i);
+ char *pmevtyper_name = g_strdup_printf("PMEVTYPER%d", i);
+ char *pmevtyper_el0_name = g_strdup_printf("PMEVTYPER%d_EL0", i);
+ ARMCPRegInfo pmcntr_regs[] = {
+ { .name = pmevcntr_name, .cp = 15, .crn = 15,
+ .crm = 8 | (3 & (i >> 3)), .opc1 = 0, .opc2 = i & 7,
+ .access = PL0_RW, .resetvalue = 0, .type = ARM_CP_ALIAS,
+ .readfn = pmevcntr_readfn, .writefn = pmevcntr_writefn,
+ .accessfn = pmreg_access },
+ { .name = pmevcntr_el0_name, .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 3, .crn = 15, .crm = 8 | (3 & (i >> 3)),
+ .opc2 = i & 7, .access = PL0_RW, .accessfn = pmreg_access,
+ .resetvalue = 0,
+ .readfn = pmevcntr_readfn, .writefn = pmevcntr_writefn },
+ { .name = pmevtyper_name, .cp = 15, .crn = 15,
+ .crm = 12 | (3 & (i >> 3)), .opc1 = 0, .opc2 = i & 7,
+ .access = PL0_RW, .resetvalue = 0, .type = ARM_CP_ALIAS,
+ .readfn = pmevtyper_readfn, .writefn = pmevtyper_writefn,
+ .accessfn = pmreg_access },
+ { .name = pmevtyper_el0_name, .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 3, .crn = 15, .crm = 12 | (3 & (i >> 3)),
+ .opc2 = i & 7, .access = PL0_RW, .accessfn = pmreg_access,
+ .resetvalue = 0,
+ .readfn = pmevtyper_readfn, .writefn = pmevtyper_writefn },
+ REGINFO_SENTINEL
+ };
+ define_arm_cp_regs(cpu, pmcntr_regs);
+ g_free(pmevcntr_name);
+ g_free(pmevcntr_el0_name);
+ g_free(pmevtyper_name);
+ g_free(pmevtyper_el0_name);
+ }
ARMCPRegInfo clidr = {
.name = "CLIDR", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 1,
@@ -3169,16 +3427,19 @@ void register_cp_regs_for_features(ARMCPU *cpu)
{ .name = "ID_AA64DFR0_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 0,
.access = PL1_R, .type = ARM_CP_CONST,
- /* We mask out the PMUVer field, because we don't currently
- * implement the PMU. Not advertising it prevents the guest
- * from trying to use it and getting UNDEFs on registers we
- * don't implement.
- */
- .resetvalue = cpu->id_aa64dfr0 & ~0xf00 },
+ .resetvalue = cpu->id_aa64dfr0 },
{ .name = "ID_AA64DFR1_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 1,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = cpu->id_aa64dfr1 },
+ { .name = "PMCEID0_EL0", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 6,
+ .access = PL1_R, .type = ARM_CP_CONST,
+ .resetvalue = cpu->pmceid0},
+ { .name = "PMCEID1_EL0", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 7,
+ .access = PL1_R, .type = ARM_CP_CONST,
+ .resetvalue = cpu->pmceid1},
{ .name = "ID_AA64AFR0_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 4,
.access = PL1_R, .type = ARM_CP_CONST,
@@ -4073,14 +4334,17 @@ void HELPER(context_check_pid)(CPUARMState *env)
void HELPER(update_instruction_count)(CPUARMState *env)
{
if (bbtrace_initialized()) {
- /*
- * If the bbv plugin is compiled in and enabled, we must account for the
- * fact that bbv_profile needs to see prof_ic before we clear it.
- * However, it doesn't always clear the counter every time this gets
- * called, so we must keep track of the last value seen to ensure we
- * update the instruction counter correctly in that case.
- */
- increment_instruction_counters(env->prof_ic - env->prof_ic_last);
+ /*
+ * If the bbv plugin is compiled in and enabled, we must account for the
+ * fact that bbv_profile needs to see prof_ic before we clear it.
+ * However, it doesn't always clear the counter every time this gets
+ * called, so we must keep track of the last value seen to ensure we
+ * update the instruction counter correctly in that case.
+ */
+ pmevcntr_increment(env, PMU_COUNTER_TYPE_INSTRUCTIONS,
+ env->prof_ic - env->prof_ic_last);
+ pmevcntr_increment(env, PMU_COUNTER_TYPE_CYCLES,
+ env->prof_ic - env->prof_ic_last);
if (env->prof_pc && env->prof_is_jmp) {
// If this is the end of a basic block, zero out last_seen counter too
env->prof_ic_last = 0;
--
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [Qemu-devel] [RFC 10/14] bbvec: Move mode/PID change detection to register writes
2015-08-05 16:51 [Qemu-devel] RFC: ARM Semihosting, PMU, and BBV Changes Christopher Covington
` (8 preceding siblings ...)
2015-08-05 16:51 ` [Qemu-devel] [RFC 09/14] Implement remaining PMU functionality Christopher Covington
@ 2015-08-05 16:51 ` Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 11/14] Print bbvec stats on 'magic' exceptions Christopher Covington
` (4 subsequent siblings)
14 siblings, 0 replies; 25+ messages in thread
From: Christopher Covington @ 2015-08-05 16:51 UTC (permalink / raw)
To: qemu-devel; +Cc: Christopher Covington
This should speed up basic block detection since these were previously
being checked for on each basic block / instruction.
Written by Aaron Lindsay.
Signed-off-by: Christopher Covington <cov@codeaurora.org>
---
target-arm/cpu.h | 13 +++++++++++++
target-arm/helper-a64.c | 2 +-
target-arm/helper.c | 39 +++++++++++++++++++++++++++++----------
target-arm/helper.h | 2 --
target-arm/translate-a64.c | 2 --
target-arm/translate.c | 2 --
6 files changed, 43 insertions(+), 17 deletions(-)
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index f6857fa..6c4ba9c 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -670,14 +670,27 @@ static inline uint32_t pstate_read(CPUARMState *env)
| env->pstate | env->daif;
}
+void update_instruction_count(CPUARMState *env);
+#ifdef CONFIG_BBVEC
+void context_check_mode(CPUARMState *env);
+#endif
+
static inline void pstate_write(CPUARMState *env, uint32_t val)
{
+ bool mode_changed = (env->pstate ^ val) & PSTATE_M;
env->ZF = (~val) & PSTATE_Z;
env->NF = val;
env->CF = (val >> 29) & 1;
env->VF = (val << 3) & 0x80000000;
env->daif = val & PSTATE_DAIF;
env->pstate = val & ~CACHED_PSTATE_BITS;
+
+ if (mode_changed) {
+ update_instruction_count(env);
+#ifdef CONFIG_BBVEC
+ context_check_mode(env);
+#endif
+ }
}
/* Return the current CPSR value. */
diff --git a/target-arm/helper-a64.c b/target-arm/helper-a64.c
index 8803293..e647b90 100644
--- a/target-arm/helper-a64.c
+++ b/target-arm/helper-a64.c
@@ -558,8 +558,8 @@ void aarch64_cpu_do_interrupt(CPUState *cs)
env->condexec_bits = 0;
}
- pstate_write(env, PSTATE_DAIF | new_mode);
env->aarch64 = 1;
+ pstate_write(env, PSTATE_DAIF | new_mode);
aarch64_restore_sp(env, new_el);
env->pc = addr;
diff --git a/target-arm/helper.c b/target-arm/helper.c
index a659e67..c1f4c47 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -387,6 +387,7 @@ static void fcse_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
}
}
+void context_check_pid(CPUARMState *env);
static void contextidr_write(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
@@ -399,6 +400,9 @@ static void contextidr_write(CPUARMState *env, const ARMCPRegInfo *ri,
arm_tlb_flush(env, 1);
}
raw_write(env, ri, value);
+#ifdef CONFIG_BBVEC
+ context_check_pid(env);
+#endif
}
static void tlbiall_write(CPUARMState *env, const ARMCPRegInfo *ri,
@@ -4292,29 +4296,34 @@ void HELPER(bbv_profile)(CPUARMState *env)
}
}
-void HELPER(context_check_mode)(CPUARMState *env)
+void context_check_mode(CPUARMState *env)
{
- uint32_t mode;
+ uint32_t priv_mode; /* nonzero if privileged */
+
+ if (!bbtrace_initialized())
+ return;
- /* Get current mode: userspace or privileged */
if (env->aarch64) {
- mode = extract32(env->pstate, 2, 2);
+ priv_mode = extract32(env->pstate, 2, 2);
}
else if ((env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_USR) {
- mode = 0;
+ priv_mode = 0;
}
/* We don't currently implement the Virtualization or TrustZone
* extensions, so PL2 and PL3 don't exist for us.
*/
- else mode = 1;
+ else priv_mode = 1;
- bb_context_check_mode(env->prof_ic, mode);
+ bb_context_check_mode(env->prof_ic, priv_mode);
}
-void HELPER(context_check_pid)(CPUARMState *env)
+void context_check_pid(CPUARMState *env)
{
uint64_t pid;
+ if (!bbtrace_initialized())
+ return;
+
/* Read pid from CONTEXTIDR register. In aarch32, if EL1 is not in AArch64
* mode, we need to shift out the address space identifier in the first 8 bits.
*
@@ -4331,7 +4340,7 @@ void HELPER(context_check_pid)(CPUARMState *env)
bb_context_check_pid(env->prof_ic, pid);
}
-void HELPER(update_instruction_count)(CPUARMState *env)
+void update_instruction_count(CPUARMState *env)
{
if (bbtrace_initialized()) {
/*
@@ -4360,7 +4369,7 @@ void HELPER(update_instruction_count)(CPUARMState *env)
#else //!CONFIG_BBVEC
-void HELPER(update_instruction_count)(CPUARMState *env)
+void update_instruction_count(CPUARMState *env)
{
pmevcntr_increment(env, PMU_COUNTER_TYPE_INSTRUCTIONS, env->prof_ic);
pmevcntr_increment(env, PMU_COUNTER_TYPE_CYCLES, env->prof_ic);
@@ -4369,6 +4378,11 @@ void HELPER(update_instruction_count)(CPUARMState *env)
#endif //CONFIG_BBVEC
+void HELPER(update_instruction_count)(CPUARMState *env)
+{
+ update_instruction_count(env);
+}
+
/* Sign/zero extend */
uint32_t HELPER(sxtb16)(uint32_t x)
{
@@ -4525,6 +4539,11 @@ void switch_mode(CPUARMState *env, int mode)
if (mode == old_mode)
return;
+ update_instruction_count(env);
+#ifdef CONFIG_BBVEC
+ context_check_mode(env);
+#endif
+
if (old_mode == ARM_CPU_MODE_FIQ) {
memcpy (env->fiq_regs, env->regs + 8, 5 * sizeof(uint32_t));
memcpy (env->regs + 8, env->usr_regs, 5 * sizeof(uint32_t));
diff --git a/target-arm/helper.h b/target-arm/helper.h
index 02edf3a..0f88080 100644
--- a/target-arm/helper.h
+++ b/target-arm/helper.h
@@ -39,8 +39,6 @@ PAS_OP(uh)
#ifdef CONFIG_BBVEC
DEF_HELPER_1(bbv_profile, void, env)
-DEF_HELPER_1(context_check_mode, void, env)
-DEF_HELPER_1(context_check_pid, void, env)
#endif // CONFIG_BBVEC
DEF_HELPER_1(update_instruction_count, void, env)
diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c
index f6f8832..2471184 100644
--- a/target-arm/translate-a64.c
+++ b/target-arm/translate-a64.c
@@ -11076,14 +11076,12 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu,
if (bbtrace_initialized()) {
gen_helper_bbv_profile(cpu_env);
gen_store_is_jmp(0);
- gen_helper_context_check_pid(cpu_env);
}
#endif // CONFIG_BBVEC
do {
#ifdef CONFIG_BBVEC
if (bbtrace_initialized()) {
- gen_helper_context_check_mode(cpu_env);
gen_pc_incr(env, dc);
}
#endif // CONFIG_BBVEC
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 113e3b6..f9d69ef 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -11635,14 +11635,12 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu,
if (bbtrace_initialized()) {
gen_helper_bbv_profile(cpu_env);
gen_store_is_jmp(0);
- gen_helper_context_check_pid(cpu_env);
}
#endif
do {
#ifdef CONFIG_BBVEC
if (bbtrace_initialized()) {
- gen_helper_context_check_mode(cpu_env);
gen_pc_incr(env, dc);
/* FIXME: this call should not be necessary if all the cases
where the prof_is_jmp flag gets set are correct. */
--
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [Qemu-devel] [RFC 11/14] Print bbvec stats on 'magic' exceptions
2015-08-05 16:51 [Qemu-devel] RFC: ARM Semihosting, PMU, and BBV Changes Christopher Covington
` (9 preceding siblings ...)
2015-08-05 16:51 ` [Qemu-devel] [RFC 10/14] bbvec: Move mode/PID change detection to register writes Christopher Covington
@ 2015-08-05 16:51 ` Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 12/14] bbvec: Detect mode changes after uncached_cpsr update Christopher Covington
` (3 subsequent siblings)
14 siblings, 0 replies; 25+ messages in thread
From: Christopher Covington @ 2015-08-05 16:51 UTC (permalink / raw)
To: qemu-devel; +Cc: Christopher Covington
This is necessary because we need a way to differentiate between
instructions executed in a PID by the benchmark we care about and those
executed by CRIU.
Written by Aaron Lindsay.
Signed-off-by: Christopher Covington <cov@codeaurora.org>
---
bbv_profiler.c | 15 +++++++++++++++
bbv_profiler.h | 1 +
target-arm/helper-a64.c | 10 ++++++++++
target-arm/helper.c | 12 ++++++++++++
4 files changed, 38 insertions(+)
diff --git a/bbv_profiler.c b/bbv_profiler.c
index 51e8060..66984b2 100644
--- a/bbv_profiler.c
+++ b/bbv_profiler.c
@@ -19,6 +19,12 @@
#include <assert.h>
#include "bbv_profiler.h"
+/* Magic number, which is set as bits 16-31 of the target of a branch which
+ * causes an exception to send a signal to the plugin.
+ */
+#define BBV_MAGIC_NUM 0xdead
+#define BBV_PRINT_STATS 0x0
+
static BasicBlockTraceHandle trace = NULL;
static uint32_t mode = 0;
static uint64_t pid = 0;
@@ -75,3 +81,12 @@ void bb_context_check_pid(uint64_t IC, uint64_t new_pid)
bbvec_pid_change(trace, new_pid, IC);
}
}
+
+/* Check if the bbv plugin is being signaled to do something by an exception */
+void bb_check_exception(uint64_t pc) {
+ if (((pc >> 16) & 0xffff) == BBV_MAGIC_NUM) {
+ uint16_t value = pc & 0xffff;
+ if (value == BBV_PRINT_STATS)
+ bbvec_print_stats(trace);
+ }
+}
diff --git a/bbv_profiler.h b/bbv_profiler.h
index 26dfa1f..b922451 100644
--- a/bbv_profiler.h
+++ b/bbv_profiler.h
@@ -31,5 +31,6 @@ int bbtrace_initialized(void);
void bb_process(uint64_t PC, uint64_t IC);
void bb_context_check_mode(uint64_t IC, uint32_t mode);
void bb_context_check_pid(uint64_t IC, uint64_t tpid);
+void bb_check_exception(uint64_t pc);
#endif
diff --git a/target-arm/helper-a64.c b/target-arm/helper-a64.c
index e647b90..95eb096 100644
--- a/target-arm/helper-a64.c
+++ b/target-arm/helper-a64.c
@@ -27,6 +27,10 @@
#include "qemu/crc32c.h"
#include <zlib.h> /* For crc32 */
+#ifdef CONFIG_BBVEC
+#include "bbv_profiler.h"
+#endif // CONFIG_BBVEC
+
/* C2.4.7 Multiply and divide */
/* special cases for 0 and LLONG_MIN are mandated by the standard */
uint64_t HELPER(udiv64)(uint64_t num, uint64_t den)
@@ -470,6 +474,12 @@ void aarch64_cpu_do_interrupt(CPUState *cs)
uint64_t mask;
#endif
+#ifdef CONFIG_BBVEC
+ if (bbtrace_initialized()) {
+ bb_check_exception(is_a64(env) ? env->pc : env->regs[15]);
+ }
+#endif
+
uint32_t syndrome =
cs->exception_index == EXCP_ARMV8_HLT ?
env->exception.syndrome & ~0xffff :
diff --git a/target-arm/helper.c b/target-arm/helper.c
index c1f4c47..297eb7c 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -4773,6 +4773,12 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
arm_log_exception(cs->exception_index);
+#ifdef CONFIG_BBVEC
+ if (bbtrace_initialized()) {
+ bb_check_exception(env->regs[15]);
+ }
+#endif
+
lr = 0xfffffff1;
if (env->v7m.current_sp)
lr |= 4;
@@ -5074,6 +5080,12 @@ void arm_cpu_do_interrupt(CPUState *cs)
return;
}
+#ifdef CONFIG_BBVEC
+ if (bbtrace_initialized()) {
+ bb_check_exception(env->regs[15]);
+ }
+#endif
+
/* If this is a debug exception we must update the DBGDSCR.MOE bits */
switch (env->exception.syndrome >> ARM_EL_EC_SHIFT) {
case EC_BREAKPOINT:
--
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [Qemu-devel] [RFC 12/14] bbvec: Detect mode changes after uncached_cpsr update
2015-08-05 16:51 [Qemu-devel] RFC: ARM Semihosting, PMU, and BBV Changes Christopher Covington
` (10 preceding siblings ...)
2015-08-05 16:51 ` [Qemu-devel] [RFC 11/14] Print bbvec stats on 'magic' exceptions Christopher Covington
@ 2015-08-05 16:51 ` Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 13/14] Enable negative icount values for QEMU Christopher Covington
` (2 subsequent siblings)
14 siblings, 0 replies; 25+ messages in thread
From: Christopher Covington @ 2015-08-05 16:51 UTC (permalink / raw)
To: qemu-devel; +Cc: Christopher Covington
The previous code checked for the mode change before the new mode was
written to env->uncached_cpsr, which unfortunately made the bbvec output
look reasonable for small tests.
Written by Aaron Lindsay.
Signed-off-by: Christopher Covington <cov@codeaurora.org>
---
target-arm/helper.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 297eb7c..ae0a4ac 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -4281,6 +4281,9 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
}
mask &= ~CACHED_CPSR_BITS;
env->uncached_cpsr = (env->uncached_cpsr & ~mask) | (val & mask);
+#ifdef CONFIG_BBVEC
+ context_check_mode(env);
+#endif
}
#ifdef CONFIG_BBVEC
@@ -4540,9 +4543,6 @@ void switch_mode(CPUARMState *env, int mode)
return;
update_instruction_count(env);
-#ifdef CONFIG_BBVEC
- context_check_mode(env);
-#endif
if (old_mode == ARM_CPU_MODE_FIQ) {
memcpy (env->fiq_regs, env->regs + 8, 5 * sizeof(uint32_t));
--
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [Qemu-devel] [RFC 13/14] Enable negative icount values for QEMU.
2015-08-05 16:51 [Qemu-devel] RFC: ARM Semihosting, PMU, and BBV Changes Christopher Covington
` (11 preceding siblings ...)
2015-08-05 16:51 ` [Qemu-devel] [RFC 12/14] bbvec: Detect mode changes after uncached_cpsr update Christopher Covington
@ 2015-08-05 16:51 ` Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 14/14] bbvec: Properly detect conditional thumb2 branching instructions Christopher Covington
2015-08-11 15:27 ` [Qemu-devel] RFC: ARM Semihosting, PMU, and BBV Changes Peter Maydell
14 siblings, 0 replies; 25+ messages in thread
From: Christopher Covington @ 2015-08-05 16:51 UTC (permalink / raw)
To: qemu-devel; +Cc: Christopher Covington
The icount setting specifies how far to shift the instruction
count as a ratio of ns to tie system time to instruction count.
Allow a negative value (i.e. a right shift instead of a left shift)
to be used.
Written by Pat Galizia.
Signed-off-by: Christopher Covington <cov@codeaurora.org>
---
cpus.c | 17 +++++++++++++----
target-arm/helper.c | 14 ++++++++++++++
2 files changed, 27 insertions(+), 4 deletions(-)
diff --git a/cpus.c b/cpus.c
index 62d157a..1c92a85 100644
--- a/cpus.c
+++ b/cpus.c
@@ -110,6 +110,8 @@ static int64_t vm_clock_warp_start = -1;
static int icount_time_shift;
/* Arbitrarily pick 1MIPS as the minimum allowable speed. */
#define MAX_ICOUNT_SHIFT 10
+/* In cases where we have a negative icount shift, specify a lower bound. */
+#define MIN_ICOUNT_SHIFT -5
static QEMUTimer *icount_rt_timer;
static QEMUTimer *icount_vm_timer;
@@ -174,6 +176,8 @@ int64_t cpu_get_icount(void)
int64_t cpu_icount_to_ns(int64_t icount)
{
+ if (icount_time_shift < 0)
+ return icount >> (-icount_time_shift);
return icount << icount_time_shift;
}
@@ -271,6 +275,7 @@ static void icount_adjust(void)
int64_t cur_time;
int64_t cur_icount;
int64_t delta;
+ int64_t icount_shifted;
/* Protected by TimersState mutex. */
static int64_t last_delta;
@@ -287,8 +292,8 @@ static void icount_adjust(void)
delta = cur_icount - cur_time;
/* FIXME: This is a very crude algorithm, somewhat prone to oscillation. */
if (delta > 0
- && last_delta + ICOUNT_WOBBLE < delta * 2
- && icount_time_shift > 0) {
+ && last_delta + ICOUNT_WOBBLE < delta * 2
+ && icount_time_shift > MIN_ICOUNT_SHIFT) {
/* The guest is getting too far ahead. Slow time down. */
icount_time_shift--;
}
@@ -299,8 +304,10 @@ static void icount_adjust(void)
icount_time_shift++;
}
last_delta = delta;
- timers_state.qemu_icount_bias = cur_icount
- - (timers_state.qemu_icount << icount_time_shift);
+ icount_shifted = (icount_time_shift >= 0) ?
+ timers_state.qemu_icount << icount_time_shift :
+ timers_state.qemu_icount >> (-icount_time_shift);
+ timers_state.qemu_icount_bias = cur_icount - icount_shifted;
seqlock_write_unlock(&timers_state.vm_clock_seqlock);
}
@@ -321,6 +328,8 @@ static void icount_adjust_vm(void *opaque)
static int64_t qemu_icount_round(int64_t count)
{
+ if (icount_time_shift < 0)
+ return count;
return (count + (1 << icount_time_shift) - 1) >> icount_time_shift;
}
diff --git a/target-arm/helper.c b/target-arm/helper.c
index ae0a4ac..0436df5 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -4345,6 +4345,11 @@ void context_check_pid(CPUARMState *env)
void update_instruction_count(CPUARMState *env)
{
+ CPUState *cpu = CPU(arm_env_get_cpu(env));
+ int prevIoState = cpu->can_do_io;
+ if (use_icount)
+ cpu->can_do_io = 1;
+
if (bbtrace_initialized()) {
/*
* If the bbv plugin is compiled in and enabled, we must account for the
@@ -4368,15 +4373,24 @@ void update_instruction_count(CPUARMState *env)
pmevcntr_increment(env, PMU_COUNTER_TYPE_CYCLES, env->prof_ic);
env->prof_ic = 0;
}
+
+ if (use_icount)
+ cpu->can_do_io = prevIoState;
}
#else //!CONFIG_BBVEC
void update_instruction_count(CPUARMState *env)
{
+ CPUState *cpu = CPU(arm_env_get_cpu(env));
+ int prevIoState = cpu->can_do_io;
+ if (use_icount)
+ cpu->can_do_io = 1;
pmevcntr_increment(env, PMU_COUNTER_TYPE_INSTRUCTIONS, env->prof_ic);
pmevcntr_increment(env, PMU_COUNTER_TYPE_CYCLES, env->prof_ic);
env->prof_ic = 0;
+ if (use_icount)
+ cpu->can_do_io = prevIoState;
}
#endif //CONFIG_BBVEC
--
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [Qemu-devel] [RFC 14/14] bbvec: Properly detect conditional thumb2 branching instructions
2015-08-05 16:51 [Qemu-devel] RFC: ARM Semihosting, PMU, and BBV Changes Christopher Covington
` (12 preceding siblings ...)
2015-08-05 16:51 ` [Qemu-devel] [RFC 13/14] Enable negative icount values for QEMU Christopher Covington
@ 2015-08-05 16:51 ` Christopher Covington
2015-08-11 15:27 ` [Qemu-devel] RFC: ARM Semihosting, PMU, and BBV Changes Peter Maydell
14 siblings, 0 replies; 25+ messages in thread
From: Christopher Covington @ 2015-08-05 16:51 UTC (permalink / raw)
To: qemu-devel; +Cc: Christopher Covington
Add bbvec detection of thumb2 branch instructions via a stripped-down
version of the thumb2 instruction decoding logic. Unfortunately, this
code still called into disas_neon_ls_insn(), which is apparently not
free from side effects. Therefore, calling into this function twice
(once in the added bbvec thumb2 branch detection code and once in the
original decoding code) causes QEMU to report segfaults and illegal
instruction exceptions to the guest kernel (possibly among other
undesirable, still-undiscovered behavior). Because neon instructions
are not allowed to write to the PC, eliminating the call to
disas_neon_ls_insn() altogether is safe.
Written by Aaron Lindsay.
Signed-off-by: Christopher Covington <cov@codeaurora.org>
---
target-arm/translate.c | 264 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 261 insertions(+), 3 deletions(-)
diff --git a/target-arm/translate.c b/target-arm/translate.c
index f9d69ef..8e9b97b 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -9411,6 +9411,260 @@ gen_thumb2_data_op(DisasContext *s, int op, int conds, uint32_t shifter_out,
return 0;
}
+#ifdef CONFIG_BBVEC
+/* Call gen_store_is_jmp(1) if the instruction is a thumb2 branch instruction.
+ * This is called by disas_thumb_insn. The switch/case/if structures are a
+ * simplified copy of the logic in disas_thumb2_insn */
+static void thumb2_branch_detection(CPUARMState *env, DisasContext *s, uint16_t insn_hw1)
+{
+ uint32_t insn, shift, rd, rn, rs;
+ int op;
+
+ if (!(arm_dc_feature(s, ARM_FEATURE_THUMB2)
+ || arm_dc_feature(s, ARM_FEATURE_M))) {
+ /* Thumb-1 cores may need to treat bl and blx as a pair of
+ 16-bit instructions to get correct prefetch abort behavior. */
+ insn = insn_hw1;
+ if ((insn & (1 << 12)) == 0) {
+ ARCH(5);
+ gen_store_is_jmp(1);
+ return;
+ }
+ if (insn & (1 << 11)) {
+ gen_store_is_jmp(1);
+ return;
+ }
+ if ((s->pc & ~TARGET_PAGE_MASK) == 0) {
+ return;
+ }
+ /* Fall through to 32-bit decode. */
+ }
+
+ insn = arm_lduw_code(env, s->pc, s->bswap_code);
+ insn |= (uint32_t)insn_hw1 << 16;
+
+ if ((insn & 0xf800e800) != 0xf000e800) {
+ ARCH(6T2);
+ }
+
+ rn = (insn >> 16) & 0xf;
+ rs = (insn >> 12) & 0xf;
+ rd = (insn >> 8) & 0xf;
+ switch ((insn >> 25) & 0xf) {
+ case 0: case 1: case 2: case 3:
+ /* 16-bit instructions. Should never happen. */
+ return;
+ case 4:
+ if (insn & (1 << 22)) {
+ /* Other load/store, table branch. */
+ if (insn & 0x01200000) {
+ /* Load/store doubleword. */
+ if (insn & (1 << 20)) {
+ /* ldrd */
+ if (rs == 15 || rd == 15)
+ gen_store_is_jmp(1);
+ }
+ } else if ((insn & (1 << 23)) == 0) {
+ /* Load/store exclusive word. */
+ } else if ((insn & (7 << 5)) == 0) {
+ /* Table Branch. */
+ gen_store_is_jmp(1);
+ } else {
+ int op2 = (insn >> 6) & 0x3;
+ op = (insn >> 4) & 0x3;
+ switch (op2) {
+ case 0:
+ return;
+ case 1:
+ /* Load/store exclusive byte/halfword/doubleword */
+ if (op == 2) {
+ return;
+ }
+ ARCH(7);
+ break;
+ case 2:
+ /* Load-acquire/store-release */
+ if (op == 3) {
+ return;
+ }
+ /* Fall through */
+ case 3:
+ /* Load-acquire/store-release exclusive */
+ ARCH(8);
+ break;
+ }
+ if (!(op2 & 1)) {
+ if (insn & (1 << 20)) {
+ if (op >= 0 && op <= 2 && rs == 15)
+ gen_store_is_jmp(1);
+ }
+ } else if (insn & (1 << 20)) {
+ if (rs == 15 || rd == 15)
+ gen_store_is_jmp(1);
+ }
+ }
+ } else {
+ /* Load/store multiple, RFE, SRS. */
+ if (((insn >> 23) & 1) == ((insn >> 24) & 1)) {
+ /* RFE, SRS: not available in user mode or on M profile */
+ if (IS_USER(s) || arm_dc_feature(s, ARM_FEATURE_M)) {
+ goto illegal_op;
+ }
+ if (insn & (1 << 20)) {
+ /* rfe */
+ if (insn & (1 << 21)) {
+ /* Base writeback. */
+ if (rn == 15)
+ gen_store_is_jmp(1);
+ }
+ }
+ } else {
+ int i, loaded_base = 0;
+ /* Load/store multiple. */
+ for (i = 0; i < 16; i++) {
+ if ((insn & (1 << i)) == 0)
+ continue;
+ if (insn & (1 << 20)) {
+ /* Load. */
+ if (i == 15) {
+ gen_store_is_jmp(1);
+ } else if (i == rn) {
+ loaded_base = 1;
+ } else {
+ if (i == 15)
+ gen_store_is_jmp(1);
+ }
+ }
+ }
+ if (loaded_base) {
+ if (rn == 15)
+ gen_store_is_jmp(1);
+ }
+ if (insn & (1 << 21)) {
+ /* Base register writeback. */
+ /* Fault if writeback register is in register list. */
+ if (insn & (1 << rn))
+ goto illegal_op;
+ if (rn == 15)
+ gen_store_is_jmp(1);
+ }
+ }
+ }
+ break;
+ case 5:
+ op = (insn >> 21) & 0xf;
+ if (op == 6) {
+ if (rd == 15)
+ gen_store_is_jmp(1);
+ }
+ break;
+ case 13: /* Misc data processing. */
+ op = ((insn >> 22) & 6) | ((insn >> 7) & 1);
+ if (op < 4 && (insn & 0xf000) != 0xf000)
+ goto illegal_op;
+ switch (op) {
+ case 0: /* Register controlled shift. */
+ if ((insn & 0x70) != 0)
+ goto illegal_op;
+ if (rd == 15)
+ gen_store_is_jmp(1);
+ break;
+ case 1: /* Sign/zero extend. */
+ op = (insn >> 20) & 7;
+ if (op < 0 || op > 5)
+ return;
+ if (rd == 15)
+ gen_store_is_jmp(1);
+ break;
+ case 2: /* SIMD add/subtract. */
+ op = (insn >> 20) & 7;
+ shift = (insn >> 4) & 7;
+ if ((op & 3) == 3 || (shift & 3) == 3)
+ goto illegal_op;
+ case 3: /* Other data processing. */
+ case 4: case 5: /* 32-bit multiply. Sum of absolute differences. */
+ if (rd == 15)
+ gen_store_is_jmp(1);
+ break;
+ case 6: case 7: /* 64-bit multiply, Divide. */
+ op = ((insn >> 4) & 0xf) | ((insn >> 16) & 0x70);
+ if ((op & 0x50) == 0x10) {
+ /* sdiv, udiv */
+ if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DIV)) {
+ goto illegal_op;
+ }
+
+ if (rd == 15)
+ gen_store_is_jmp(1);
+ }
+ break;
+ }
+ break;
+ case 8: case 9: case 10: case 11:
+ if (insn & (1 << 15)) {
+ /* Branches, misc control. */
+ if (insn & 0x5000) {
+ /* Unconditional branch. */
+ gen_store_is_jmp(1);
+ } else if (((insn >> 23) & 7) == 7) {
+ /* Misc control */
+ if (insn & (1 << 13))
+ goto illegal_op;
+
+ if ((insn & (1 << 26)) == 0) {
+ op = (insn >> 20) & 7;
+ if (op == 4) /* bxj */
+ gen_store_is_jmp(1);
+ }
+ } else {
+ /* Conditional branch. */
+ gen_store_is_jmp(1);
+ }
+ } else {
+ /* Data processing immediate. */
+ if (insn & (1 << 25)) {
+ if (insn & (1 << 24)) {
+ if (insn & (1 << 20))
+ goto illegal_op;
+ /* Bitfield/Saturate. */
+ if (rd == 15)
+ gen_store_is_jmp(1);
+ } else {
+ if (rd == 15)
+ gen_store_is_jmp(1);
+ }
+ }
+ }
+ break;
+ case 12: /* Load/store single data item. */
+ {
+ if ((insn & 0x01100000) == 0x01000000)
+ break; // Ignore neon instructions, they should not update the PC
+ op = ((insn >> 21) & 3) | ((insn >> 22) & 4);
+ if (insn & (1 << 20)) {
+ /* Load. */
+ switch (op) {
+ case 0:
+ case 4:
+ case 1:
+ case 5:
+ case 2:
+ break;
+ default:
+ goto illegal_op;
+ }
+ if (rs == 15) {
+ gen_store_is_jmp(1);
+ }
+ }
+ }
+ break;
+ }
+illegal_op:
+ return;
+}
+#endif // CONFIG_BBVEC
+
/* Translate a 32-bit thumb instruction. Returns nonzero if the instruction
is not legal. */
static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw1)
@@ -10763,11 +11017,15 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s)
gen_store_is_jmp(1);
break;
case 14:
- /* branch instruction */
- if (!(insn & (1 << 11)))
+ if (insn & (1 << 11)) {
+ thumb2_branch_detection(env, s, insn);
+ } else {
+ /* branch instruction */
gen_store_is_jmp(1);
+ }
break;
- default:
+ case 15:
+ thumb2_branch_detection(env, s, insn);
break;
}
}
--
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related [flat|nested] 25+ messages in thread
* Re: [Qemu-devel] [RFC 01/14] Make unknown semihosting calls non-fatal
2015-08-05 16:51 ` [Qemu-devel] [RFC 01/14] Make unknown semihosting calls non-fatal Christopher Covington
@ 2015-08-06 9:11 ` Alex Bennée
2015-08-06 17:59 ` Christopher Covington
0 siblings, 1 reply; 25+ messages in thread
From: Alex Bennée @ 2015-08-06 9:11 UTC (permalink / raw)
To: Christopher Covington; +Cc: qemu-devel
Christopher Covington <cov@codeaurora.org> writes:
> Signed-off-by: Christopher Covington <cov@codeaurora.org>
> ---
> target-arm/arm-semi.c | 7 +++----
> 1 file changed, 3 insertions(+), 4 deletions(-)
>
> diff --git a/target-arm/arm-semi.c b/target-arm/arm-semi.c
> index a8b83e6..bcc70ec 100644
> --- a/target-arm/arm-semi.c
> +++ b/target-arm/arm-semi.c
> @@ -186,8 +186,6 @@ static void arm_semi_flen_cb(CPUState *cs, target_ulong ret, target_ulong err)
> #define SET_ARG(n, val) put_user_ual(val, args + (n) * 4)
> uint32_t do_arm_semihosting(CPUARMState *env)
> {
> - ARMCPU *cpu = arm_env_get_cpu(env);
> - CPUState *cs = CPU(cpu);
> target_ulong args;
> target_ulong arg0, arg1, arg2, arg3;
> char * s;
> @@ -195,6 +193,8 @@ uint32_t do_arm_semihosting(CPUARMState *env)
> uint32_t ret;
> uint32_t len;
> #ifdef CONFIG_USER_ONLY
> + ARMCPU *cpu = arm_env_get_cpu(env);
> + CPUState *cs = CPU(cpu);
> TaskState *ts = cs->opaque;
> #else
> CPUARMState *ts = env;
> @@ -562,7 +562,6 @@ uint32_t do_arm_semihosting(CPUARMState *env)
> exit(ret);
> default:
> fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr);
> - cpu_dump_state(cs, stderr, fprintf, 0);
> - abort();
> + return -1;
Do SemiHosting calls return anything to the guest? If so removing the
abort means the guest may go unpredictable if the call is just ignored.
If so the argument would be to leave the abort() in and just add the
missing semihosting calls.
> }
> }
--
Alex Bennée
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [Qemu-devel] [RFC 01/14] Make unknown semihosting calls non-fatal
2015-08-06 9:11 ` Alex Bennée
@ 2015-08-06 17:59 ` Christopher Covington
0 siblings, 0 replies; 25+ messages in thread
From: Christopher Covington @ 2015-08-06 17:59 UTC (permalink / raw)
To: Alex Bennée; +Cc: qemu-devel
Hi Alex,
Thanks for taking a look.
On 08/06/2015 05:11 AM, Alex Bennée wrote:
>
> Christopher Covington <cov@codeaurora.org> writes:
>
>> Signed-off-by: Christopher Covington <cov@codeaurora.org>
>> ---
>> target-arm/arm-semi.c | 7 +++----
>> 1 file changed, 3 insertions(+), 4 deletions(-)
>>
>> diff --git a/target-arm/arm-semi.c b/target-arm/arm-semi.c
>> index a8b83e6..bcc70ec 100644
>> --- a/target-arm/arm-semi.c
>> +++ b/target-arm/arm-semi.c
>> @@ -186,8 +186,6 @@ static void arm_semi_flen_cb(CPUState *cs, target_ulong ret, target_ulong err)
>> #define SET_ARG(n, val) put_user_ual(val, args + (n) * 4)
>> uint32_t do_arm_semihosting(CPUARMState *env)
>> {
>> - ARMCPU *cpu = arm_env_get_cpu(env);
>> - CPUState *cs = CPU(cpu);
>> target_ulong args;
>> target_ulong arg0, arg1, arg2, arg3;
>> char * s;
>> @@ -195,6 +193,8 @@ uint32_t do_arm_semihosting(CPUARMState *env)
>> uint32_t ret;
>> uint32_t len;
>> #ifdef CONFIG_USER_ONLY
>> + ARMCPU *cpu = arm_env_get_cpu(env);
>> + CPUState *cs = CPU(cpu);
>> TaskState *ts = cs->opaque;
>> #else
>> CPUARMState *ts = env;
>> @@ -562,7 +562,6 @@ uint32_t do_arm_semihosting(CPUARMState *env)
>> exit(ret);
>> default:
>> fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr);
>> - cpu_dump_state(cs, stderr, fprintf, 0);
>> - abort();
>> + return -1;
>
> Do SemiHosting calls return anything to the guest? If so removing the
> abort means the guest may go unpredictable if the call is just ignored.
> If so the argument would be to leave the abort() in and just add the
> missing semihosting calls.
If were to pursue this further, I would want to review how external debuggers
supporting semihosting on hardware behave. However my current thinking is that
there are more commonly used mechanisms than semihosting that we can move to
for equivalent functionality.
Functionality: standard mechanism
Guest-initiated host filesystem access: VirtIO 9P passthrough filesystem
Guest-initiated exit: poweroff (PSCI)
Guest-initiated instruction counting: perf stat -e instructions:u (PMU)
Guest-initiated checkpoint dump: [maybe QMP over guest agent VirtIO serial?]
Thanks,
Christopher Covington
--
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [Qemu-devel] RFC: ARM Semihosting, PMU, and BBV Changes
2015-08-05 16:51 [Qemu-devel] RFC: ARM Semihosting, PMU, and BBV Changes Christopher Covington
` (13 preceding siblings ...)
2015-08-05 16:51 ` [Qemu-devel] [RFC 14/14] bbvec: Properly detect conditional thumb2 branching instructions Christopher Covington
@ 2015-08-11 15:27 ` Peter Maydell
14 siblings, 0 replies; 25+ messages in thread
From: Peter Maydell @ 2015-08-11 15:27 UTC (permalink / raw)
To: Christopher Covington; +Cc: QEMU Developers
On 5 August 2015 at 17:51, Christopher Covington <cov@codeaurora.org> wrote:
> This series is a jumble of changes that I have found useful for
> creating samples of long-running applications. I do not expect any of
> these patches to be merged upstream as-is but I'm publishing them as a
> way to ask for high-level feedback, as there may very well be much
> better ways to achieve the same goal. These changes are based on
> commit 1fd1a2cc182d4e331acd92c2cdbb52b3e78c040e.
Hi. Thanks for sending these patches. However, I'm afraid
this is not an approach likely to produce a lot of useful
feedback, because it's generally a fairly tedious task to
sort through a mess of patches which (a) address several
different issues (b) might not apply to current master
(c) might even be fixed already in master (d) have not
had the necessary cleanup effort applied to convert them
from a jumble to a coherent patchset.
The commit hash you quote is not one from mainline, by the way.
> While the patches are in chronological order, their functionality can
> roughly be put in the following categories:
>
> A) Functional fixes
> B) Guest->emulator/host communication
> C) Instrumentation/profiling capabilities
>
> A) Patches 3, 4, 5, and 13 can perhaps be categorized as functional
> fixes. They may be fixed on the current tip. As a write this, I'm
> thinking this category may be the best one to target for initial
> upstreaming.
Yes, if you have specific bugfixes, these are best handled by
producing patches which apply to master and sending them
individually (or as small patchsets, if several patches form
a coherent collection).
> B) Patches 1, 2, and 11 implement communications mechanisms. We have
> used Angel semihosting and "magic exceptions" to perform various kinds
> of guest to emulator and guest to host communication. Since these
> patches were originally developed, I've been able to reduce or remove
> our need for them, but if anyone has suggestions on better ways to not
> need to communicate in the first place or use existing mechanisms to
> communicate, that'd be appreciated. As an example, we previously used
> semihosting open(), read(), and write() calls for host filesystem
> access, but have been able to replace those by mounting a VirtIO-MMIO
> 9P filesystem. Another example is using poweroff, which ends up making
> a PSCI call, to end the run, rather than a custom executable that
> makes a semihosting exit call.
For semihosting, we should just implement all of the AArch64
semihosting API. This shouldn't be terribly difficult -- you
sent a patchset a while back which is a good place to start.
I've been meaning to pick that up and just fix up the other
semihosting calls.
> C) The instrumentation implemented in patches 6, 7, 8, 9, 10, 12, and
> 14 allow us to measure instruction counts and "block vectors", with
> the primary application of running the offline SimPoint algorithm [1]
> on the collected block vectors and dumping application level
> checkpoints using CRIU [2] in a second pass.
Instrumentation is tricky because it's a "large project":
it needs a coherent framework in place in QEMU to allow
target frontends to use it, users to runtime enable and
disable it, it needs to not hurt performance when tracing
is disabled, and so on. In principle I'm definitely in
favour of QEMU improving in this area though -- it's
quite a common user use-case request...
thanks
-- PMM
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [Qemu-devel] [RFC 02/14] Added semihosting support for A64 in full-system mode
2015-08-05 16:51 ` [Qemu-devel] [RFC 02/14] Added semihosting support for A64 in full-system mode Christopher Covington
@ 2015-08-11 18:16 ` Peter Maydell
0 siblings, 0 replies; 25+ messages in thread
From: Peter Maydell @ 2015-08-11 18:16 UTC (permalink / raw)
To: Christopher Covington; +Cc: QEMU Developers
On 5 August 2015 at 17:51, Christopher Covington <cov@codeaurora.org> wrote:
> This is for full-system only; not implemented in user mode
>
> Written by Derek Hower.
> - cpu_memory_rw_debug(cs, env->regs[13]-64+32, (uint8_t *)&size, 4, 0);
> - env->regs[0] = be32_to_cpu(size);
> + if (env->aarch64) {
> + cpu_memory_rw_debug(cs, env->pc-64+32, (uint8_t *)&size, 4, 0);
> + env->xregs[0] = be32_to_cpu(size);
> + } else {
> + cpu_memory_rw_debug(cs, env->regs[13]-64+32, (uint8_t *)&size, 4, 0);
> + env->regs[0] = be32_to_cpu(size);
> + }
> case TARGET_SYS_FLEN:
> GET_ARG(0);
> if (use_gdb_syscalls()) {
> + if (env->aarch64) {
> + gdb_do_syscall(arm_semi_flen_cb, "fstat,%x,%x",
> + arg0, env->pc-64);
> + return env->xregs[0];
> +
> + } else {
> gdb_do_syscall(arm_semi_flen_cb, "fstat,%x,%x",
> arg0, env->regs[13]-64);
> return env->regs[0];
> + }
These two bits are badly buggy if you ever try to use this semihosting
call with gdb syscalls enabled on A64. r13 is SP, not PC, but your
A64 code is telling gdb to write the struct stat buf to guest memory
starting at pc-64, so it will corrupt the code we've just executed...
(I'm working on an inspired-by-this but rewritten patchset for
A64 semihosting, so this is just in case you were using these
patches somewhere in the interim.)
thanks
-- PMM
^ permalink raw reply [flat|nested] 25+ messages in thread
* [Qemu-devel] [PATCHv2] target-arm: Use physical addresses for ldrex/strex
2015-08-05 16:51 ` [Qemu-devel] [RFC 04/14] Modify load exclusive/store exclusive to use physical addresses with the monitor Christopher Covington
@ 2015-09-23 17:19 ` Christopher Covington
0 siblings, 0 replies; 25+ messages in thread
From: Christopher Covington @ 2015-09-23 17:19 UTC (permalink / raw)
To: Peter Maydell, qemu-devel; +Cc: Christopher Covington
As different virtual addresses may end up aliasing by pointing to
the same physical address, modify load- and store-exclusive to
use physical addresses with the exclusive monitor.
Written by Derek Hower.
Signed-off-by: Christopher Covington <cov@codeaurora.org>
---
target-arm/helper-a64.h | 2 ++
target-arm/helper.c | 25 +++++++++++++++++++++++++
target-arm/translate-a64.c | 25 +++++++++++++++++++++++--
3 files changed, 50 insertions(+), 2 deletions(-)
diff --git a/target-arm/helper-a64.h b/target-arm/helper-a64.h
index 1d3d10f..a713d29 100644
--- a/target-arm/helper-a64.h
+++ b/target-arm/helper-a64.h
@@ -46,3 +46,5 @@ DEF_HELPER_FLAGS_2(frecpx_f32, TCG_CALL_NO_RWG, f32, f32, ptr)
DEF_HELPER_FLAGS_2(fcvtx_f64_to_f32, TCG_CALL_NO_RWG, f32, f64, env)
DEF_HELPER_FLAGS_3(crc32_64, TCG_CALL_NO_RWG_SE, i64, i64, i64, i32)
DEF_HELPER_FLAGS_3(crc32c_64, TCG_CALL_NO_RWG_SE, i64, i64, i64, i32)
+
+DEF_HELPER_3(get_phys_addr64, i64, env, i64, i32)
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 12ea88f..7bcff98 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -24,6 +24,31 @@ static inline bool get_phys_addr(CPUARMState *env, target_ulong address,
#define PMCRE 0x1
#endif
+#ifdef TARGET_AARCH64
+
+uint64_t HELPER(get_phys_addr64)(CPUARMState *env,
+ uint64_t vaddr, uint32_t memidx)
+{
+#ifdef CONFIG_USER_ONLY
+ return vaddr;
+#else
+ hwaddr phys_addr;
+ int prot; /* ignored */
+ target_ulong page_size; /* ignored */
+ MemTxAttrs attrs = {}; /* ignored */
+ uint32_t fsr; /* ignored */
+
+ /* We just want the address from this function and don't care about faults.
+ * Therefore, we always assume the operation is a load.
+ */
+ get_phys_addr(env, vaddr, 0, memidx == 0, &phys_addr, &attrs, &prot,
+ &page_size, &fsr);
+ return phys_addr;
+#endif
+}
+
+#endif
+
static int vfp_gdb_get_reg(CPUARMState *env, uint8_t *buf, int reg)
{
int nregs;
diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c
index ec0936c..fb34de2 100644
--- a/target-arm/translate-a64.c
+++ b/target-arm/translate-a64.c
@@ -1708,7 +1708,17 @@ static void gen_load_exclusive(DisasContext *s, int rt, int rt2,
tcg_gen_mov_i64(cpu_reg(s, rt), tmp);
tcg_temp_free_i64(tmp);
- tcg_gen_mov_i64(cpu_exclusive_addr, addr);
+
+ /* The monitor must be set on the physical address. We've already read the
+ * address at this point, so we know the translation won't fault.
+ */
+ TCGv_i64 physaddr = tcg_temp_new_i64();
+ TCGv_i32 idx = tcg_temp_new_i32();
+ tcg_gen_movi_i32(idx, get_mem_index(s));
+ gen_helper_get_phys_addr64(physaddr, cpu_env, addr, idx);
+ tcg_gen_mov_i64(cpu_exclusive_addr, physaddr);
+ tcg_temp_free_i64(physaddr);
+ tcg_temp_free_i32(idx);
}
#ifdef CONFIG_USER_ONLY
@@ -1745,13 +1755,24 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2,
* basic block ends at the branch insn.
*/
tcg_gen_mov_i64(addr, inaddr);
- tcg_gen_brcond_i64(TCG_COND_NE, addr, cpu_exclusive_addr, fail_label);
tmp = tcg_temp_new_i64();
tcg_gen_qemu_ld_i64(tmp, addr, get_mem_index(s), MO_TE + size);
tcg_gen_brcond_i64(TCG_COND_NE, tmp, cpu_exclusive_val, fail_label);
tcg_temp_free_i64(tmp);
+ /* The monitor must be checked on the physical address. We've alredy loaded
+ * this address, so we don't need to check for a fault condition.
+ */
+ TCGv_i64 physaddr = tcg_temp_new_i64();
+ TCGv_i32 idx = tcg_temp_new_i32();
+ tcg_gen_movi_i32(idx, get_mem_index(s));
+ gen_helper_get_phys_addr64(physaddr, cpu_env, addr, idx);
+
+ tcg_gen_brcond_i64(TCG_COND_NE, physaddr, cpu_exclusive_addr, fail_label);
+ tcg_temp_free_i64(physaddr);
+ tcg_temp_free_i32(idx);
+
if (is_pair) {
TCGv_i64 addrhi = tcg_temp_new_i64();
TCGv_i64 tmphi = tcg_temp_new_i64();
--
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related [flat|nested] 25+ messages in thread
* Re: [Qemu-devel] [RFC 09/14] Implement remaining PMU functionality
2015-08-05 16:51 ` [Qemu-devel] [RFC 09/14] Implement remaining PMU functionality Christopher Covington
@ 2016-02-02 21:22 ` Alistair Francis
2016-02-02 23:01 ` Christopher Covington
0 siblings, 1 reply; 25+ messages in thread
From: Alistair Francis @ 2016-02-02 21:22 UTC (permalink / raw)
To: Christopher Covington; +Cc: qemu-devel@nongnu.org Developers
On Wed, Aug 5, 2015 at 9:51 AM, Christopher Covington
<cov@codeaurora.org> wrote:
> This adds logic to increment PMEVCNTR's based on different event inputs,
> implements all remaining CP registers, and triggers an interrupt on
> event overflow.
We (Xilinx) need parts of this patch to avoid kernel panics when
booting the 4.4 Linux kernel. Have you done any more work on it? If
you can send out a pach set I'm happy to have a look at it.
I can't find a cover letter though, so I haven't looked at the other
patches in the series.
Thanks,
Alistair
>
> Written by Aaron Lindsay.
>
> Signed-off-by: Christopher Covington <cov@codeaurora.org>
> ---
> target-arm/cpu-qom.h | 2 +
> target-arm/cpu.c | 2 +
> target-arm/cpu.h | 37 ++--
> target-arm/cpu64.c | 2 +
> target-arm/helper.c | 538 ++++++++++++++++++++++++++++++++++++++-------------
> 5 files changed, 425 insertions(+), 156 deletions(-)
>
> diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h
> index bb6722f..2a0f3f4 100644
> --- a/target-arm/cpu-qom.h
> +++ b/target-arm/cpu-qom.h
> @@ -136,6 +136,8 @@ typedef struct ARMCPU {
> uint32_t id_pfr0;
> uint32_t id_pfr1;
> uint32_t id_dfr0;
> + uint32_t pmceid0;
> + uint32_t pmceid1;
> uint32_t id_afr0;
> uint32_t id_mmfr0;
> uint32_t id_mmfr1;
> diff --git a/target-arm/cpu.c b/target-arm/cpu.c
> index 87d0772..6a728d9 100644
> --- a/target-arm/cpu.c
> +++ b/target-arm/cpu.c
> @@ -938,6 +938,8 @@ static void cortex_a15_initfn(Object *obj)
> cpu->id_pfr0 = 0x00001131;
> cpu->id_pfr1 = 0x00011011;
> cpu->id_dfr0 = 0x02010555;
> + cpu->pmceid0 = 0x00000481; /* PMUv3 events 0x0, 0x8, and 0x11 */
> + cpu->pmceid1 = 0x00000000;
> cpu->id_afr0 = 0x00000000;
> cpu->id_mmfr0 = 0x10201105;
> cpu->id_mmfr1 = 0x20000000;
> diff --git a/target-arm/cpu.h b/target-arm/cpu.h
> index 44084a5..f6857fa 100644
> --- a/target-arm/cpu.h
> +++ b/target-arm/cpu.h
> @@ -112,11 +112,22 @@ typedef struct ARMGenericTimer {
> uint64_t ctl; /* Timer Control register */
> } ARMGenericTimer;
>
> +/* Indices into ARMCPU.ppi_outputs (and .gt_timer for timers) */
> #define NUM_GTIMERS 2
> #define GTIMER_PHYS 0
> #define GTIMER_VIRT 1
> #define PMU_IDX 2
>
> +enum pmu_counter_type {
> + PMU_COUNTER_TYPE_SWINC = 0x000,
> + PMU_COUNTER_TYPE_INSTRUCTIONS = 0x008,
> + PMU_COUNTER_TYPE_CYCLES = 0x011
> +};
> +
> +/* Performance monitor event counter state */
> +#define NUM_PMU_COUNTERS 4 /* 0-30, inclusive, doesn't count cycle counter */
> +#define PMU_COUNTER_MASK 0x8000000F /* Mask of bits allowed for PMINTEN{SET|CLR}*/
> +
> typedef struct {
> uint64_t raw_tcr;
> uint32_t mask;
> @@ -287,12 +298,13 @@ typedef struct CPUARMState {
> };
> uint32_t c9_insn; /* Cache lockdown registers. */
> uint32_t c9_data;
> - uint64_t c9_pmcr; /* performance monitor control register */
> - uint64_t c9_pmcnten; /* perf monitor counter enables */
> + uint32_t c9_pmcr; /* performance monitor control register */
> + uint64_t c9_pmccntr;
> + uint32_t c9_pmcnten; /* perf monitor counter enables */
> uint32_t c9_pmovsr; /* perf monitor overflow status */
> - uint32_t c9_pmxevtyper; /* perf monitor event type */
> uint32_t c9_pmuserenr; /* perf monitor user enable */
> uint32_t c9_pminten; /* perf monitor interrupt enables */
> + uint32_t c9_pmselr; /* perf monitor event counter selection */
> union { /* Memory attribute redirection */
> struct {
> #ifdef HOST_WORDS_BIGENDIAN
> @@ -354,6 +366,9 @@ typedef struct CPUARMState {
> uint64_t tpidruro_ns;
> uint64_t tpidrro_el[1];
> };
> + uint32_t c14_pmccfiltr; /* Performance Monitor Filter Register */
> + uint32_t c14_pmevcntr[NUM_PMU_COUNTERS];
> + uint32_t c14_pmevtyper[NUM_PMU_COUNTERS];
> uint64_t c14_cntfrq; /* Counter Frequency register */
> uint64_t c14_cntkctl; /* Timer Control register */
> ARMGenericTimer c14_timer[NUM_GTIMERS];
> @@ -371,11 +386,6 @@ typedef struct CPUARMState {
> uint64_t dbgwvr[16]; /* watchpoint value registers */
> uint64_t dbgwcr[16]; /* watchpoint control registers */
> uint64_t mdscr_el1;
> - /* If the counter is enabled, this stores the last time the counter
> - * was reset. Otherwise it stores the counter value
> - */
> - uint64_t c15_ccnt;
> - uint64_t pmccfiltr_el0; /* Performance Monitor Filter Register */
> } cp15;
>
> struct {
> @@ -508,17 +518,6 @@ int cpu_arm_signal_handler(int host_signum, void *pinfo,
> int arm_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int rw,
> int mmu_idx);
>
> -/**
> - * pmccntr_sync
> - * @env: CPUARMState
> - *
> - * Synchronises the counter in the PMCCNTR. This must always be called twice,
> - * once before any action that might affect the timer and again afterwards.
> - * The function is used to swap the state of the register if required.
> - * This only happens when not in user mode (!CONFIG_USER_ONLY)
> - */
> -void pmccntr_sync(CPUARMState *env);
> -
> /* SCTLR bit meanings. Several bits have been reused in newer
> * versions of the architecture; in that case we define constants
> * for both old and new bit meanings. Code which tests against those
> diff --git a/target-arm/cpu64.c b/target-arm/cpu64.c
> index 270bc2f..b2a3a74 100644
> --- a/target-arm/cpu64.c
> +++ b/target-arm/cpu64.c
> @@ -132,6 +132,8 @@ static void aarch64_a57_initfn(Object *obj)
> cpu->id_isar5 = 0x00011121;
> cpu->id_aa64pfr0 = 0x00002222;
> cpu->id_aa64dfr0 = 0x10305106;
> + cpu->pmceid0 = 0x00000481; /* PMUv3 events 0x0, 0x8, and 0x11 */
> + cpu->pmceid1 = 0x00000000;
> cpu->id_aa64isar0 = 0x00011120;
> cpu->id_aa64mmfr0 = 0x00001124;
> cpu->dbgdidr = 0x3516d000;
> diff --git a/target-arm/helper.c b/target-arm/helper.c
> index be3ad01..a659e67 100644
> --- a/target-arm/helper.c
> +++ b/target-arm/helper.c
> @@ -21,11 +21,6 @@ static inline int get_phys_addr(CPUARMState *env, target_ulong address,
> int access_type, ARMMMUIdx mmu_idx,
> hwaddr *phys_ptr, MemTxAttrs *attrs, int *prot,
> target_ulong *page_size);
> -
> -/* Definitions for the PMCCNTR and PMCR registers */
> -#define PMCRD 0x8
> -#define PMCRC 0x4
> -#define PMCRE 0x1
> #endif
>
> #ifdef TARGET_AARCH64
> @@ -660,150 +655,337 @@ static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri)
> return CP_ACCESS_OK;
> }
>
> -#ifndef CONFIG_USER_ONLY
> +/* Definitions for the PMU CP registers */
> +#define PMCR_LC 0x40
> +#define PMCR_D 0x8
> +#define PMCR_C 0x4
> +#define PMCR_E 0x1
> +
> +#define PMCNTEN_C 0x80000000
> +
> +#define PMCCFILTR_NSH 0x08000000
> +#define PMCCFILTR_P 0x80000000
> +#define PMCCFILTR_U 0x40000000
>
> -static inline bool arm_ccnt_enabled(CPUARMState *env)
> +#define PMEVTYPER_NSH 0x08000000
> +#define PMEVTYPER_P 0x80000000
> +#define PMEVTYPER_U 0x40000000
> +#define PMEVTYPER_EVTCOUNT 0x000003ff
> +
> +#define PMXEVTYPER_P 0x80000000
> +#define PMXEVTYPER_U 0x40000000
> +
> +static void pmccfiltr_write(CPUARMState *env, const ARMCPRegInfo *ri,
> + uint64_t value)
> {
> - /* This does not support checking PMCCFILTR_EL0 register */
> + env->cp15.c14_pmccfiltr = value & 0xfc000000;
> +}
>
> - if (!(env->cp15.c9_pmcr & PMCRE)) {
> - return false;
> +static uint64_t pmevcntr_read(CPUARMState *env, const ARMCPRegInfo *ri,
> + const uint8_t idx)
> +{
> + if (idx >= NUM_PMU_COUNTERS) {
> + return arm_cp_read_zero(env, ri);
> }
> + return env->cp15.c14_pmevcntr[idx];
> +}
>
> - return true;
> +static uint64_t pmevcntr_readfn(CPUARMState *env, const ARMCPRegInfo *ri)
> +{
> + uint8_t idx = ((ri->crm & 3) << 3) | (ri->opc2 & 7);
> + return pmevcntr_read(env, ri, idx);
> }
>
> -/* Called by anything that wants to be an input for event counts to the PMU
> - * (except for SWINC, event 0x000, since its events can target specific
> - * counters)
> - */
> -static void pmevcntr_increment(CPUARMState *env, uint8_t event_type,
> - uint64_t increment_by)
> +static void pmevcntr_write(CPUARMState *env, const ARMCPRegInfo *ri,
> + uint64_t value, const uint8_t idx)
> {
> + if (idx >= NUM_PMU_COUNTERS) {
> + arm_cp_write_ignore(env, ri, value);
> + } else {
> + env->cp15.c14_pmevcntr[idx] = value;
> + }
> }
>
> -void pmccntr_sync(CPUARMState *env)
> +static void pmevcntr_writefn(CPUARMState *env, const ARMCPRegInfo *ri,
> + uint64_t value)
> {
> - uint64_t temp_ticks;
> + uint8_t idx = ((ri->crm & 3) << 3) | (ri->opc2 & 7);
> + pmevcntr_write(env, ri, value, idx);
>
> - temp_ticks = muldiv64(qemu_clock_get_us(QEMU_CLOCK_VIRTUAL),
> - get_ticks_per_sec(), 1000000);
> +}
>
> - if (env->cp15.c9_pmcr & PMCRD) {
> - /* Increment once every 64 processor clock cycles */
> - temp_ticks /= 64;
> +static uint64_t pmevtyper_read(CPUARMState *env, const ARMCPRegInfo *ri,
> + const uint8_t idx)
> +{
> + if (idx >= NUM_PMU_COUNTERS) {
> + return arm_cp_read_zero(env, ri);
> }
> + return env->cp15.c14_pmevtyper[idx];
> +}
> +
> +static uint64_t pmevtyper_readfn(CPUARMState *env, const ARMCPRegInfo *ri)
> +{
> + uint8_t idx = ((ri->crm & 3) << 3) | (ri->opc2 & 7);
> + return pmevtyper_read(env, ri, idx);
> +}
>
> - if (arm_ccnt_enabled(env)) {
> - env->cp15.c15_ccnt = temp_ticks - env->cp15.c15_ccnt;
> +static void pmevtyper_write(CPUARMState *env, const ARMCPRegInfo *ri,
> + uint64_t value, const uint8_t idx)
> +{
> + if (idx >= NUM_PMU_COUNTERS) {
> + arm_cp_write_ignore(env, ri, value);
> + } else {
> + env->cp15.c14_pmevtyper[idx] = value & 0xf80003ff;
> }
> }
>
> -static void pmcr_write(CPUARMState *env, const ARMCPRegInfo *ri,
> - uint64_t value)
> +static void pmevtyper_writefn(CPUARMState *env, const ARMCPRegInfo *ri,
> + uint64_t value)
> {
> - pmccntr_sync(env);
> + uint8_t idx = ((ri->crm & 3) << 3) | (ri->opc2 & 7);
> + pmevtyper_write(env, ri, value, idx);
> +}
>
> - if (value & PMCRC) {
> - /* The counter has been reset */
> - env->cp15.c15_ccnt = 0;
> +static void pmselr_write(CPUARMState *env, const ARMCPRegInfo *ri,
> + uint64_t value)
> +{
> + env->cp15.c9_pmselr = value & 31;
> +}
> +
> +static uint64_t pmxevcntr_read(CPUARMState *env, const ARMCPRegInfo *ri)
> +{
> + return pmevcntr_read(env, ri, env->cp15.c9_pmselr & 31);
> +}
> +
> +static void pmxevcntr_write(CPUARMState *env, const ARMCPRegInfo *ri,
> + uint64_t value)
> +{
> + pmevcntr_write(env, ri, value, env->cp15.c9_pmselr & 31);
> +}
> +
> +static uint64_t pmxevtyper_read(CPUARMState *env, const ARMCPRegInfo *ri)
> +{
> + uint8_t idx = env->cp15.c9_pmselr & 31;
> + if (idx == 31)
> + return env->cp15.c14_pmccfiltr;
> + return pmevtyper_read(env, ri, env->cp15.c9_pmselr & 31);
> +}
> +
> +static void pmxevtyper_write(CPUARMState *env, const ARMCPRegInfo *ri,
> + uint64_t value)
> +{
> + uint8_t idx = env->cp15.c9_pmselr & 31;
> + if (idx == 31)
> + pmccfiltr_write(env, ri, value);
> + else
> + pmevtyper_write(env, ri, value, env->cp15.c9_pmselr & 31);
> +}
> +
> +static inline bool pmccntr_enabled(CPUARMState *env)
> +{
> + /* This does not check PMCR.E to see if all events are disabled, it is
> + * assumed that is being checked externally, and doesn't support checking
> + * for the secure/non-secure components of the PMCCFILTR_EL0 register.
> + */
> +
> + if (!(env->cp15.c9_pmcnten & PMCNTEN_C)) {
> + return false;
> }
>
> - /* only the DP, X, D and E bits are writable */
> - env->cp15.c9_pmcr &= ~0x39;
> - env->cp15.c9_pmcr |= (value & 0x39);
> + switch (arm_current_el(env)) {
> + case 2:
> + if (!(env->cp15.c14_pmccfiltr & PMCCFILTR_NSH)) {
> + return false;
> + } else {
> + break;
> + }
> + case 1:
> + if (env->cp15.c14_pmccfiltr & PMCCFILTR_P) {
> + return false;
> + } else {
> + break;
> + }
> + case 0:
> + if (env->cp15.c14_pmccfiltr & PMCCFILTR_U) {
> + return false;
> + } else {
> + break;
> + }
> + }
>
> - pmccntr_sync(env);
> + return true;
> }
>
> -static uint64_t pmccntr_read(CPUARMState *env, const ARMCPRegInfo *ri)
> +static inline bool pmevcntr_enabled(CPUARMState *env, uint8_t idx)
> {
> - uint64_t total_ticks;
> + /* This does not check PMCR.E to see if all events are disabled, it is
> + * assumed that is being checked externally, and doesn't support checking
> + * for the secure/non-secure components of the PMEVTYPER<n>_EL0 registers
> + */
> +
> + if (!(env->cp15.c9_pmcnten & (1U << idx))) {
> + return false;
> + }
>
> - if (!arm_ccnt_enabled(env)) {
> - /* Counter is disabled, do not change value */
> - return env->cp15.c15_ccnt;
> + switch (arm_current_el(env)) {
> + case 2:
> + if (!(env->cp15.c14_pmevtyper[idx] & PMEVTYPER_NSH)) {
> + return false;
> + } else {
> + break;
> + }
> + case 1:
> + if (env->cp15.c14_pmevtyper[idx] & PMEVTYPER_P) {
> + return false;
> + } else {
> + break;
> + }
> + case 0:
> + if (env->cp15.c14_pmevtyper[idx] & PMEVTYPER_U) {
> + return false;
> + } else {
> + break;
> + }
> }
>
> - total_ticks = muldiv64(qemu_clock_get_us(QEMU_CLOCK_VIRTUAL),
> - get_ticks_per_sec(), 1000000);
> + return true;
> +}
> +
> +static void pmu_update_irq(CPUARMState *env)
> +{
> + ARMCPU *cpu = arm_env_get_cpu(env);
> + qemu_set_irq(cpu->ppi_outputs[PMU_IDX], (env->cp15.c9_pmcr & PMCR_E) &&
> + (env->cp15.c9_pminten & env->cp15.c9_pmovsr));
> +}
> +
> +static void pmccntr_increment(CPUARMState *env, uint64_t increment_by)
> +{
> + if (pmccntr_enabled(env)) {
> + uint64_t new_pmccntr = env->cp15.c9_pmccntr + increment_by;
> + unsigned int overflow_bit = (env->cp15.c9_pmcr & PMCR_LC) ? 63 : 31;
>
> - if (env->cp15.c9_pmcr & PMCRD) {
> - /* Increment once every 64 processor clock cycles */
> - total_ticks /= 64;
> + if (!(new_pmccntr & (1 << overflow_bit)) &&
> + env->cp15.c9_pmccntr & (1 << overflow_bit)) {
> + env->cp15.c9_pmovsr |= (1 << 31);
> + pmu_update_irq(env);
> + }
> + env->cp15.c9_pmccntr = new_pmccntr;
> }
> - return total_ticks - env->cp15.c15_ccnt;
> }
>
> -static void pmccntr_write(CPUARMState *env, const ARMCPRegInfo *ri,
> - uint64_t value)
> +/* Called by anything that wants to be an input for event counts to the PMU
> + * (except for SWINC, event 0x000 since its events can target specific counters
> + */
> +static void pmevcntr_increment(CPUARMState *env, uint8_t event_type,
> + uint64_t increment_by)
> {
> - uint64_t total_ticks;
> + unsigned int i;
>
> - if (!arm_ccnt_enabled(env)) {
> - /* Counter is disabled, set the absolute value */
> - env->cp15.c15_ccnt = value;
> + //early out if no counters are enabled
> + if (!(env->cp15.c9_pmcr & PMCR_E) || !env->cp15.c9_pmcnten)
> return;
> - }
>
> - total_ticks = muldiv64(qemu_clock_get_us(QEMU_CLOCK_VIRTUAL),
> - get_ticks_per_sec(), 1000000);
> + if (event_type == PMU_COUNTER_TYPE_CYCLES)
> + pmccntr_increment(env, increment_by);
> +
> + for (i = 0; i < NUM_PMU_COUNTERS; i++) {
> + if (pmevcntr_enabled(env, i) &&
> + (env->cp15.c14_pmevtyper[i] & PMEVTYPER_EVTCOUNT) == event_type) {
> + uint32_t new_pmevcntr = env->cp15.c14_pmevcntr[i] + increment_by;
>
> - if (env->cp15.c9_pmcr & PMCRD) {
> - /* Increment once every 64 processor clock cycles */
> - total_ticks /= 64;
> + if (!(new_pmevcntr & (1 << 31)) &&
> + (env->cp15.c14_pmevcntr[i] & (1 << 31))) {
> + env->cp15.c9_pmovsr |= (1 << i);
> + pmu_update_irq(env);
> + }
> + env->cp15.c14_pmevcntr[i] = new_pmevcntr;
> + }
> }
> - env->cp15.c15_ccnt = total_ticks - value;
> }
>
> -static void pmccntr_write32(CPUARMState *env, const ARMCPRegInfo *ri,
> - uint64_t value)
> +static void pmswinc_write(CPUARMState *env, const ARMCPRegInfo *ri,
> + uint64_t value)
> {
> - uint64_t cur_val = pmccntr_read(env, NULL);
> + unsigned int i;
>
> - pmccntr_write(env, ri, deposit64(cur_val, 0, 32, value));
> + for (i = 0; i < NUM_PMU_COUNTERS; i++) {
> + if (value & (1 << i) &&
> + pmevcntr_enabled(env, i) &&
> + (env->cp15.c14_pmevtyper[i] & PMEVTYPER_EVTCOUNT) == PMU_COUNTER_TYPE_SWINC) {
> + uint32_t new_pmevcntr = env->cp15.c14_pmevcntr[i] + 1;
> +
> + if (!(new_pmevcntr & (1 << 31)) &&
> + (env->cp15.c14_pmevcntr[i] & (1 << 31))) {
> + env->cp15.c9_pmovsr |= (1 << i);
> + pmu_update_irq(env);
> + }
> + env->cp15.c14_pmevcntr[i] = new_pmevcntr;
> + }
> + }
> }
>
> -#else /* CONFIG_USER_ONLY */
> +static void pmcr_write(CPUARMState *env, const ARMCPRegInfo *ri,
> + uint64_t value)
> +{
> + if (value & PMCR_C) {
> + /* The counter has been reset */
> + env->cp15.c9_pmccntr = 0;
> + }
> +
> + /* only the DP, X, D and E bits are writable */
> + env->cp15.c9_pmcr &= ~0x39;
> + env->cp15.c9_pmcr |= (value & 0x39);
> +}
>
> -void pmccntr_sync(CPUARMState *env)
> +static uint64_t pmccntr_read(CPUARMState *env, const ARMCPRegInfo *ri)
> {
> + return env->cp15.c9_pmccntr;
> }
>
> -#endif
> +static void pmccntr_write(CPUARMState *env, const ARMCPRegInfo *ri,
> + uint64_t value)
> +{
> + env->cp15.c9_pmccntr = value;
> +}
>
> -static void pmccfiltr_write(CPUARMState *env, const ARMCPRegInfo *ri,
> +static void pmccntr_write32(CPUARMState *env, const ARMCPRegInfo *ri,
> uint64_t value)
> {
> - pmccntr_sync(env);
> - env->cp15.pmccfiltr_el0 = value & 0x7E000000;
> - pmccntr_sync(env);
> + uint64_t cur_val = pmccntr_read(env, NULL);
> +
> + pmccntr_write(env, ri, deposit64(cur_val, 0, 32, value));
> }
>
> static void pmcntenset_write(CPUARMState *env, const ARMCPRegInfo *ri,
> uint64_t value)
> {
> - value &= (1 << 31);
> + value &= PMU_COUNTER_MASK;
> env->cp15.c9_pmcnten |= value;
> + pmu_update_irq(env);
> }
>
> static void pmcntenclr_write(CPUARMState *env, const ARMCPRegInfo *ri,
> uint64_t value)
> {
> - value &= (1 << 31);
> + value &= PMU_COUNTER_MASK;
> env->cp15.c9_pmcnten &= ~value;
> + pmu_update_irq(env);
> }
>
> -static void pmovsr_write(CPUARMState *env, const ARMCPRegInfo *ri,
> +static void pmovsset_write(CPUARMState *env, const ARMCPRegInfo *ri,
> uint64_t value)
> {
> - env->cp15.c9_pmovsr &= ~value;
> + value &= PMU_COUNTER_MASK;
> + env->cp15.c9_pmovsr |= value;
> + pmu_update_irq(env);
> }
>
> -static void pmxevtyper_write(CPUARMState *env, const ARMCPRegInfo *ri,
> - uint64_t value)
> +static void pmovsr_write(CPUARMState *env, const ARMCPRegInfo *ri,
> + uint64_t value)
> {
> - env->cp15.c9_pmxevtyper = value & 0xff;
> + value &= PMU_COUNTER_MASK;
> + env->cp15.c9_pmovsr &= ~value;
> + pmu_update_irq(env);
> }
>
> static void pmuserenr_write(CPUARMState *env, const ARMCPRegInfo *ri,
> @@ -815,16 +997,17 @@ static void pmuserenr_write(CPUARMState *env, const ARMCPRegInfo *ri,
> static void pmintenset_write(CPUARMState *env, const ARMCPRegInfo *ri,
> uint64_t value)
> {
> - /* We have no event counters so only the C bit can be changed */
> - value &= (1 << 31);
> + value &= PMU_COUNTER_MASK;
> env->cp15.c9_pminten |= value;
> + pmu_update_irq(env);
> }
>
> static void pmintenclr_write(CPUARMState *env, const ARMCPRegInfo *ri,
> uint64_t value)
> {
> - value &= (1 << 31);
> + value &= PMU_COUNTER_MASK;
> env->cp15.c9_pminten &= ~value;
> + pmu_update_irq(env);
> }
>
> static void vbar_write(CPUARMState *env, const ARMCPRegInfo *ri,
> @@ -905,11 +1088,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
> /* the old v6 WFI, UNPREDICTABLE in v7 but we choose to NOP */
> { .name = "NOP", .cp = 15, .crn = 7, .crm = 0, .opc1 = 0, .opc2 = 4,
> .access = PL1_W, .type = ARM_CP_NOP },
> - /* Performance monitors are implementation defined in v7,
> - * but with an ARM recommended set of registers, which we
> - * follow (although we don't actually implement any counters)
> - *
> - * Performance registers fall into three categories:
> + /* Performance monitor registers fall into three categories:
> * (a) always UNDEF in PL0, RW in PL1 (PMINTENSET, PMINTENCLR)
> * (b) RO in PL0 (ie UNDEF on write), RW in PL1 (PMUSERENR)
> * (c) UNDEF in PL0 if PMUSERENR.EN==0, otherwise accessible (all others)
> @@ -918,7 +1097,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
> */
> { .name = "PMCNTENSET", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 1,
> .access = PL0_RW, .type = ARM_CP_ALIAS,
> - .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcnten),
> + .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten),
> .writefn = pmcntenset_write,
> .accessfn = pmreg_access,
> .raw_writefn = raw_write },
> @@ -929,71 +1108,117 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
> .writefn = pmcntenset_write, .raw_writefn = raw_write },
> { .name = "PMCNTENCLR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 2,
> .access = PL0_RW,
> - .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcnten),
> + .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten),
> .accessfn = pmreg_access,
> - .writefn = pmcntenclr_write,
> + .writefn = pmcntenclr_write, .raw_writefn = raw_write,
> .type = ARM_CP_ALIAS },
> { .name = "PMCNTENCLR_EL0", .state = ARM_CP_STATE_AA64,
> .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 2,
> .access = PL0_RW, .accessfn = pmreg_access,
> .type = ARM_CP_ALIAS,
> .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten),
> - .writefn = pmcntenclr_write },
> + .writefn = pmcntenclr_write, .raw_writefn = raw_write },
> + { .name = "PMOVSSET_EL0", .state = ARM_CP_STATE_AA64,
> + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 14, .opc2 = 3,
> + .access = PL0_RW, .accessfn = pmreg_access,
> + .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr), .resetvalue = 0,
> + .writefn = pmovsset_write, .raw_writefn = raw_write },
> { .name = "PMOVSR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 3,
> .access = PL0_RW, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr),
> - .accessfn = pmreg_access,
> + .accessfn = pmreg_access, .type = ARM_CP_ALIAS,
> + .writefn = pmovsr_write,
> + .raw_writefn = raw_write },
> + { .name = "PMOVSCLR_EL0", .state = ARM_CP_STATE_AA64,
> + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 3,
> + .access = PL0_RW, .accessfn = pmreg_access,
> + .type = ARM_CP_ALIAS,
> + .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr),
> .writefn = pmovsr_write,
> .raw_writefn = raw_write },
> /* Unimplemented so WI. */
> { .name = "PMSWINC", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 4,
> - .access = PL0_W, .accessfn = pmreg_access, .type = ARM_CP_NOP },
> - /* Since we don't implement any events, writing to PMSELR is UNPREDICTABLE.
> - * We choose to RAZ/WI.
> - */
> + .access = PL0_W, .accessfn = pmreg_access, .type = ARM_CP_NO_RAW,
> + .writefn = pmswinc_write },
> + { .name = "PMSWINC_EL0", .state = ARM_CP_STATE_AA64,
> + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 4,
> + .access = PL0_W, .accessfn = pmreg_access, .type = ARM_CP_NO_RAW,
> + .writefn = pmswinc_write },
> { .name = "PMSELR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 5,
> - .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0,
> - .accessfn = pmreg_access },
> -#ifndef CONFIG_USER_ONLY
> + .access = PL0_RW, .type = ARM_CP_ALIAS,
> + .accessfn = pmreg_access, .writefn = pmselr_write,
> + .fieldoffset = offsetof(CPUARMState, cp15.c9_pmselr) },
> + { .name = "PMSELR_EL0", .state = ARM_CP_STATE_AA64,
> + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 5,
> + .access = PL0_RW, .accessfn = pmreg_access,
> + .fieldoffset = offsetof(CPUARMState, cp15.c9_pmselr),
> + .writefn = pmselr_write, .resetvalue = 0 },
> + { .name = "PMXEVCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 2,
> + .access = PL0_RW, .type = ARM_CP_ALIAS,
> + .accessfn = pmreg_access, .writefn = pmxevcntr_write,
> + .readfn = pmxevcntr_read },
> + { .name = "PMXEVCNTR_EL0", .state = ARM_CP_STATE_AA64,
> + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 2,
> + .access = PL0_RW, .type = ARM_CP_ALIAS,
> + .accessfn = pmreg_access, .writefn = pmxevcntr_write,
> + .readfn = pmxevcntr_read },
> + { .name = "PMXEVTYPER", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 1,
> + .access = PL0_RW, .type = ARM_CP_ALIAS,
> + .accessfn = pmreg_access, .writefn = pmxevtyper_write,
> + .readfn = pmxevtyper_read },
> + { .name = "PMXEVTYPER_EL0", .state = ARM_CP_STATE_AA64,
> + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 1,
> + .access = PL0_RW, .type = ARM_CP_ALIAS,
> + .accessfn = pmreg_access, .writefn = pmxevtyper_write,
> + .readfn = pmxevtyper_read },
> { .name = "PMCCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 0,
> - .access = PL0_RW, .resetvalue = 0, .type = ARM_CP_IO,
> + .access = PL0_RW, .resetvalue = 0,
> .readfn = pmccntr_read, .writefn = pmccntr_write32,
> .accessfn = pmreg_access },
> { .name = "PMCCNTR_EL0", .state = ARM_CP_STATE_AA64,
> .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 0,
> .access = PL0_RW, .accessfn = pmreg_access,
> - .type = ARM_CP_IO,
> - .readfn = pmccntr_read, .writefn = pmccntr_write, },
> -#endif
> + .type = ARM_CP_ALIAS,
> + .readfn = pmccntr_read, .writefn = pmccntr_write },
> + { .name = "PMCCFILTR", .cp = 15, .crn = 14, .crm = 15, .opc1 = 0, .opc2 = 7,
> + .access = PL0_RW, .accessfn = pmreg_access,
> + .writefn = pmccfiltr_write,
> + .fieldoffset = offsetof(CPUARMState, cp15.c14_pmccfiltr),
> + .resetvalue = 0 },
> { .name = "PMCCFILTR_EL0", .state = ARM_CP_STATE_AA64,
> .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 15, .opc2 = 7,
> .writefn = pmccfiltr_write,
> .access = PL0_RW, .accessfn = pmreg_access,
> - .type = ARM_CP_IO,
> - .fieldoffset = offsetof(CPUARMState, cp15.pmccfiltr_el0),
> - .resetvalue = 0, },
> - { .name = "PMXEVTYPER", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 1,
> - .access = PL0_RW,
> - .fieldoffset = offsetof(CPUARMState, cp15.c9_pmxevtyper),
> - .accessfn = pmreg_access, .writefn = pmxevtyper_write,
> - .raw_writefn = raw_write },
> - /* Unimplemented, RAZ/WI. */
> - { .name = "PMXEVCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 2,
> - .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0,
> - .accessfn = pmreg_access },
> + .type = ARM_CP_ALIAS,
> + .fieldoffset = offsetof(CPUARMState, cp15.c14_pmccfiltr) },
> { .name = "PMUSERENR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 0,
> + .access = PL0_R | PL1_RW, .type = ARM_CP_ALIAS,
> + .fieldoffset = offsetof(CPUARMState, cp15.c9_pmuserenr),
> + .writefn = pmuserenr_write, .raw_writefn = raw_write },
> + { .name = "PMUSERENR_EL0", .state = ARM_CP_STATE_AA64,
> + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 14, .opc2 = 0,
> .access = PL0_R | PL1_RW,
> .fieldoffset = offsetof(CPUARMState, cp15.c9_pmuserenr),
> .resetvalue = 0,
> .writefn = pmuserenr_write, .raw_writefn = raw_write },
> { .name = "PMINTENSET", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 1,
> - .access = PL1_RW,
> + .access = PL1_RW, .type = ARM_CP_ALIAS,
> .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten),
> .resetvalue = 0,
> .writefn = pmintenset_write, .raw_writefn = raw_write },
> + { .name = "PMINTENSET_EL1", .state = ARM_CP_STATE_AA64,
> + .opc0 = 3, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 1,
> + .access = PL1_RW,
> + .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), .resetvalue = 0,
> + .writefn = pmintenset_write, .raw_writefn = raw_write },
> { .name = "PMINTENCLR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 2,
> .access = PL1_RW, .type = ARM_CP_ALIAS,
> .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten),
> .resetvalue = 0, .writefn = pmintenclr_write, },
> + { .name = "PMINTENCLR_EL1", .state = ARM_CP_STATE_AA64,
> + .opc0 = 3, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 2,
> + .access = PL1_RW, .type = ARM_CP_ALIAS,
> + .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten),
> + .writefn = pmintenclr_write },
> { .name = "VBAR", .state = ARM_CP_STATE_BOTH,
> .opc0 = 3, .crn = 12, .crm = 0, .opc1 = 0, .opc2 = 0,
> .access = PL1_RW, .writefn = vbar_write,
> @@ -3024,6 +3249,7 @@ static void define_debug_regs(ARMCPU *cpu)
>
> void register_cp_regs_for_features(ARMCPU *cpu)
> {
> + unsigned int i;
> /* Register all the coprocessor registers based on feature bits */
> CPUARMState *env = &cpu->env;
> if (arm_feature(env, ARM_FEATURE_M)) {
> @@ -3120,15 +3346,14 @@ void register_cp_regs_for_features(ARMCPU *cpu)
> }
> if (arm_feature(env, ARM_FEATURE_V7)) {
> /* v7 performance monitor control register: same implementor
> - * field as main ID register, and we implement only the cycle
> - * count register.
> + * field as main ID register.
> */
> -#ifndef CONFIG_USER_ONLY
> ARMCPRegInfo pmcr = {
> .name = "PMCR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 0,
> .access = PL0_RW,
> - .type = ARM_CP_IO | ARM_CP_ALIAS,
> - .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcr),
> + .type = ARM_CP_ALIAS,
> + .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcr),
> + .resetvalue = (cpu->midr & 0xff000000) | (NUM_PMU_COUNTERS << 11),
> .accessfn = pmreg_access, .writefn = pmcr_write,
> .raw_writefn = raw_write,
> };
> @@ -3136,14 +3361,47 @@ void register_cp_regs_for_features(ARMCPU *cpu)
> .name = "PMCR_EL0", .state = ARM_CP_STATE_AA64,
> .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 0,
> .access = PL0_RW, .accessfn = pmreg_access,
> - .type = ARM_CP_IO,
> .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcr),
> - .resetvalue = cpu->midr & 0xff000000,
> + .resetvalue = (cpu->midr & 0xff000000) | (NUM_PMU_COUNTERS << 11),
> .writefn = pmcr_write, .raw_writefn = raw_write,
> };
> define_one_arm_cp_reg(cpu, &pmcr);
> define_one_arm_cp_reg(cpu, &pmcr64);
> -#endif
> +
> + for (i = 0; i < 31; i++) {
> + char *pmevcntr_name = g_strdup_printf("PMEVCNTR%d", i);
> + char *pmevcntr_el0_name = g_strdup_printf("PMEVCNTR%d_EL0", i);
> + char *pmevtyper_name = g_strdup_printf("PMEVTYPER%d", i);
> + char *pmevtyper_el0_name = g_strdup_printf("PMEVTYPER%d_EL0", i);
> + ARMCPRegInfo pmcntr_regs[] = {
> + { .name = pmevcntr_name, .cp = 15, .crn = 15,
> + .crm = 8 | (3 & (i >> 3)), .opc1 = 0, .opc2 = i & 7,
> + .access = PL0_RW, .resetvalue = 0, .type = ARM_CP_ALIAS,
> + .readfn = pmevcntr_readfn, .writefn = pmevcntr_writefn,
> + .accessfn = pmreg_access },
> + { .name = pmevcntr_el0_name, .state = ARM_CP_STATE_AA64,
> + .opc0 = 3, .opc1 = 3, .crn = 15, .crm = 8 | (3 & (i >> 3)),
> + .opc2 = i & 7, .access = PL0_RW, .accessfn = pmreg_access,
> + .resetvalue = 0,
> + .readfn = pmevcntr_readfn, .writefn = pmevcntr_writefn },
> + { .name = pmevtyper_name, .cp = 15, .crn = 15,
> + .crm = 12 | (3 & (i >> 3)), .opc1 = 0, .opc2 = i & 7,
> + .access = PL0_RW, .resetvalue = 0, .type = ARM_CP_ALIAS,
> + .readfn = pmevtyper_readfn, .writefn = pmevtyper_writefn,
> + .accessfn = pmreg_access },
> + { .name = pmevtyper_el0_name, .state = ARM_CP_STATE_AA64,
> + .opc0 = 3, .opc1 = 3, .crn = 15, .crm = 12 | (3 & (i >> 3)),
> + .opc2 = i & 7, .access = PL0_RW, .accessfn = pmreg_access,
> + .resetvalue = 0,
> + .readfn = pmevtyper_readfn, .writefn = pmevtyper_writefn },
> + REGINFO_SENTINEL
> + };
> + define_arm_cp_regs(cpu, pmcntr_regs);
> + g_free(pmevcntr_name);
> + g_free(pmevcntr_el0_name);
> + g_free(pmevtyper_name);
> + g_free(pmevtyper_el0_name);
> + }
> ARMCPRegInfo clidr = {
> .name = "CLIDR", .state = ARM_CP_STATE_BOTH,
> .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 1,
> @@ -3169,16 +3427,19 @@ void register_cp_regs_for_features(ARMCPU *cpu)
> { .name = "ID_AA64DFR0_EL1", .state = ARM_CP_STATE_AA64,
> .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 0,
> .access = PL1_R, .type = ARM_CP_CONST,
> - /* We mask out the PMUVer field, because we don't currently
> - * implement the PMU. Not advertising it prevents the guest
> - * from trying to use it and getting UNDEFs on registers we
> - * don't implement.
> - */
> - .resetvalue = cpu->id_aa64dfr0 & ~0xf00 },
> + .resetvalue = cpu->id_aa64dfr0 },
> { .name = "ID_AA64DFR1_EL1", .state = ARM_CP_STATE_AA64,
> .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 1,
> .access = PL1_R, .type = ARM_CP_CONST,
> .resetvalue = cpu->id_aa64dfr1 },
> + { .name = "PMCEID0_EL0", .state = ARM_CP_STATE_AA64,
> + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 6,
> + .access = PL1_R, .type = ARM_CP_CONST,
> + .resetvalue = cpu->pmceid0},
> + { .name = "PMCEID1_EL0", .state = ARM_CP_STATE_AA64,
> + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 7,
> + .access = PL1_R, .type = ARM_CP_CONST,
> + .resetvalue = cpu->pmceid1},
> { .name = "ID_AA64AFR0_EL1", .state = ARM_CP_STATE_AA64,
> .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 4,
> .access = PL1_R, .type = ARM_CP_CONST,
> @@ -4073,14 +4334,17 @@ void HELPER(context_check_pid)(CPUARMState *env)
> void HELPER(update_instruction_count)(CPUARMState *env)
> {
> if (bbtrace_initialized()) {
> - /*
> - * If the bbv plugin is compiled in and enabled, we must account for the
> - * fact that bbv_profile needs to see prof_ic before we clear it.
> - * However, it doesn't always clear the counter every time this gets
> - * called, so we must keep track of the last value seen to ensure we
> - * update the instruction counter correctly in that case.
> - */
> - increment_instruction_counters(env->prof_ic - env->prof_ic_last);
> + /*
> + * If the bbv plugin is compiled in and enabled, we must account for the
> + * fact that bbv_profile needs to see prof_ic before we clear it.
> + * However, it doesn't always clear the counter every time this gets
> + * called, so we must keep track of the last value seen to ensure we
> + * update the instruction counter correctly in that case.
> + */
> + pmevcntr_increment(env, PMU_COUNTER_TYPE_INSTRUCTIONS,
> + env->prof_ic - env->prof_ic_last);
> + pmevcntr_increment(env, PMU_COUNTER_TYPE_CYCLES,
> + env->prof_ic - env->prof_ic_last);
> if (env->prof_pc && env->prof_is_jmp) {
> // If this is the end of a basic block, zero out last_seen counter too
> env->prof_ic_last = 0;
> --
> Qualcomm Innovation Center, Inc.
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project
>
>
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [Qemu-devel] [RFC 09/14] Implement remaining PMU functionality
2016-02-02 21:22 ` Alistair Francis
@ 2016-02-02 23:01 ` Christopher Covington
2016-02-02 23:22 ` Alistair Francis
0 siblings, 1 reply; 25+ messages in thread
From: Christopher Covington @ 2016-02-02 23:01 UTC (permalink / raw)
To: Alistair Francis; +Cc: Aaron Lindsay, qemu-devel@nongnu.org Developers
Hi Alistair,
On 02/02/2016 04:22 PM, Alistair Francis wrote:
> On Wed, Aug 5, 2015 at 9:51 AM, Christopher Covington
> <cov@codeaurora.org> wrote:
>> This adds logic to increment PMEVCNTR's based on different event inputs,
>> implements all remaining CP registers, and triggers an interrupt on
>> event overflow.
>
> We (Xilinx) need parts of this patch to avoid kernel panics when
> booting the 4.4 Linux kernel. Have you done any more work on it? If
> you can send out a pach set I'm happy to have a look at it.
This issue sounds related to Lorenzo Piersali's patch arm64: kernel: fix
PMUv3 registers unconditional access.
As for the status of our TCG PMU patches, unfortunately, last I recall,
I was writing some kvm-unit-tests that Drew wanted me to test against
the KVM PMU, which required real hardware. I got distracted with using
an upstream kernel and a certain distribution on the hardware and have
yet to return.
Cov
--
Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [Qemu-devel] [RFC 09/14] Implement remaining PMU functionality
2016-02-02 23:01 ` Christopher Covington
@ 2016-02-02 23:22 ` Alistair Francis
2016-02-03 18:37 ` Peter Maydell
0 siblings, 1 reply; 25+ messages in thread
From: Alistair Francis @ 2016-02-02 23:22 UTC (permalink / raw)
To: Christopher Covington; +Cc: Aaron Lindsay, qemu-devel@nongnu.org Developers
On Tue, Feb 2, 2016 at 3:01 PM, Christopher Covington
<cov@codeaurora.org> wrote:
> Hi Alistair,
>
> On 02/02/2016 04:22 PM, Alistair Francis wrote:
>> On Wed, Aug 5, 2015 at 9:51 AM, Christopher Covington
>> <cov@codeaurora.org> wrote:
>>> This adds logic to increment PMEVCNTR's based on different event inputs,
>>> implements all remaining CP registers, and triggers an interrupt on
>>> event overflow.
>>
>> We (Xilinx) need parts of this patch to avoid kernel panics when
>> booting the 4.4 Linux kernel. Have you done any more work on it? If
>> you can send out a pach set I'm happy to have a look at it.
>
> This issue sounds related to Lorenzo Piersali's patch arm64: kernel: fix
> PMUv3 registers unconditional access.
Thanks, I found that fix. I still see problems with the pmintenclr_el1
and pmovsclr_el0 registers though. At least in the Xilinx tree.
>
> As for the status of our TCG PMU patches, unfortunately, last I recall,
> I was writing some kvm-unit-tests that Drew wanted me to test against
> the KVM PMU, which required real hardware. I got distracted with using
> an upstream kernel and a certain distribution on the hardware and have
> yet to return.
That's unfortunate. Could this patch be split out and applied by itself?
Thanks,
Alistair
>
> Cov
>
> --
> Qualcomm Innovation Center, Inc.
> Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
> a Linux Foundation Collaborative Project
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [Qemu-devel] [RFC 09/14] Implement remaining PMU functionality
2016-02-02 23:22 ` Alistair Francis
@ 2016-02-03 18:37 ` Peter Maydell
2016-02-04 0:37 ` Alistair Francis
0 siblings, 1 reply; 25+ messages in thread
From: Peter Maydell @ 2016-02-03 18:37 UTC (permalink / raw)
To: Alistair Francis
Cc: Aaron Lindsay, Christopher Covington,
qemu-devel@nongnu.org Developers
On 2 February 2016 at 23:22, Alistair Francis <alistair23@gmail.com> wrote:
> On Tue, Feb 2, 2016 at 3:01 PM, Christopher Covington
> <cov@codeaurora.org> wrote:
>> As for the status of our TCG PMU patches, unfortunately, last I recall,
>> I was writing some kvm-unit-tests that Drew wanted me to test against
>> the KVM PMU, which required real hardware. I got distracted with using
>> an upstream kernel and a certain distribution on the hardware and have
>> yet to return.
>
> That's unfortunate. Could this patch be split out and applied by itself?
That sounds like a good idea. It needs splitting up into more
digestible chunks for review as well.
thanks
-- PMM
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [Qemu-devel] [RFC 09/14] Implement remaining PMU functionality
2016-02-03 18:37 ` Peter Maydell
@ 2016-02-04 0:37 ` Alistair Francis
0 siblings, 0 replies; 25+ messages in thread
From: Alistair Francis @ 2016-02-04 0:37 UTC (permalink / raw)
To: Peter Maydell
Cc: Aaron Lindsay, Christopher Covington,
qemu-devel@nongnu.org Developers
On Wed, Feb 3, 2016 at 10:37 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
> On 2 February 2016 at 23:22, Alistair Francis <alistair23@gmail.com> wrote:
>> On Tue, Feb 2, 2016 at 3:01 PM, Christopher Covington
>> <cov@codeaurora.org> wrote:
>>> As for the status of our TCG PMU patches, unfortunately, last I recall,
>>> I was writing some kvm-unit-tests that Drew wanted me to test against
>>> the KVM PMU, which required real hardware. I got distracted with using
>>> an upstream kernel and a certain distribution on the hardware and have
>>> yet to return.
>>
>> That's unfortunate. Could this patch be split out and applied by itself?
>
> That sounds like a good idea. It needs splitting up into more
> digestible chunks for review as well.
Ok, I had a look at the patch. It is massive, it touches so much code!
I split out a few things and I will send a patch set for that. It
includes the part that I am most interested in, but doesn't do any of
the timer changes or interrupts that you did.
Thanks,
Alistair
>
> thanks
> -- PMM
^ permalink raw reply [flat|nested] 25+ messages in thread
end of thread, other threads:[~2016-02-04 0:37 UTC | newest]
Thread overview: 25+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-08-05 16:51 [Qemu-devel] RFC: ARM Semihosting, PMU, and BBV Changes Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 01/14] Make unknown semihosting calls non-fatal Christopher Covington
2015-08-06 9:11 ` Alex Bennée
2015-08-06 17:59 ` Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 02/14] Added semihosting support for A64 in full-system mode Christopher Covington
2015-08-11 18:16 ` Peter Maydell
2015-08-05 16:51 ` [Qemu-devel] [RFC 03/14] Fix makefile Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 04/14] Modify load exclusive/store exclusive to use physical addresses with the monitor Christopher Covington
2015-09-23 17:19 ` [Qemu-devel] [PATCHv2] target-arm: Use physical addresses for ldrex/strex Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 05/14] Fixed TLB invalidate ops Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 06/14] Added support for block profiling for AArch32 and Aarch64 Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 07/14] Add PMU to ARM virt platform Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 08/14] Add instruction-counting infrastructure to target-arm Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 09/14] Implement remaining PMU functionality Christopher Covington
2016-02-02 21:22 ` Alistair Francis
2016-02-02 23:01 ` Christopher Covington
2016-02-02 23:22 ` Alistair Francis
2016-02-03 18:37 ` Peter Maydell
2016-02-04 0:37 ` Alistair Francis
2015-08-05 16:51 ` [Qemu-devel] [RFC 10/14] bbvec: Move mode/PID change detection to register writes Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 11/14] Print bbvec stats on 'magic' exceptions Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 12/14] bbvec: Detect mode changes after uncached_cpsr update Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 13/14] Enable negative icount values for QEMU Christopher Covington
2015-08-05 16:51 ` [Qemu-devel] [RFC 14/14] bbvec: Properly detect conditional thumb2 branching instructions Christopher Covington
2015-08-11 15:27 ` [Qemu-devel] RFC: ARM Semihosting, PMU, and BBV Changes Peter Maydell
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).