/* * BRBTS_EL1 is currently not used for branch stack implementation * purpose but BRBCR_ELx.TS needs to have a valid value from all * available options. BRBCR_ELx_TS_VIRTUAL is selected for this.
*/ #define BRBCR_ELx_DEFAULT_TS FIELD_PREP(BRBCR_ELx_TS_MASK, BRBCR_ELx_TS_VIRTUAL)
/* * BRBE Buffer Organization * * BRBE buffer is arranged as multiple banks of 32 branch record * entries each. An individual branch record in a given bank could * be accessed, after selecting the bank in BRBFCR_EL1.BANK and * accessing the registers i.e [BRBSRC, BRBTGT, BRBINF] set with * indices [0..31]. * * Bank 0 * * --------------------------------- ------ * | 00 | BRBSRC | BRBTGT | BRBINF | | 00 | * --------------------------------- ------ * | 01 | BRBSRC | BRBTGT | BRBINF | | 01 | * --------------------------------- ------ * | .. | BRBSRC | BRBTGT | BRBINF | | .. | * --------------------------------- ------ * | 31 | BRBSRC | BRBTGT | BRBINF | | 31 | * --------------------------------- ------ * * Bank 1 * * --------------------------------- ------ * | 32 | BRBSRC | BRBTGT | BRBINF | | 00 | * --------------------------------- ------ * | 33 | BRBSRC | BRBTGT | BRBINF | | 01 | * --------------------------------- ------ * | .. | BRBSRC | BRBTGT | BRBINF | | .. | * --------------------------------- ------ * | 63 | BRBSRC | BRBTGT | BRBINF | | 31 | * --------------------------------- ------
*/ #define BRBE_BANK_MAX_ENTRIES 32
static u16 brbinf_get_cycles(u64 brbinf)
{
u32 exp, mant, cycles; /* * Captured cycle count is unknown and hence * should not be passed on to userspace.
*/ if (brbinf & BRBINFx_EL1_CCU) return 0;
void brbe_invalidate(void)
{ /* Ensure all branches before this point are recorded */
isb(); asmvolatile(BRB_IALL_INSN); /* Ensure all branch records are invalidated after this point */
isb();
}
/* * Generic perf branch filters supported on BRBE * * New branch filters need to be evaluated whether they could be supported on * BRBE. This ensures that such branch filters would not just be accepted, to * fail silently. PERF_SAMPLE_BRANCH_HV is a special case that is selectively * supported only on platforms where kernel is in hyp mode.
*/ #define BRBE_EXCLUDE_BRANCH_FILTERS (PERF_SAMPLE_BRANCH_ABORT_TX | \
PERF_SAMPLE_BRANCH_IN_TX | \
PERF_SAMPLE_BRANCH_NO_TX | \
PERF_SAMPLE_BRANCH_CALL_STACK | \
PERF_SAMPLE_BRANCH_COUNTERS)
/* * BRBE supports the following functional branch type filters while * generating branch records. These branch filters can be enabled, * either individually or as a group i.e ORing multiple filters * with each other. * * BRBFCR_EL1_CONDDIR - Conditional direct branch * BRBFCR_EL1_DIRCALL - Direct call * BRBFCR_EL1_INDCALL - Indirect call * BRBFCR_EL1_INDIRECT - Indirect branch * BRBFCR_EL1_DIRECT - Direct branch * BRBFCR_EL1_RTN - Subroutine return
*/ static u64 branch_type_to_brbfcr(int branch_type)
{
u64 brbfcr = 0;
if (branch_type & PERF_SAMPLE_BRANCH_ANY_RETURN)
brbfcr |= BRBFCR_EL1_RTN;
if (branch_type & PERF_SAMPLE_BRANCH_IND_CALL)
brbfcr |= BRBFCR_EL1_INDCALL;
if (branch_type & PERF_SAMPLE_BRANCH_COND)
brbfcr |= BRBFCR_EL1_CONDDIR;
if (branch_type & PERF_SAMPLE_BRANCH_IND_JUMP)
brbfcr |= BRBFCR_EL1_INDIRECT;
if (branch_type & PERF_SAMPLE_BRANCH_CALL)
brbfcr |= BRBFCR_EL1_DIRCALL;
return brbfcr;
}
/* * BRBE supports the following privilege mode filters while generating * branch records. * * BRBCR_ELx_E0BRE - EL0 branch records * BRBCR_ELx_ExBRE - EL1/EL2 branch records * * BRBE also supports the following additional functional branch type * filters while generating branch records. * * BRBCR_ELx_EXCEPTION - Exception * BRBCR_ELx_ERTN - Exception return
*/ static u64 branch_type_to_brbcr(int branch_type)
{
u64 brbcr = BRBCR_ELx_FZP | BRBCR_ELx_DEFAULT_TS;
if (branch_type & PERF_SAMPLE_BRANCH_USER)
brbcr |= BRBCR_ELx_E0BRE;
/* * When running in the hyp mode, writing into BRBCR_EL1 * actually writes into BRBCR_EL2 instead. Field E2BRE * is also at the same position as E1BRE.
*/ if (branch_type & PERF_SAMPLE_BRANCH_KERNEL)
brbcr |= BRBCR_ELx_ExBRE;
if (branch_type & PERF_SAMPLE_BRANCH_HV) { if (is_kernel_in_hyp_mode())
brbcr |= BRBCR_ELx_ExBRE;
}
if (!(branch_type & PERF_SAMPLE_BRANCH_NO_CYCLES))
brbcr |= BRBCR_ELx_CC;
if (!(branch_type & PERF_SAMPLE_BRANCH_NO_FLAGS))
brbcr |= BRBCR_ELx_MPRED;
/* * The exception and exception return branches could be * captured, irrespective of the perf event's privilege. * If the perf event does not have enough privilege for * a given exception level, then addresses which falls * under that exception level will be reported as zero * for the captured branch record, creating source only * or target only records.
*/ if (branch_type & PERF_SAMPLE_BRANCH_KERNEL) { if (branch_type & PERF_SAMPLE_BRANCH_ANY) {
brbcr |= BRBCR_ELx_EXCEPTION;
brbcr |= BRBCR_ELx_ERTN;
}
if (branch_type & PERF_SAMPLE_BRANCH_ANY_CALL)
brbcr |= BRBCR_ELx_EXCEPTION;
/* * Ensure both perf branch filter allowed and exclude * masks are always in sync with the generic perf ABI.
*/
BUILD_BUG_ON(BRBE_PERF_BRANCH_FILTERS != (PERF_SAMPLE_BRANCH_MAX - 1));
if (branch_type & BRBE_EXCLUDE_BRANCH_FILTERS) {
pr_debug("requested branch filter not supported 0x%llx\n", branch_type); returnfalse;
}
/* Ensure at least 1 branch type is enabled */ if (!(branch_type & BRBE_ALLOWED_BRANCH_TYPES)) {
pr_debug("no branch type enabled 0x%llx\n", branch_type); returnfalse;
}
/* * No branches are recorded in guests nor nVHE hypervisors, so * excluding the host or both kernel and user is invalid. * * Ideally we'd just require exclude_guest and exclude_hv, but setting * event filters with perf for kernel or user don't set exclude_guest. * So effectively, exclude_guest and exclude_hv are ignored.
*/ if (event->attr.exclude_host || (event->attr.exclude_user && event->attr.exclude_kernel)) {
pr_debug("branch filter in hypervisor or guest only not supported 0x%llx\n", branch_type); returnfalse;
}
/* * In VHE mode with MDCR_EL2.HPMN equal to PMCR_EL0.N, BRBCR_EL1.FZP * controls freezing the branch records on counter overflow rather than * BRBCR_EL2.FZP (which writes to BRBCR_EL1 are redirected to). * The exception levels are enabled/disabled in BRBCR_EL2, so keep EL1 * and EL0 recording disabled for guests. * * As BRBCR_EL1 CC and MPRED bits also need to match, use the same * value for both registers just masking the exception levels.
*/ if (is_kernel_in_hyp_mode())
write_sysreg_s(brbcr & ~(BRBCR_ELx_ExBRE | BRBCR_ELx_E0BRE), SYS_BRBCR_EL12);
write_sysreg_s(brbcr, SYS_BRBCR_EL1); /* Ensure BRBCR_ELx settings take effect before unpausing */
isb();
void brbe_disable(void)
{ /* * No need for synchronization here as synchronization in PMCR write * ensures ordering and in the interrupt handler this is a NOP as * we're already paused.
*/
write_sysreg_s(BRBFCR_EL1_PAUSED, SYS_BRBFCR_EL1);
write_sysreg_s(0, SYS_BRBCR_EL1);
}
if (!branch_sample_no_cycles(event))
entry->cycles = brbinf_get_cycles(brbinf);
if (!branch_sample_no_flags(event)) { /* Mispredict info is available for source only and complete branch records. */ if (!brbe_record_is_target_only(brbinf)) {
entry->mispred = brbinf_get_mispredict(brbinf);
entry->predicted = !entry->mispred;
}
/* * Currently TME feature is neither implemented in any hardware * nor it is being supported in the kernel. Just warn here once * if TME related information shows up rather unexpectedly.
*/ if (brbinf_get_lastfailed(brbinf) || brbinf_get_in_tx(brbinf))
pr_warn_once("Unknown transaction states\n");
}
/* * Branch privilege level is available for target only and complete * branch records.
*/ if (!brbe_record_is_source_only(brbinf))
entry->priv = brbinf_get_perf_priv(brbinf);
if (branch_sample & PERF_SAMPLE_BRANCH_IND_JUMP)
set_bit(PERF_BR_IND, event_type_mask);
if (branch_sample & PERF_SAMPLE_BRANCH_COND)
set_bit(PERF_BR_COND, event_type_mask);
if (branch_sample & PERF_SAMPLE_BRANCH_CALL)
set_bit(PERF_BR_CALL, event_type_mask);
if (branch_sample & PERF_SAMPLE_BRANCH_IND_CALL)
set_bit(PERF_BR_IND_CALL, event_type_mask);
if (branch_sample & PERF_SAMPLE_BRANCH_ANY_RETURN) {
set_bit(PERF_BR_RET, event_type_mask);
if (branch_sample & PERF_SAMPLE_BRANCH_KERNEL)
set_bit(PERF_BR_ERET, event_type_mask);
}
}
/* * BRBE is configured with an OR of permissions from all events, so there may * be events which have to be dropped or events where just the source or target * address has to be zeroed.
*/ staticbool filter_branch_privilege(struct perf_branch_entry *entry, u64 branch_sample_type)
{ bool from_user = access_ok((void __user *)(unsignedlong)entry->from, 4); bool to_user = access_ok((void __user *)(unsignedlong)entry->to, 4); bool exclude_kernel = !((branch_sample_type & PERF_SAMPLE_BRANCH_KERNEL) ||
(is_kernel_in_hyp_mode() && (branch_sample_type & PERF_SAMPLE_BRANCH_HV)));
/* We can only have a half record if permissions have not been expanded */ if (!entry->from || !entry->to) returntrue;
/* * If record is within a single exception level, just need to either * drop or keep the entire record.
*/ if (from_user == to_user) return ((entry->priv == PERF_BR_PRIV_KERNEL) && !exclude_kernel) ||
((entry->priv == PERF_BR_PRIV_USER) &&
(branch_sample_type & PERF_SAMPLE_BRANCH_USER));
/* * Record is across exception levels, mask addresses for the exception * level we're not capturing.
*/ if (!(branch_sample_type & PERF_SAMPLE_BRANCH_USER)) { if (from_user)
entry->from = 0; if (to_user)
entry->to = 0;
}
if (exclude_kernel) { if (!from_user)
entry->from = 0; if (!to_user)
entry->to = 0;
}
for (int bank = 0; bank < nr_banks; bank++) { int nr_remaining = nr_hw - (bank * BRBE_BANK_MAX_ENTRIES); int nr_this_bank = min(nr_remaining, BRBE_BANK_MAX_ENTRIES);
select_brbe_bank(bank);
for (int i = 0; i < nr_this_bank; i++) { struct perf_branch_entry *pbe = &branch_stack->entries[nr_filtered];
if (!perf_entry_from_brbe_regset(i, pbe, event)) goto done;
if (!filter_branch_record(pbe, branch_sample_type, event_type_mask)) continue;
nr_filtered++;
}
}
done:
branch_stack->nr = nr_filtered;
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.10 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.