diff --git a/thermosphere/src/cpu/hvisor_cpu_debug_register_pair.hpp b/thermosphere/src/cpu/hvisor_cpu_debug_register_pair.hpp
index 9c0d6dafc..1595d195d 100644
--- a/thermosphere/src/cpu/hvisor_cpu_debug_register_pair.hpp
+++ b/thermosphere/src/cpu/hvisor_cpu_debug_register_pair.hpp
@@ -63,9 +63,10 @@ namespace ams::hvisor::cpu {
// Watchpoints only
enum LoadStoreControl {
- Load = 1,
- Store = 2,
- LoadStore = 3,
+ NotAWatchpoint = 0,
+ Load = 1,
+ Store = 2,
+ LoadStore = 3,
};
// bas only 4 bits for breakpoints, other bits res0.
diff --git a/thermosphere/src/hvisor_hw_breakpoint_manager.cpp b/thermosphere/src/hvisor_hw_breakpoint_manager.cpp
new file mode 100644
index 000000000..f26acf5c5
--- /dev/null
+++ b/thermosphere/src/hvisor_hw_breakpoint_manager.cpp
@@ -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 .
+ */
+
+#include "hvisor_hw_breakpoint_manager.hpp"
+#include "cpu/hvisor_cpu_instructions.hpp"
+
+#define _REENT_ONLY
+#include
+
+namespace ams::hvisor {
+
+ HwBreakpointManager HwBreakpointManager::instance{};
+
+ void HwBreakpointManager::ReloadOnAllCoresSgiHandler()
+ {
+ // TODO
+ }
+
+ void HwBreakpointManager::ReloadOnAllCores() const
+ {
+ cpu::dmb();
+ // TODO
+ }
+
+ bool HwBreakpointManager::FindPredicate(const cpu::DebugRegisterPair &pair, uintptr_t addr, size_t, cpu::DebugRegisterPair::LoadStoreControl) const
+ {
+ return pair.vr == addr;
+ }
+
+ // Note: A32/T32/T16 support intentionnally left out
+ // Note: addresses are supposed to be well-formed regarding the sign extension bits
+ int HwBreakpointManager::Add(uintptr_t addr)
+ {
+ // Reject misaligned addresses
+ if (addr & 3) {
+ return -EINVAL;
+ }
+
+ cpu::DebugRegisterPair bp{};
+ bp.cr.bt = cpu::DebugRegisterPair::AddressMatch;
+ bp.cr.bas = 0xF; // mandated
+ bp.vr = addr;
+
+ return AddImpl(addr, 0, bp);
+ }
+
+ int HwBreakpointManager::Remove(uintptr_t addr)
+ {
+ // Reject misaligned addresses
+ if (addr & 3) {
+ return -EINVAL;
+ }
+
+ return RemoveImpl(addr, 0, cpu::DebugRegisterPair::NotAWatchpoint);
+ }
+}
diff --git a/thermosphere/src/hvisor_hw_breakpoint_manager.hpp b/thermosphere/src/hvisor_hw_breakpoint_manager.hpp
new file mode 100644
index 000000000..b24fcc464
--- /dev/null
+++ b/thermosphere/src/hvisor_hw_breakpoint_manager.hpp
@@ -0,0 +1,43 @@
+/*
+ * 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 "hvisor_hw_stop_point_manager.hpp"
+
+namespace ams::hvisor {
+
+ class HwBreakpointManager final : public HwStopPointManager {
+ protected:
+ virtual bool FindPredicate(const cpu::DebugRegisterPair &pair, uintptr_t addr, size_t, cpu::DebugRegisterPair::LoadStoreControl) const;
+
+ private:
+ static HwBreakpointManager instance;
+
+ public:
+ virtual void ReloadOnAllCores() const;
+ static void ReloadOnAllCoresSgiHandler();
+
+ cpu::DebugRegisterPair RetrieveWatchpointConfig(uintptr_t addr, cpu::DebugRegisterPair::LoadStoreControl direction) const;
+ 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_hw_stop_point_manager.cpp b/thermosphere/src/hvisor_hw_stop_point_manager.cpp
new file mode 100644
index 000000000..7c501de88
--- /dev/null
+++ b/thermosphere/src/hvisor_hw_stop_point_manager.cpp
@@ -0,0 +1,104 @@
+/*
+ * 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_hw_stop_point_manager.hpp"
+#include
+
+#define _REENT_ONLY
+#include
+
+namespace ams::hvisor {
+
+ cpu::DebugRegisterPair *HwStopPointManager::Allocate()
+ {
+ size_t pos = __builtin_ffs(m_freeBitmap);
+ if (pos == 0) {
+ return nullptr;
+ } else {
+ m_freeBitmap &= ~BIT(pos - 1);
+ m_usedBitmap |= BIT(pos - 1);
+ return &m_stopPoints[pos - 1];
+ }
+ }
+
+ void HwStopPointManager::Free(size_t pos)
+ {
+ m_stopPoints[pos] = {};
+ m_freeBitmap |= BIT(pos);
+ m_usedBitmap &= ~BIT(pos);
+ }
+
+ const cpu::DebugRegisterPair *HwStopPointManager::Find(uintptr_t addr, size_t size, cpu::DebugRegisterPair::LoadStoreControl dir) const
+ {
+ for (auto bit: util::BitsOf{m_usedBitmap}) {
+ auto *p = &m_stopPoints[bit];
+ if (FindPredicate(*p, addr, size, dir)) {
+ return p;
+ }
+ }
+
+ return nullptr;
+ }
+
+ int HwStopPointManager::AddImpl(uintptr_t addr, size_t size, cpu::DebugRegisterPair preconfiguredPair)
+ {
+ std::scoped_lock lk{m_lock};
+ auto lsc = preconfiguredPair.cr.lsc;
+
+ if (m_freeBitmap == 0) {
+ // Oops
+ return -EBUSY;
+ }
+
+ if (Find(addr, size, lsc) != nullptr) {
+ // Already exists
+ return -EEXIST;
+ }
+
+ auto *regs = Allocate();
+ regs->SetDefaults();
+
+ // Apply preconfig
+ regs->cr.raw |= preconfiguredPair.cr.raw;
+ regs->vr = preconfiguredPair.vr;
+ regs->cr.enabled = true;
+
+ ReloadOnAllCores();
+ return 0;
+ }
+
+ int HwStopPointManager::RemoveImpl(uintptr_t addr, size_t size, cpu::DebugRegisterPair::LoadStoreControl dir)
+ {
+ std::scoped_lock lk{m_lock};
+ const auto *p = Find(addr, size, dir);
+ if (p == nullptr) {
+ return -ENOENT;
+ }
+
+ Free(p - m_stopPoints.data());
+ return 0;
+ }
+
+ void HwStopPointManager::RemoveAll()
+ {
+ std::scoped_lock lk{m_lock};
+ m_freeBitmap |= m_usedBitmap;
+ m_usedBitmap = 0;
+ std::fill(m_stopPoints.begin(), m_stopPoints.end(), cpu::DebugRegisterPair{});
+ ReloadOnAllCores();
+ }
+
+}
diff --git a/thermosphere/src/hvisor_hw_stop_point_manager.hpp b/thermosphere/src/hvisor_hw_stop_point_manager.hpp
new file mode 100644
index 000000000..350181298
--- /dev/null
+++ b/thermosphere/src/hvisor_hw_stop_point_manager.hpp
@@ -0,0 +1,53 @@
+/*
+ * 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"
+#include "cpu/hvisor_cpu_debug_register_pair.hpp"
+
+namespace ams::hvisor {
+
+ class HwStopPointManager {
+ NON_COPYABLE(HwStopPointManager);
+ NON_MOVEABLE(HwStopPointManager);
+ protected:
+ static constexpr size_t maxStopPoints = std::max(MAX_BCR, MAX_WCR);
+
+ mutable RecursiveSpinlock m_lock{};
+ u16 m_freeBitmap;
+ u16 m_usedBitmap = 0;
+ std::array m_stopPoints{};
+
+ protected:
+ cpu::DebugRegisterPair *Allocate();
+ void Free(size_t pos);
+ const cpu::DebugRegisterPair *Find(uintptr_t addr, size_t size, cpu::DebugRegisterPair::LoadStoreControl dir) const;
+
+ virtual bool FindPredicate(const cpu::DebugRegisterPair &pair, uintptr_t addr, size_t size, cpu::DebugRegisterPair::LoadStoreControl direction) const = 0;
+
+ int AddImpl(uintptr_t addr, size_t size, cpu::DebugRegisterPair preconfiguredPair);
+ int RemoveImpl(uintptr_t addr, size_t size, cpu::DebugRegisterPair::LoadStoreControl direction);
+
+ protected:
+ constexpr HwStopPointManager(size_t numStopPoints) : m_freeBitmap(MASK(numStopPoints)) {}
+
+ public:
+ virtual void ReloadOnAllCores() const = 0;
+ void RemoveAll();
+ };
+}
diff --git a/thermosphere/src/hvisor_synchronization.hpp b/thermosphere/src/hvisor_synchronization.hpp
index d87ee207d..8f3a91581 100644
--- a/thermosphere/src/hvisor_synchronization.hpp
+++ b/thermosphere/src/hvisor_synchronization.hpp
@@ -27,7 +27,7 @@ namespace ams::hvisor {
private:
u32 m_val = 0;
public:
- Spinlock() = default;
+ constexpr Spinlock() = default;
void lock();
void unlock() noexcept;
};
@@ -38,7 +38,7 @@ namespace ams::hvisor {
private:
u32 m_val = 0;
public:
- Barrier() = default;
+ constexpr Barrier() = default;
void Join();
constexpr void Reset(u32 val)
@@ -55,7 +55,7 @@ namespace ams::hvisor {
u32 m_tag = 0;
u32 m_count = 0;
public:
- RecursiveSpinlock() = default;
+ constexpr RecursiveSpinlock() = default;
void lock();
void unlock() noexcept;
};
diff --git a/thermosphere/src/hvisor_watchpoint_manager.cpp b/thermosphere/src/hvisor_watchpoint_manager.cpp
new file mode 100644
index 000000000..7caa24d9b
--- /dev/null
+++ b/thermosphere/src/hvisor_watchpoint_manager.cpp
@@ -0,0 +1,126 @@
+/*
+ * 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_watchpoint_manager.hpp"
+#include "cpu/hvisor_cpu_instructions.hpp"
+#include
+
+#define _REENT_ONLY
+#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.
+ bool ret = (size & (size - 1)) == 0 && size >= 16 && (addr & (size - 1)) == 0;
+ return ret;
+ }
+
+ constexpr bool CheckWatchpointAddressAndSizeParams(uintptr_t addr, size_t size)
+ {
+ if (size == 0) {
+ return false;
+ } else if (size > 8) {
+ return IsRangeMaskWatchpoint(addr, size);
+ } else {
+ return ((addr + size) & ~7ul) == (addr & ~7ul);
+ }
+ }
+}
+
+namespace ams::hvisor {
+
+ WatchpointManager WatchpointManager::instance{};
+
+ void WatchpointManager::ReloadOnAllCoresSgiHandler()
+ {
+ // TODO
+ }
+
+ void WatchpointManager::ReloadOnAllCores() const
+ {
+ cpu::dmb();
+ // TODO
+ }
+
+ bool WatchpointManager::FindPredicate(const cpu::DebugRegisterPair &pair, uintptr_t addr, size_t size, cpu::DebugRegisterPair::LoadStoreControl direction) const
+ {
+ size_t off;
+ size_t sz;
+ size_t nmask;
+
+ if (pair.cr.mask != 0) {
+ off = 0;
+ sz = MASK(pair.cr.mask);
+ nmask = ~sz;
+ } else {
+ off = __builtin_ffs(pair.cr.bas) - 1;
+ sz = __builtin_popcount(pair.cr.bas);
+ nmask = ~7ul;
+ }
+
+ if (size != 0) {
+ // Strict watchpoint check
+ if (addr == pair.vr + off && direction == pair.cr.lsc && sz == size) {
+ return true;
+ }
+ } else {
+ // Return first wp that could have triggered the exception
+ if ((addr & nmask) == pair.vr && (direction & pair.cr.lsc) != 0) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ cpu::DebugRegisterPair WatchpointManager::RetrieveWatchpointConfig(uintptr_t addr, cpu::DebugRegisterPair::LoadStoreControl direction) const
+ {
+ std::scoped_lock lk{m_lock};
+ const cpu::DebugRegisterPair *p = Find(addr, 0, direction);
+ return p != nullptr ? *p : cpu::DebugRegisterPair{};
+ }
+
+ int WatchpointManager::Add(uintptr_t addr, size_t size, cpu::DebugRegisterPair::LoadStoreControl direction)
+ {
+ if (!CheckWatchpointAddressAndSizeParams(addr, size)) {
+ return -EINVAL;
+ }
+
+ cpu::DebugRegisterPair wp{};
+ wp.cr.lsc = direction;
+ if (IsRangeMaskWatchpoint(addr, size)) {
+ wp.vr = addr;
+ wp.cr.bas = 0xFF; // TRM-mandated
+ wp.cr.mask = static_cast(__builtin_ffsl(size) - 1);
+ } else {
+ size_t off = addr & 7ull;
+ wp.vr = addr & ~7ul;
+ wp.cr.bas = MASK2(off + size, off);
+ }
+
+ return AddImpl(addr, size, wp);
+ }
+
+ int WatchpointManager::Remove(uintptr_t addr, size_t size, cpu::DebugRegisterPair::LoadStoreControl direction)
+ {
+ if (!CheckWatchpointAddressAndSizeParams(addr, size)) {
+ return -EINVAL;
+ }
+
+ return RemoveImpl(addr, size, direction);
+ }
+}
diff --git a/thermosphere/src/hvisor_watchpoint_manager.hpp b/thermosphere/src/hvisor_watchpoint_manager.hpp
new file mode 100644
index 000000000..4cbaac6d1
--- /dev/null
+++ b/thermosphere/src/hvisor_watchpoint_manager.hpp
@@ -0,0 +1,43 @@
+/*
+ * 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 "hvisor_hw_stop_point_manager.hpp"
+
+namespace ams::hvisor {
+
+ class WatchpointManager final : public HwStopPointManager {
+ protected:
+ virtual bool FindPredicate(const cpu::DebugRegisterPair &pair, uintptr_t addr, size_t size, cpu::DebugRegisterPair::LoadStoreControl direction) const;
+
+ private:
+ static WatchpointManager instance;
+
+ public:
+ virtual void ReloadOnAllCores() const;
+ static void ReloadOnAllCoresSgiHandler();
+
+ cpu::DebugRegisterPair RetrieveWatchpointConfig(uintptr_t addr, cpu::DebugRegisterPair::LoadStoreControl direction) const;
+ 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) {}
+ };
+}