blob: b0b6ea3f4ea11d475958ecde4563a15e16af6a3c [file] [log] [blame]
// 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.
#ifndef SYSROOT_ZIRCON_HW_DEBUG_ARM64_H_
#define SYSROOT_ZIRCON_HW_DEBUG_ARM64_H_
#include <stdint.h>
// ARM64 Hardware Debug Resources
// =================================================================================================
// Hardware Breakpoints ----------------------------------------------------------------------------
//
// Hardware breakpoints permits to stop a thread when it executes 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.
#define ARM64_MIN_HW_BREAKPOINTS 2
#define ARM64_MAX_HW_BREAKPOINTS 16
// Access macros:
// All the relevant register fields are exposed through macros.
// For convenience of use, use the get/set macros:
//
// uint64_t ARM64_<REG>_<FIELD>_GET(uint64_t reg)
// void ARM64_<REG>_<FIELD>_SET(uint64_t* reg, uint64_t value)
//
// Examples:
// uint64_t bas = ARM64_DBGBCR_BAS_GET(dbgbcr);
// ARM64_DBGBCR_BAS_SET(&dbgbcr, modified_bas);
// DBGBCR<n>: Debug Control Register for HW Breakpoint #n.
//
// Control register for HW breakpoints. There is one foreach 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 following fields are accepted by the user. All other fields are ignored (masked).
//
// - E
// This mask is applied when to DBGBCR. Any set values on those fields will be overwritten.
//
// - PMC = 0b10
// - BAS = 0b1111
// - HMC = 0
// - SSC = 0
// - LBN = 0
// - BT = 0
// Enable/disable the breakpoint.
#define ARM64_DBGBCR_E 1lu // Bit 0
#define ARM64_DBGBCR_E_SHIFT 0u
#define ARM64_DBGBCR_E_MASK (ARM64_DBGBCR_E << ARM64_DBGBCR_E_SHIFT)
#define ARM64_DBGBCR_E_GET(dbgbcr) \
__arm64_internal_hw_debug_get_reg_value((dbgbcr), ARM64_DBGBCR_E_MASK, ARM64_DBGBCR_E_SHIFT)
#define ARM64_DBGBCR_E_SET(dbgbcr, value) \
__arm64_internal_hw_debug_set_reg_value((dbgbcr), (value), ARM64_DBGBCR_E_MASK, \
ARM64_DBGBCR_E_SHIFT)
// PMC, HMC, SSC define the environment where the breakpoint will trigger.
#define ARM64_DBGBCR_PMC 0b11lu // Bits 1-2.
#define ARM64_DBGBCR_PMC_SHIFT 1u
#define ARM64_DBGBCR_PMC_MASK (ARM64_DBGBCR_PMC << ARM64_DBGBCR_PMC_SHIFT)
#define ARM64_DBGBCR_PMC_GET(dbgbcr) \
__arm64_internal_hw_debug_get_reg_value((dbgbcr), ARM64_DBGBCR_PMC_MASK, ARM64_DBGBCR_PMC_SHIFT)
#define ARM64_DBGBCR_PMC_SET(dbgbcr, value) \
__arm64_internal_hw_debug_set_reg_value((dbgbcr), (value), ARM64_DBGBCR_PMC_MASK, \
ARM64_DBGBCR_PMC_SHIFT)
// Byte Address Select. Defines which half-words triggers the breakpoint.
// In AArch64 implementations (which zircon targets), is res1.
#define ARM64_DBGBCR_BAS 0b1111lu // Bits 5-8.
#define ARM64_DBGBCR_BAS_SHIFT 5u
#define ARM64_DBGBCR_BAS_MASK (ARM64_DBGBCR_BAS << ARM64_DBGBCR_BAS_SHIFT)
#define ARM64_DBGBCR_BAS_GET(dbgbcr) \
__arm64_internal_hw_debug_get_reg_value((dbgbcr), ARM64_DBGBCR_BAS_MASK, ARM64_DBGBCR_BAS_SHIFT)
#define ARM64_DBGBCR_BAS_SET(dbgbcr, value) \
__arm64_internal_hw_debug_set_reg_value((dbgbcr), (value), ARM64_DBGBCR_BAS_MASK, \
ARM64_DBGBCR_BAS_SHIFT)
// PMC, HMC, SSC define the environment where the breakpoint will trigger.
#define ARM64_DBGBCR_HMC 0b1lu // Bit 13.
#define ARM64_DBGBCR_HMC_SHIFT 13u
#define ARM64_DBGBCR_HMC_MASK (ARM64_DBGBCR_HMC << ARM64_DBGBCR_HMC_SHIFT)
#define ARM64_DBGBCR_HMC_GET(dbgbcr) \
__arm64_internal_hw_debug_get_reg_value((dbgbcr), ARM64_DBGBCR_HMC_MASK, ARM64_DBGBCR_HMC_SHIFT)
#define ARM64_DBGBCR_HMC_SET(dbgbcr, value) \
__arm64_internal_hw_debug_set_reg_value((dbgbcr), (value), ARM64_DBGBCR_HMC_MASK, \
ARM64_DBGBCR_HMC_SHIFT)
// PMC, HMC, SSC define the environment where the breakpoint will trigger.
#define ARM64_DBGBCR_SSC 0b11lu // Bits 14-15.
#define ARM64_DBGBCR_SSC_SHIFT 14u
#define ARM64_DBGBCR_SSC_MASK (ARM64_DBGBCR_SSC << ARM64_DBGBCR_SSC_SHIFT)
#define ARM64_DBGBCR_SSC_GET(dbgbcr) \
__arm64_internal_hw_debug_get_reg_value((dbgbcr), ARM64_DBGBCR_SSC_MASK, ARM64_DBGBCR_SSC_SHIFT)
#define ARM64_DBGBCR_SSC_SET(dbgbcr, value) \
__arm64_internal_hw_debug_set_reg_value((dbgbcr), (value), ARM64_DBGBCR_SSC_MASK, \
ARM64_DBGBCR_SSC_SHIFT)
// Linked Breakpoint Number. Zircon doesn't use this feature. Always zero.
#define ARM64_DBGBCR_LBN 0b1111lu // Bits 16-19.
#define ARM64_DBGBCR_LBN_SHIFT 16u
#define ARM64_DBGBCR_LBN_MASK (ARM64_DBGBCR_LBN << ARM64_DBGBCR_LBN_SHIFT)
#define ARM64_DBGBCR_LBN_GET(dbgbcr) \
__arm64_internal_hw_debug_get_reg_value((dbgbcr), ARM64_DBGBCR_LBN_MASK, ARM64_DBGBCR_LBN_SHIFT)
#define ARM64_DBGBCR_LBN_SET(dbgbcr, value) \
__arm64_internal_hw_debug_set_reg_value((dbgbcr), (value), ARM64_DBGBCR_LBN_MASK, \
ARM64_DBGBCR_LBN_SHIFT)
// Breakpoint Type. Zircon only uses unlinked address match (zero).
#define ARM64_DBGBCR_BT 0b1111lu // Bits 20-23.
#define ARM64_DBGBCR_BT_SHIFT 20u
#define ARM64_DBGBCR_BT_MASK (ARM64_DBGBCR_BT << ARM64_DBGBCR_BT_SHIFT)
#define ARM64_DBGBCR_BT_GET(dbgbcr) \
__arm64_internal_hw_debug_get_reg_value((dbgbcr), ARM64_DBGBCR_BT_MASK, ARM64_DBGBCR_BT_SHIFT)
#define ARM64_DBGBCR_BT_SET(dbgbcr, value) \
__arm64_internal_hw_debug_set_reg_value((dbgbcr), (value), ARM64_DBGBCR_BT_MASK, \
ARM64_DBGBCR_BT_SHIFT)
// 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.
#define ARM64_MIN_HW_WATCHPOINTS 2
#define ARM64_MAX_HW_WATCHPOINTS 16
// 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 fields are accepted by the user. All other fields are ignored (masked).
//
// - E
// - BAS
// - TODO(donosoc): Expose LSC.
// This mask is applied when to DBGWCR. Any set values on those fields will be overwritten.
//
// - PAC = 0b10
// - LSC = 0b10: Write watchpoint. TODO(donosoc): Expose to users so they can define it.
// - HMC = 0
// - SSC = 0b01
// - LBN = 0
// - WT = 0
// Enable/disable the watchpoint.
#define ARM64_DBGWCR_E 1lu // Bit 1.
#define ARM64_DBGWCR_E_SHIFT 0u
#define ARM64_DBGWCR_E_MASK (ARM64_DBGWCR_E << ARM64_DBGWCR_E_SHIFT)
#define ARM64_DBGWCR_E_GET(dbgwcr) \
__arm64_internal_hw_debug_get_reg_value((dbgwcr), ARM64_DBGWCR_E_MASK, ARM64_DBGWCR_E_SHIFT)
#define ARM64_DBGWCR_E_SET(dbgwcr, value) \
__arm64_internal_hw_debug_set_reg_value((dbgwcr), (value), ARM64_DBGWCR_E_MASK, \
ARM64_DBGWCR_E_SHIFT)
// PAC, SSC, HMC define the environment where the watchpoint will trigger.
#define ARM64_DBGWCR_PAC 0b11lu // Bits 1-2.
#define ARM64_DBGWCR_PAC_SHIFT 1u
#define ARM64_DBGWCR_PAC_MASK (ARM64_DBGWCR_PAC << ARM64_DBGWCR_PAC_SHIFT)
#define ARM64_DBGWCR_PAC_GET(dbgwcr) \
__arm64_internal_hw_debug_get_reg_value((dbgwcr), ARM64_DBGWCR_PAC_MASK, ARM64_DBGWCR_PAC_SHIFT)
#define ARM64_DBGWCR_PAC_SET(dbgwcr, value) \
__arm64_internal_hw_debug_set_reg_value((dbgwcr), (value), ARM64_DBGWCR_PAC_MASK, \
ARM64_DBGWCR_PAC_SHIFT)
// Load/Store Control.
//
// On what event the watchpoint trigger:
// 01: Read from address.
// 10: Write to address.
// 11: Read/Write to address.
#define ARM64_DBGWCR_LSC 0b11lu // Bits 3-4.
#define ARM64_DBGWCR_LSC_SHIFT 3u
#define ARM64_DBGWCR_LSC_MASK (ARM64_DBGWCR_LSC << ARM64_DBGWCR_LSC_SHIFT)
#define ARM64_DBGWCR_LSC_GET(dbgwcr) \
__arm64_internal_hw_debug_get_reg_value((dbgwcr), ARM64_DBGWCR_LSC_MASK, ARM64_DBGWCR_LSC_SHIFT)
#define ARM64_DBGWCR_LSC_SET(dbgwcr, value) \
__arm64_internal_hw_debug_set_reg_value((dbgwcr), (value), ARM64_DBGWCR_LSC_MASK, \
ARM64_DBGWCR_LSC_SHIFT)
// Byte Address Select.
//
// 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
#define ARM64_DBGWCR_BAS 0b11111111lu // Bits 5-12.
#define ARM64_DBGWCR_BAS_SHIFT 5u
#define ARM64_DBGWCR_BAS_MASK (ARM64_DBGWCR_BAS << ARM64_DBGWCR_BAS_SHIFT)
#define ARM64_DBGWCR_BAS_GET(dbgwcr) \
__arm64_internal_hw_debug_get_reg_value((dbgwcr), ARM64_DBGWCR_BAS_MASK, ARM64_DBGWCR_BAS_SHIFT)
#define ARM64_DBGWCR_BAS_SET(dbgwcr, value) \
__arm64_internal_hw_debug_set_reg_value((dbgwcr), (value), ARM64_DBGWCR_BAS_MASK, \
ARM64_DBGWCR_BAS_SHIFT)
// PAC, SSC, HMC define the environment where the watchpoint will trigger.
#define ARM64_DBGWCR_HMC 1lu // Bit 13.
#define ARM64_DBGWCR_HMC_SHIFT 13u
#define ARM64_DBGWCR_HMC_MASK (ARM64_DBGWCR_HMC << ARM64_DBGWCR_HMC_SHIFT)
#define ARM64_DBGWCR_HMC_GET(dbgwcr) \
__arm64_internal_hw_debug_get_reg_value((dbgwcr), ARM64_DBGWCR_HMC_MASK, ARM64_DBGWCR_HMC_SHIFT)
#define ARM64_DBGWCR_HMC_SET(dbgwcr, value) \
__arm64_internal_hw_debug_set_reg_value((dbgwcr), (value), ARM64_DBGWCR_HMC_MASK, \
ARM64_DBGWCR_HMC_SHIFT)
// PAC, SSC, HMC define the environment where the watchpoint will trigger.
#define ARM64_DBGWCR_SSC 0b11lu // Bits 14-15.
#define ARM64_DBGWCR_SSC_SHIFT 14u
#define ARM64_DBGWCR_SSC_MASK (ARM64_DBGWCR_SSC << ARM64_DBGWCR_SSC_SHIFT)
#define ARM64_DBGWCR_SSC_GET(dbgwcr) \
__arm64_internal_hw_debug_get_reg_value((dbgwcr), ARM64_DBGWCR_SSC_MASK, ARM64_DBGWCR_SSC_SHIFT)
#define ARM64_DBGWCR_SSC_SET(dbgwcr, value) \
__arm64_internal_hw_debug_set_reg_value((dbgwcr), (value), ARM64_DBGWCR_SSC_MASK, \
ARM64_DBGWCR_SSC_SHIFT)
// Linked Breakpoint Number. Zircon doesn't use this feature. Always zero.
#define ARM64_DBGWCR_LBN 0b1111lu // Bits 16-19.
#define ARM64_DBGWCR_LBN_SHIFT 16u
#define ARM64_DBGWCR_LBN_MASK (ARM64_DBGWCR_LBN << ARM64_DBGWCR_LBN_SHIFT)
#define ARM64_DBGWCR_LBN_GET(dbgwcr) \
__arm64_internal_hw_debug_get_reg_value((dbgwcr), ARM64_DBGWCR_LBN_MASK, ARM64_DBGWCR_LBN_SHIFT)
#define ARM64_DBGWCR_LBN_SET(dbgwcr, value) \
__arm64_internal_hw_debug_set_reg_value((dbgwcr), (value), ARM64_DBGWCR_LBN_MASK, \
ARM64_DBGWCR_LBN_SHIFT)
// Watchpoint Type. Zircon always use unlinked (0).
#define ARM64_DBGWCR_WT 1lu // Bit 20.
#define ARM64_DBGWCR_WT_SHIFT 20u
#define ARM64_DBGWCR_WT_MASK (ARM64_DBGWCR_WT << ARM64_DBGWCR_WT_SHIFT)
#define ARM64_DBGWCR_WT_GET(dbgwcr) \
__arm64_internal_hw_debug_get_reg_value((dbgwcr), ARM64_DBGWCR_WT_MASK, ARM64_DBGWCR_WT_SHIFT)
#define ARM64_DBGWCR_WT_SET(dbgwcr, value) \
__arm64_internal_hw_debug_set_reg_value((dbgwcr), (value), ARM64_DBGWCR_WT_MASK, \
ARM64_DBGWCR_WT_SHIFT)
// Mask. How many address bits to mask.
// This permits the watchpoint to track up to 2G worth of addresses.
// TODO(donosoc): Initially the debugger is going for parity with x64, which only permits 8 bytes.
// Eventually expose the ability to track bigger ranges.
#define ARM64_DBGWCR_MSK 0b11111lu // Bits 24-28.
#define ARM64_DBGWCR_MSK_SHIFT 24u
#define ARM64_DBGWCR_MSK_MASK (ARM64_DBGWCR_MSK << ARM64_DBGWCR_MSK_SHIFT)
#define ARM64_DBGWCR_MSK_GET(dbgwcr) \
__arm64_internal_hw_debug_get_reg_value((dbgwcr), ARM64_DBGWCR_MSK_MASK, ARM64_DBGWCR_MSK_SHIFT)
#define ARM64_DBGWCR_MSK_SET(dbgwcr, value) \
__arm64_internal_hw_debug_set_reg_value((dbgwcr), (value), ARM64_DBGWCR_MSK_MASK, \
ARM64_DBGWCR_MSK_SHIFT)
// Helper functions ================================================================================
inline uint32_t __arm64_internal_hw_debug_get_reg_value(uint32_t reg, uint32_t mask,
uint32_t shift) {
return (reg & mask) >> shift;
}
inline void __arm64_internal_hw_debug_set_reg_value(uint32_t* reg, uint32_t value, uint32_t mask,
uint32_t shift) {
*reg &= ~mask;
*reg |= (value << shift) & mask;
}
#endif // SYSROOT_ZIRCON_HW_DEBUG_ARM64_H_