diff --git a/stratosphere/fatal/source/fatal_debug.cpp b/stratosphere/fatal/source/fatal_debug.cpp index b19fca621..6a4d1a660 100644 --- a/stratosphere/fatal/source/fatal_debug.cpp +++ b/stratosphere/fatal/source/fatal_debug.cpp @@ -28,6 +28,31 @@ namespace ams::fatal::srv { u64 lr; }; + constexpr inline size_t MaxThreads = 0x60; + + constinit std::pair g_thread_id_to_tls_map[MaxThreads]; + constinit size_t g_tls_map_index = 0; + + void ResetThreadTlsMap() { + g_tls_map_index = 0; + } + + void SetThreadTls(u64 thread_id, u64 tls) { + if (g_tls_map_index < MaxThreads) { + g_thread_id_to_tls_map[g_tls_map_index++] = std::make_pair(thread_id, tls); + } + } + + bool GetThreadTls(u64 *out, u64 thread_id) { + for (size_t i = 0; i < g_tls_map_index; ++i) { + if (g_thread_id_to_tls_map[i].first == thread_id) { + *out = g_thread_id_to_tls_map[i].second; + return true; + } + } + return false; + } + bool IsThreadFatalCaller(Result result, u32 debug_handle, u64 thread_id, u64 thread_tls_addr, ThreadContext *thread_ctx) { /* Verify that the thread is running or waiting. */ { @@ -177,7 +202,7 @@ namespace ams::fatal::srv { ON_SCOPE_EXIT { R_ABORT_UNLESS(svc::CloseHandle(debug_handle)); }; /* First things first, check if process is 64 bits, and get list of thread infos. */ - std::unordered_map thread_id_to_tls; + ResetThreadTlsMap(); { bool got_create_process = false; svc::DebugEventInfo d; @@ -189,7 +214,7 @@ namespace ams::fatal::srv { got_create_process = true; break; case svc::DebugEvent_CreateThread: - thread_id_to_tls[d.info.create_thread.thread_id] = d.info.create_thread.tls_address; + SetThreadTls(d.info.create_thread.thread_id, d.info.create_thread.tls_address); break; case svc::DebugEvent_Exception: case svc::DebugEvent_ExitProcess: @@ -224,13 +249,14 @@ namespace ams::fatal::srv { /* We need to locate the thread that's called fatal. */ for (s32 i = 0; i < thread_count; i++) { const u64 cur_thread_id = thread_ids[i]; - if (thread_id_to_tls.find(cur_thread_id) == thread_id_to_tls.end()) { + u64 cur_thread_tls; + if (!GetThreadTls(std::addressof(cur_thread_tls), cur_thread_id)) { continue; } - if (IsThreadFatalCaller(ctx->result, debug_handle, cur_thread_id, thread_id_to_tls[cur_thread_id], &thread_ctx)) { - thread_id = cur_thread_id; - thread_tls = thread_id_to_tls[thread_id]; + if (IsThreadFatalCaller(ctx->result, debug_handle, cur_thread_id, cur_thread_tls, &thread_ctx)) { + thread_id = cur_thread_id; + thread_tls = cur_thread_tls; found_fatal_caller = true; break; } diff --git a/stratosphere/fatal/source/fatal_font.cpp b/stratosphere/fatal/source/fatal_font.cpp index af967bdc8..5c4df1695 100644 --- a/stratosphere/fatal/source/fatal_font.cpp +++ b/stratosphere/fatal/source/fatal_font.cpp @@ -17,12 +17,41 @@ #include "fatal_config.hpp" #include "fatal_font.hpp" +namespace ams::fatal::srv::font { + + constinit lmem::HeapHandle g_font_heap_handle; + + void SetHeapMemory(void *memory, size_t memory_size) { + g_font_heap_handle = lmem::CreateExpHeap(memory, memory_size, lmem::CreateOption_None); + } + + void *AllocateForFont(size_t size) { + return lmem::AllocateFromExpHeap(g_font_heap_handle, size); + } + + void DeallocateForFont(void *p) { + if (p != nullptr) { + return lmem::FreeToExpHeap(g_font_heap_handle, p); + } + } + + +} + +#define STBTT_assert(x) AMS_ASSERT(x) +#define STBTT_malloc(x,u) ((void)(u),ams::fatal::srv::font::AllocateForFont(x)) +#define STBTT_free(x,u) ((void)(u),ams::fatal::srv::font::DeallocateForFont(x)) + #define STBTT_STATIC #define STB_TRUETYPE_IMPLEMENTATION #include "stb_truetype.h" #undef STBTT_STATIC #undef STB_TRUETYPE_IMPLEMENTATION +#undef STBTT_malloc +#undef STBTT_free +#undef STBTT_assert + /* Define color conversion macros. */ #define RGB888_TO_RGB565(r, g, b) ((((r >> 3) << 11) & 0xF800) | (((g >> 2) << 5) & 0x7E0) | ((b >> 3) & 0x1F)) #define RGB565_GET_R8(c) ((((c >> 11) & 0x1F) << 3) | ((c >> 13) & 7)) @@ -66,7 +95,7 @@ namespace ams::fatal::srv::font { void DrawCodePoint(u32 codepoint, u32 x, u32 y) { int width = 0, height = 0; u8* imageptr = stbtt_GetCodepointBitmap(&g_stb_font, g_font_size, g_font_size, codepoint, &width, &height, 0, 0); - ON_SCOPE_EXIT { std::free(imageptr); }; + ON_SCOPE_EXIT { DeallocateForFont(imageptr); }; for (int tmpy = 0; tmpy < height; tmpy++) { for (int tmpx = 0; tmpx < width; tmpx++) { diff --git a/stratosphere/fatal/source/fatal_font.hpp b/stratosphere/fatal/source/fatal_font.hpp index 3dcc446b4..62a343b26 100644 --- a/stratosphere/fatal/source/fatal_font.hpp +++ b/stratosphere/fatal/source/fatal_font.hpp @@ -20,6 +20,7 @@ namespace ams::fatal::srv::font { Result InitializeSharedFont(); void ConfigureFontFramebuffer(u16 *fb, u32 (*unswizzle_func)(u32, u32)); + void SetHeapMemory(void *memory, size_t memory_size); void SetFontColor(u16 color); void SetPosition(u32 x, u32 y); diff --git a/stratosphere/fatal/source/fatal_main.cpp b/stratosphere/fatal/source/fatal_main.cpp index 09e866241..223119eaf 100644 --- a/stratosphere/fatal/source/fatal_main.cpp +++ b/stratosphere/fatal/source/fatal_main.cpp @@ -25,7 +25,7 @@ extern "C" { u32 __nx_applet_type = AppletType_None; u32 __nx_fs_num_sessions = 1; - #define INNER_HEAP_SIZE 0x240000 + #define INNER_HEAP_SIZE 0x0 size_t nx_inner_heap_size = INNER_HEAP_SIZE; char nx_inner_heap[INNER_HEAP_SIZE]; @@ -40,6 +40,9 @@ extern "C" { alignas(16) u8 __nx_exception_stack[ams::os::MemoryPageSize]; u64 __nx_exception_stack_size = sizeof(__nx_exception_stack); void __libnx_exception_handler(ThreadExceptionDump *ctx); + + void *__libnx_thread_alloc(size_t size); + void __libnx_thread_free(void *mem); } namespace ams { @@ -56,6 +59,29 @@ namespace ams { using namespace ams; +namespace ams::fatal { + + namespace { + + constinit u8 g_fs_heap_memory[2_KB]; + lmem::HeapHandle g_fs_heap_handle; + + void *AllocateForFs(size_t size) { + return lmem::AllocateFromExpHeap(g_fs_heap_handle, size); + } + + void DeallocateForFs(void *p, size_t size) { + return lmem::FreeToExpHeap(g_fs_heap_handle, p); + } + + void InitializeFsHeap() { + g_fs_heap_handle = lmem::CreateExpHeap(g_fs_heap_memory, sizeof(g_fs_heap_memory), lmem::CreateOption_ThreadSafe); + } + + } + +} + void __libnx_exception_handler(ThreadExceptionDump *ctx) { ams::CrashHandler(ctx); } @@ -75,6 +101,9 @@ void __libnx_initheap(void) { void __appInit(void) { hos::InitializeForStratosphere(); + fatal::InitializeFsHeap(); + fs::SetAllocator(fatal::AllocateForFs, fatal::DeallocateForFs); + sm::DoWithSession([&]() { R_ABORT_UNLESS(setInitialize()); R_ABORT_UNLESS(setsysInitialize()); @@ -137,8 +166,38 @@ namespace { sf::hipc::ServerManager g_server_manager; + constinit sf::UnmanagedServiceObject g_user_service_object; + constinit sf::UnmanagedServiceObject g_private_service_object; + } +namespace ams { + + void *Malloc(size_t size) { + AMS_ABORT("ams::Malloc was called"); + } + + void Free(void *ptr) { + AMS_ABORT("ams::Free was called"); + } + +} + +void *operator new(size_t size) { + AMS_ABORT("operator new(size_t) was called"); +} + +void operator delete(void *p) { + AMS_ABORT("operator delete(void *) was called"); +} + +void *__libnx_thread_alloc(size_t size) { + AMS_ABORT("__libnx_thread_alloc was called"); +} + +void __libnx_thread_free(void *mem) { + AMS_ABORT("__libnx_thread_free was called"); +} int main(int argc, char **argv) { @@ -156,8 +215,8 @@ int main(int argc, char **argv) fatal::srv::CheckRepairStatus(); /* Create services. */ - R_ABORT_UNLESS((g_server_manager.RegisterServer(PrivateServiceName, PrivateMaxSessions))); - R_ABORT_UNLESS((g_server_manager.RegisterServer(UserServiceName, UserMaxSessions))); + R_ABORT_UNLESS(g_server_manager.RegisterObjectForServer(g_user_service_object.GetShared(), UserServiceName, UserMaxSessions)); + R_ABORT_UNLESS(g_server_manager.RegisterObjectForServer(g_private_service_object.GetShared(), PrivateServiceName, PrivateMaxSessions)); /* Add dirty event holder. */ auto *dirty_event_holder = ams::fatal::srv::GetFatalDirtyWaitableHolder(); diff --git a/stratosphere/fatal/source/fatal_service.cpp b/stratosphere/fatal/source/fatal_service.cpp index 5c0744c35..7afccf666 100644 --- a/stratosphere/fatal/source/fatal_service.cpp +++ b/stratosphere/fatal/source/fatal_service.cpp @@ -145,7 +145,7 @@ namespace ams::fatal::srv { return g_context.ThrowFatalWithCpuContext(result, client_pid.GetValue(), policy, cpu_ctx); } - Result PrivateService::GetFatalEvent(sf::OutCopyHandle out_h) { + Result Service::GetFatalEvent(sf::OutCopyHandle out_h) { const os::SystemEventType *event; R_TRY(g_context.GetEvent(std::addressof(event))); out_h.SetValue(os::GetReadableHandleOfSystemEvent(event)); diff --git a/stratosphere/fatal/source/fatal_service.hpp b/stratosphere/fatal/source/fatal_service.hpp index ca20c5e9f..e89956820 100644 --- a/stratosphere/fatal/source/fatal_service.hpp +++ b/stratosphere/fatal/source/fatal_service.hpp @@ -18,19 +18,15 @@ namespace ams::fatal::srv { - class Service final { + class Service { public: Result ThrowFatal(Result error, const sf::ClientProcessId &client_pid); Result ThrowFatalWithPolicy(Result error, const sf::ClientProcessId &client_pid, FatalPolicy policy); Result ThrowFatalWithCpuContext(Result error, const sf::ClientProcessId &client_pid, FatalPolicy policy, const CpuContext &cpu_ctx); - }; - static_assert(fatal::impl::IsIService); - - class PrivateService final { - public: Result GetFatalEvent(sf::OutCopyHandle out_h); }; - static_assert(fatal::impl::IsIPrivateService); + static_assert(fatal::impl::IsIService); + static_assert(fatal::impl::IsIPrivateService); } diff --git a/stratosphere/fatal/source/fatal_task_screen.cpp b/stratosphere/fatal/source/fatal_task_screen.cpp index 1e6730cfa..3988c6559 100644 --- a/stratosphere/fatal/source/fatal_task_screen.cpp +++ b/stratosphere/fatal/source/fatal_task_screen.cpp @@ -21,7 +21,6 @@ namespace ams::fatal::srv { /* Include Atmosphere logo into its own anonymous namespace. */ - namespace { #include "fatal_ams_logo.inc" @@ -39,6 +38,44 @@ namespace ams::fatal::srv { constexpr u32 FatalScreenWidthAlignedBytes = (FatalScreenWidth * FatalScreenBpp + 63) & ~63; constexpr u32 FatalScreenWidthAligned = FatalScreenWidthAlignedBytes / FatalScreenBpp; + /* There should only be a single transfer memory (for nv). */ + alignas(os::MemoryPageSize) constinit u8 g_nv_transfer_memory[0x40000]; + + /* There should only be a single (1280*768) framebuffer. */ + alignas(os::MemoryPageSize) constinit u8 g_framebuffer_memory[FatalScreenWidthAlignedBytes * util::AlignUp(FatalScreenHeight, 128)]; + + } + +} + +extern "C" void *__libnx_tmem_alloc(size_t size) { + return ams::fatal::srv::g_nv_transfer_memory; +} + +extern "C" void __libnx_tmem_free(void *mem) { + /* ... */ +} + +extern "C" void *__libnx_framebuffer_alloc(size_t size) { + return ams::fatal::srv::g_framebuffer_memory; +} + +extern "C" void __libnx_framebuffer_free(void *mem) { + /* ... */ +} + +extern "C" void *__libnx_framebuffer_linear_alloc(size_t size) { + AMS_ABORT("__libnx_framebuffer_linear_alloc was called"); +} + +extern "C" void __libnx_framebuffer_linear_free(void *mem) { + AMS_ABORT("__libnx_framebuffer_linear_free was called"); +} + +namespace ams::fatal::srv { + + namespace { + /* Pixel calculation helper. */ constexpr u32 GetPixelOffset(u32 x, u32 y) { u32 tmp_pos = ((y & 127) / 16) + (x/32*8) + ((y/16/8)*(((FatalScreenWidthAligned/2)/16*8))); @@ -60,6 +97,7 @@ namespace ams::fatal::srv { Result SetupDisplayInternal(); Result SetupDisplayExternal(); Result PrepareScreenForDrawing(); + void PreRenderFrameBuffer(); Result ShowFatal(); public: virtual Result Run() override; @@ -178,24 +216,22 @@ namespace ams::fatal::srv { return ResultSuccess(); } - Result ShowFatalTask::ShowFatal() { + void ShowFatalTask::PreRenderFrameBuffer() { const FatalConfig &config = GetFatalConfig(); - /* Prepare screen for drawing. */ - sm::DoWithSession([&]() { - R_ABORT_UNLESS(PrepareScreenForDrawing()); - }); + /* Pre-render the image into the static framebuffer. */ + u16 *tiled_buf = reinterpret_cast(g_framebuffer_memory); - /* Dequeue a buffer. */ - u16 *tiled_buf = reinterpret_cast(framebufferBegin(&this->fb, NULL)); - R_UNLESS(tiled_buf != nullptr, ResultNullGraphicsBuffer()); + /* Temporarily use the NV transfer memory as font backing heap. */ + font::SetHeapMemory(g_nv_transfer_memory, sizeof(g_nv_transfer_memory)); + ON_SCOPE_EXIT { std::memset(g_nv_transfer_memory, 0, sizeof(g_nv_transfer_memory)); }; /* Let the font manager know about our framebuffer. */ font::ConfigureFontFramebuffer(tiled_buf, GetPixelOffset); font::SetFontColor(0xFFFF); /* Draw a background. */ - for (size_t i = 0; i < this->fb.fb_size / sizeof(*tiled_buf); i++) { + for (size_t i = 0; i < sizeof(g_framebuffer_memory) / sizeof(*tiled_buf); i++) { tiled_buf[i] = 0x39C9; } @@ -412,6 +448,22 @@ namespace ams::fatal::srv { } } } + } + + Result ShowFatalTask::ShowFatal() { + /* Pre-render the framebuffer. */ + PreRenderFrameBuffer(); + + /* Prepare screen for drawing. */ + sm::DoWithSession([&]() { + R_ABORT_UNLESS(PrepareScreenForDrawing()); + }); + + /* Dequeue a buffer. */ + R_UNLESS(framebufferBegin(&this->fb, NULL) != nullptr, ResultNullGraphicsBuffer()); + + /* We've already pre-rendered the frame into the static buffer. */ + /* ... */ /* Enqueue the buffer. */ framebufferEnd(&fb);