| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // See the README.md in this directory for documentation. |
| |
| #include <assert.h> |
| |
| #include <ddk/debug.h> |
| #include <ddk/protocol/platform/device.h> |
| |
| #include "perf-mon.h" |
| |
| namespace perfmon { |
| |
| // There's only a few fixed events, so handle them directly. |
| enum FixedEventId { |
| #define DEF_FIXED_EVENT(symbol, event_name, id, regnum, flags, readable_name, description) \ |
| symbol##_ID = MakeEventId(kGroupFixed, id), |
| #include <lib/zircon-internal/device/cpu-trace/arm64-pm-events.inc> |
| }; |
| |
| // Verify each fixed counter regnum < ARM64_PMU_MAX_FIXED_COUNTERS. |
| #define DEF_FIXED_EVENT(symbol, event_name, id, regnum, flags, readable_name, description) \ |
| &&(regnum) < ARM64_PMU_MAX_FIXED_COUNTERS |
| static_assert(1 |
| #include <lib/zircon-internal/device/cpu-trace/arm64-pm-events.inc> |
| , |
| ""); |
| |
| enum ArchEvent { |
| #define DEF_ARCH_EVENT(symbol, event_name, id, pmceid_bit, event, flags, readable_name, \ |
| description) \ |
| symbol, |
| #include <lib/zircon-internal/device/cpu-trace/arm64-pm-events.inc> |
| }; |
| |
| static const EventDetails kArchEvents[] = { |
| #define DEF_ARCH_EVENT(symbol, event_name, id, pmceid_bit, event, flags, readable_name, \ |
| description) \ |
| {id, event, flags}, |
| #include <lib/zircon-internal/device/cpu-trace/arm64-pm-events.inc> |
| }; |
| |
| // A table to map event id to index in |kArchEvents|. |
| // We use the kConstant naming style as once computed it is constant. |
| static const uint16_t* kArchEventMap; |
| static size_t kArchEventMapSize; |
| |
| // Initialize the event maps. |
| // If there's a problem with the database just flag the error but don't crash. |
| |
| static zx_status_t InitializeEventMaps() { |
| zx_status_t status = |
| BuildEventMap(kArchEvents, countof(kArchEvents), &kArchEventMap, &kArchEventMapSize); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| return ZX_OK; |
| } |
| |
| // Each arch provides its own |InitOnce()| method. |
| |
| zx_status_t PerfmonDevice::InitOnce() { |
| zx_status_t status = InitializeEventMaps(); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| return ZX_OK; |
| } |
| |
| // Architecture-provided helpers for |PmuStageConfig()|. |
| |
| void PerfmonDevice::InitializeStagingState(StagingState* ss) { |
| ss->max_num_fixed = pmu_hw_properties_.common.max_num_fixed_events; |
| ss->max_num_programmable = pmu_hw_properties_.common.max_num_programmable_events; |
| ss->max_fixed_value = (pmu_hw_properties_.common.max_fixed_counter_width < 64 |
| ? (1ul << pmu_hw_properties_.common.max_fixed_counter_width) - 1 |
| : ~0ul); |
| ss->max_programmable_value = |
| (pmu_hw_properties_.common.max_programmable_counter_width < 64 |
| ? (1ul << pmu_hw_properties_.common.max_programmable_counter_width) - 1 |
| : ~0ul); |
| } |
| |
| zx_status_t PerfmonDevice::StageFixedConfig(const FidlPerfmonConfig* icfg, StagingState* ss, |
| unsigned input_index, PmuConfig* ocfg) { |
| const unsigned ii = input_index; |
| const EventId id = icfg->events[ii].event; |
| EventRate rate = icfg->events[ii].rate; |
| fidl_perfmon::EventConfigFlags flags = icfg->events[ii].flags; |
| bool uses_timebase = ocfg->timebase_event != kEventIdNone && rate == 0; |
| |
| // There's only one fixed counter on ARM64, the cycle counter. |
| if (id != FIXED_CYCLE_COUNTER_ID) { |
| zxlogf(ERROR, "%s: Invalid fixed event [%u]", __func__, ii); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| if (ss->num_fixed > 0) { |
| zxlogf(ERROR, "%s: Fixed event [%u] already provided", __func__, id); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| ocfg->fixed_events[ss->num_fixed] = id; |
| |
| if (rate == 0) { |
| ocfg->fixed_initial_value[ss->num_fixed] = 0; |
| } else { |
| #if 0 // TODO(ZX-3302): Disable until overflow interrupts are working. |
| // The cycle counter is 64 bits so there's no need to check |
| // |icfg->rate[ii]| here. |
| ZX_DEBUG_ASSERT(ss->max_fixed_value == UINT64_MAX); |
| ocfg->fixed_initial_value[ss->num_fixed] = |
| ss->max_fixed_value - rate + 1; |
| #else |
| zxlogf(ERROR, "%s: data collection rates not supported yet", __func__); |
| return ZX_ERR_NOT_SUPPORTED; |
| #endif |
| } |
| |
| // TODO(ZX-3302): Disable until overflow interrupts are working. |
| if (uses_timebase) { |
| zxlogf(ERROR, "%s: data collection rates not supported yet", __func__); |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| uint32_t pmu_flags = 0; |
| if (flags & fidl_perfmon::EventConfigFlags::COLLECT_OS) { |
| pmu_flags |= kPmuConfigFlagOs; |
| } |
| if (flags & fidl_perfmon::EventConfigFlags::COLLECT_USER) { |
| pmu_flags |= kPmuConfigFlagUser; |
| } |
| // TODO(ZX-3302): PC flag. |
| ocfg->fixed_flags[ss->num_fixed] = pmu_flags; |
| |
| ++ss->num_fixed; |
| return ZX_OK; |
| } |
| |
| zx_status_t PerfmonDevice::StageProgrammableConfig(const FidlPerfmonConfig* icfg, StagingState* ss, |
| unsigned input_index, PmuConfig* ocfg) { |
| const unsigned ii = input_index; |
| EventId id = icfg->events[ii].event; |
| unsigned group = GetEventIdGroup(id); |
| unsigned event = GetEventIdEvent(id); |
| EventRate rate = icfg->events[ii].rate; |
| fidl_perfmon::EventConfigFlags flags = icfg->events[ii].flags; |
| bool uses_timebase = ocfg->timebase_event != kEventIdNone && rate == 0; |
| |
| // TODO(dje): Verify no duplicates. |
| if (ss->num_programmable == ss->max_num_programmable) { |
| zxlogf(ERROR, "%s: Too many programmable counters provided", __func__); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| ocfg->programmable_events[ss->num_programmable] = id; |
| |
| if (rate == 0) { |
| ocfg->programmable_initial_value[ss->num_programmable] = 0; |
| } else { |
| #if 0 // TODO(ZX-3302): Disable until overflow interrupts are working. |
| // The cycle counter is 64 bits so there's no need to check |
| // |icfg->rate[ii]| here. |
| if (icfg->rate[ii] > ss->max_programmable_value) { |
| zxlogf(ERROR, "%s: Rate too large, event [%u]", __func__, ii); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| ocfg->programmable_initial_value[ss->num_programmable] = |
| ss->max_programmable_value - icfg->rate[ii] + 1; |
| #else |
| zxlogf(ERROR, "%s: data collection rates not supported yet", __func__); |
| return ZX_ERR_NOT_SUPPORTED; |
| #endif |
| } |
| |
| const EventDetails* details = NULL; |
| switch (group) { |
| case kGroupArch: |
| if (event >= kArchEventMapSize) { |
| zxlogf(ERROR, "%s: Invalid event id, event [%u]", __func__, ii); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| details = &kArchEvents[kArchEventMap[event]]; |
| break; |
| default: |
| zxlogf(ERROR, "%s: Invalid event id, event [%u]", __func__, ii); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| // Arch events have at least ARM64_PMU_REG_FLAG_{ARCH,MICROARCH} set. |
| if (details->flags == 0) { |
| zxlogf(ERROR, "%s: Invalid event id, event [%u]", __func__, ii); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| ZX_DEBUG_ASSERT((details->flags & (ARM64_PMU_REG_FLAG_ARCH | ARM64_PMU_REG_FLAG_MICROARCH)) != 0); |
| |
| ocfg->programmable_hw_events[ss->num_programmable] = details->event; |
| |
| // TODO(ZX-3302): Disable until overflow interrupts are working. |
| if (uses_timebase) { |
| zxlogf(ERROR, "%s: data collection rates not supported yet", __func__); |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| uint32_t pmu_flags = 0; |
| if (flags & fidl_perfmon::EventConfigFlags::COLLECT_OS) { |
| pmu_flags |= kPmuConfigFlagOs; |
| } |
| if (flags & fidl_perfmon::EventConfigFlags::COLLECT_USER) { |
| pmu_flags |= kPmuConfigFlagUser; |
| } |
| // TODO(ZX-3302): PC flag. |
| ocfg->programmable_flags[ss->num_programmable] = pmu_flags; |
| |
| ++ss->num_programmable; |
| return ZX_OK; |
| } |
| |
| zx_status_t PerfmonDevice::StageMiscConfig(const FidlPerfmonConfig* icfg, StagingState* ss, |
| unsigned input_index, PmuConfig* ocfg) { |
| // There are no misc events yet. |
| zxlogf(ERROR, "%s: Invalid event [%u] (no misc events)", __func__, input_index); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| } // namespace perfmon |