|  | // Copyright 2019 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. | 
|  |  | 
|  | #include "src/developer/debug/ipc/decode_exception.h" | 
|  |  | 
|  | #include <lib/syslog/cpp/macros.h> | 
|  | #include <zircon/hw/debug/x86.h> | 
|  | #include <zircon/syscalls/exception.h> | 
|  |  | 
|  | #include "src/developer/debug/shared/arch_arm64.h" | 
|  | #include "src/developer/debug/shared/arch_x86.h" | 
|  | #include "src/developer/debug/shared/logging/logging.h" | 
|  |  | 
|  | namespace debug_ipc { | 
|  | namespace { | 
|  |  | 
|  | // clang-format off | 
|  | ExceptionType DecodeZircon(uint32_t code) { | 
|  | switch (code) { | 
|  | case ZX_EXCP_SW_BREAKPOINT: return ExceptionType::kSoftwareBreakpoint; | 
|  | case ZX_EXCP_HW_BREAKPOINT: return ExceptionType::kHardwareBreakpoint; | 
|  | case ZX_EXCP_GENERAL: return ExceptionType::kGeneral; | 
|  | case ZX_EXCP_FATAL_PAGE_FAULT: return ExceptionType::kPageFault; | 
|  | case ZX_EXCP_UNDEFINED_INSTRUCTION: return ExceptionType::kUndefinedInstruction; | 
|  | case ZX_EXCP_UNALIGNED_ACCESS: return ExceptionType::kUnalignedAccess; | 
|  | case ZX_EXCP_THREAD_STARTING: return ExceptionType::kThreadStarting; | 
|  | case ZX_EXCP_PROCESS_STARTING: return ExceptionType::kProcessStarting; | 
|  | case ZX_EXCP_THREAD_EXITING: return ExceptionType::kThreadExiting; | 
|  | case ZX_EXCP_POLICY_ERROR: return ExceptionType::kPolicyError; | 
|  | default: | 
|  | return ExceptionType::kUnknown; | 
|  | } | 
|  | } | 
|  | // clang-format on | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // x64 --------------------------------------------------------------------------------------------- | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | ExceptionType DecodeHardwareRegister(uint64_t dr7, int slot) { | 
|  | // clang-format off | 
|  | bool is_watchpoint = false; | 
|  | switch (slot) { | 
|  | case 0: is_watchpoint = X86_DBG_CONTROL_RW0_GET(dr7) != 0; break; | 
|  | case 1: is_watchpoint = X86_DBG_CONTROL_RW1_GET(dr7) != 0; break; | 
|  | case 2: is_watchpoint = X86_DBG_CONTROL_RW2_GET(dr7) != 0; break; | 
|  | case 3: is_watchpoint = X86_DBG_CONTROL_RW3_GET(dr7) != 0; break; | 
|  | default: | 
|  | FX_NOTREACHED(); | 
|  | return ExceptionType::kUnknown; | 
|  | } | 
|  | // clang-format on | 
|  |  | 
|  | return is_watchpoint ? ExceptionType::kWatchpoint : ExceptionType::kHardwareBreakpoint; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | ExceptionType DecodeException(uint32_t code, const X64ExceptionInfo& info) { | 
|  | // All zircon exceptions don't need further analysis, except hardware which can represent a single | 
|  | // step, a hw breakpoint or a watchpoint. | 
|  | ExceptionType type = DecodeZircon(code); | 
|  | if (type != ExceptionType::kHardwareBreakpoint) | 
|  | return type; | 
|  |  | 
|  | std::optional<X64ExceptionInfo::DebugRegs> regs; | 
|  | if (auto got = info.FetchDebugRegs()) { | 
|  | DEBUG_LOG(Archx64) << "DR6: " << debug_ipc::DR6ToString(got->dr6); | 
|  | regs = std::move(got.value()); | 
|  | } | 
|  |  | 
|  | // If we could not get the registers, we return the zircon exception. In the case of the ambiguous | 
|  | // hardware type, we assume single step. | 
|  | if (!regs.has_value()) | 
|  | return ExceptionType::kSingleStep; | 
|  |  | 
|  | // TODO(fxbug.dev/6246): This permits only one trigger per exception, when overlaps | 
|  | //                could occur. For a first pass this is acceptable. | 
|  |  | 
|  | if (X86_DBG_STATUS_BS_GET(regs->dr6)) | 
|  | return ExceptionType::kSingleStep; | 
|  |  | 
|  | if (X86_DBG_STATUS_B0_GET(regs->dr6)) { | 
|  | return DecodeHardwareRegister(regs->dr7, 0); | 
|  | } else if (X86_DBG_STATUS_B1_GET(regs->dr6)) { | 
|  | return DecodeHardwareRegister(regs->dr7, 1); | 
|  | } else if (X86_DBG_STATUS_B2_GET(regs->dr6)) { | 
|  | return DecodeHardwareRegister(regs->dr7, 2); | 
|  | } else if (X86_DBG_STATUS_B3_GET(regs->dr6)) { | 
|  | return DecodeHardwareRegister(regs->dr7, 3); | 
|  | } else { | 
|  | FX_NOTREACHED() << "x86: No known hw exception set in DR6"; | 
|  | return ExceptionType::kUnknown; | 
|  | } | 
|  | } | 
|  |  | 
|  | // arm64 ------------------------------------------------------------------------------------------- | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | ExceptionType DecodeESR(uint32_t esr) { | 
|  | // The ESR register holds information about the last exception in the form of: | 
|  | // |31      26|25|24                              0| | 
|  | // |    EC    |IL|             ISS                 | | 
|  | // | 
|  | // Where: | 
|  | // - EC: Exception class field (what exception occurred). | 
|  | // - IL: Instruction length (whether the trap was 16-bit of 32-bit instruction). | 
|  | // - ISS: Instruction Specific Syndrome. The value is specific to each EC. | 
|  | uint32_t ec = esr >> 26; | 
|  |  | 
|  | switch (ec) { | 
|  | case 0b111000: /* BRK from arm32 */ | 
|  | case 0b111100: /* BRK from arm64 */ | 
|  | return ExceptionType::kSoftwareBreakpoint; | 
|  | case 0b110000: /* HW breakpoint from a lower level */ | 
|  | case 0b110001: /* HW breakpoint from same level */ | 
|  | return ExceptionType::kHardwareBreakpoint; | 
|  | case 0b110010: /* software step from lower level */ | 
|  | case 0b110011: /* software step from same level */ | 
|  | return ExceptionType::kSingleStep; | 
|  | case 0b110100: /* HW watchpoint from a lower level */ | 
|  | case 0b110101: /* HW watchpoint from same level */ | 
|  | return ExceptionType::kWatchpoint; | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | return ExceptionType::kUnknown; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | ExceptionType DecodeException(uint32_t code, const Arm64ExceptionInfo& info) { | 
|  | // HW exceptions have to be analysed further. | 
|  | ExceptionType type = DecodeZircon(code); | 
|  | if (type != ExceptionType::kHardwareBreakpoint) | 
|  | return type; | 
|  |  | 
|  | uint32_t esr; | 
|  |  | 
|  | if (auto got = info.FetchESR()) { | 
|  | esr = *got; | 
|  | } else { | 
|  | return ExceptionType::kUnknown; | 
|  | } | 
|  |  | 
|  | auto decoded_type = DecodeESR(esr); | 
|  | if (decoded_type == ExceptionType::kUnknown) { | 
|  | FX_NOTREACHED() << "Received invalid ESR value: 0x" << std::hex << esr << " (EC: 0x" | 
|  | << (esr >> 26) << ")."; | 
|  | return debug_ipc::ExceptionType::kUnknown; | 
|  | } | 
|  |  | 
|  | return decoded_type; | 
|  | } | 
|  |  | 
|  | }  // namespace debug_ipc |