diff --git a/libraries/libmesosphere/include/mesosphere.hpp b/libraries/libmesosphere/include/mesosphere.hpp index 98a126a9f..3029090b1 100644 --- a/libraries/libmesosphere/include/mesosphere.hpp +++ b/libraries/libmesosphere/include/mesosphere.hpp @@ -29,7 +29,7 @@ /* Core pre-initialization includes. */ #include -#include +#include /* Initialization headers. */ #include diff --git a/libraries/libmesosphere/include/mesosphere/board/nintendo/switch/kern_k_system_control.hpp b/libraries/libmesosphere/include/mesosphere/board/nintendo/switch/kern_k_system_control.hpp index 88e2b8374..2cd4c64af 100644 --- a/libraries/libmesosphere/include/mesosphere/board/nintendo/switch/kern_k_system_control.hpp +++ b/libraries/libmesosphere/include/mesosphere/board/nintendo/switch/kern_k_system_control.hpp @@ -36,6 +36,9 @@ namespace ams::kern { static u64 GenerateRandomRange(u64 min, u64 max); }; public: + /* Initialization. */ + static NOINLINE void Initialize(); + /* Randomness. */ static void GenerateRandomBytes(void *dst, size_t size); static u64 GenerateRandomRange(u64 min, u64 max); diff --git a/libraries/libmesosphere/include/mesosphere/kern_common.hpp b/libraries/libmesosphere/include/mesosphere/kern_common.hpp index 8f12b78f7..57ef9f117 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_common.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_common.hpp @@ -23,3 +23,12 @@ namespace ams::kern { constexpr size_t PageSize = 4_KB; } + +#ifdef MESOSPHERE_BUILD_FOR_AUDITING +#define MESOSPHERE_BUILD_FOR_DEBUGGING +#endif + +#ifdef MESOSPHERE_BUILD_FOR_DEBUGGING +#define MESOSPHERE_ENABLE_ASSERTIONS +#define MESOSPHERE_ENABLE_DEBUG_PRINT +#endif diff --git a/libraries/libmesosphere/include/mesosphere/kern_debug_log.hpp b/libraries/libmesosphere/include/mesosphere/kern_debug_log.hpp new file mode 100644 index 000000000..53352dcf7 --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/kern_debug_log.hpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018-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 + +namespace ams::kern { + + class KDebugLog { + private: + static NOINLINE void VSNPrintf(char *dst, const size_t dst_size, const char *format, ::std::va_list vl); + public: + static NOINLINE void Initialize(); + + static NOINLINE void Printf(const char *format, ...); + static NOINLINE void VPrintf(const char *format, ::std::va_list vl); + }; + +} + +#ifndef MESOSPHERE_DEBUG_LOG_SELECTED + + #ifdef ATMOSPHERE_BOARD_NINTENDO_SWITCH + #define MESOSPHERE_DEBUG_LOG_USE_UART_A + #else + #error "Unknown board for Default Debug Log Source" + #endif + + #define MESOSPHERE_DEBUG_LOG_SELECTED + +#endif + +#define MESOSPHERE_RELEASE_LOG(fmt, ...) ::ams::kern::KDebugLog::Printf((fmt), ## __VA_ARGS__) +#define MESOSPHERE_RELEASE_VLOG(fmt, vl) ::ams::kern::KDebugLog::VPrintf((fmt), (vl)) + +#ifdef MESOSPHERE_ENABLE_DEBUG_PRINT +#define MESOSPHERE_LOG(fmt, ...) MESOSPHERE_RELEASE_LOG((fmt), ## __VA_ARGS__) +#define MESOSPHERE_VLOG(fmt, vl) MESOSPHERE_RELEASE_VLOG((fmt), (vl)) +#else +#define MESOSPHERE_LOG(fmt, ...) +#define MESOSPHERE_VLOG(fmt, vl) +#endif diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp index f8bfcb59b..526070b75 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp @@ -204,6 +204,24 @@ namespace ams::kern { struct DerivedRegionExtents { const KMemoryRegion *first_region; const KMemoryRegion *last_region; + + constexpr DerivedRegionExtents() : first_region(nullptr), last_region(nullptr) { /* ... */ } + + constexpr ALWAYS_INLINE uintptr_t GetAddress() const { + return this->first_region->GetAddress(); + } + + constexpr ALWAYS_INLINE uintptr_t GetEndAddress() const { + return this->last_region->GetEndAddress(); + } + + constexpr ALWAYS_INLINE size_t GetSize() const { + return this->GetEndAddress() - this->GetAddress(); + } + + constexpr ALWAYS_INLINE uintptr_t GetLastAddress() const { + return this->GetEndAddress() - 1; + } }; private: using TreeType = util::IntrusiveRedBlackTreeBaseTraits::TreeType; @@ -258,7 +276,10 @@ namespace ams::kern { DerivedRegionExtents GetDerivedRegionExtents(u32 type_id) { - DerivedRegionExtents extents = { .first_region = nullptr, .last_region = nullptr }; + DerivedRegionExtents extents; + + MESOSPHERE_INIT_ABORT_UNLESS(extents.first_region == nullptr); + MESOSPHERE_INIT_ABORT_UNLESS(extents.last_region == nullptr); for (auto it = this->cbegin(); it != this->cend(); it++) { if (it->IsDerivedFrom(type_id)) { @@ -433,6 +454,14 @@ namespace ams::kern { return GetPhysicalMemoryRegionTree().FindFirstDerivedRegion(KMemoryRegionType_InterruptCpuInterface)->GetPairAddress(); } + static NOINLINE KVirtualAddress GetUartAddress() { + return GetPhysicalMemoryRegionTree().FindFirstDerivedRegion(KMemoryRegionType_Uart)->GetPairAddress(); + } + + static NOINLINE auto GetCarveoutRegionExtents() { + return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionAttr_CarveoutProtected); + } + static void InitializeLinearMemoryRegionTrees(KPhysicalAddress aligned_linear_phys_start, KVirtualAddress linear_virtual_start); }; diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_target_system.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_target_system.hpp new file mode 100644 index 000000000..123595b4c --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/kern_k_target_system.hpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018-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 + +namespace ams::kern { + + class KSystemControl; + + class KTargetSystem { + private: + friend class KSystemControl; + private: + static inline bool s_is_debug_mode; + static inline bool s_enable_debug_logging; + static inline bool s_enable_user_exception_handlers; + static inline bool s_enable_debug_memory_fill; + static inline bool s_enable_user_pmu_access; + static inline bool s_enable_kernel_debugging; + private: + static ALWAYS_INLINE void SetIsDebugMode(bool en) { s_is_debug_mode = en; } + static ALWAYS_INLINE void EnableDebugLogging(bool en) { s_enable_debug_logging = en; } + static ALWAYS_INLINE void EnableUserExceptionHandlers(bool en) { s_enable_user_exception_handlers = en; } + static ALWAYS_INLINE void EnableDebugMemoryFill(bool en) { s_enable_debug_memory_fill = en; } + static ALWAYS_INLINE void EnableUserPmuAccess(bool en) { s_enable_user_pmu_access = en; } + static ALWAYS_INLINE void EnableKernelDebugging(bool en) { s_enable_kernel_debugging = en; } + public: + static ALWAYS_INLINE bool IsDebugMode() { return s_is_debug_mode; } + static ALWAYS_INLINE bool IsDebugLoggingEnabled() { return s_enable_debug_logging; } + static ALWAYS_INLINE bool IsUserExceptionHandlersEnabled() { return s_enable_user_exception_handlers; } + static ALWAYS_INLINE bool IsDebugMemoryFillEnabled() { return s_enable_debug_memory_fill; } + static ALWAYS_INLINE bool IsUserPmuAccessEnabled() { return s_enable_user_pmu_access; } + static ALWAYS_INLINE bool IsKernelDebuggingEnabled() { return s_enable_kernel_debugging; } + }; + +} \ No newline at end of file diff --git a/libraries/libmesosphere/include/mesosphere/kern_panic.hpp b/libraries/libmesosphere/include/mesosphere/kern_panic.hpp index 63bef9b34..d7af5acb9 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_panic.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_panic.hpp @@ -15,6 +15,7 @@ */ #pragma once #include +#include namespace ams::kern { @@ -32,7 +33,7 @@ namespace ams::kern { #ifdef MESOSPHERE_ENABLE_ASSERTIONS #define MESOSPHERE_ASSERT_IMPL(expr, ...) \ ({ \ - if (AMS_UNLIKELY(!(expr))) { \ + if (AMS_UNLIKELY(!(expr))) { \ MESOSPHERE_PANIC(__VA_ARGS__); \ } \ }) diff --git a/libraries/libmesosphere/include/mesosphere/kern_select_k_system_control.hpp b/libraries/libmesosphere/include/mesosphere/kern_select_system_control.hpp similarity index 90% rename from libraries/libmesosphere/include/mesosphere/kern_select_k_system_control.hpp rename to libraries/libmesosphere/include/mesosphere/kern_select_system_control.hpp index ff53a5fe3..75c0a4002 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_select_k_system_control.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_select_system_control.hpp @@ -14,6 +14,8 @@ * along with this program. If not, see . */ #pragma once +#include +#include #ifdef ATMOSPHERE_BOARD_NINTENDO_SWITCH #include diff --git a/libraries/libmesosphere/source/board/nintendo/switch/kern_debug_log_impl.cpp b/libraries/libmesosphere/source/board/nintendo/switch/kern_debug_log_impl.cpp new file mode 100644 index 000000000..662377939 --- /dev/null +++ b/libraries/libmesosphere/source/board/nintendo/switch/kern_debug_log_impl.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2018-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 +#include "../../../kern_debug_log_impl.hpp" + +namespace ams::kern { + + namespace { + + enum UartRegister { + UartRegister_THR = 0, + UartRegister_IER = 1, + UartRegister_FCR = 2, + UartRegister_LCR = 3, + + UartRegister_LSR = 5, + + UartRegister_DLL = 0, + UartRegister_DLH = 1, + }; + + KVirtualAddress g_uart_address = 0; + + NOINLINE u32 ReadUartRegister(UartRegister which) { + return GetPointer(g_uart_address)[which]; + } + + NOINLINE void WriteUartRegister(UartRegister which, u32 value) { + GetPointer(g_uart_address)[which] = value; + } + + } + + bool KDebugLogImpl::Initialize() { + /* Set the uart register base address. */ + g_uart_address = KMemoryLayout::GetUartAddress(); + + /* Parameters for uart. */ + constexpr u32 BaudRate = 115200; + constexpr u32 Pllp = 408000000; + constexpr u32 Rate = 16 * BaudRate; + constexpr u32 Divisor = (Pllp + Rate / 2) / Rate; + + /* Initialize the UART registers. */ + /* Set Divisor Latch Access bit, to allow access to DLL/DLH */ + WriteUartRegister(UartRegister_LCR, 0x80); + ReadUartRegister(UartRegister_LCR); + + /* Program the divisor into DLL/DLH. */ + WriteUartRegister(UartRegister_DLL, Divisor & 0xFF); + WriteUartRegister(UartRegister_DLH, (Divisor >> 8) & 0xFF); + ReadUartRegister(UartRegister_DLH); + + /* Set word length to 3, clear Divisor Latch Access. */ + WriteUartRegister(UartRegister_LCR, 0x03); + ReadUartRegister(UartRegister_LCR); + + /* Disable UART interrupts. */ + WriteUartRegister(UartRegister_IER, 0x00); + + /* Configure the FIFOO to be enabled and clear receive. */ + WriteUartRegister(UartRegister_FCR, 0x03); + ReadUartRegister(UartRegister_FCR); + + return true; + } + + void KDebugLogImpl::PutChar(char c) { + while (ReadUartRegister(UartRegister_LSR) & 0x100) { + /* While the FIFO is full, yield. */ + __asm__ __volatile__("yield" ::: "memory"); + } + WriteUartRegister(UartRegister_THR, c); + cpu::DataSynchronizationBarrier(); + } + + void KDebugLogImpl::Flush() { + while ((ReadUartRegister(UartRegister_LSR) & 0x40) == 0) { + /* Wait for the TMTY bit to be one (transmit empty). */ + } + } + +} diff --git a/libraries/libmesosphere/source/board/nintendo/switch/kern_k_memory_layout.board.nintendo_switch.cpp b/libraries/libmesosphere/source/board/nintendo/switch/kern_k_memory_layout.board.nintendo_switch.cpp index 816de9a8f..d36c45cbc 100644 --- a/libraries/libmesosphere/source/board/nintendo/switch/kern_k_memory_layout.board.nintendo_switch.cpp +++ b/libraries/libmesosphere/source/board/nintendo/switch/kern_k_memory_layout.board.nintendo_switch.cpp @@ -22,13 +22,27 @@ namespace ams::kern { constexpr uintptr_t DramPhysicalAddress = 0x80000000; constexpr size_t ReservedEarlyDramSize = 0x60000; + ALWAYS_INLINE bool SetupUartPhysicalMemoryRegion() { + #if defined(MESOSPHERE_DEBUG_LOG_USE_UART_A) + return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006000, 0x40, KMemoryRegionType_Uart | KMemoryRegionAttr_ShouldKernelMap); + #elif defined(MESOSPHERE_DEBUG_LOG_USE_UART_B) + return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006040, 0x40, KMemoryRegionType_Uart | KMemoryRegionAttr_ShouldKernelMap); + #elif defined(MESOSPHERE_DEBUG_LOG_USE_UART_C) + return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006200, 0x100, KMemoryRegionType_Uart | KMemoryRegionAttr_ShouldKernelMap); + #elif defined(MESOSPHERE_DEBUG_LOG_USE_UART_D) + return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006300, 0x100, KMemoryRegionType_Uart | KMemoryRegionAttr_ShouldKernelMap); + #else + #error "Unknown Debug UART device!" + #endif + } + } namespace init { void SetupDevicePhysicalMemoryRegions() { /* TODO: Give these constexpr defines somewhere? */ - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006000, 0x40, KMemoryRegionType_Uart | KMemoryRegionAttr_ShouldKernelMap)); + MESOSPHERE_INIT_ABORT_UNLESS(SetupUartPhysicalMemoryRegion()); MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70019000, 0x1000, KMemoryRegionType_MemoryController | KMemoryRegionAttr_NoUserMap)); MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x7001C000, 0x1000, KMemoryRegionType_MemoryController0 | KMemoryRegionAttr_NoUserMap)); MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x7001D000, 0x1000, KMemoryRegionType_MemoryController1 | KMemoryRegionAttr_NoUserMap)); diff --git a/libraries/libmesosphere/source/board/nintendo/switch/kern_k_system_control.cpp b/libraries/libmesosphere/source/board/nintendo/switch/kern_k_system_control.cpp index 5f501b6d2..17f8e1f11 100644 --- a/libraries/libmesosphere/source/board/nintendo/switch/kern_k_system_control.cpp +++ b/libraries/libmesosphere/source/board/nintendo/switch/kern_k_system_control.cpp @@ -20,6 +20,9 @@ namespace ams::kern { namespace { + /* Global variables for panic. */ + bool g_call_smc_on_panic; + /* Global variables for randomness. */ /* Incredibly, N really does use std:: randomness... */ bool g_initialized_random_generator; @@ -70,6 +73,20 @@ namespace ams::kern { return value; } + ALWAYS_INLINE u64 GetConfigU64(smc::ConfigItem which) { + u64 value; + smc::GetConfig(&value, 1, which); + return value; + } + + ALWAYS_INLINE u32 GetConfigU32(smc::ConfigItem which) { + return static_cast(GetConfigU64(which)); + } + + ALWAYS_INLINE bool GetConfigBool(smc::ConfigItem which) { + return GetConfigU64(which) != 0; + } + } /* Initialization. */ @@ -160,6 +177,44 @@ namespace ams::kern { } } + /* System Initialization. */ + void KSystemControl::Initialize() { + /* Set IsDebugMode. */ + { + KTargetSystem::SetIsDebugMode(GetConfigBool(smc::ConfigItem::IsDebugMode)); + + /* If debug mode, we want to initialize uart logging. */ + KTargetSystem::EnableDebugLogging(KTargetSystem::IsDebugMode()); + KDebugLog::Initialize(); + } + + /* Set Kernel Configuration. */ + { + const auto kernel_config = util::BitPack32{GetConfigU32(smc::ConfigItem::KernelConfiguration)}; + + KTargetSystem::EnableDebugMemoryFill(kernel_config.Get()); + KTargetSystem::EnableUserExceptionHandlers(kernel_config.Get()); + KTargetSystem::EnableUserPmuAccess(kernel_config.Get()); + + g_call_smc_on_panic = kernel_config.Get(); + } + + /* Set Program Verification. */ + { + /* NOTE: This is used to restrict access to SvcKernelDebug/SvcChangeKernelTraceState. */ + /* Mesosphere may wish to not require this, as we'd ideally keep ProgramVerification enabled for userland. */ + KTargetSystem::EnableKernelDebugging(GetConfigBool(smc::ConfigItem::DisableProgramVerification)); + } + + /* Configure the Kernel Carveout region. */ + { + const auto carveout = KMemoryLayout::GetCarveoutRegionExtents(); + smc::ConfigureCarveout(0, carveout.GetAddress(), carveout.GetSize()); + } + + /* TODO: KResourceLimit initialization. */ + } + /* Randomness. */ void KSystemControl::GenerateRandomBytes(void *dst, size_t size) { MESOSPHERE_INIT_ABORT_UNLESS(size <= 0x38); @@ -181,8 +236,10 @@ namespace ams::kern { } void KSystemControl::StopSystem() { - /* Display a panic screen via exosphere. */ - smc::Panic(0xF00); + if (g_call_smc_on_panic) { + /* Display a panic screen via secure monitor. */ + smc::Panic(0xF00); + } while (true) { /* ... */ } } diff --git a/libraries/libmesosphere/source/board/nintendo/switch/kern_secure_monitor.cpp b/libraries/libmesosphere/source/board/nintendo/switch/kern_secure_monitor.cpp index 8eb223ab0..1aac226a7 100644 --- a/libraries/libmesosphere/source/board/nintendo/switch/kern_secure_monitor.cpp +++ b/libraries/libmesosphere/source/board/nintendo/switch/kern_secure_monitor.cpp @@ -143,6 +143,20 @@ namespace ams::kern::smc { } + void GetConfig(u64 *out, size_t num_qwords, ConfigItem config_item) { + SecureMonitorArguments args = { FunctionId_GetConfig, static_cast(config_item) }; + CallPrivilegedSecureMonitorFunction(args); + MESOSPHERE_ABORT_UNLESS((static_cast(args.x[0]) == SmcResult::Success)); + for (size_t i = 0; i < num_qwords && i < 7; i++) { + out[i] = args.x[1 + i]; + } + } + + void ConfigureCarveout(size_t which, uintptr_t address, size_t size) { + SecureMonitorArguments args = { FunctionId_ConfigureCarveout, static_cast(which), static_cast(address), static_cast(size) }; + CallPrivilegedSecureMonitorFunction(args); + MESOSPHERE_ABORT_UNLESS((static_cast(args.x[0]) == SmcResult::Success)); + } void GenerateRandomBytes(void *dst, size_t size) { /* Setup for call. */ diff --git a/libraries/libmesosphere/source/board/nintendo/switch/kern_secure_monitor.hpp b/libraries/libmesosphere/source/board/nintendo/switch/kern_secure_monitor.hpp index d6bce37f2..14c1fc38e 100644 --- a/libraries/libmesosphere/source/board/nintendo/switch/kern_secure_monitor.hpp +++ b/libraries/libmesosphere/source/board/nintendo/switch/kern_secure_monitor.hpp @@ -85,6 +85,8 @@ namespace ams::kern::smc { /* TODO: Rest of Secure Monitor API. */ void GenerateRandomBytes(void *dst, size_t size); + void GetConfig(u64 *out, size_t num_qwords, ConfigItem config_item); + void ConfigureCarveout(size_t which, uintptr_t address, size_t size); void NORETURN Panic(u32 color); namespace init { diff --git a/libraries/libmesosphere/source/kern_debug_log.cpp b/libraries/libmesosphere/source/kern_debug_log.cpp new file mode 100644 index 000000000..503313c0b --- /dev/null +++ b/libraries/libmesosphere/source/kern_debug_log.cpp @@ -0,0 +1,453 @@ +/* + * Copyright (c) 2018-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 +#include "kern_debug_log_impl.hpp" + +namespace ams::kern { + + namespace { + + /* Useful definitions for our VSNPrintf implementation. */ + enum FormatSpecifierFlag : u32 { + FormatSpecifierFlag_None = 0, + FormatSpecifierFlag_EmptySign = (1 << 0), + FormatSpecifierFlag_ForceSign = (1 << 1), + FormatSpecifierFlag_Hash = (1 << 2), + FormatSpecifierFlag_LeftJustify = (1 << 3), + FormatSpecifierFlag_ZeroPad = (1 << 4), + FormatSpecifierFlag_Char = (1 << 5), + FormatSpecifierFlag_Short = (1 << 6), + FormatSpecifierFlag_Long = (1 << 7), + FormatSpecifierFlag_LongLong = (1 << 8), + FormatSpecifierFlag_Uppercase = (1 << 9), + FormatSpecifierFlag_HasPrecision = (1 << 10), + }; + + using FormatSpecifierFlagStorage = std::underlying_type::type; + + constexpr ALWAYS_INLINE bool IsDigit(char c) { + return '0' <= c && c <= '9'; + } + + constexpr ALWAYS_INLINE u32 ParseU32(const char *&str) { + u32 value = 0; + do { + value = (value * 10) + static_cast(*(str++) - '0'); + } while (IsDigit(*str)); + return value; + } + + constexpr ALWAYS_INLINE size_t Strnlen(const char *str, size_t max) { + const char *cur = str; + while (*cur && max--) { + cur++; + } + return static_cast(cur - str); + } + + ALWAYS_INLINE void VSNPrintfImpl(char * const dst, const size_t dst_size, const char *format, ::std::va_list vl) { + size_t dst_index = 0; + + auto WriteCharacter = [dst, dst_size, &dst_index](char c) ALWAYS_INLINE_LAMBDA { + if (dst_index < dst_size) { + dst[dst_index++] = c; + } + }; + + /* Loop over every character in the string, looking for format specifiers. */ + while (*format) { + if (const char c = *(format++); c != '%') { + WriteCharacter(c); + continue; + } + + /* We have to parse a format specifier. */ + /* Start by parsing flags. */ + FormatSpecifierFlagStorage flags = FormatSpecifierFlag_None; + auto SetFlag = [&flags](FormatSpecifierFlag f) ALWAYS_INLINE_LAMBDA { flags |= f; }; + auto ClearFlag = [&flags](FormatSpecifierFlag f) ALWAYS_INLINE_LAMBDA { flags &= ~f; }; + auto HasFlag = [&flags](FormatSpecifierFlag f) ALWAYS_INLINE_LAMBDA { return (flags & f) != 0; }; + { + bool parsed_flags = false; + while (!parsed_flags) { + switch (*format) { + case ' ': SetFlag(FormatSpecifierFlag_EmptySign); format++; break; + case '+': SetFlag(FormatSpecifierFlag_ForceSign); format++; break; + case '#': SetFlag(FormatSpecifierFlag_Hash); format++; break; + case '-': SetFlag(FormatSpecifierFlag_LeftJustify); format++; break; + case '0': SetFlag(FormatSpecifierFlag_ZeroPad); format++; break; + default: + parsed_flags = true; + break; + } + } + } + + /* Next, parse width. */ + u32 width = 0; + if (IsDigit(*format)) { + /* Integer width. */ + width = ParseU32(format); + } else if (*format == '*') { + /* Dynamic width. */ + const int _width = va_arg(vl, int); + if (_width >= 0) { + width = static_cast(_width); + } else { + SetFlag(FormatSpecifierFlag_LeftJustify); + width = static_cast(-_width); + } + format++; + } + + /* Next, parse precision if present. */ + u32 precision = 0; + if (*format == '.') { + SetFlag(FormatSpecifierFlag_HasPrecision); + format++; + + if (IsDigit(*format)) { + /* Integer precision. */ + precision = ParseU32(format); + } else if (*format == '*') { + /* Dynamic precision. */ + const int _precision = va_arg(vl, int); + if (_precision > 0) { + precision = static_cast(_precision); + } + format++; + } + } + + /* Parse length. */ + constexpr bool SizeIsLong = sizeof(size_t) == sizeof(long); + constexpr bool IntMaxIsLong = sizeof(intmax_t) == sizeof(long); + constexpr bool PtrDiffIsLong = sizeof(ptrdiff_t) == sizeof(long); + switch (*format) { + case 'z': + SetFlag(SizeIsLong ? FormatSpecifierFlag_Long : FormatSpecifierFlag_LongLong); + format++; + break; + case 'j': + SetFlag(IntMaxIsLong ? FormatSpecifierFlag_Long : FormatSpecifierFlag_LongLong); + format++; + break; + case 't': + SetFlag(PtrDiffIsLong ? FormatSpecifierFlag_Long : FormatSpecifierFlag_LongLong); + format++; + break; + case 'h': + SetFlag(FormatSpecifierFlag_Short); + format++; + if (*format == 'h') { + SetFlag(FormatSpecifierFlag_Char); + format++; + } + break; + case 'l': + SetFlag(FormatSpecifierFlag_Long); + format++; + if (*format == 'l') { + SetFlag(FormatSpecifierFlag_LongLong); + format++; + } + break; + default: + break; + } + + const char specifier = *(format++); + switch (specifier) { + case 'p': + if constexpr (sizeof(uintptr_t) == sizeof(long long)) { + SetFlag(FormatSpecifierFlag_LongLong); + } else { + SetFlag(FormatSpecifierFlag_Long); + } + SetFlag(FormatSpecifierFlag_Hash); + [[fallthrough]]; + case 'd': + case 'i': + case 'u': + case 'b': + case 'o': + case 'x': + case 'X': + { + /* Determine the base to print with. */ + u32 base; + switch (specifier) { + case 'b': + base = 2; + break; + case 'o': + base = 8; + break; + case 'X': + SetFlag(FormatSpecifierFlag_Uppercase); + [[fallthrough]]; + case 'p': + case 'x': + base = 16; + break; + default: + base = 10; + ClearFlag(FormatSpecifierFlag_Hash); + break; + } + + /* Precision implies no zero-padding. */ + if (HasFlag(FormatSpecifierFlag_HasPrecision)) { + ClearFlag(FormatSpecifierFlag_ZeroPad); + } + + /* Unsigned types don't get signs. */ + const bool is_unsigned = base != 10 || specifier == 'u'; + if (is_unsigned) { + ClearFlag(FormatSpecifierFlag_EmptySign); + ClearFlag(FormatSpecifierFlag_ForceSign); + } + + auto PrintInteger = [&](bool negative, uintmax_t value) { + constexpr size_t BufferSize = 64; /* Binary digits for 64-bit numbers may use 64 digits. */ + char buf[BufferSize]; + size_t len = 0; + + /* No hash flag for zero. */ + if (value == 0) { + ClearFlag(FormatSpecifierFlag_Hash); + } + + if (!HasFlag(FormatSpecifierFlag_HasPrecision) || value != 0) { + do { + const char digit = static_cast(value % base); + buf[len++] = (digit < 10) ? ('0' + digit) : ((HasFlag(FormatSpecifierFlag_Uppercase) ? 'A' : 'a') + digit - 10); + value /= base; + } while (value); + } + + /* Determine our prefix length. */ + size_t prefix_len = 0; + const bool has_sign = negative || HasFlag(FormatSpecifierFlag_ForceSign) || HasFlag(FormatSpecifierFlag_EmptySign); + if (has_sign) { + prefix_len++; + } + if (HasFlag(FormatSpecifierFlag_Hash)) { + prefix_len += (base != 8) ? 2 : 1; + } + + /* Determine zero-padding count. */ + size_t num_zeroes = (len < precision) ? precision - len : 0; + if (!HasFlag(FormatSpecifierFlag_LeftJustify) && HasFlag(FormatSpecifierFlag_ZeroPad)) { + num_zeroes = (len + prefix_len < width) ? width - len - prefix_len : 0; + } + + /* Print out left padding. */ + if (!HasFlag(FormatSpecifierFlag_LeftJustify)) { + for (size_t i = len + prefix_len + num_zeroes; i < static_cast(width); i++) { + WriteCharacter(' '); + } + } + + /* Print out sign. */ + if (negative) { + WriteCharacter('-'); + } else if (HasFlag(FormatSpecifierFlag_ForceSign)) { + WriteCharacter('+'); + } else if (HasFlag(FormatSpecifierFlag_EmptySign)) { + WriteCharacter(' '); + } + + /* Print out base prefix. */ + if (HasFlag(FormatSpecifierFlag_Hash)) { + WriteCharacter('0'); + if (base == 2) { + WriteCharacter('b'); + } else if (base == 16) { + WriteCharacter('x'); + } + } + + /* Print out zeroes. */ + for (size_t i = 0; i < num_zeroes; i++) { + WriteCharacter('0'); + } + + /* Print out digits. */ + for (size_t i = 0; i < len; i++) { + WriteCharacter(buf[len - 1 - i]); + } + + /* Print out right padding. */ + if (HasFlag(FormatSpecifierFlag_LeftJustify)) { + for (size_t i = len + prefix_len + num_zeroes; i < static_cast(width); i++) { + WriteCharacter(' '); + } + } + }; + + /* Output the integer. */ + if (is_unsigned) { + uintmax_t n = 0; + if (HasFlag(FormatSpecifierFlag_LongLong)) { + n = static_cast(va_arg(vl, unsigned long long)); + } else if (HasFlag(FormatSpecifierFlag_Long)) { + n = static_cast(va_arg(vl, unsigned long)); + } else if (HasFlag(FormatSpecifierFlag_Char)) { + n = static_cast(va_arg(vl, unsigned int)); + } else if (HasFlag(FormatSpecifierFlag_Short)) { + n = static_cast(va_arg(vl, unsigned int)); + } else { + n = static_cast(va_arg(vl, unsigned int)); + } + if (specifier == 'p' && n == 0) { + WriteCharacter('('); + WriteCharacter('n'); + WriteCharacter('i'); + WriteCharacter('l'); + WriteCharacter(')'); + } else { + PrintInteger(false, n); + } + } else { + intmax_t n = 0; + if (HasFlag(FormatSpecifierFlag_LongLong)) { + n = static_cast(va_arg(vl, signed long long)); + } else if (HasFlag(FormatSpecifierFlag_Long)) { + n = static_cast(va_arg(vl, signed long)); + } if (HasFlag(FormatSpecifierFlag_Char)) { + n = static_cast(va_arg(vl, signed int)); + } else if (HasFlag(FormatSpecifierFlag_Short)) { + n = static_cast(va_arg(vl, signed int)); + } else { + n = static_cast(va_arg(vl, signed int)); + } + const bool negative = n < 0; + const uintmax_t u = (negative) ? static_cast(-n) : static_cast(n); + PrintInteger(negative, u); + } + } + break; + case 'c': + { + size_t len = 1; + if (!HasFlag(FormatSpecifierFlag_LeftJustify)) { + while (len++ < width) { + WriteCharacter(' '); + } + } + WriteCharacter(static_cast(va_arg(vl, int))); + if (HasFlag(FormatSpecifierFlag_LeftJustify)) { + while (len++ < width) { + WriteCharacter(' '); + } + } + } + break; + case 's': + { + const char *str = va_arg(vl, char *); + if (str == nullptr) { + str = "(null)"; + } + + size_t len = Strnlen(str, precision > 0 ? precision : std::numeric_limits::max()); + if (HasFlag(FormatSpecifierFlag_HasPrecision)) { + len = (len < precision) ? len : precision; + } + if (!HasFlag(FormatSpecifierFlag_LeftJustify)) { + while (len++ < width) { + WriteCharacter(' '); + } + } + while (*str && (!HasFlag(FormatSpecifierFlag_HasPrecision) || (precision--) != 0)) { + WriteCharacter(*(str++)); + } + if (HasFlag(FormatSpecifierFlag_LeftJustify)) { + while (len++ < width) { + WriteCharacter(' '); + } + } + } + break; + case '%': + default: + WriteCharacter(specifier); + break; + } + } + + /* Ensure null termination. */ + WriteCharacter('\0'); + dst[dst_size - 1] = '\0'; + } + + KSpinLock g_debug_log_lock; + bool g_initialized_impl; + + /* NOTE: Nintendo's print buffer is size 0x100. */ + char g_print_buffer[0x400]; + + void PutString(const char *str) { + if (g_initialized_impl) { + while (*str) { + const char c = *(str++); + if (c == '\n') { + KDebugLogImpl::PutChar('\r'); + } + KDebugLogImpl::PutChar(c); + } + KDebugLogImpl::Flush(); + } + } + + } + + void KDebugLog::Initialize() { + if (KTargetSystem::IsDebugLoggingEnabled()) { + KScopedInterruptDisable di; + KScopedSpinLock lk(g_debug_log_lock); + + if (!g_initialized_impl) { + g_initialized_impl = KDebugLogImpl::Initialize(); + } + } + } + + void KDebugLog::Printf(const char *format, ...) { + if (KTargetSystem::IsDebugLoggingEnabled()) { + ::std::va_list vl; + va_start(vl, format); + VPrintf(format, vl); + va_end(vl); + } + } + + void KDebugLog::VPrintf(const char *format, ::std::va_list vl) { + if (KTargetSystem::IsDebugLoggingEnabled()) { + KScopedInterruptDisable di; + KScopedSpinLock lk(g_debug_log_lock); + + VSNPrintf(g_print_buffer, util::size(g_print_buffer), format, vl); + PutString(g_print_buffer); + } + } + + void KDebugLog::VSNPrintf(char *dst, const size_t dst_size, const char *format, ::std::va_list vl) { + VSNPrintfImpl(dst, dst_size, format, vl); + } + +} diff --git a/libraries/libmesosphere/source/kern_debug_log_impl.hpp b/libraries/libmesosphere/source/kern_debug_log_impl.hpp new file mode 100644 index 000000000..1a278a03c --- /dev/null +++ b/libraries/libmesosphere/source/kern_debug_log_impl.hpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018-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 + +namespace ams::kern { + + class KDebugLogImpl { + public: + static NOINLINE bool Initialize(); + static NOINLINE void PutChar(char c); + static NOINLINE void Flush(); + }; + +} diff --git a/libraries/libmesosphere/source/kern_k_memory_layout.cpp b/libraries/libmesosphere/source/kern_k_memory_layout.cpp index bf3578489..ef6ae623b 100644 --- a/libraries/libmesosphere/source/kern_k_memory_layout.cpp +++ b/libraries/libmesosphere/source/kern_k_memory_layout.cpp @@ -76,10 +76,10 @@ namespace ams::kern { const auto extents = this->GetDerivedRegionExtents(type_id); /* Ensure that our alignment is correct. */ - MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(extents.first_region->GetAddress(), alignment)); + MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(extents.GetAddress(), alignment)); - const uintptr_t first_address = extents.first_region->GetAddress(); - const uintptr_t last_address = extents.last_region->GetLastAddress(); + const uintptr_t first_address = extents.GetAddress(); + const uintptr_t last_address = extents.GetLastAddress(); while (true) { const uintptr_t candidate = util::AlignDown(KSystemControl::Init::GenerateRandomRange(first_address, last_address), alignment); @@ -209,9 +209,9 @@ namespace ams::kern { /* Use the l1 page table for each core to map the core local region for each core. */ for (size_t i = 0; i < cpu::NumCores; i++) { KInitialPageTable temp_pt(core_l1_ttbr1_phys[i], KInitialPageTable::NoClear{}); - temp_pt.Map(core_local_virt_start, PageSize, core_l1_ttbr1_phys[i], KernelRwDataAttribute, page_allocator); + temp_pt.Map(core_local_virt_start, PageSize, core_local_region_start_phys[i], KernelRwDataAttribute, page_allocator); for (size_t j = 0; j < cpu::NumCores; j++) { - temp_pt.Map(core_local_virt_start + (j + 1) * PageSize, PageSize, core_l1_ttbr1_phys[j], KernelRwDataAttribute, page_allocator); + temp_pt.Map(core_local_virt_start + (j + 1) * PageSize, PageSize, core_local_region_start_phys[j], KernelRwDataAttribute, page_allocator); } /* Setup the InitArguments. */ @@ -239,16 +239,16 @@ namespace ams::kern { const uintptr_t pool_partitions_start = KMemoryLayout::GetPhysicalMemoryRegionTree().FindFirstRegionByTypeAttr(KMemoryRegionType_DramPoolPartition)->GetAddress(); /* Decide on starting addresses for our pools. */ - const uintptr_t application_pool_start = dram_extents.last_region->GetEndAddress() - application_pool_size; + const uintptr_t application_pool_start = dram_extents.GetEndAddress() - application_pool_size; const uintptr_t applet_pool_start = application_pool_start - applet_pool_size; const uintptr_t unsafe_system_pool_start = std::min(kernel_dram_start + CarveoutSizeMax, util::AlignDown(applet_pool_start - unsafe_system_pool_min_size, CarveoutAlignment)); const size_t unsafe_system_pool_size = applet_pool_start - unsafe_system_pool_start; /* We want to arrange application pool depending on where the middle of dram is. */ - const uintptr_t dram_midpoint = (dram_extents.first_region->GetAddress() + dram_extents.last_region->GetEndAddress()) / 2; + const uintptr_t dram_midpoint = (dram_extents.GetAddress() + dram_extents.GetEndAddress()) / 2; u32 cur_pool_attr = 0; size_t total_overhead_size = 0; - if (dram_extents.last_region->GetEndAddress() <= dram_midpoint || dram_midpoint <= application_pool_start) { + if (dram_extents.GetEndAddress() <= dram_midpoint || dram_midpoint <= application_pool_start) { InsertPoolPartitionRegionIntoBothTrees(application_pool_start, application_pool_size, KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool, cur_pool_attr); total_overhead_size += KMemoryManager::CalculateMetadataOverheadSize(application_pool_size); } else { diff --git a/libraries/libmesosphere/source/kern_main.cpp b/libraries/libmesosphere/source/kern_main.cpp index 0a773e47a..bf589c3ef 100644 --- a/libraries/libmesosphere/source/kern_main.cpp +++ b/libraries/libmesosphere/source/kern_main.cpp @@ -35,6 +35,9 @@ namespace ams::kern { } if (core_id == 0) { + /* Initialize KSystemControl. */ + KSystemControl::Initialize(); + /* Note: this is not actually done here, it's done later in main after more stuff is setup. */ /* However, for testing (and to manifest this code in the produced binary, this is here for now. */ /* TODO: Do this better. */