mirror of
https://github.com/yuzu-emu/unicorn.git
synced 2024-12-23 17:05:36 +00:00
target/arm: Finish implementation of PM[X]EVCNTR and PM[X]EVTYPER
Add arrays to hold the registers, the definitions themselves, access functions, and logic to reset counters when PMCR.P is set. Update filtering code to support counters other than PMCCNTR. Support migration with raw read/write functions. Backports commit 5ecdd3e47cadae83a62dc92b472f1fe163b56f59 from qemu
This commit is contained in:
parent
c8c3defb18
commit
1a815a1afc
|
@ -478,6 +478,9 @@ typedef struct CPUARMState {
|
|||
* pmccntr_op_finish.
|
||||
*/
|
||||
uint64_t c15_ccnt_delta;
|
||||
uint64_t c14_pmevcntr[31];
|
||||
uint64_t c14_pmevcntr_delta[31];
|
||||
uint64_t c14_pmevtyper[31];
|
||||
uint64_t pmccfiltr_el0; /* Performance Monitor Filter Register */
|
||||
uint64_t vpidr_el2; /* Virtualization Processor ID Register */
|
||||
uint64_t vmpidr_el2; /* Virtualization Multiprocessor ID Register */
|
||||
|
|
|
@ -843,6 +843,7 @@ static const ARMCPRegInfo v6_cp_reginfo[] = {
|
|||
#define PMCRDP 0x10
|
||||
#define PMCRD 0x8
|
||||
#define PMCRC 0x4
|
||||
#define PMCRP 0x2
|
||||
#define PMCRE 0x1
|
||||
|
||||
#define PMXEVTYPER_P 0x80000000
|
||||
|
@ -922,6 +923,17 @@ uint64_t get_pmceid(CPUARMState *env, unsigned which)
|
|||
return pmceid;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check at runtime whether a PMU event is supported for the current machine
|
||||
*/
|
||||
static bool event_supported(const CPUARMState *env, uint16_t number)
|
||||
{
|
||||
if (number > MAX_EVENT_ID) {
|
||||
return false;
|
||||
}
|
||||
return env->supported_event_map[number] != UNSUPPORTED_EVENT;
|
||||
}
|
||||
|
||||
static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
bool isread)
|
||||
{
|
||||
|
@ -1041,9 +1053,11 @@ static bool pmu_counter_enabled(CPUARMState *env, uint8_t counter)
|
|||
prohibited = env->cp15.c9_pmcr & PMCRDP;
|
||||
}
|
||||
|
||||
/* TODO Remove assert, set filter to correct PMEVTYPER */
|
||||
assert(counter == 31);
|
||||
if (counter == 31) {
|
||||
filter = env->cp15.pmccfiltr_el0;
|
||||
} else {
|
||||
filter = env->cp15.c14_pmevtyper[counter];
|
||||
}
|
||||
|
||||
p = filter & PMXEVTYPER_P;
|
||||
u = filter & PMXEVTYPER_U;
|
||||
|
@ -1063,6 +1077,17 @@ static bool pmu_counter_enabled(CPUARMState *env, uint8_t counter)
|
|||
filtered = m != p;
|
||||
}
|
||||
|
||||
if (counter != 31) {
|
||||
/*
|
||||
* If not checking PMCCNTR, ensure the counter is setup to an event we
|
||||
* support
|
||||
*/
|
||||
uint16_t event = filter & PMXEVTYPER_EVTCOUNT;
|
||||
if (!event_supported(env, event)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return enabled && !prohibited && !filtered;
|
||||
}
|
||||
|
||||
|
@ -1109,14 +1134,47 @@ void pmccntr_op_finish(CPUARMState *env)
|
|||
}
|
||||
}
|
||||
|
||||
static void pmevcntr_op_start(CPUARMState *env, uint8_t counter)
|
||||
{
|
||||
|
||||
uint16_t event = env->cp15.c14_pmevtyper[counter] & PMXEVTYPER_EVTCOUNT;
|
||||
uint64_t count = 0;
|
||||
if (event_supported(env, event)) {
|
||||
uint16_t event_idx = env->supported_event_map[event];
|
||||
count = pm_events[event_idx].get_count(env);
|
||||
}
|
||||
|
||||
if (pmu_counter_enabled(env, counter)) {
|
||||
env->cp15.c14_pmevcntr[counter] =
|
||||
count - env->cp15.c14_pmevcntr_delta[counter];
|
||||
}
|
||||
env->cp15.c14_pmevcntr_delta[counter] = count;
|
||||
}
|
||||
|
||||
static void pmevcntr_op_finish(CPUARMState *env, uint8_t counter)
|
||||
{
|
||||
if (pmu_counter_enabled(env, counter)) {
|
||||
env->cp15.c14_pmevcntr_delta[counter] -=
|
||||
env->cp15.c14_pmevcntr[counter];
|
||||
}
|
||||
}
|
||||
|
||||
void pmu_op_start(CPUARMState *env)
|
||||
{
|
||||
unsigned int i;
|
||||
pmccntr_op_start(env);
|
||||
for (i = 0; i < pmu_num_counters(env); i++) {
|
||||
pmevcntr_op_start(env, i);
|
||||
}
|
||||
}
|
||||
|
||||
void pmu_op_finish(CPUARMState *env)
|
||||
{
|
||||
unsigned int i;
|
||||
pmccntr_op_finish(env);
|
||||
for (i = 0; i < pmu_num_counters(env); i++) {
|
||||
pmevcntr_op_finish(env, i);
|
||||
}
|
||||
}
|
||||
|
||||
void pmu_pre_el_change(ARMCPU *cpu, void *ignored)
|
||||
|
@ -1139,6 +1197,13 @@ static void pmcr_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
|||
env->cp15.c15_ccnt = 0;
|
||||
}
|
||||
|
||||
if (value & PMCRP) {
|
||||
unsigned int i;
|
||||
for (i = 0; i < pmu_num_counters(env); i++) {
|
||||
env->cp15.c14_pmevcntr[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* only the DP, X, D and E bits are writable */
|
||||
env->cp15.c9_pmcr &= ~0x39;
|
||||
env->cp15.c9_pmcr |= (value & 0x39);
|
||||
|
@ -1192,6 +1257,14 @@ void pmccntr_op_finish(CPUARMState *env)
|
|||
{
|
||||
}
|
||||
|
||||
void pmevcntr_op_start(CPUARMState *env, uint8_t i)
|
||||
{
|
||||
}
|
||||
|
||||
void pmevcntr_op_finish(CPUARMState *env, uint8_t i)
|
||||
{
|
||||
}
|
||||
|
||||
void pmu_op_start(CPUARMState *env)
|
||||
{
|
||||
}
|
||||
|
@ -1262,30 +1335,175 @@ static void pmovsset_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
|||
env->cp15.c9_pmovsr |= value;
|
||||
}
|
||||
|
||||
static void pmxevtyper_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
uint64_t value)
|
||||
static void pmevtyper_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
uint64_t value, const uint8_t counter)
|
||||
{
|
||||
if (counter == 31) {
|
||||
pmccfiltr_write(env, ri, value);
|
||||
} else if (counter < pmu_num_counters(env)) {
|
||||
pmevcntr_op_start(env, counter);
|
||||
|
||||
/*
|
||||
* If this counter's event type is changing, store the current
|
||||
* underlying count for the new type in c14_pmevcntr_delta[counter] so
|
||||
* pmevcntr_op_finish has the correct baseline when it converts back to
|
||||
* a delta.
|
||||
*/
|
||||
uint16_t old_event = env->cp15.c14_pmevtyper[counter] &
|
||||
PMXEVTYPER_EVTCOUNT;
|
||||
uint16_t new_event = value & PMXEVTYPER_EVTCOUNT;
|
||||
if (old_event != new_event) {
|
||||
uint64_t count = 0;
|
||||
if (event_supported(env, new_event)) {
|
||||
uint16_t event_idx = env->supported_event_map[new_event];
|
||||
count = pm_events[event_idx].get_count(env);
|
||||
}
|
||||
env->cp15.c14_pmevcntr_delta[counter] = count;
|
||||
}
|
||||
|
||||
env->cp15.c14_pmevtyper[counter] = value & PMXEVTYPER_MASK;
|
||||
pmevcntr_op_finish(env, counter);
|
||||
}
|
||||
|
||||
/* Attempts to access PMXEVTYPER are CONSTRAINED UNPREDICTABLE when
|
||||
* PMSELR value is equal to or greater than the number of implemented
|
||||
* counters, but not equal to 0x1f. We opt to behave as a RAZ/WI.
|
||||
*/
|
||||
if (env->cp15.c9_pmselr == 0x1f) {
|
||||
pmccfiltr_write(env, ri, value);
|
||||
}
|
||||
|
||||
static uint64_t pmevtyper_read(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
const uint8_t counter)
|
||||
{
|
||||
if (counter == 31) {
|
||||
return env->cp15.pmccfiltr_el0;
|
||||
} else if (counter < pmu_num_counters(env)) {
|
||||
return env->cp15.c14_pmevtyper[counter];
|
||||
} else {
|
||||
/*
|
||||
* We opt to behave as a RAZ/WI when attempts to access PMXEVTYPER
|
||||
* are CONSTRAINED UNPREDICTABLE. See comments in pmevtyper_write().
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void pmevtyper_writefn(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
uint64_t value)
|
||||
{
|
||||
uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7);
|
||||
pmevtyper_write(env, ri, value, counter);
|
||||
}
|
||||
|
||||
static void pmevtyper_rawwrite(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
uint64_t value)
|
||||
{
|
||||
uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7);
|
||||
env->cp15.c14_pmevtyper[counter] = value;
|
||||
|
||||
/*
|
||||
* pmevtyper_rawwrite is called between a pair of pmu_op_start and
|
||||
* pmu_op_finish calls when loading saved state for a migration. Because
|
||||
* we're potentially updating the type of event here, the value written to
|
||||
* c14_pmevcntr_delta by the preceeding pmu_op_start call may be for a
|
||||
* different counter type. Therefore, we need to set this value to the
|
||||
* current count for the counter type we're writing so that pmu_op_finish
|
||||
* has the correct count for its calculation.
|
||||
*/
|
||||
uint16_t event = value & PMXEVTYPER_EVTCOUNT;
|
||||
if (event_supported(env, event)) {
|
||||
uint16_t event_idx = env->supported_event_map[event];
|
||||
env->cp15.c14_pmevcntr_delta[counter] =
|
||||
pm_events[event_idx].get_count(env);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t pmevtyper_readfn(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||
{
|
||||
uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7);
|
||||
return pmevtyper_read(env, ri, counter);
|
||||
}
|
||||
|
||||
static void pmxevtyper_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
uint64_t value)
|
||||
{
|
||||
pmevtyper_write(env, ri, value, env->cp15.c9_pmselr & 31);
|
||||
}
|
||||
|
||||
static uint64_t pmxevtyper_read(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||
{
|
||||
/* We opt to behave as a RAZ/WI when attempts to access PMXEVTYPER
|
||||
* are CONSTRAINED UNPREDICTABLE. See comments in pmxevtyper_write().
|
||||
return pmevtyper_read(env, ri, env->cp15.c9_pmselr & 31);
|
||||
}
|
||||
|
||||
static void pmevcntr_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
uint64_t value, uint8_t counter)
|
||||
{
|
||||
if (counter < pmu_num_counters(env)) {
|
||||
pmevcntr_op_start(env, counter);
|
||||
env->cp15.c14_pmevcntr[counter] = value;
|
||||
pmevcntr_op_finish(env, counter);
|
||||
}
|
||||
/*
|
||||
* We opt to behave as a RAZ/WI when attempts to access PM[X]EVCNTR
|
||||
* are CONSTRAINED UNPREDICTABLE.
|
||||
*/
|
||||
if (env->cp15.c9_pmselr == 0x1f) {
|
||||
return env->cp15.pmccfiltr_el0;
|
||||
}
|
||||
|
||||
static uint64_t pmevcntr_read(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
uint8_t counter)
|
||||
{
|
||||
if (counter < pmu_num_counters(env)) {
|
||||
uint64_t ret;
|
||||
pmevcntr_op_start(env, counter);
|
||||
ret = env->cp15.c14_pmevcntr[counter];
|
||||
pmevcntr_op_finish(env, counter);
|
||||
return ret;
|
||||
} else {
|
||||
/* We opt to behave as a RAZ/WI when attempts to access PM[X]EVCNTR
|
||||
* are CONSTRAINED UNPREDICTABLE. */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void pmevcntr_writefn(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
uint64_t value)
|
||||
{
|
||||
uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7);
|
||||
pmevcntr_write(env, ri, value, counter);
|
||||
}
|
||||
|
||||
static uint64_t pmevcntr_readfn(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||
{
|
||||
uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7);
|
||||
return pmevcntr_read(env, ri, counter);
|
||||
}
|
||||
|
||||
static void pmevcntr_rawwrite(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
uint64_t value)
|
||||
{
|
||||
uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7);
|
||||
assert(counter < pmu_num_counters(env));
|
||||
env->cp15.c14_pmevcntr[counter] = value;
|
||||
pmevcntr_write(env, ri, value, counter);
|
||||
}
|
||||
|
||||
static uint64_t pmevcntr_rawread(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||
{
|
||||
uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7);
|
||||
assert(counter < pmu_num_counters(env));
|
||||
return env->cp15.c14_pmevcntr[counter];
|
||||
}
|
||||
|
||||
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 pmxevcntr_read(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||
{
|
||||
return pmevcntr_read(env, ri, env->cp15.c9_pmselr & 31);
|
||||
}
|
||||
|
||||
static void pmuserenr_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
uint64_t value)
|
||||
{
|
||||
|
@ -1464,16 +1682,18 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
|
|||
{ "PMCCFILTR_EL0", 0,14,15, 3,3,7, ARM_CP_STATE_AA64,
|
||||
ARM_CP_IO, PL0_RW, 0, NULL, 0, offsetof(CPUARMState, cp15.pmccfiltr_el0), {0, 0},
|
||||
pmreg_access, NULL, pmccfiltr_write, NULL, raw_write },
|
||||
{ "PMXEVTYPER", 15,9,13, 0,0,1, 0, ARM_CP_NO_RAW,
|
||||
{ "PMXEVTYPER", 15,9,13, 0,0,1, 0, ARM_CP_NO_RAW | ARM_CP_IO,
|
||||
PL0_RW, 0, NULL, 0, 0, {0, 0},
|
||||
pmreg_access, pmxevtyper_read, pmxevtyper_write },
|
||||
{ "PMXEVTYPER_EL0", 0,9,13, 3,3,1, ARM_CP_STATE_AA64, ARM_CP_NO_RAW,
|
||||
{ "PMXEVTYPER_EL0", 0,9,13, 3,3,1, ARM_CP_STATE_AA64, ARM_CP_NO_RAW | ARM_CP_IO,
|
||||
PL0_RW, 0, NULL, 0, 0, {0, 0},
|
||||
pmreg_access, pmxevtyper_read, pmxevtyper_write },
|
||||
/* Unimplemented, RAZ/WI. */
|
||||
{ "PMXEVCNTR", 15,9,13, 0,0,2, 0,
|
||||
ARM_CP_CONST, PL0_RW, 0, NULL, 0, 0, {0, 0},
|
||||
pmreg_access_xevcntr },
|
||||
ARM_CP_NO_RAW | ARM_CP_IO, PL0_RW, 0, NULL, 0, 0, {0, 0},
|
||||
pmreg_access_xevcntr, pmxevcntr_read, pmxevcntr_write },
|
||||
{ "PMXEVCNTR_EL0", 0,9,13, 3,3,2, ARM_CP_STATE_AA64, ARM_CP_NO_RAW | ARM_CP_IO,
|
||||
PL0_RW, 0, NULL, 0, 0, {0, 0},
|
||||
pmreg_access_xevcntr, pmxevcntr_read, pmxevcntr_write },
|
||||
{ "PMUSERENR", 15,9,14, 0,0,0, 0,
|
||||
0, PL0_R | PL1_RW, 0, NULL, 0, offsetoflow32(CPUARMState, cp15.c9_pmuserenr), {0, 0},
|
||||
access_tpm, NULL, pmuserenr_write, NULL, raw_write },
|
||||
|
@ -3964,7 +4184,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
|
|||
#endif
|
||||
/* The only field of MDCR_EL2 that has a defined architectural reset value
|
||||
* is MDCR_EL2.HPMN which should reset to the value of PMCR_EL0.N; but we
|
||||
* don't impelment any PMU event counters, so using zero as a reset
|
||||
* don't implement any PMU event counters, so using zero as a reset
|
||||
* value for MDCR_EL2 is okay
|
||||
*/
|
||||
{ "MDCR_EL2", 0,1,1, 3,4,1, ARM_CP_STATE_BOTH, 0,
|
||||
|
@ -4805,6 +5025,7 @@ void register_cp_regs_for_features(ARMCPU *cpu)
|
|||
* field as main ID register, and we implement only the cycle
|
||||
* count register.
|
||||
*/
|
||||
unsigned int i, pmcrn = 0;
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
ARMCPRegInfo pmcr = {
|
||||
"PMCR", 15,9,12, 0,0,0, 0,
|
||||
|
@ -4818,6 +5039,34 @@ void register_cp_regs_for_features(ARMCPU *cpu)
|
|||
};
|
||||
define_one_arm_cp_reg(cpu, &pmcr);
|
||||
define_one_arm_cp_reg(cpu, &pmcr64);
|
||||
for (i = 0; i < pmcrn; 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 pmev_regs[] = {
|
||||
{ pmevcntr_name, 15, 15, 8 | (3 & (i >> 3)), 0,0,i & 7,
|
||||
0, ARM_CP_IO | ARM_CP_ALIAS, PL0_RW, 0, NULL, 0, 0, {0, 0},
|
||||
pmreg_access, pmevcntr_readfn, pmevcntr_writefn },
|
||||
{ pmevcntr_el0_name, 0,15, 8 | (3 & (i >> 3)), 3,3,i & 7,
|
||||
ARM_CP_STATE_AA64, ARM_CP_IO, PL0_RW, 0, NULL, 0, 0, {0, 0},
|
||||
pmreg_access, pmevcntr_readfn, pmevcntr_writefn,
|
||||
pmevcntr_rawread, pmevcntr_rawwrite },
|
||||
{ pmevtyper_name, 15,15,12 | (3 & (i >> 3)), 0,0,i & 7,
|
||||
0, ARM_CP_IO | ARM_CP_ALIAS, PL0_RW, 0, NULL, 0, 0, {0, 0},
|
||||
pmreg_access, pmevtyper_readfn, pmevtyper_writefn },
|
||||
{ pmevtyper_el0_name, 0,15,12 | (3 & (i >> 3)), 3,3,i & 7,
|
||||
ARM_CP_STATE_AA64, ARM_CP_IO, PL0_RW, 0, NULL, 0, 0, {0, 0},
|
||||
pmreg_access, pmevtyper_readfn, pmevtyper_writefn, NULL,
|
||||
pmevtyper_rawwrite },
|
||||
REGINFO_SENTINEL
|
||||
};
|
||||
define_arm_cp_regs(cpu, pmev_regs);
|
||||
g_free(pmevcntr_name);
|
||||
g_free(pmevcntr_el0_name);
|
||||
g_free(pmevtyper_name);
|
||||
g_free(pmevtyper_el0_name);
|
||||
}
|
||||
#endif
|
||||
ARMCPRegInfo clidr = {
|
||||
"CLIDR", 0,0,0, 3,1,1, ARM_CP_STATE_BOTH,
|
||||
|
|
Loading…
Reference in a new issue