blob: cd1cbd67ea38b872917705a90534f03bb97ecae8 [file] [log] [blame]
// Copyright 2023 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/arch/arm64/system.h>
#include <stdio.h>
#include <arch/arm64/mmu.h>
#include <arch/crashlog.h>
#include <arch/vm.h>
#include <ktl/atomic.h>
namespace {
ktl::atomic<bool> gSuppressELRDump{false};
}
void arch_render_crashlog_registers(FILE& target, const crashlog_regs_t& regs) {
if (regs.iframe == nullptr) {
fprintf(&target, "ARM64 REGISTERS: missing\n");
return;
}
fprintf(&target,
// clang-format off
"REGISTERS (v1.0)\n"
" x0: %#18" PRIx64 "\n"
" x1: %#18" PRIx64 "\n"
" x2: %#18" PRIx64 "\n"
" x3: %#18" PRIx64 "\n"
" x4: %#18" PRIx64 "\n"
" x5: %#18" PRIx64 "\n"
" x6: %#18" PRIx64 "\n"
" x7: %#18" PRIx64 "\n"
" x8: %#18" PRIx64 "\n"
" x9: %#18" PRIx64 "\n"
" x10: %#18" PRIx64 "\n"
" x11: %#18" PRIx64 "\n"
" x12: %#18" PRIx64 "\n"
" x13: %#18" PRIx64 "\n"
" x14: %#18" PRIx64 "\n"
" x15: %#18" PRIx64 "\n"
" x16: %#18" PRIx64 "\n"
" x17: %#18" PRIx64 "\n"
" x18: %#18" PRIx64 "\n"
" x19: %#18" PRIx64 "\n"
" x20: %#18" PRIx64 "\n"
" x21: %#18" PRIx64 "\n"
" x22: %#18" PRIx64 "\n"
" x23: %#18" PRIx64 "\n"
" x24: %#18" PRIx64 "\n"
" x25: %#18" PRIx64 "\n"
" x26: %#18" PRIx64 "\n"
" x27: %#18" PRIx64 "\n"
" x28: %#18" PRIx64 "\n"
" x29: %#18" PRIx64 "\n"
" lr: %#18" PRIx64 "\n"
" usp: %#18" PRIx64 "\n"
" elr: %#18" PRIx64 "\n"
"spsr: %#18" PRIx64 "\n"
" esr: %#18" PRIx32 "\n"
" far: %#18" PRIx64 "\n"
"\n",
// clang-format on
regs.iframe->r[0], regs.iframe->r[1], regs.iframe->r[2], regs.iframe->r[3],
regs.iframe->r[4], regs.iframe->r[5], regs.iframe->r[6], regs.iframe->r[7],
regs.iframe->r[8], regs.iframe->r[9], regs.iframe->r[10], regs.iframe->r[11],
regs.iframe->r[12], regs.iframe->r[13], regs.iframe->r[14], regs.iframe->r[15],
regs.iframe->r[16], regs.iframe->r[17], regs.iframe->r[18], regs.iframe->r[19],
regs.iframe->r[20], regs.iframe->r[21], regs.iframe->r[22], regs.iframe->r[23],
regs.iframe->r[24], regs.iframe->r[25], regs.iframe->r[26], regs.iframe->r[27],
regs.iframe->r[28], regs.iframe->r[29], regs.iframe->lr, regs.iframe->usp,
regs.iframe->elr, regs.iframe->spsr, regs.esr, regs.far);
// Depending on the exception class, conditionally make an attempt to print
// out some of the memory immediately surrounding the ELR. Particularly for
// an UnknownException, we would like to know whether or not the exception
// was taken because we happened to fetch an instruction which had been
// corrupted (somehow).
//
// Note, do not attempt to do this if we have even attempted to read from
// this memory before. We absolutely do not want to be in a situation where
// we attempt to read the memory that the system faulted on, and we just
// fault again for some unknown reason, putting us into an infinite fault
// recursion situation.
if (gSuppressELRDump.load(ktl::memory_order_seq_cst)) {
fprintf(&target, "ELR region dumping suppressed\n");
return;
}
using EC = arch::ArmExceptionSyndromeRegister::ExceptionClass;
const auto esr_reg = arch::ArmExceptionSyndromeRegister::Get().FromValue(regs.esr);
switch (esr_reg.ec()) {
case EC::kUnknown:
case EC::kDataAbortSameEl:
// Set our suppression bit before we attempt any accesses.
gSuppressELRDump.store(true, ktl::memory_order_seq_cst);
if (regs.iframe->elr & 0x3) {
fprintf(&target, "no ELR memory dump, misaligned ELR\n\n");
} else {
for (int offset = -4; offset <= 4; offset += 4) {
paddr_t paddr{0};
vaddr_t vaddr{0};
if (add_overflow(regs.iframe->elr, offset, &vaddr)) {
fprintf(&target, "ELR[%s%d] = over/underflow\n", (offset < 0) ? "" : "+", offset);
continue;
}
if (!is_kernel_address(vaddr)) {
fprintf(&target, "ELR[%s%d] = non-kernel\n", (offset < 0) ? "" : "+", offset);
} else if (arm64_mmu_translate(vaddr, &paddr, false /* user */, false /* write */) !=
ZX_OK) {
fprintf(&target, "ELR[%s%d] = bad addr\n", (offset < 0) ? "" : "+", offset);
} else {
uint32_t word = ktl::atomic_ref(reinterpret_cast<uint32_t*>(vaddr)[0])
.load(ktl::memory_order_relaxed);
const bool in_text = ((vaddr >= reinterpret_cast<vaddr_t>(__code_start)) &&
(vaddr < reinterpret_cast<vaddr_t>(__code_end)));
fprintf(&target, "ELR[%s%d] = 0x%08x%s\n", (offset < 0) ? "" : "+", offset, word,
in_text ? "" : " (not in .text)");
}
}
fprintf(&target, "\n");
}
break;
default:
break;
};
}