blob: 4e0bda128d62f3057fb1437de2129462d986f5d4 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <inttypes.h>
#include <lib/lockup_detector.h>
#include <lib/zircon-internal/macros.h>
#include <arch/arm64/smccc.h>
#include <kernel/auto_preempt_disabler.h>
#include <kernel/event_limiter.h>
#include <kernel/scheduler.h>
#include "ddk_priv.h"
namespace {
// Rate limit the OOPS message to avoid spamming the log.
EventLimiter<ZX_SEC(1)> oops_rate_limiter;
// Emit an OOPS if the current thread has exceeded its targeted preemption time by |threshold|.
//
// Return true if |threshold| was exceeded.
//
// Must be called with preemption disabled.
bool CheckForOverrun(zx_duration_t threshold) {
DEBUG_ASSERT(Thread::Current::preemption_state().PreemptDisableCount() > 0);
const zx_time_t now = current_time();
const zx_time_t target_preemption_time = Scheduler::GetTargetPreemptionTime();
const zx_duration_t overrun = zx_time_sub_time(now, target_preemption_time);
if (overrun > threshold && oops_rate_limiter.Ready()) {
printf(
"WARNING: lockup_detector: thread has overrun its preemption time, overrun=%ldns, "
"threshold=%ldns (message rate limited)\n",
overrun, threshold);
return true;
}
return false;
}
} // namespace
zx_status_t arch_smc_call(const zx_smc_parameters_t* params, zx_smc_result_t* result) {
const uint32_t client_and_secure_os_id =
static_cast<uint32_t>(params->secure_os_id) << 16 | static_cast<uint32_t>(params->client_id);
arm_smccc_result_t arm_result;
{
AutoPreemptDisabler disabler;
const zx_time_t before = current_time();
LOCKUP_TIMED_BEGIN(SOURCE_TAG);
arm_result = arm_smccc_smc(params->func_id, params->arg1, params->arg2, params->arg3,
params->arg4, params->arg5, params->arg6, client_and_secure_os_id);
LOCKUP_TIMED_END();
const zx_duration_t delta = zx_time_sub_time(current_time(), before);
// Amount of time this thread may overrun its target preemption time before an OOPS is emitted.
//
// This value should be larger than the longest running SMC Fast Call, but small enough to
// detect temporary hangs and issues that could affect system performance or interactivity.
constexpr zx_duration_t kOverrunThreshold = ZX_MSEC(10);
// Were we in EL3 longer than we should have been?
if (CheckForOverrun(kOverrunThreshold)) {
printf("SMC arguments: w0=0x%" PRIx32 ", x1=0x%" PRIx64 ", x2=0x%" PRIx64 ", x3=0x%" PRIx64
", x4=0x%" PRIx64 ", x5=0x%" PRIx64 ", x6=0x%" PRIx64 ", w7=0x%" PRIx32
"\nSMC results: x0=0x%" PRIx64 ", x1=0x%" PRIx64 ", x2=0x%" PRIx64 ", x3=0x%" PRIx64
", x6=0x%" PRIx64 "\nduration=%ldns\n",
params->func_id, params->arg1, params->arg2, params->arg3, params->arg4, params->arg5,
params->arg6, client_and_secure_os_id, arm_result.x0, arm_result.x1, arm_result.x2,
arm_result.x3, arm_result.x6, delta);
}
}
result->arg0 = arm_result.x0;
result->arg1 = arm_result.x1;
result->arg2 = arm_result.x2;
result->arg3 = arm_result.x3;
result->arg6 = arm_result.x6;
return ZX_OK;
}