From 785b7e1a3784ac718342b81ea186be0264376089 Mon Sep 17 00:00:00 2001 From: TuxSH <1922548+TuxSH@users.noreply.github.com> Date: Mon, 10 Feb 2020 18:58:47 +0000 Subject: [PATCH] thermosphere: mostly rewrite sw breakpoint manager --- .../cpu/hvisor_cpu_debug_register_pair.hpp | 3 +- .../src/cpu/hvisor_cpu_exception_sysregs.hpp | 9 +- .../src/hvisor_hw_breakpoint_manager.hpp | 4 +- .../src/hvisor_sw_breakpoint_manager.cpp | 161 ++++++++++++++++++ .../src/hvisor_sw_breakpoint_manager.hpp | 69 ++++++++ .../src/hvisor_watchpoint_manager.cpp | 2 + .../src/hvisor_watchpoint_manager.hpp | 4 +- 7 files changed, 242 insertions(+), 10 deletions(-) create mode 100644 thermosphere/src/hvisor_sw_breakpoint_manager.cpp create mode 100644 thermosphere/src/hvisor_sw_breakpoint_manager.hpp diff --git a/thermosphere/src/cpu/hvisor_cpu_debug_register_pair.hpp b/thermosphere/src/cpu/hvisor_cpu_debug_register_pair.hpp index 1595d195d..6989c6db4 100644 --- a/thermosphere/src/cpu/hvisor_cpu_debug_register_pair.hpp +++ b/thermosphere/src/cpu/hvisor_cpu_debug_register_pair.hpp @@ -105,5 +105,6 @@ namespace ams::hvisor::cpu { } }; - static_assert(std::is_pod_v); + static_assert(std::is_standard_layout_v); + static_assert(std::is_trivial_v); } diff --git a/thermosphere/src/cpu/hvisor_cpu_exception_sysregs.hpp b/thermosphere/src/cpu/hvisor_cpu_exception_sysregs.hpp index de49d0801..5949d69bc 100644 --- a/thermosphere/src/cpu/hvisor_cpu_exception_sysregs.hpp +++ b/thermosphere/src/cpu/hvisor_cpu_exception_sysregs.hpp @@ -110,10 +110,9 @@ namespace ams::hvisor::cpu { }; - static_assert(std::is_pod_v); - static_assert(std::is_pod_v); - - - + static_assert(std::is_standard_layout_v); + static_assert(std::is_standard_layout_v); + static_assert(std::is_trivial_v); + static_assert(std::is_trivial_v); } diff --git a/thermosphere/src/hvisor_hw_breakpoint_manager.hpp b/thermosphere/src/hvisor_hw_breakpoint_manager.hpp index b24fcc464..9967e4a97 100644 --- a/thermosphere/src/hvisor_hw_breakpoint_manager.hpp +++ b/thermosphere/src/hvisor_hw_breakpoint_manager.hpp @@ -26,6 +26,8 @@ namespace ams::hvisor { private: static HwBreakpointManager instance; + public: + static HwBreakpointManager &GetInstance() { return instance; } public: virtual void ReloadOnAllCores() const; @@ -35,8 +37,6 @@ namespace ams::hvisor { int Add(uintptr_t addr); int Remove(uintptr_t addr); - HwBreakpointManager &GetInstance() { return instance; } - public: constexpr HwBreakpointManager() : HwStopPointManager(MAX_BCR) {} }; diff --git a/thermosphere/src/hvisor_sw_breakpoint_manager.cpp b/thermosphere/src/hvisor_sw_breakpoint_manager.cpp new file mode 100644 index 000000000..b02f4d85e --- /dev/null +++ b/thermosphere/src/hvisor_sw_breakpoint_manager.cpp @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2019-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "hvisor_sw_breakpoint_manager.hpp" +#include "cpu/hvisor_cpu_instructions.hpp" + +#include + +#include "guest_memory.h" + +#define _REENT_ONLY +#include + +/* + Consider the following: + - Breakpoints are based on VA + - Translation tables may change + - Translation tables may differ from core to core + + We also define sw breakpoints on invalid addresses (for one or more cores) UNPREDICTABLE. +*/ + +namespace ams::hvisor { + + SwBreakpointManager SwBreakpointManager::instance{}; + + size_t SwBreakpointManager::FindClosest(uintptr_t addr) const + { + auto endit = m_breakpoints.cbegin() + m_numBreakpoints; + auto it = std::lower_bound( + m_breakpoints.cbegin(), + endit, + addr, + [] (const Breakpoint &a, const Breakpoint &b) { + return a.address < b.address; + } + ); + + return it == endit ? m_numBreakpoints : static_cast(it - m_breakpoints.cbegin()); + } + + bool SwBreakpointManager::DoApply(size_t id) + { + Breakpoint &bp = m_breakpoints[id]; + u32 brkInst = 0xD4200000 | (bp.uid << 5); + + size_t sz = guestReadWriteMemory(bp.address, 4, &bp.savedInstruction, &brkInst); + bp.applied = sz == 4; + m_triedToApplyOrRevertBreakpoint.store(true); + return sz == 4; + } + + bool SwBreakpointManager::DoRevert(size_t id) + { + Breakpoint &bp = m_breakpoints[id]; + size_t sz = guestWriteMemory(bp.address, 4, &bp.savedInstruction); + bp.applied = sz != 4; + m_triedToApplyOrRevertBreakpoint.store(true); + return sz == 4; + } + + // TODO apply revert handlers + + int SwBreakpointManager::Add(uintptr_t addr, bool persistent) + { + if ((addr & 3) != 0) { + return -EINVAL; + } + + std::scoped_lock lk{m_lock}; + + if (m_numBreakpoints == MAX_SW_BREAKPOINTS) { + return -EBUSY; + } + + size_t id = FindClosest(addr); + if (id != m_numBreakpoints && m_breakpoints[id].uid != 0) { + return -EEXIST; + } + + // Insert + for(size_t i = m_numBreakpoints; i > id && i != 0; i--) { + m_breakpoints[i] = m_breakpoints[i - 1]; + } + ++m_numBreakpoints; + + Breakpoint &bp = m_breakpoints[id]; + bp.address = addr; + bp.persistent = persistent; + bp.applied = false; + bp.uid = static_cast(0x2000 + m_bpUniqueCounter++); + + return Apply(id) ? 0 : -EFAULT; + } + + int SwBreakpointManager::Remove(uintptr_t addr, bool keepPersistent) + { + if ((addr & 3) != 0) { + return -EINVAL; + } + + std::scoped_lock lk{m_lock}; + + if (m_numBreakpoints == MAX_SW_BREAKPOINTS) { + return -EBUSY; + } + + size_t id = FindClosest(addr); + if (id == m_numBreakpoints || m_breakpoints[id].uid == 0) { + return -ENOENT; + } + + Breakpoint &bp = m_breakpoints[id]; + bool ok = true; + if (!keepPersistent || !bp.persistent) { + ok = Revert(id); + } + + for(size_t i = id; i < m_numBreakpoints - 1; i++) { + m_breakpoints[i] = m_breakpoints[i + 1]; + } + + m_breakpoints[--m_numBreakpoints] = {}; + + return ok ? 0 : -EFAULT; + } + + int SwBreakpointManager::RemoveAll(bool keepPersistent) + { + std::scoped_lock lk{m_lock}; + + bool ok = true; + for (size_t id = 0; id < m_numBreakpoints; id++) { + Breakpoint &bp = m_breakpoints[id]; + if (!keepPersistent || !bp.persistent) { + ok = ok && Revert(id); + } + } + + std::fill_n(m_breakpoints.begin(), m_breakpoints.end(), Breakpoint{}); + m_numBreakpoints = 0; + m_bpUniqueCounter = 0; + + return ok ? 0 : -EFAULT; + } + + +} \ No newline at end of file diff --git a/thermosphere/src/hvisor_sw_breakpoint_manager.hpp b/thermosphere/src/hvisor_sw_breakpoint_manager.hpp new file mode 100644 index 000000000..4d8a9e7fe --- /dev/null +++ b/thermosphere/src/hvisor_sw_breakpoint_manager.hpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2019-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "defines.hpp" +#include "hvisor_synchronization.hpp" + +#define MAX_SW_BREAKPOINTS 16 + +namespace ams::hvisor { + + class SwBreakpointManager { + NON_COPYABLE(SwBreakpointManager); + NON_MOVEABLE(SwBreakpointManager); + private: + struct Breakpoint { + uintptr_t address; + u32 savedInstruction; + u16 uid; + bool persistent; + bool applied; + }; + + private: + static SwBreakpointManager instance; + private: + mutable RecursiveSpinlock m_lock{}; + std::atomic m_triedToApplyOrRevertBreakpoint{}; + + u32 m_bpUniqueCounter = 0; + size_t m_numBreakpoints = 0; + std::array m_breakpoints{}; + + private: + size_t FindClosest(uintptr_t addr) const; + + bool DoApply(size_t id); + bool DoRevert(size_t id); + + // TODO apply, revert handler + bool Apply(size_t id); + bool Revert(size_t id); + + public: + static SwBreakpointManager &GetInstance() { return instance; } + + public: + int Add(uintptr_t addr, bool persistent); + int Remove(uintptr_t addr, bool keepPersistent); + int RemoveAll(bool keepPersistent); + + public: + constexpr SwBreakpointManager() = default; + }; +} diff --git a/thermosphere/src/hvisor_watchpoint_manager.cpp b/thermosphere/src/hvisor_watchpoint_manager.cpp index 7caa24d9b..8dce1b6c0 100644 --- a/thermosphere/src/hvisor_watchpoint_manager.cpp +++ b/thermosphere/src/hvisor_watchpoint_manager.cpp @@ -22,6 +22,7 @@ #include namespace { + constexpr bool IsRangeMaskWatchpoint(uintptr_t addr, size_t size) { // size needs to be a power of 2, at least 8 (we'll only allow 16+ though), addr needs to be aligned. @@ -39,6 +40,7 @@ namespace { return ((addr + size) & ~7ul) == (addr & ~7ul); } } + } namespace ams::hvisor { diff --git a/thermosphere/src/hvisor_watchpoint_manager.hpp b/thermosphere/src/hvisor_watchpoint_manager.hpp index 4cbaac6d1..a0bf1373a 100644 --- a/thermosphere/src/hvisor_watchpoint_manager.hpp +++ b/thermosphere/src/hvisor_watchpoint_manager.hpp @@ -26,6 +26,8 @@ namespace ams::hvisor { private: static WatchpointManager instance; + public: + static WatchpointManager &GetInstance() { return instance; } public: virtual void ReloadOnAllCores() const; @@ -35,8 +37,6 @@ namespace ams::hvisor { int Add(uintptr_t addr, size_t size, cpu::DebugRegisterPair::LoadStoreControl direction); int Remove(uintptr_t addr, size_t size, cpu::DebugRegisterPair::LoadStoreControl direction); - WatchpointManager &GetInstance() { return instance; } - public: constexpr WatchpointManager() : HwStopPointManager(MAX_WCR) {} };