// SPDX-License-Identifier: MPL-2.0 // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "nce/guest_common.h" namespace skyline { using KHandle = u32; //!< The type of a kernel handle namespace constant { // Memory constexpr auto BaseAddress = 0x8000000; //!< The address space base constexpr auto DefStackSize = 0x1E8480; //!< The default amount of stack: 2 MB // Kernel constexpr std::pair AndroidPriority = {19, -8}; //!< The range of priority for Android constexpr std::pair SwitchPriority = {0, 63}; //!< The range of priority for the Nintendo Switch // Display constexpr auto HandheldResolutionW = 1280; //!< The width component of the handheld resolution constexpr auto HandheldResolutionH = 720; //!< The height component of the handheld resolution constexpr auto DockedResolutionW = 1920; //!< The width component of the docked resolution constexpr auto DockedResolutionH = 1080; //!< The height component of the docked resolution // Status codes namespace status { constexpr u32 Success = 0x0; //!< "Success" constexpr u32 NoMessages = 0x680; //!< "No message available" constexpr u32 ServiceInvName = 0xC15; //!< "Invalid name" constexpr u32 ServiceNotReg = 0xE15; //!< "Service not registered" constexpr u32 InvSize = 0xCA01; //!< "Invalid size" constexpr u32 InvAddress = 0xCC01; //!< "Invalid address" constexpr u32 InvState = 0xD401; //!< "Invalid MemoryState" constexpr u32 InvPermission = 0xD801; //!< "Invalid Permission" constexpr u32 InvMemRange = 0xD801; //!< "Invalid Memory Range" constexpr u32 InvPriority = 0xE001; //!< "Invalid Priority" constexpr u32 InvHandle = 0xE401; //!< "Invalid handle" constexpr u32 InvCombination = 0xE801; //!< "Invalid combination" constexpr u32 Timeout = 0xEA01; //!< "Timeout" constexpr u32 Interrupted = 0xEC01; //!< "Interrupted" constexpr u32 MaxHandles = 0xEE01; //!< "Too many handles" constexpr u32 NotFound = 0xF201; //!< "Not found" constexpr u32 Unimpl = 0x177202; //!< "Unimplemented behaviour" } }; /** * @brief This enumerates the types of the ROM * @note This needs to be synchronized with emu.skyline.loader.BaseLoader.TitleFormat */ enum class TitleFormat { NRO, //!< The NRO format: https://switchbrew.org/wiki/NRO XCI, //!< The XCI format: https://switchbrew.org/wiki/XCI NSP, //!< The NSP format from "nspwn" exploit: https://switchbrew.org/wiki/Switch_System_Flaws }; namespace util { /** * @brief Returns the current time in nanoseconds * @return The current time in nanoseconds */ inline u64 GetTimeNs() { constexpr uint64_t nsInSecond = 1000000000; static u64 frequency{}; if (!frequency) asm("MRS %0, CNTFRQ_EL0" : "=r"(frequency)); u64 ticks; asm("MRS %0, CNTVCT_EL0" : "=r"(ticks)); return ((ticks / frequency) * nsInSecond) + (((ticks % frequency) * nsInSecond + (frequency / 2)) / frequency); } /** * @brief Aligns up a value to a multiple of two * @tparam Type The type of the values * @param value The value to round up * @param multiple The multiple to round up to (Should be a multiple of 2) * @tparam TypeVal The type of the value * @tparam TypeMul The type of the multiple * @return The aligned value */ template constexpr inline TypeVal AlignUp(TypeVal value, TypeMul multiple) { static_assert(std::is_integral() && std::is_integral()); multiple--; return (value + multiple) & ~(multiple); } /** * @brief Aligns down a value to a multiple of two * @param value The value to round down * @param multiple The multiple to round down to (Should be a multiple of 2) * @tparam TypeVal The type of the value * @tparam TypeMul The type of the multiple * @return The aligned value */ template constexpr inline TypeVal AlignDown(TypeVal value, TypeMul multiple) { static_assert(std::is_integral() && std::is_integral()); return value & ~(multiple - 1); } /** * @param address The address to check for alignment * @return If the address is page aligned */ constexpr inline bool PageAligned(u64 address) { return !(address & (PAGE_SIZE - 1U)); } /** * @param address The address to check for alignment * @return If the address is word aligned */ constexpr inline bool WordAligned(u64 address) { return !(address & 3U); } /** * @param string The string to create a magic from * @return The magic of the supplied string */ template constexpr Type MakeMagic(std::string_view string) { Type object{}; auto offset = 0; for (auto &character : string) { object |= static_cast(character) << offset; offset += sizeof(character) * 8; } return object; } } /** * @brief The Mutex class is a wrapper around an atomic bool used for synchronization */ class Mutex { std::atomic_flag flag = ATOMIC_FLAG_INIT; //!< An atomic flag to hold the state of the mutex public: /** * @brief Wait on and lock the mutex */ void lock(); /** * @brief Try to lock the mutex if it is unlocked else return * @return If the mutex was successfully locked or not */ inline bool try_lock() { return !flag.test_and_set(std::memory_order_acquire); } /** * @brief Unlock the mutex if it is held by this thread */ inline void unlock() { flag.clear(std::memory_order_release); } }; /** * @brief The GroupMutex class is a special type of mutex that allows two groups of users and only allows one group to run in parallel */ class GroupMutex { public: /** * @brief This enumeration holds all the possible owners of the mutex */ enum class Group : u8 { None = 0, //!< No group owns this mutex Group1 = 1, //!< Group 1 owns this mutex Group2 = 2 //!< Group 2 owns this mutex }; /** * @brief Wait on and lock the mutex */ void lock(Group group = Group::Group1); /** * @brief Unlock the mutex * @note Undefined behavior in case unlocked by thread in non-owner group */ void unlock(); private: std::atomic flag{Group::None}; //!< An atomic flag to hold which group holds the mutex std::atomic next{Group::None}; //!< An atomic flag to hold which group will hold the mutex next std::atomic num{0}; //!< An atomic u8 keeping track of how many users are holding the mutex Mutex mtx; //!< A mutex to lock before changing of num and flag }; /** * @brief The Logger class is to write log output to file and logcat */ class Logger { private: std::ofstream logFile; //!< An output stream to the log file const char *levelStr[4] = {"0", "1", "2", "3"}; //!< This is used to denote the LogLevel when written out to a file static constexpr int levelSyslog[4] = {LOG_ERR, LOG_WARNING, LOG_INFO, LOG_DEBUG}; //!< This corresponds to LogLevel and provides it's equivalent for syslog Mutex mtx; //!< A mutex to lock before logging anything public: enum class LogLevel { Error, Warn, Info, Debug }; //!< The level of a particular log LogLevel configLevel; //!< The level of logs to write /** * @param logFd A FD to the log file * @param configLevel The minimum level of logs to write */ Logger(const int logFd, LogLevel configLevel); /** * @brief Writes the termination message to the log file */ ~Logger(); /** * @brief Writes a header, should only be used for emulation starting and ending * @param str The value to be written */ void WriteHeader(const std::string &str); /** * @brief Write a log to the log file * @param level The level of the log * @param str The value to be written */ void Write(const LogLevel level, std::string str); /** * @brief Write an error log with libfmt formatting * @param formatStr The value to be written, with libfmt formatting * @param args The arguments based on format_str */ template inline void Error(const S &formatStr, Args &&... args) { if (LogLevel::Error <= configLevel) { Write(LogLevel::Error, fmt::format(formatStr, args...)); } } /** * @brief Write a debug log with libfmt formatting * @param formatStr The value to be written, with libfmt formatting * @param args The arguments based on format_str */ template inline void Warn(const S &formatStr, Args &&... args) { if (LogLevel::Warn <= configLevel) { Write(LogLevel::Warn, fmt::format(formatStr, args...)); } } /** * @brief Write a debug log with libfmt formatting * @param formatStr The value to be written, with libfmt formatting * @param args The arguments based on format_str */ template inline void Info(const S &formatStr, Args &&... args) { if (LogLevel::Info <= configLevel) { Write(LogLevel::Info, fmt::format(formatStr, args...)); } } /** * @brief Write a debug log with libfmt formatting * @param formatStr The value to be written, with libfmt formatting * @param args The arguments based on format_str */ template inline void Debug(const S &formatStr, Args &&... args) { if (LogLevel::Debug <= configLevel) { Write(LogLevel::Debug, fmt::format(formatStr, args...)); } } }; /** * @brief The Settings class is used to access the parameters set in the Java component of the application */ class Settings { private: std::map stringMap; //!< A mapping from all keys to their corresponding string value std::map boolMap; //!< A mapping from all keys to their corresponding boolean value std::map intMap; //!< A mapping from all keys to their corresponding integer value public: /** * @param preferenceFd An FD to the preference XML file */ Settings(const int preferenceFd); /** * @brief Retrieves a particular setting as a string * @param key The key of the setting * @return The string value of the setting */ std::string GetString(const std::string &key); /** * @brief Retrieves a particular setting as a boolean * @param key The key of the setting * @return The boolean value of the setting */ bool GetBool(const std::string &key); /** * @brief Retrieves a particular setting as a integer * @param key The key of the setting * @return The integer value of the setting */ int GetInt(const std::string &key); /** * @brief Writes all settings keys and values to syslog. This function is for development purposes. */ void List(const std::shared_ptr &logger); }; /** * @brief This is a std::runtime_error with libfmt formatting */ class exception : public std::runtime_error { public: /** * @param formatStr The exception string to be written, with libfmt formatting * @param args The arguments based on format_str */ template inline exception(const S &formatStr, Args &&... args) : runtime_error(fmt::format(formatStr, args...)) {} }; class NCE; class JvmManager; namespace gpu { class GPU; } namespace kernel { namespace type { class KProcess; class KThread; } class OS; } namespace audio { class Audio; } /** * @brief This struct is used to hold the state of a device */ struct DeviceState { DeviceState(kernel::OS *os, std::shared_ptr &process, std::shared_ptr jvmManager, std::shared_ptr settings, std::shared_ptr logger); kernel::OS *os; //!< This holds a reference to the OS class std::shared_ptr &process; //!< This holds a reference to the process object thread_local static std::shared_ptr thread; //!< This holds a reference to the current thread object thread_local static ThreadContext *ctx; //!< This holds the context of the thread std::shared_ptr nce; //!< This holds a reference to the NCE class std::shared_ptr gpu; //!< This holds a reference to the GPU class std::shared_ptr audio; //!< This holds a reference to the Audio class std::shared_ptr jvm; //!< This holds a reference to the JvmManager class std::shared_ptr settings; //!< This holds a reference to the Settings class std::shared_ptr logger; //!< This holds a reference to the Logger class }; }