| // 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. | |
| #include "src/developer/debug/debug_agent/arch.h" | |
| // Notes on ARM64 architecture: | |
| // | |
| // Information was obtained from the ArmĀ® Architecture Reference Manual Armv8, for Armv8-A | |
| // architecture profile: | |
| // | |
| // https://developer.arm.com/docs/ddi0487/latest/arm-architecture-reference-manual-armv8-for-armv8-a-architecture-profile | |
| // | |
| // In order to obtain information about the registers below, the easiest way to do it is to do | |
| // search (ctrl-f) in the browser and the hit will probably will a link that you can press into | |
| // the corresponding definition (eg. search for "dbgwcr" and then click on the link). | |
| // | |
| // See zircon/system/public/hw/debug/arm64.h for more detailed information. | |
| // | |
| // Hardware Breakpoints | |
| // ------------------------------------------------------------------------------------------------- | |
| // | |
| // Hardware breakpoints permits to stop a thread when it accesses an address setup in one of the | |
| // hw breakpoints registers. They will work independent whether the address in question is | |
| // read-only or not. | |
| // ARMv8 assures at least 2 hardware breakpoints. | |
| // | |
| // DBGBVR<n>: HW Breakpoint Value Register. | |
| // | |
| // This register defines the value of the hw breakpoint <n> within the system. How that value is | |
| // interpreted depends on the correspondent value of DBGBCR<n>. | |
| // | |
| // DBGBCR<n>: HW Breakpoint Control Register. | |
| // | |
| // Control register for HW breakpoints. There is one for each HW breakpoint present within the | |
| // system. They go numbering by DBGBCR0, DBGBCR1, ... until the value defined in ID_AADFR0_EL1. | |
| // | |
| // For each control register, there is an equivalent DBGBVR<n> that holds the address the thread | |
| // will compare against. | |
| // | |
| // The only register bit that needs to be set by the user is E (Bit 1). The other bits are opaque | |
| // and should be handled by the kernel. | |
| // | |
| // Watchpoints | |
| // ------------------------------------------------------------------------------------------------- | |
| // | |
| // Watchpoints permits to stop a thread when it read/writes to a particular address in memory. | |
| // This will work even if the address is read-only memory (for a read, of course). | |
| // ARMv8 assures at least 2 watchpoints. | |
| // | |
| // DBGWVR<n>: Watchpoint Value Register. | |
| // | |
| // This register defines the value of the watchpoint <n> within the system. How that value is | |
| // interpreted depends on the correspondent value of DBGWCR<n>. | |
| // | |
| // DBGWCR<n>: Watchpoint Control Register. | |
| // | |
| // Control register for watchpoints. There is one for each watchpoint present within the system. | |
| // They go numbering by DBGWCR0, DBGWCR1, ... until the value defined ID_AAFR0_EL1. | |
| // For each control register, there is an equivalent DBGWCR<n> that holds the address the thread | |
| // will compare against. How this address is interpreted depends upon the configuration of the | |
| // associated control register. | |
| // | |
| // The following are the bits that are most important, | |
| // | |
| // - E (Bit 1): Defines whether the watchpoint is enabled or not. | |
| // | |
| // - LSC (bits 3-4): Defines how the watchpoint works: | |
| // 01: Read from address. | |
| // 10: Write to address. | |
| // 11: Read/Write to address. | |
| // | |
| // - BAS (Bits 5-12): Defines which bytes are to be "matched" starting from the one defined in the | |
| // value register. Each bit defines what bytes to match onto: | |
| // | |
| // 0bxxxx'xxx1: Match DBGWVR<n> + 0 | |
| // 0bxxxx'xx1x: Match DBGWVR<n> + 1 | |
| // 0bxxxx'x1xx: Match DBGWVR<n> + 2 | |
| // 0bxxxx'1xxx: Match DBGWVR<n> + 3 | |
| // 0bxxx1'xxxx: Match DBGWVR<n> + 4 | |
| // 0bxx1x'xxxx: Match DBGWVR<n> + 5 | |
| // 0bx1xx'xxxx: Match DBGWVR<n> + 6 | |
| // 0b1xxx'xxxx: Match DBGWVR<n> + 7 | |
| // | |
| // These bits must be set contiguosly (there cannot be gaps between the first | |
| // set bit and the last). Having DBGWVR not be 4-bytes aligned is deprecated. | |
| namespace debug_agent { | |
| namespace arch { | |
| // "BRK 0" instruction. | |
| // - Low 5 bits = 0. | |
| // - High 11 bits = 11010100001 | |
| // - In between 16 bits is the argument to the BRK instruction (in this case zero). | |
| const BreakInstructionType kBreakInstruction = 0xd4200000; | |
| // ARM reports the exception for the exception instruction itself. | |
| const int64_t kExceptionOffsetForSoftwareBreakpoint = 0; | |
| ::debug::Arch GetCurrentArch() { return ::debug::Arch::kArm64; } | |
| bool IsBreakpointInstruction(BreakInstructionType instruction) { | |
| // The BRK instruction could have any number associated with it, even though we only write "BRK | |
| // 0", so check for the low 5 and high 11 bytes as described above. | |
| constexpr BreakInstructionType kMask = 0b11111111111000000000000000011111; | |
| return (instruction & kMask) == kBreakInstruction; | |
| } | |
| uint64_t BreakpointInstructionForHardwareExceptionAddress(uint64_t exception_addr) { | |
| // arm64 will return the address of the instruction *about* to be executed. | |
| return exception_addr; | |
| } | |
| } // namespace arch | |
| } // namespace debug_agent |