blob: 9c5911f69397101b0003144fc7973b6e95c6c0f5 [file] [log] [blame]
// 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 "garnet/bin/debug_agent/arch_arm64_helpers.h"
#include <gtest/gtest.h>
#include <optional>
#include "garnet/lib/debug_ipc/helper/file_line_function.h"
#include "garnet/lib/debug_ipc/helper/zx_status.h"
#include "lib/fxl/arraysize.h"
namespace debug_agent {
namespace arch {
namespace {
constexpr uint64_t kDbgbvrE = 1u;
zx_thread_state_debug_regs_t GetDefaultRegs() {
zx_thread_state_debug_regs_t debug_regs = {};
debug_regs.hw_bps_count = 4;
return debug_regs;
}
void SetupHWBreakpointTest(debug_ipc::FileLineFunction file_line,
zx_thread_state_debug_regs_t* debug_regs,
uint64_t address, zx_status_t expected_result) {
zx_status_t result = SetupHWBreakpoint(address, debug_regs);
ASSERT_EQ(result, expected_result)
<< "[" << file_line.ToString() << "] "
<< "Got: " << debug_ipc::ZxStatusToString(result)
<< ", expected: " << debug_ipc::ZxStatusToString(expected_result);
}
void RemoveHWBreakpointTest(debug_ipc::FileLineFunction file_line,
zx_thread_state_debug_regs_t* debug_regs,
uint64_t address, zx_status_t expected_result) {
zx_status_t result = RemoveHWBreakpoint(address, debug_regs);
ASSERT_EQ(result, expected_result)
<< "[" << file_line.ToString() << "] "
<< "Got: " << debug_ipc::ZxStatusToString(result)
<< ", expected: " << debug_ipc::ZxStatusToString(expected_result);
}
constexpr uint64_t kAddress1 = 0x0123;
constexpr uint64_t kAddress2 = 0x4567;
constexpr uint64_t kAddress3 = 0x89ab;
constexpr uint64_t kAddress4 = 0xcdef;
constexpr uint64_t kAddress5 = 0xdeadbeef;
} // namespace
TEST(arm64Helpers, SettingBreakpoints) {
auto debug_regs = GetDefaultRegs();
SetupHWBreakpointTest(FROM_HERE_NO_FUNC, &debug_regs, kAddress1, ZX_OK);
EXPECT_EQ(debug_regs.hw_bps[0].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[0].dbgbvr, kAddress1);
for (size_t i = 1; i < arraysize(debug_regs.hw_bps); i++) {
EXPECT_EQ(debug_regs.hw_bps[i].dbgbcr & kDbgbvrE, 0u);
EXPECT_EQ(debug_regs.hw_bps[i].dbgbvr, 0u);
}
// Adding the same breakpoint should detect that the same already exists.
SetupHWBreakpointTest(FROM_HERE_NO_FUNC, &debug_regs, kAddress1, ZX_OK);
EXPECT_EQ(debug_regs.hw_bps[0].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[0].dbgbvr, kAddress1);
for (size_t i = 1; i < arraysize(debug_regs.hw_bps); i++) {
EXPECT_EQ(debug_regs.hw_bps[i].dbgbcr & kDbgbvrE, 0u);
EXPECT_EQ(debug_regs.hw_bps[i].dbgbvr, 0u);
}
// Continuing adding should append.
SetupHWBreakpointTest(FROM_HERE_NO_FUNC, &debug_regs, kAddress2, ZX_OK);
EXPECT_EQ(debug_regs.hw_bps[0].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[0].dbgbvr, kAddress1);
EXPECT_EQ(debug_regs.hw_bps[1].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[1].dbgbvr, kAddress2);
for (size_t i = 2; i < arraysize(debug_regs.hw_bps); i++) {
EXPECT_EQ(debug_regs.hw_bps[i].dbgbcr & kDbgbvrE, 0u);
EXPECT_EQ(debug_regs.hw_bps[i].dbgbvr, 0u);
}
SetupHWBreakpointTest(FROM_HERE_NO_FUNC, &debug_regs, kAddress3, ZX_OK);
EXPECT_EQ(debug_regs.hw_bps[0].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[0].dbgbvr, kAddress1);
EXPECT_EQ(debug_regs.hw_bps[1].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[1].dbgbvr, kAddress2);
EXPECT_EQ(debug_regs.hw_bps[2].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[2].dbgbvr, kAddress3);
for (size_t i = 3; i < arraysize(debug_regs.hw_bps); i++) {
EXPECT_EQ(debug_regs.hw_bps[i].dbgbcr & kDbgbvrE, 0u);
EXPECT_EQ(debug_regs.hw_bps[i].dbgbvr, 0u);
}
SetupHWBreakpointTest(FROM_HERE_NO_FUNC, &debug_regs, kAddress4, ZX_OK);
EXPECT_EQ(debug_regs.hw_bps[0].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[0].dbgbvr, kAddress1);
EXPECT_EQ(debug_regs.hw_bps[1].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[1].dbgbvr, kAddress2);
EXPECT_EQ(debug_regs.hw_bps[2].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[2].dbgbvr, kAddress3);
EXPECT_EQ(debug_regs.hw_bps[3].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[3].dbgbvr, kAddress4);
for (size_t i = 4; i < arraysize(debug_regs.hw_bps); i++) {
EXPECT_EQ(debug_regs.hw_bps[i].dbgbcr & kDbgbvrE, 0u);
EXPECT_EQ(debug_regs.hw_bps[i].dbgbvr, 0u);
}
// No more registers left should not change anything.
SetupHWBreakpointTest(FROM_HERE_NO_FUNC, &debug_regs, kAddress5,
ZX_ERR_NO_RESOURCES);
EXPECT_EQ(debug_regs.hw_bps[0].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[0].dbgbvr, kAddress1);
EXPECT_EQ(debug_regs.hw_bps[1].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[1].dbgbvr, kAddress2);
EXPECT_EQ(debug_regs.hw_bps[2].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[2].dbgbvr, kAddress3);
EXPECT_EQ(debug_regs.hw_bps[3].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[3].dbgbvr, kAddress4);
for (size_t i = 4; i < arraysize(debug_regs.hw_bps); i++) {
EXPECT_EQ(debug_regs.hw_bps[i].dbgbcr & kDbgbvrE, 0u);
EXPECT_EQ(debug_regs.hw_bps[i].dbgbvr, 0u);
}
}
TEST(arm64Helpers, Removing) {
auto debug_regs = GetDefaultRegs();
// Previous state verifies the state of this calls.
SetupHWBreakpointTest(FROM_HERE_NO_FUNC, &debug_regs, kAddress1, ZX_OK);
SetupHWBreakpointTest(FROM_HERE_NO_FUNC, &debug_regs, kAddress2, ZX_OK);
SetupHWBreakpointTest(FROM_HERE_NO_FUNC, &debug_regs, kAddress3, ZX_OK);
SetupHWBreakpointTest(FROM_HERE_NO_FUNC, &debug_regs, kAddress4, ZX_OK);
SetupHWBreakpointTest(FROM_HERE_NO_FUNC, &debug_regs, kAddress5,
ZX_ERR_NO_RESOURCES);
RemoveHWBreakpointTest(FROM_HERE_NO_FUNC, &debug_regs, kAddress3, ZX_OK);
EXPECT_EQ(debug_regs.hw_bps[0].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[0].dbgbvr, kAddress1);
EXPECT_EQ(debug_regs.hw_bps[1].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[1].dbgbvr, kAddress2);
EXPECT_EQ(debug_regs.hw_bps[2].dbgbcr & kDbgbvrE, 0u);
EXPECT_EQ(debug_regs.hw_bps[2].dbgbvr, 0u);
EXPECT_EQ(debug_regs.hw_bps[3].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[3].dbgbvr, kAddress4);
for (size_t i = 4; i < arraysize(debug_regs.hw_bps); i++) {
EXPECT_EQ(debug_regs.hw_bps[i].dbgbcr & kDbgbvrE, 0u);
EXPECT_EQ(debug_regs.hw_bps[i].dbgbvr, 0u);
}
// Removing same breakpoint should not work.
RemoveHWBreakpointTest(FROM_HERE_NO_FUNC, &debug_regs, kAddress3,
ZX_ERR_OUT_OF_RANGE);
EXPECT_EQ(debug_regs.hw_bps[0].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[0].dbgbvr, kAddress1);
EXPECT_EQ(debug_regs.hw_bps[1].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[1].dbgbvr, kAddress2);
EXPECT_EQ(debug_regs.hw_bps[2].dbgbcr & kDbgbvrE, 0u);
EXPECT_EQ(debug_regs.hw_bps[2].dbgbvr, 0u);
EXPECT_EQ(debug_regs.hw_bps[3].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[3].dbgbvr, kAddress4);
for (size_t i = 4; i < arraysize(debug_regs.hw_bps); i++) {
EXPECT_EQ(debug_regs.hw_bps[i].dbgbcr & kDbgbvrE, 0u);
EXPECT_EQ(debug_regs.hw_bps[i].dbgbvr, 0u);
}
// Removing an unknown address should warn and change nothing.
RemoveHWBreakpointTest(FROM_HERE_NO_FUNC, &debug_regs, 0xaaaaaaa,
ZX_ERR_OUT_OF_RANGE);
EXPECT_EQ(debug_regs.hw_bps[0].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[0].dbgbvr, kAddress1);
EXPECT_EQ(debug_regs.hw_bps[1].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[1].dbgbvr, kAddress2);
EXPECT_EQ(debug_regs.hw_bps[2].dbgbcr & kDbgbvrE, 0u);
EXPECT_EQ(debug_regs.hw_bps[2].dbgbvr, 0u);
EXPECT_EQ(debug_regs.hw_bps[3].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[3].dbgbvr, kAddress4);
for (size_t i = 4; i < arraysize(debug_regs.hw_bps); i++) {
EXPECT_EQ(debug_regs.hw_bps[i].dbgbcr & kDbgbvrE, 0u);
EXPECT_EQ(debug_regs.hw_bps[i].dbgbvr, 0u);
}
RemoveHWBreakpointTest(FROM_HERE_NO_FUNC, &debug_regs, kAddress1, ZX_OK);
EXPECT_EQ(debug_regs.hw_bps[0].dbgbcr & kDbgbvrE, 0u);
EXPECT_EQ(debug_regs.hw_bps[0].dbgbvr, 0u);
EXPECT_EQ(debug_regs.hw_bps[1].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[1].dbgbvr, kAddress2);
EXPECT_EQ(debug_regs.hw_bps[2].dbgbcr & kDbgbvrE, 0u);
EXPECT_EQ(debug_regs.hw_bps[2].dbgbvr, 0u);
EXPECT_EQ(debug_regs.hw_bps[3].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[3].dbgbvr, kAddress4);
for (size_t i = 4; i < arraysize(debug_regs.hw_bps); i++) {
EXPECT_EQ(debug_regs.hw_bps[i].dbgbcr & kDbgbvrE, 0u);
EXPECT_EQ(debug_regs.hw_bps[i].dbgbvr, 0u);
}
// Adding again should work.
SetupHWBreakpointTest(FROM_HERE_NO_FUNC, &debug_regs, kAddress5, ZX_OK);
EXPECT_EQ(debug_regs.hw_bps[0].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[0].dbgbvr, kAddress5);
EXPECT_EQ(debug_regs.hw_bps[1].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[1].dbgbvr, kAddress2);
EXPECT_EQ(debug_regs.hw_bps[2].dbgbcr & kDbgbvrE, 0u);
EXPECT_EQ(debug_regs.hw_bps[2].dbgbvr, 0u);
EXPECT_EQ(debug_regs.hw_bps[3].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[3].dbgbvr, kAddress4);
for (size_t i = 4; i < arraysize(debug_regs.hw_bps); i++) {
EXPECT_EQ(debug_regs.hw_bps[i].dbgbcr & kDbgbvrE, 0u);
EXPECT_EQ(debug_regs.hw_bps[i].dbgbvr, 0u);
}
SetupHWBreakpointTest(FROM_HERE_NO_FUNC, &debug_regs, kAddress1, ZX_OK);
EXPECT_EQ(debug_regs.hw_bps[0].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[0].dbgbvr, kAddress5);
EXPECT_EQ(debug_regs.hw_bps[1].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[1].dbgbvr, kAddress2);
EXPECT_EQ(debug_regs.hw_bps[2].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[2].dbgbvr, kAddress1);
EXPECT_EQ(debug_regs.hw_bps[3].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[3].dbgbvr, kAddress4);
for (size_t i = 4; i < arraysize(debug_regs.hw_bps); i++) {
EXPECT_EQ(debug_regs.hw_bps[i].dbgbcr & kDbgbvrE, 0u);
EXPECT_EQ(debug_regs.hw_bps[i].dbgbvr, 0u);
}
// Already exists should not change anything.
SetupHWBreakpointTest(FROM_HERE_NO_FUNC, &debug_regs, kAddress5, ZX_OK);
EXPECT_EQ(debug_regs.hw_bps[0].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[0].dbgbvr, kAddress5);
EXPECT_EQ(debug_regs.hw_bps[1].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[1].dbgbvr, kAddress2);
EXPECT_EQ(debug_regs.hw_bps[2].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[2].dbgbvr, kAddress1);
EXPECT_EQ(debug_regs.hw_bps[3].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[3].dbgbvr, kAddress4);
for (size_t i = 4; i < arraysize(debug_regs.hw_bps); i++) {
EXPECT_EQ(debug_regs.hw_bps[i].dbgbcr & kDbgbvrE, 0u);
EXPECT_EQ(debug_regs.hw_bps[i].dbgbvr, 0u);
}
SetupHWBreakpointTest(FROM_HERE_NO_FUNC, &debug_regs, kAddress3,
ZX_ERR_NO_RESOURCES);
EXPECT_EQ(debug_regs.hw_bps[0].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[0].dbgbvr, kAddress5);
EXPECT_EQ(debug_regs.hw_bps[1].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[1].dbgbvr, kAddress2);
EXPECT_EQ(debug_regs.hw_bps[2].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[2].dbgbvr, kAddress1);
EXPECT_EQ(debug_regs.hw_bps[3].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[3].dbgbvr, kAddress4);
for (size_t i = 4; i < arraysize(debug_regs.hw_bps); i++) {
EXPECT_EQ(debug_regs.hw_bps[i].dbgbcr & kDbgbvrE, 0u);
EXPECT_EQ(debug_regs.hw_bps[i].dbgbvr, 0u);
}
// No more registers.
SetupHWBreakpointTest(FROM_HERE_NO_FUNC, &debug_regs, kAddress3,
ZX_ERR_NO_RESOURCES);
EXPECT_EQ(debug_regs.hw_bps[0].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[0].dbgbvr, kAddress5);
EXPECT_EQ(debug_regs.hw_bps[1].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[1].dbgbvr, kAddress2);
EXPECT_EQ(debug_regs.hw_bps[2].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[2].dbgbvr, kAddress1);
EXPECT_EQ(debug_regs.hw_bps[3].dbgbcr & kDbgbvrE, 1u);
EXPECT_EQ(debug_regs.hw_bps[3].dbgbvr, kAddress4);
for (size_t i = 4; i < arraysize(debug_regs.hw_bps); i++) {
EXPECT_EQ(debug_regs.hw_bps[i].dbgbcr & kDbgbvrE, 0u);
EXPECT_EQ(debug_regs.hw_bps[i].dbgbvr, 0u);
}
}
} // namespace arch
} // namespace debug_agent