diff --git a/thermosphere/src/cpu/hvisor_cpu_instructions.hpp b/thermosphere/src/cpu/hvisor_cpu_instructions.hpp new file mode 100644 index 000000000..6a454f113 --- /dev/null +++ b/thermosphere/src/cpu/hvisor_cpu_instructions.hpp @@ -0,0 +1,67 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "../defines.hpp" + +#define _ASM_ARITHMETIC_UNARY_HELPER(sz, regalloc, op) ({\ + u##sz res;\ + __asm__ __volatile__ (STRINGIZE(op) " %" STRINGIZE(regalloc) "[res], %" STRINGIZE(regalloc) "[val]" : [res] "=r" (res) : [val] "r" (val));\ + res;\ +}) + +#define DECLARE_SINGLE_ASM_INSN2(name, what) static inline void name() { __asm__ __volatile__ (what ::: "memory"); } +#define DECLARE_SINGLE_ASM_INSN(name) static inline void name() { __asm__ __volatile__ (STRINGIZE(name) ::: "memory"); } + +namespace ams::hvisor::cpu { + + + template<typename T> + ALWAYS_INLINE static T rbit(T val) + { + static_assert(std::is_integral_v<T> && (sizeof(T) == 8 || sizeof(T) == 4)); + if constexpr (sizeof(T) == 8) { + return _ASM_ARITHMETIC_UNARY_HELPER(64, x, rbit); + } else { + return _ASM_ARITHMETIC_UNARY_HELPER(32, w, rbit); + } + } + + DECLARE_SINGLE_ASM_INSN(wfi) + DECLARE_SINGLE_ASM_INSN(wfe) + DECLARE_SINGLE_ASM_INSN(sevl) + DECLARE_SINGLE_ASM_INSN(sev) + DECLARE_SINGLE_ASM_INSN2(dmb, "dmb ish") + DECLARE_SINGLE_ASM_INSN2(dmbSy, "dmb sy") + DECLARE_SINGLE_ASM_INSN2(dsb, "dsb ish") + DECLARE_SINGLE_ASM_INSN2(dsbSy, "dsb sy") + DECLARE_SINGLE_ASM_INSN(isb) + + DECLARE_SINGLE_ASM_INSN2(TlbInvalidateEl2, "tlbi alle2is") + DECLARE_SINGLE_ASM_INSN2(TlbInvalidateEl1, "tlbi vmalle1is") + DECLARE_SINGLE_ASM_INSN2(TlbInvalidateEl1Stage12, "tlbi alle1is") + + ALWAYS_INLINE static void TlbInvalidateEl2Page(uintptr_t addr) + { + __asm__ __volatile__ ("tlbi vae2is, %0" :: "r"(addr) : "memory"); + } + +} + +#undef DECLARE_SINGLE_ASM_INSN +#undef DECLARE_SINGLE_ASM_INSN2 +#undef _ASM_ARITHMETIC_UNARY_HELPER diff --git a/thermosphere/src/defines.hpp b/thermosphere/src/defines.hpp index 05aa8b135..d715c0142 100644 --- a/thermosphere/src/defines.hpp +++ b/thermosphere/src/defines.hpp @@ -24,4 +24,5 @@ #include <tuple> #include <array> +#include "preprocessor.h" #include "debug_log.h" diff --git a/thermosphere/src/hvisor_synchronization.cpp b/thermosphere/src/hvisor_synchronization.cpp new file mode 100644 index 000000000..84d302317 --- /dev/null +++ b/thermosphere/src/hvisor_synchronization.cpp @@ -0,0 +1,95 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "hvisor_synchronization.hpp" +#include "core_ctx.h" + +namespace ams::hvisor { + + void Spinlock::lock() + { + u32 tmp1; + const u32 tmp2 = 1; + __asm__ __volatile__( + "prfm pstl1keep, %[val] \n" + "sevl \n" + "1: \n" + " wfe \n" + " 2: \n" + " ldaxr %[tmp1], %[val] \n" + " cbnz %[tmp1], 1b \n" + " stxr %[tmp1], %[tmp2], %[val] \n" + " cbnz %[tmp1], 2b \n" + : [tmp1] "=&r"(tmp1), [val] "+Q" (m_val) + : [tmp2] "r"(tmp2) + : "cc", "memory" + ); + } + + void Spinlock::unlock() noexcept + { + __asm__ __volatile__("stlr wzr, %[val]" : [val] "=Q" (m_val) :: "memory"); + } + + void Barrier::Join() + { + const u32 mask = BIT(currentCoreCtx->coreId); + u32 newval, tmp; + __asm__ __volatile__( + "prfm pstl1keep, %[val] \n" + + /* Fetch-and */ + "1: \n" + " ldaxr %[newval], %[val] \n" + " bic %[newval], %[newval], %[mask] \n" + " stlxr %[tmp], %[newval], %[val] \n" + " cbnz %[tmp], 1b \n" + + /* Check if now/already 0, wait if not */ + "cbz %[newval], 3f \n" + + /* Event will be signaled if the stlxr succeeds for another core... */ + "2: \n" + " wfe \n" + " ldaxr %[newval], %[val] \n" + " cbnz %[newval], 2b \n" + "3: \n" + : [newval] "=&r"(newval), [tmp] "=&r" (tmp), [val] "+Q" (m_val) + : [mask] "r"(mask) + : "cc", "memory" + ); + } + + void RecursiveSpinlock::lock() + { + u32 tag = currentCoreCtx->coreId + 1; + if (AMS_LIKELY(tag != m_tag)) { + m_spinlock.lock(); + m_tag = tag; + m_count = 1; + } else { + ++m_count; + } + } + + void RecursiveSpinlock::unlock() noexcept + { + if (AMS_LIKELY(--m_count == 0)) { + m_tag = 0; + m_spinlock.unlock(); + } + } +} diff --git a/thermosphere/src/hvisor_synchronization.hpp b/thermosphere/src/hvisor_synchronization.hpp new file mode 100644 index 000000000..d87ee207d --- /dev/null +++ b/thermosphere/src/hvisor_synchronization.hpp @@ -0,0 +1,63 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "defines.hpp" + + +namespace ams::hvisor { + + class Spinlock final { + NON_COPYABLE(Spinlock); + NON_MOVEABLE(Spinlock); + private: + u32 m_val = 0; + public: + Spinlock() = default; + void lock(); + void unlock() noexcept; + }; + + class Barrier final { + NON_COPYABLE(Barrier); + NON_MOVEABLE(Barrier); + private: + u32 m_val = 0; + public: + Barrier() = default; + void Join(); + + constexpr void Reset(u32 val) + { + m_val = val; + } + }; + + class RecursiveSpinlock final { + NON_COPYABLE(RecursiveSpinlock); + NON_MOVEABLE(RecursiveSpinlock); + private: + Spinlock m_spinlock{}; + u32 m_tag = 0; + u32 m_count = 0; + public: + RecursiveSpinlock() = default; + void lock(); + void unlock() noexcept; + }; + +}