From 61fcf5e0f4ac7f5e4468cff79b4ddf1418237621 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 26 Jun 2019 15:46:19 -0700 Subject: [PATCH] loader: completely rewrite. --- stratosphere/ams_mitm/source/utils.cpp | 8 +- stratosphere/libstratosphere | 2 +- stratosphere/loader/source/ini.c | 269 --- stratosphere/loader/source/ini.h | 130 -- stratosphere/loader/source/ldr_arguments.cpp | 77 + ...ldr_launch_queue.hpp => ldr_arguments.hpp} | 33 +- .../loader/source/ldr_capabilities.cpp | 428 ++++ .../loader/source/ldr_capabilities.hpp | 29 + .../loader/source/ldr_content_management.cpp | 700 +++---- .../loader/source/ldr_content_management.hpp | 91 +- .../loader/source/ldr_debug_monitor.cpp | 39 - .../loader/source/ldr_debug_monitor.hpp | 40 - stratosphere/loader/source/ldr_ecs.cpp | 110 + stratosphere/loader/source/ldr_ecs.hpp | 29 + stratosphere/loader/source/ldr_hid.cpp | 48 - .../loader/source/ldr_launch_queue.cpp | 102 - .../loader/source/ldr_launch_record.cpp | 51 + .../{ldr_hid.hpp => ldr_launch_record.hpp} | 13 +- .../loader/source/ldr_loader_service.cpp | 104 + .../loader/source/ldr_loader_service.hpp | 116 + stratosphere/loader/source/ldr_main.cpp | 17 +- stratosphere/loader/source/ldr_map.cpp | 113 - stratosphere/loader/source/ldr_map.hpp | 78 - stratosphere/loader/source/ldr_meta.cpp | 217 ++ stratosphere/loader/source/ldr_meta.hpp | 43 + stratosphere/loader/source/ldr_npdm.cpp | 500 ----- stratosphere/loader/source/ldr_npdm.hpp | 116 - stratosphere/loader/source/ldr_nso.cpp | 357 ---- stratosphere/loader/source/ldr_nso.hpp | 116 - stratosphere/loader/source/ldr_patcher.cpp | 172 +- stratosphere/loader/source/ldr_patcher.hpp | 12 +- .../loader/source/ldr_process_creation.cpp | 880 ++++++-- .../loader/source/ldr_process_creation.hpp | 29 +- .../loader/source/ldr_process_manager.cpp | 142 -- .../loader/source/ldr_process_manager.hpp | 62 - .../loader/source/ldr_registration.cpp | 198 -- .../loader/source/ldr_registration.hpp | 65 - stratosphere/loader/source/ldr_ro_manager.cpp | 172 ++ stratosphere/loader/source/ldr_ro_manager.hpp | 32 + stratosphere/loader/source/ldr_shell.cpp | 48 - stratosphere/loader/source/ldr_shell.hpp | 45 - stratosphere/loader/source/lz4.c | 1857 ----------------- stratosphere/loader/source/lz4.h | 569 ----- stratosphere/pm/source/pm_boot2.cpp | 25 +- stratosphere/pm/source/pm_info.cpp | 28 +- stratosphere/pm/source/pm_info.hpp | 9 + stratosphere/pm/source/pm_main.cpp | 6 +- stratosphere/pm/source/pm_registration.cpp | 11 +- stratosphere/pm/source/pm_registration.hpp | 2 + 49 files changed, 2523 insertions(+), 5817 deletions(-) delete mode 100644 stratosphere/loader/source/ini.c delete mode 100644 stratosphere/loader/source/ini.h create mode 100644 stratosphere/loader/source/ldr_arguments.cpp rename stratosphere/loader/source/{ldr_launch_queue.hpp => ldr_arguments.hpp} (50%) create mode 100644 stratosphere/loader/source/ldr_capabilities.cpp create mode 100644 stratosphere/loader/source/ldr_capabilities.hpp delete mode 100644 stratosphere/loader/source/ldr_debug_monitor.cpp delete mode 100644 stratosphere/loader/source/ldr_debug_monitor.hpp create mode 100644 stratosphere/loader/source/ldr_ecs.cpp create mode 100644 stratosphere/loader/source/ldr_ecs.hpp delete mode 100644 stratosphere/loader/source/ldr_hid.cpp delete mode 100644 stratosphere/loader/source/ldr_launch_queue.cpp create mode 100644 stratosphere/loader/source/ldr_launch_record.cpp rename stratosphere/loader/source/{ldr_hid.hpp => ldr_launch_record.hpp} (76%) create mode 100644 stratosphere/loader/source/ldr_loader_service.cpp create mode 100644 stratosphere/loader/source/ldr_loader_service.hpp delete mode 100644 stratosphere/loader/source/ldr_map.cpp delete mode 100644 stratosphere/loader/source/ldr_map.hpp create mode 100644 stratosphere/loader/source/ldr_meta.cpp create mode 100644 stratosphere/loader/source/ldr_meta.hpp delete mode 100644 stratosphere/loader/source/ldr_npdm.cpp delete mode 100644 stratosphere/loader/source/ldr_npdm.hpp delete mode 100644 stratosphere/loader/source/ldr_nso.cpp delete mode 100644 stratosphere/loader/source/ldr_nso.hpp delete mode 100644 stratosphere/loader/source/ldr_process_manager.cpp delete mode 100644 stratosphere/loader/source/ldr_process_manager.hpp delete mode 100644 stratosphere/loader/source/ldr_registration.cpp delete mode 100644 stratosphere/loader/source/ldr_registration.hpp create mode 100644 stratosphere/loader/source/ldr_ro_manager.cpp create mode 100644 stratosphere/loader/source/ldr_ro_manager.hpp delete mode 100644 stratosphere/loader/source/ldr_shell.cpp delete mode 100644 stratosphere/loader/source/ldr_shell.hpp delete mode 100644 stratosphere/loader/source/lz4.c delete mode 100644 stratosphere/loader/source/lz4.h diff --git a/stratosphere/ams_mitm/source/utils.cpp b/stratosphere/ams_mitm/source/utils.cpp index 990b88c50..96b38d4a3 100644 --- a/stratosphere/ams_mitm/source/utils.cpp +++ b/stratosphere/ams_mitm/source/utils.cpp @@ -37,7 +37,7 @@ static std::atomic_bool g_has_hid_session = false; /* Content override support variables/types */ static OverrideKey g_default_override_key = { - .key_combination = KEY_R, + .key_combination = KEY_L, .override_by_default = true }; @@ -49,11 +49,11 @@ struct HblOverrideConfig { static HblOverrideConfig g_hbl_override_config = { .override_key = { - .key_combination = KEY_L, - .override_by_default = true + .key_combination = KEY_R, + .override_by_default = false }, .title_id = TitleId_AppletPhotoViewer, - .override_any_app = false + .override_any_app = true }; /* Static buffer for loader.ini contents at runtime. */ diff --git a/stratosphere/libstratosphere b/stratosphere/libstratosphere index cf5c6cdad..e37089d16 160000 --- a/stratosphere/libstratosphere +++ b/stratosphere/libstratosphere @@ -1 +1 @@ -Subproject commit cf5c6cdad9ec4066d763c3317e98f7027d3172a6 +Subproject commit e37089d167ff2cd8009924bfa32ab179db918376 diff --git a/stratosphere/loader/source/ini.c b/stratosphere/loader/source/ini.c deleted file mode 100644 index 63626c72d..000000000 --- a/stratosphere/loader/source/ini.c +++ /dev/null @@ -1,269 +0,0 @@ -/* inih -- simple .INI file parser - -inih is released under the New BSD license (see LICENSE.txt). Go to the project -home page for more info: - -https://github.com/benhoyt/inih - -*/ - -#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) -#define _CRT_SECURE_NO_WARNINGS -#endif - -#include -#include -#include - -#include "ini.h" - -#if !INI_USE_STACK -#include -#endif - -#define MAX_SECTION 50 -#define MAX_NAME 50 - -/* Used by ini_parse_string() to keep track of string parsing state. */ -typedef struct { - const char* ptr; - size_t num_left; -} ini_parse_string_ctx; - -/* Strip whitespace chars off end of given string, in place. Return s. */ -static char* rstrip(char* s) -{ - char* p = s + strlen(s); - while (p > s && isspace((unsigned char)(*--p))) - *p = '\0'; - return s; -} - -/* Return pointer to first non-whitespace char in given string. */ -static char* lskip(const char* s) -{ - while (*s && isspace((unsigned char)(*s))) - s++; - return (char*)s; -} - -/* Return pointer to first char (of chars) or inline comment in given string, - or pointer to null at end of string if neither found. Inline comment must - be prefixed by a whitespace character to register as a comment. */ -static char* find_chars_or_comment(const char* s, const char* chars) -{ -#if INI_ALLOW_INLINE_COMMENTS - int was_space = 0; - while (*s && (!chars || !strchr(chars, *s)) && - !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) { - was_space = isspace((unsigned char)(*s)); - s++; - } -#else - while (*s && (!chars || !strchr(chars, *s))) { - s++; - } -#endif - return (char*)s; -} - -/* Version of strncpy that ensures dest (size bytes) is null-terminated. */ -static char* strncpy0(char* dest, const char* src, size_t size) -{ - strncpy(dest, src, size - 1); - dest[size - 1] = '\0'; - return dest; -} - -/* See documentation in header file. */ -int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, - void* user) -{ - /* Uses a fair bit of stack (use heap instead if you need to) */ -#if INI_USE_STACK - char line[INI_MAX_LINE]; - int max_line = INI_MAX_LINE; -#else - char* line; - int max_line = INI_INITIAL_ALLOC; -#endif -#if INI_ALLOW_REALLOC - char* new_line; - int offset; -#endif - char section[MAX_SECTION] = ""; - char prev_name[MAX_NAME] = ""; - - char* start; - char* end; - char* name; - char* value; - int lineno = 0; - int error = 0; - -#if !INI_USE_STACK - line = (char*)malloc(INI_INITIAL_ALLOC); - if (!line) { - return -2; - } -#endif - -#if INI_HANDLER_LINENO -#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno) -#else -#define HANDLER(u, s, n, v) handler(u, s, n, v) -#endif - - /* Scan through stream line by line */ - while (reader(line, max_line, stream) != NULL) { -#if INI_ALLOW_REALLOC - offset = strlen(line); - while (offset == max_line - 1 && line[offset - 1] != '\n') { - max_line *= 2; - if (max_line > INI_MAX_LINE) - max_line = INI_MAX_LINE; - new_line = realloc(line, max_line); - if (!new_line) { - free(line); - return -2; - } - line = new_line; - if (reader(line + offset, max_line - offset, stream) == NULL) - break; - if (max_line >= INI_MAX_LINE) - break; - offset += strlen(line + offset); - } -#endif - - lineno++; - - start = line; -#if INI_ALLOW_BOM - if (lineno == 1 && (unsigned char)start[0] == 0xEF && - (unsigned char)start[1] == 0xBB && - (unsigned char)start[2] == 0xBF) { - start += 3; - } -#endif - start = lskip(rstrip(start)); - - if (strchr(INI_START_COMMENT_PREFIXES, *start)) { - /* Start-of-line comment */ - } -#if INI_ALLOW_MULTILINE - else if (*prev_name && *start && start > line) { - /* Non-blank line with leading whitespace, treat as continuation - of previous name's value (as per Python configparser). */ - if (!HANDLER(user, section, prev_name, start) && !error) - error = lineno; - } -#endif - else if (*start == '[') { - /* A "[section]" line */ - end = find_chars_or_comment(start + 1, "]"); - if (*end == ']') { - *end = '\0'; - strncpy0(section, start + 1, sizeof(section)); - *prev_name = '\0'; - } - else if (!error) { - /* No ']' found on section line */ - error = lineno; - } - } - else if (*start) { - /* Not a comment, must be a name[=:]value pair */ - end = find_chars_or_comment(start, "=:"); - if (*end == '=' || *end == ':') { - *end = '\0'; - name = rstrip(start); - value = end + 1; -#if INI_ALLOW_INLINE_COMMENTS - end = find_chars_or_comment(value, NULL); - if (*end) - *end = '\0'; -#endif - value = lskip(value); - rstrip(value); - - /* Valid name[=:]value pair found, call handler */ - strncpy0(prev_name, name, sizeof(prev_name)); - if (!HANDLER(user, section, name, value) && !error) - error = lineno; - } - else if (!error) { - /* No '=' or ':' found on name[=:]value line */ - error = lineno; - } - } - -#if INI_STOP_ON_FIRST_ERROR - if (error) - break; -#endif - } - -#if !INI_USE_STACK - free(line); -#endif - - return error; -} - -/* See documentation in header file. */ -int ini_parse_file(FILE* file, ini_handler handler, void* user) -{ - return ini_parse_stream((ini_reader)fgets, file, handler, user); -} - -/* See documentation in header file. */ -int ini_parse(const char* filename, ini_handler handler, void* user) -{ - FILE* file; - int error; - - file = fopen(filename, "r"); - if (!file) - return -1; - error = ini_parse_file(file, handler, user); - fclose(file); - return error; -} - -/* An ini_reader function to read the next line from a string buffer. This - is the fgets() equivalent used by ini_parse_string(). */ -static char* ini_reader_string(char* str, int num, void* stream) { - ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream; - const char* ctx_ptr = ctx->ptr; - size_t ctx_num_left = ctx->num_left; - char* strp = str; - char c; - - if (ctx_num_left == 0 || num < 2) - return NULL; - - while (num > 1 && ctx_num_left != 0) { - c = *ctx_ptr++; - ctx_num_left--; - *strp++ = c; - if (c == '\n') - break; - num--; - } - - *strp = '\0'; - ctx->ptr = ctx_ptr; - ctx->num_left = ctx_num_left; - return str; -} - -/* See documentation in header file. */ -int ini_parse_string(const char* string, ini_handler handler, void* user) { - ini_parse_string_ctx ctx; - - ctx.ptr = string; - ctx.num_left = strlen(string); - return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler, - user); -} diff --git a/stratosphere/loader/source/ini.h b/stratosphere/loader/source/ini.h deleted file mode 100644 index f45ba40ba..000000000 --- a/stratosphere/loader/source/ini.h +++ /dev/null @@ -1,130 +0,0 @@ -/* inih -- simple .INI file parser - -inih is released under the New BSD license (see LICENSE.txt). Go to the project -home page for more info: - -https://github.com/benhoyt/inih - -*/ - -#ifndef __INI_H__ -#define __INI_H__ - -/* Make this header file easier to include in C++ code */ -#ifdef __cplusplus -extern "C" { -#endif - -#include - -/* Nonzero if ini_handler callback should accept lineno parameter. */ -#ifndef INI_HANDLER_LINENO -#define INI_HANDLER_LINENO 0 -#endif - -/* Typedef for prototype of handler function. */ -#if INI_HANDLER_LINENO -typedef int (*ini_handler)(void* user, const char* section, - const char* name, const char* value, - int lineno); -#else -typedef int (*ini_handler)(void* user, const char* section, - const char* name, const char* value); -#endif - -/* Typedef for prototype of fgets-style reader function. */ -typedef char* (*ini_reader)(char* str, int num, void* stream); - -/* Parse given INI-style file. May have [section]s, name=value pairs - (whitespace stripped), and comments starting with ';' (semicolon). Section - is "" if name=value pair parsed before any section heading. name:value - pairs are also supported as a concession to Python's configparser. - - For each name=value pair parsed, call handler function with given user - pointer as well as section, name, and value (data only valid for duration - of handler call). Handler should return nonzero on success, zero on error. - - Returns 0 on success, line number of first error on parse error (doesn't - stop on first error), -1 on file open error, or -2 on memory allocation - error (only when INI_USE_STACK is zero). -*/ -int ini_parse(const char* filename, ini_handler handler, void* user); - -/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't - close the file when it's finished -- the caller must do that. */ -int ini_parse_file(FILE* file, ini_handler handler, void* user); - -/* Same as ini_parse(), but takes an ini_reader function pointer instead of - filename. Used for implementing custom or string-based I/O (see also - ini_parse_string). */ -int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, - void* user); - -/* Same as ini_parse(), but takes a zero-terminated string with the INI data -instead of a file. Useful for parsing INI data from a network socket or -already in memory. */ -int ini_parse_string(const char* string, ini_handler handler, void* user); - -/* Nonzero to allow multi-line value parsing, in the style of Python's - configparser. If allowed, ini_parse() will call the handler with the same - name for each subsequent line parsed. */ -#ifndef INI_ALLOW_MULTILINE -#define INI_ALLOW_MULTILINE 1 -#endif - -/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of - the file. See http://code.google.com/p/inih/issues/detail?id=21 */ -#ifndef INI_ALLOW_BOM -#define INI_ALLOW_BOM 1 -#endif - -/* Chars that begin a start-of-line comment. Per Python configparser, allow - both ; and # comments at the start of a line by default. */ -#ifndef INI_START_COMMENT_PREFIXES -#define INI_START_COMMENT_PREFIXES ";#" -#endif - -/* Nonzero to allow inline comments (with valid inline comment characters - specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match - Python 3.2+ configparser behaviour. */ -#ifndef INI_ALLOW_INLINE_COMMENTS -#define INI_ALLOW_INLINE_COMMENTS 1 -#endif -#ifndef INI_INLINE_COMMENT_PREFIXES -#define INI_INLINE_COMMENT_PREFIXES ";" -#endif - -/* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */ -#ifndef INI_USE_STACK -#define INI_USE_STACK 1 -#endif - -/* Maximum line length for any line in INI file (stack or heap). Note that - this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */ -#ifndef INI_MAX_LINE -#define INI_MAX_LINE 200 -#endif - -/* Nonzero to allow heap line buffer to grow via realloc(), zero for a - fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is - zero. */ -#ifndef INI_ALLOW_REALLOC -#define INI_ALLOW_REALLOC 0 -#endif - -/* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK - is zero. */ -#ifndef INI_INITIAL_ALLOC -#define INI_INITIAL_ALLOC 200 -#endif - -/* Stop parsing on first error (default is to keep parsing). */ -#ifndef INI_STOP_ON_FIRST_ERROR -#define INI_STOP_ON_FIRST_ERROR 0 -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* __INI_H__ */ diff --git a/stratosphere/loader/source/ldr_arguments.cpp b/stratosphere/loader/source/ldr_arguments.cpp new file mode 100644 index 000000000..e31678963 --- /dev/null +++ b/stratosphere/loader/source/ldr_arguments.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018-2019 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 "ldr_arguments.hpp" + +namespace sts::ldr::args { + + namespace { + + /* Convenience definitions. */ + constexpr ncm::TitleId FreeTitleId = {}; + constexpr size_t MaxArgumentInfos = 10; + + /* Global storage. */ + ArgumentInfo g_argument_infos[MaxArgumentInfos]; + + /* Helpers. */ + ArgumentInfo *GetArgumentInfo(ncm::TitleId title_id) { + for (size_t i = 0; i < MaxArgumentInfos; i++) { + if (g_argument_infos[i].title_id == title_id) { + return &g_argument_infos[i]; + } + } + return nullptr; + } + + ArgumentInfo *GetFreeArgumentInfo() { + return GetArgumentInfo(FreeTitleId); + } + + } + + /* API. */ + const ArgumentInfo *Get(ncm::TitleId title_id) { + return GetArgumentInfo(title_id); + } + + Result Set(ncm::TitleId title_id, const void *args, size_t args_size) { + if (args_size >= ArgumentSizeMax) { + return ResultLoaderTooLongArgument; + } + + ArgumentInfo *arg_info = GetArgumentInfo(title_id); + if (arg_info == nullptr) { + arg_info = GetFreeArgumentInfo(); + } + if (arg_info == nullptr) { + return ResultLoaderTooManyArguments; + } + + arg_info->title_id = title_id; + arg_info->args_size = args_size; + std::memcpy(arg_info->args, args, args_size); + return ResultSuccess; + } + + Result Clear() { + for (size_t i = 0; i < MaxArgumentInfos; i++) { + g_argument_infos[i].title_id = FreeTitleId; + } + return ResultSuccess; + } + +} diff --git a/stratosphere/loader/source/ldr_launch_queue.hpp b/stratosphere/loader/source/ldr_arguments.hpp similarity index 50% rename from stratosphere/loader/source/ldr_launch_queue.hpp rename to stratosphere/loader/source/ldr_arguments.hpp index 76e7ab486..41f93bef2 100644 --- a/stratosphere/loader/source/ldr_launch_queue.hpp +++ b/stratosphere/loader/source/ldr_arguments.hpp @@ -16,27 +16,22 @@ #pragma once #include +#include +#include -#define LAUNCH_QUEUE_SIZE (10) -#define LAUNCH_QUEUE_FULL (-1) +namespace sts::ldr::args { -#define LAUNCH_QUEUE_ARG_SIZE_MAX (0x8000) + constexpr size_t ArgumentSizeMax = 0x8000; -class LaunchQueue { - public: - struct LaunchItem { - u64 tid; - u64 arg_size; - char args[LAUNCH_QUEUE_ARG_SIZE_MAX]; - }; + struct ArgumentInfo { + ncm::TitleId title_id; + size_t args_size; + u8 args[ArgumentSizeMax]; + }; - static LaunchQueue::LaunchItem *GetItem(u64 tid); + /* API. */ + const ArgumentInfo *Get(ncm::TitleId title_id); + Result Set(ncm::TitleId title_id, const void *args, size_t args_size); + Result Clear(); - static Result Add(u64 tid, const char *args, u64 arg_size); - static Result AddItem(const LaunchItem *item); - static Result AddCopy(u64 tid_base, u64 new_tid); - static int GetIndex(u64 tid); - static int GetFreeIndex(u64 tid); - static bool Contains(u64 tid); - static void Clear(); -}; \ No newline at end of file +} diff --git a/stratosphere/loader/source/ldr_capabilities.cpp b/stratosphere/loader/source/ldr_capabilities.cpp new file mode 100644 index 000000000..2fb953434 --- /dev/null +++ b/stratosphere/loader/source/ldr_capabilities.cpp @@ -0,0 +1,428 @@ +/* + * Copyright (c) 2018-2019 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 "ldr_capabilities.hpp" + +namespace sts::ldr { + + namespace { + + /* Types. */ + enum class CapabilityId { + KernelFlags = 3, + SyscallMask = 4, + MapRange = 6, + MapPage = 7, + InterruptPair = 11, + ApplicationType = 13, + KernelVersion = 14, + HandleTable = 15, + DebugFlags = 16, + + Empty = 32, + }; + + constexpr CapabilityId GetCapabilityId(u32 cap) { + return static_cast(__builtin_ctz(~cap)); + } + + template + class Capability { + public: + static constexpr u32 ValueShift = static_cast(id) + 1; + static constexpr u32 IdMask = (1u << (ValueShift - 1)) - 1; + private: + u32 value; + public: + Capability(u32 v) : value(v) { /* ... */ } + + CapabilityId GetId() const { + return id; + } + + u32 GetValue() const { + return this->value >> ValueShift; + } + }; + +#define CAPABILITY_CLASS_NAME(id) Capability##id +#define CAPABILITY_BASE_CLASS(id) Capability + +#define DEFINE_CAPABILITY_CLASS(id, member_functions) \ + class CAPABILITY_CLASS_NAME(id) : public CAPABILITY_BASE_CLASS(id) { \ + public: \ + CAPABILITY_CLASS_NAME(id)(u32 v) : CAPABILITY_BASE_CLASS(id)(v) { /* ... */ } \ +\ + static CAPABILITY_CLASS_NAME(id) Decode(u32 v) { return CAPABILITY_CLASS_NAME(id)(v); } \ +\ + member_functions \ + } + + /* Class definitions. */ + DEFINE_CAPABILITY_CLASS(KernelFlags, + u32 GetMaximumThreadPriority() const { + return (this->GetValue() >> 0) & 0x3F; + } + + u32 GetMinimumThreadPriority() const { + return (this->GetValue() >> 6) & 0x3F; + } + + u32 GetMinimumCoreId() const { + return (this->GetValue() >> 12) & 0xFF; + } + + u32 GetMaximumCoreId() const { + return (this->GetValue() >> 20) & 0xFF; + } + + bool IsValid(const u32 *kac, size_t kac_count) const { + for (size_t i = 0; i < kac_count; i++) { + if (GetCapabilityId(kac[i]) == this->GetId()) { + const auto restrict = Decode(kac[i]); + + if (this->GetMinimumThreadPriority() < restrict.GetMinimumThreadPriority() || + this->GetMaximumThreadPriority() > restrict.GetMaximumThreadPriority() || + this->GetMinimumThreadPriority() > this->GetMaximumThreadPriority()) { + return false; + } + + if (this->GetMinimumCoreId() < restrict.GetMinimumCoreId() || + this->GetMaximumCoreId() > restrict.GetMaximumCoreId() || + this->GetMinimumCoreId() > this->GetMaximumCoreId()) { + return false; + } + + return true; + } + } + return false; + } + ); + + DEFINE_CAPABILITY_CLASS(SyscallMask, + u32 GetMask() const { + return (this->GetValue() >> 0) & 0xFFFFFF; + } + + u32 GetIndex() const { + return (this->GetValue() >> 24) & 0x7; + } + + bool IsValid(const u32 *kac, size_t kac_count) const { + for (size_t i = 0; i < kac_count; i++) { + if (GetCapabilityId(kac[i]) == this->GetId()) { + const auto restrict = Decode(kac[i]); + + if (this->GetIndex() == restrict.GetIndex() && this->GetMask() == restrict.GetMask()) { + return true; + } + } + } + return false; + } + ); + + DEFINE_CAPABILITY_CLASS(MapRange, + static constexpr size_t SizeMax = 0x100000; + + u32 GetAddressSize() const { + return (this->GetValue() >> 0) & 0xFFFFFF; + } + + u32 GetFlag() const { + return (this->GetValue() >> 24) & 0x1; + } + + bool IsValid(const u32 next_cap, const u32 *kac, size_t kac_count) const { + if (GetCapabilityId(next_cap) != this->GetId()) { + return false; + } + + const auto next = Decode(next_cap); + const u32 start = this->GetAddressSize(); + const u32 size = next.GetAddressSize(); + const u32 end = start + size; + if (size >= SizeMax) { + return false; + } + + for (size_t i = 0; i < kac_count; i++) { + if (GetCapabilityId(kac[i]) == this->GetId()) { + const auto restrict = Decode(kac[i]); + i++; + if (i >= kac_count || GetCapabilityId(kac[i]) != this->GetId()) { + return false; + } + const auto restrict_next = Decode(kac[i]); + const u32 restrict_start = restrict.GetAddressSize(); + const u32 restrict_size = restrict_next.GetAddressSize(); + const u32 restrict_end = restrict_start + restrict_size; + + if (restrict_size >= SizeMax) { + continue; + } + + if (this->GetFlag() == restrict.GetFlag() && next.GetFlag() == restrict_next.GetFlag()) { + if (restrict_start <= start && start <= restrict_end && end <= restrict_end) { + return true; + } + } + } + } + return false; + } + ); + + DEFINE_CAPABILITY_CLASS(MapPage, + u32 GetAddress() const { + return (this->GetValue() >> 0) & 0xFFFFFF; + } + + bool IsValid(const u32 *kac, size_t kac_count) const { + for (size_t i = 0; i < kac_count; i++) { + if (GetCapabilityId(kac[i]) == this->GetId()) { + const auto restrict = Decode(kac[i]); + + if (this->GetValue() == restrict.GetValue()) { + return true; + } + } + } + return false; + } + ); + + DEFINE_CAPABILITY_CLASS(InterruptPair, + Result Validate(const u32 *kac, size_t kac_count) const; + + static constexpr u32 EmptyInterruptId = 0x3FF; + + u32 GetInterruptId0() const { + return (this->GetValue() >> 0) & 0x3FF; + } + + u32 GetInterruptId1() const { + return (this->GetValue() >> 10) & 0x3FF; + } + + bool IsSingleIdValid(const u32 id, const u32 *kac, size_t kac_count) const { + for (size_t i = 0; i < kac_count; i++) { + if (GetCapabilityId(kac[i]) == this->GetId()) { + const auto restrict = Decode(kac[i]); + + if (restrict.GetInterruptId0() == EmptyInterruptId && restrict.GetInterruptId1() == EmptyInterruptId) { + return true; + } + + if (restrict.GetInterruptId0() == id || restrict.GetInterruptId1() == id) { + return true; + } + } + } + return false; + } + + bool IsValid(const u32 *kac, size_t kac_count) const { + return IsSingleIdValid(this->GetInterruptId0(), kac, kac_count) && IsSingleIdValid(this->GetInterruptId1(), kac, kac_count); + } + ); + + DEFINE_CAPABILITY_CLASS(ApplicationType, + u32 GetApplicationType() const { + return (this->GetValue() >> 0) & 0x3; + } + + bool IsValid(const u32 *kac, size_t kac_count) const { + for (size_t i = 0; i < kac_count; i++) { + if (GetCapabilityId(kac[i]) == this->GetId()) { + const auto restrict = Decode(kac[i]); + + return restrict.GetValue() == this->GetValue(); + } + } + return false; + } + + static constexpr u32 Encode(u32 app_type) { + return ((app_type & 3) << ValueShift) | IdMask; + } + ); + + DEFINE_CAPABILITY_CLASS(KernelVersion, + u32 GetMinorVersion() const { + return (this->GetValue() >> 0) & 0xF; + } + + u32 GetMajorVersion() const { + /* TODO: Are upper bits unused? */ + return (this->GetValue() >> 4) & 0x1FFF; + } + + bool IsValid(const u32 *kac, size_t kac_count) const { + for (size_t i = 0; i < kac_count; i++) { + if (GetCapabilityId(kac[i]) == this->GetId()) { + const auto restrict = Decode(kac[i]); + + return restrict.GetValue() == this->GetValue(); + } + } + return false; + } + ); + + DEFINE_CAPABILITY_CLASS(HandleTable, + u32 GetSize() const { + return (this->GetValue() >> 0) & 0x3FF; + } + + bool IsValid(const u32 *kac, size_t kac_count) const { + for (size_t i = 0; i < kac_count; i++) { + if (GetCapabilityId(kac[i]) == this->GetId()) { + const auto restrict = Decode(kac[i]); + + return this->GetSize() <= restrict.GetSize(); + } + } + return false; + } + ); + + DEFINE_CAPABILITY_CLASS(DebugFlags, + bool GetAllowDebug() const { + return ((this->GetValue() >> 0) & 1) != 0; + } + + bool GetForceDebug() const { + return ((this->GetValue() >> 1) & 1) != 0; + } + + bool IsValid(const u32 *kac, size_t kac_count) const { + for (size_t i = 0; i < kac_count; i++) { + if (GetCapabilityId(kac[i]) == this->GetId()) { + const auto restrict = Decode(kac[i]); + + return (restrict.GetValue() & this->GetValue()) == this->GetValue(); + } + } + return false; + } + + static constexpr u32 Encode(bool allow_debug, bool force_debug) { + const u32 desc = (static_cast(force_debug) << 1) | (static_cast(allow_debug) << 0); + return (desc << ValueShift) | IdMask; + } + ); + + } + + /* Capabilities API. */ + Result ValidateCapabilities(const void *acid_kac, size_t acid_kac_size, const void *aci_kac, size_t aci_kac_size) { + const u32 *acid_caps = reinterpret_cast(acid_kac); + const u32 *aci_caps = reinterpret_cast(aci_kac); + const size_t num_acid_caps = acid_kac_size / sizeof(*acid_caps); + const size_t num_aci_caps = aci_kac_size / sizeof(*aci_caps); + + for (size_t i = 0; i < num_aci_caps; i++) { + const u32 cur_cap = aci_caps[i]; + const auto id = GetCapabilityId(cur_cap); + +#define VALIDATE_CASE(id) \ + case CapabilityId::id: \ + if (!Capability##id::Decode(cur_cap).IsValid(acid_caps, num_acid_caps)) { \ + return ResultLoaderInvalidCapability##id; \ + } \ + break + switch (id) { + VALIDATE_CASE(KernelFlags); + VALIDATE_CASE(SyscallMask); + VALIDATE_CASE(MapPage); + VALIDATE_CASE(InterruptPair); + VALIDATE_CASE(ApplicationType); + VALIDATE_CASE(KernelVersion); + VALIDATE_CASE(HandleTable); + VALIDATE_CASE(DebugFlags); + case CapabilityId::MapRange: + { + /* Map Range needs extra logic because there it involves two sequential caps. */ + i++; + if (i >= num_aci_caps || !CapabilityMapRange::Decode(cur_cap).IsValid(aci_caps[i], acid_caps, num_acid_caps)) { + return ResultLoaderInvalidCapabilityMapRange; + } + } + break; + default: + if (id != CapabilityId::Empty) { + return ResultLoaderUnknownCapability; + } + break; + } +#undef VALIDATE_CASE + } + + return ResultSuccess; + } + + u16 GetProgramInfoFlags(const void *kac, size_t kac_size) { + const u32 *caps = reinterpret_cast(kac); + const size_t num_caps = kac_size / sizeof(*caps); + u16 flags = 0; + + for (size_t i = 0; i < num_caps; i++) { + const u32 cur_cap = caps[i]; + + switch (GetCapabilityId(cur_cap)) { + case CapabilityId::ApplicationType: + { + const auto app_type = CapabilityApplicationType::Decode(cur_cap).GetApplicationType() & ProgramInfoFlag_ApplicationTypeMask; + if (app_type != ProgramInfoFlag_InvalidType) { + flags |= app_type; + } + } + break; + case CapabilityId::DebugFlags: + if (CapabilityDebugFlags::Decode(cur_cap).GetAllowDebug()) { + flags |= ProgramInfoFlag_AllowDebug; + } + break; + default: + break; + } + } + + return flags; + } + + void SetProgramInfoFlags(u16 flags, void *kac, size_t kac_size) { + u32 *caps = reinterpret_cast(kac); + const size_t num_caps = kac_size / sizeof(*caps); + + for (size_t i = 0; i < num_caps; i++) { + const u32 cur_cap = caps[i]; + switch (GetCapabilityId(cur_cap)) { + case CapabilityId::ApplicationType: + caps[i] = CapabilityApplicationType::Encode(flags & ProgramInfoFlag_ApplicationTypeMask); + break; + case CapabilityId::DebugFlags: + caps[i] = CapabilityDebugFlags::Encode((flags & ProgramInfoFlag_AllowDebug) != 0, CapabilityDebugFlags::Decode(cur_cap).GetForceDebug()); + break; + default: + break; + } + } + } + +} diff --git a/stratosphere/loader/source/ldr_capabilities.hpp b/stratosphere/loader/source/ldr_capabilities.hpp new file mode 100644 index 000000000..752d54cc5 --- /dev/null +++ b/stratosphere/loader/source/ldr_capabilities.hpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018-2019 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 +#include +#include + +namespace sts::ldr { + + /* Capabilities API. */ + Result ValidateCapabilities(const void *acid_kac, size_t acid_kac_size, const void *aci_kac, size_t aci_kac_size); + u16 GetProgramInfoFlags(const void *kac, size_t kac_size); + void SetProgramInfoFlags(u16 flags, void *kac, size_t kac_size); + +} diff --git a/stratosphere/loader/source/ldr_content_management.cpp b/stratosphere/loader/source/ldr_content_management.cpp index d07b0cf34..8fb6b1202 100644 --- a/stratosphere/loader/source/ldr_content_management.cpp +++ b/stratosphere/loader/source/ldr_content_management.cpp @@ -13,459 +13,331 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#include + #include -#include -#include -#include -#include -#include -#include -#include - -#include "ldr_registration.hpp" #include "ldr_content_management.hpp" -#include "ldr_hid.hpp" -#include "ldr_npdm.hpp" +#include "ldr_ecs.hpp" -#include "ini.h" +namespace sts::ldr { -static FsFileSystem g_CodeFileSystem = {}; -static FsFileSystem g_HblFileSystem = {}; + namespace { -static std::vector g_created_titles; -static bool g_has_initialized_fs_dev = false; + /* DeviceNames. */ + constexpr const char *CodeFileSystemDeviceName = "code"; + constexpr const char *HblFileSystemDeviceName = "hbl"; + constexpr const char *SdCardFileSystemDeviceName = "sdmc"; -/* Default to Key R, hold disables override, HBL at atmosphere/hbl.nsp. */ -static bool g_mounted_hbl_nsp = false; -static char g_hbl_sd_path[FS_MAX_PATH+1] = "@Sdcard:/atmosphere/hbl.nsp\x00"; + constexpr const char *SdCardStorageMountPoint = "@Sdcard"; -static OverrideKey g_default_override_key = { - .key_combination = KEY_L, - .override_by_default = true -}; + /* Globals. */ + bool g_has_mounted_sd_card = false; -struct HblOverrideConfig { - OverrideKey override_key; - u64 title_id; - bool override_any_app; -}; + ncm::TitleId g_should_override_title_id; + bool g_should_override_hbl = false; + bool g_should_override_sd = false; -static HblOverrideConfig g_hbl_override_config = { - .override_key = { - .key_combination = KEY_R, - .override_by_default = true - }, - .title_id = TitleId_AppletPhotoViewer, - .override_any_app = false -}; + /* Helpers. */ + inline void FixFileSystemPath(char *path) { + for (size_t i = 0; i < FS_MAX_PATH && path[i]; i++) { + if (path[i] == '\\') { + path[i] = '/'; + } + } + } -/* Static buffer for loader.ini contents at runtime. */ -static char g_config_ini_data[0x800]; + inline const char *GetRelativePathStart(const char *relative_path) { + while (*relative_path == '/' || *relative_path == '\\') { + relative_path++; + } + return relative_path; + } -/* SetExternalContentSource extension */ -static std::map g_external_content_sources; + void UpdateShouldOverrideCache(ncm::TitleId title_id) { + if (g_should_override_title_id != title_id) { + cfg::GetOverrideKeyHeldStatus(&g_should_override_hbl, &g_should_override_sd, title_id); + } + g_should_override_title_id = title_id; + } -Result ContentManagement::MountCode(u64 tid, FsStorageId sid) { - char path[FS_MAX_PATH] = {0}; + void InvalidateShouldOverrideCache() { + g_should_override_title_id = {}; + } + + bool ShouldOverrideWithHbl(ncm::TitleId title_id) { + UpdateShouldOverrideCache(title_id); + return g_should_override_hbl; + } + + bool ShouldOverrideWithSd(ncm::TitleId title_id) { + UpdateShouldOverrideCache(title_id); + return g_should_override_sd; + } + + Result MountSdCardFileSystem() { + return fsdevMountSdmc(); + } + + Result MountNspFileSystem(const char *device_name, const char *path) { + FsFileSystem fs; + R_TRY(fsOpenFileSystemWithId(&fs, 0, FsFileSystemType_ApplicationPackage, path)); + if(fsdevMountDevice(device_name, fs) == -1) { + std::abort(); + } + return ResultSuccess; + } + + Result MountHblFileSystem() { + char path[FS_MAX_PATH]; + + /* Print and fix path. */ + std::snprintf(path, FS_MAX_PATH, "%s:/%s", SdCardStorageMountPoint, GetRelativePathStart(cfg::GetHblPath())); + FixFileSystemPath(path); + return MountNspFileSystem(HblFileSystemDeviceName, path); + } + + Result MountSdCardCodeFileSystem(ncm::TitleId title_id) { + char path[FS_MAX_PATH]; + + /* Print and fix path. */ + std::snprintf(path, FS_MAX_PATH, "%s:/atmosphere/titles/%016lx/exefs.nsp", SdCardStorageMountPoint, static_cast(title_id)); + FixFileSystemPath(path); + return MountNspFileSystem(CodeFileSystemDeviceName, path); + } + + Result MountCodeFileSystem(const ncm::TitleLocation &loc) { + char path[FS_MAX_PATH]; + + /* Try to get the content path. */ + R_TRY(ResolveContentPath(path, loc)); + + /* Try to mount the content path. */ + FsFileSystem fs; + R_TRY(fsldrOpenCodeFileSystem(static_cast(loc.title_id), path, &fs)); + if(fsdevMountDevice(CodeFileSystemDeviceName, fs) == -1) { + std::abort(); + } + return ResultSuccess; + } + + FILE *OpenFile(const char *device_name, const char *relative_path) { + /* Allow nullptr device_name/relative path -- those are simply not openable. */ + if (device_name == nullptr || relative_path == nullptr) { + return nullptr; + } + + char path[FS_MAX_PATH]; + std::snprintf(path, FS_MAX_PATH, "%s:/%s", device_name, GetRelativePathStart(relative_path)); + FixFileSystemPath(path); + return fopen(path, "rb"); + } + + FILE *OpenLooseSdFile(ncm::TitleId title_id, const char *relative_path) { + /* Allow nullptr relative path -- those are simply not openable. */ + if (relative_path == nullptr) { + return nullptr; + } + + char path[FS_MAX_PATH]; + std::snprintf(path, FS_MAX_PATH, "/atmosphere/titles/%016lx/exefs/%s", static_cast(title_id), GetRelativePathStart(relative_path)); + FixFileSystemPath(path); + return OpenFile(SdCardFileSystemDeviceName, path); + } + + bool IsFileStubbed(ncm::TitleId title_id, const char *relative_path) { + /* Allow nullptr relative path -- those are simply not openable. */ + if (relative_path == nullptr) { + return true; + } + + /* Only allow stubbing in the case where we're considering SD card content. */ + if (!ShouldOverrideWithSd(title_id)) { + return false; + } + + char path[FS_MAX_PATH]; + std::snprintf(path, FS_MAX_PATH, "/atmosphere/titles/%016lx/exefs/%s.stub", static_cast(title_id), GetRelativePathStart(relative_path)); + FixFileSystemPath(path); + FILE *f = OpenFile(SdCardFileSystemDeviceName, path); + if (f == nullptr) { + return false; + } + fclose(f); + return true; + } + + bool IsMounted(const char *device_name) { + /* Allow nullptr device_name -- those are simply not openable. */ + if (device_name == nullptr) { + return false; + } + + char path[FS_MAX_PATH]; + std::snprintf(path, FS_MAX_PATH, "%s:", device_name); + return FindDevice(device_name) >= 0; + } + + FILE *OpenBaseExefsFile(ncm::TitleId title_id, const char *relative_path) { + /* Allow nullptr relative path -- those are simply not openable. */ + if (relative_path == nullptr) { + return nullptr; + } + + /* Check if stubbed. */ + if (IsFileStubbed(title_id, relative_path)) { + return nullptr; + } + + return OpenFile(CodeFileSystemDeviceName, relative_path); + } - /* We defer SD card mounting, so if relevant ensure it is mounted. */ - if (!g_has_initialized_fs_dev) { - TryMountSdCard(); } - if (g_has_initialized_fs_dev) { - RefreshConfigurationData(); + ScopedCodeMount::~ScopedCodeMount() { + /* Unmount devices. */ + if (this->is_code_mounted) { + fsdevUnmountDevice(CodeFileSystemDeviceName); + } + if (this->is_hbl_mounted) { + fsdevUnmountDevice(HblFileSystemDeviceName); + } + + /* Unmounting code means we should invalidate our configuration cache. */ + InvalidateShouldOverrideCache(); } - if (ShouldOverrideContentsWithSD(tid) && R_SUCCEEDED(MountCodeNspOnSd(tid))) { + Result MountCode(ScopedCodeMount &out, const ncm::TitleLocation &loc) { + ScopedCodeMount mount; + + bool is_sd_initialized = cfg::IsSdCardInitialized(); + + /* Check if we're ready to mount the SD card. */ + if (!g_has_mounted_sd_card) { + if (is_sd_initialized) { + R_ASSERT(MountSdCardFileSystem()); + g_has_mounted_sd_card = true; + } + } + + /* Check if we should override contents. */ + if (ShouldOverrideWithHbl(loc.title_id)) { + /* Try to mount HBL. */ + if (R_SUCCEEDED(MountHblFileSystem())) { + mount.SetHblMounted(); + } + } + if (ShouldOverrideWithSd(loc.title_id)) { + /* Try to mount Code NSP on SD. */ + if (R_SUCCEEDED(MountSdCardCodeFileSystem(loc.title_id))) { + mount.SetCodeMounted(); + } + } + + /* If we haven't already mounted code, mount it. */ + if (!mount.IsCodeMounted()) { + R_TRY(MountCodeFileSystem(loc)); + mount.SetCodeMounted(); + } + + /* Set out to scoped holder. */ + out = std::move(mount); return ResultSuccess; } - R_TRY(ResolveContentPath(path, tid, sid)); + Result OpenCodeFile(FILE *&out, ncm::TitleId title_id, const char *relative_path) { + FILE *f = nullptr; + const char *ecs_device_name = ecs::Get(title_id); - /* Fix up path. */ - for (unsigned int i = 0; i < FS_MAX_PATH && path[i] != '\x00'; i++) { - if (path[i] == '\\') { - path[i] = '/'; + if (IsMounted(ecs_device_name)) { + /* First priority: Open from external content. */ + f = OpenFile(ecs_device_name, relative_path); + } else if (ShouldOverrideWithHbl(title_id)) { + /* Next, try to mount from HBL. */ + f = OpenFile(HblFileSystemDeviceName, relative_path); + } else { + /* If not ECS or HBL, try a loose file on the SD. */ + if (ShouldOverrideWithSd(title_id)) { + f = OpenLooseSdFile(title_id, relative_path); + } + + /* If we fail, try the original exefs. */ + if (f == nullptr) { + f = OpenBaseExefsFile(title_id, relative_path); + } } - } - /* Always re-initialize fsp-ldr, in case it's closed */ - DoWithSmSession([&]() { - R_ASSERT(fsldrInitialize()); - }); - ON_SCOPE_EXIT { fsldrExit(); }; - - R_TRY(fsldrOpenCodeFileSystem(tid, path, &g_CodeFileSystem)); - - fsdevMountDevice("code", g_CodeFileSystem); - TryMountHblNspOnSd(); - return ResultSuccess; -} - -Result ContentManagement::UnmountCode() { - if (g_mounted_hbl_nsp) { - fsdevUnmountDevice("hbl"); - g_mounted_hbl_nsp = false; - } - fsdevUnmountDevice("code"); - return ResultSuccess; -} - - -void ContentManagement::TryMountHblNspOnSd() { - char path[FS_MAX_PATH + 1]; - strncpy(path, g_hbl_sd_path, FS_MAX_PATH); - path[FS_MAX_PATH] = 0; - for (unsigned int i = 0; i < FS_MAX_PATH && path[i] != '\x00'; i++) { - if (path[i] == '\\') { - path[i] = '/'; + /* If nothing worked, we failed to find the path. */ + if (f == nullptr) { + return ResultFsPathNotFound; } + + out = f; + return ResultSuccess; } - if (g_has_initialized_fs_dev && !g_mounted_hbl_nsp && R_SUCCEEDED(fsOpenFileSystemWithId(&g_HblFileSystem, 0, FsFileSystemType_ApplicationPackage, path))) { - fsdevMountDevice("hbl", g_HblFileSystem); - g_mounted_hbl_nsp = true; + + Result OpenCodeFileFromBaseExefs(FILE *&out, ncm::TitleId title_id, const char *relative_path) { + /* Open the file. */ + FILE *f = OpenBaseExefsFile(title_id, relative_path); + if (f == nullptr) { + return ResultFsPathNotFound; + } + + out = f; + return ResultSuccess; } -} -Result ContentManagement::MountCodeNspOnSd(u64 tid) { - char path[FS_MAX_PATH+1] = {0}; - snprintf(path, FS_MAX_PATH, "@Sdcard:/atmosphere/titles/%016lx/exefs.nsp", tid); + /* Redirection API. */ + Result ResolveContentPath(char *out_path, const ncm::TitleLocation &loc) { + char path[FS_MAX_PATH]; - R_TRY(fsOpenFileSystemWithId(&g_CodeFileSystem, 0, FsFileSystemType_ApplicationPackage, path)); - fsdevMountDevice("code", g_CodeFileSystem); - TryMountHblNspOnSd(); + /* Try to get the path from the registered resolver. */ + LrRegisteredLocationResolver reg; + R_TRY(lrOpenRegisteredLocationResolver(®)); + ON_SCOPE_EXIT { serviceClose(®.s); }; - return ResultSuccess; -} + R_TRY_CATCH(lrRegLrResolveProgramPath(®, static_cast(loc.title_id), path)) { + R_CATCH(ResultLrProgramNotFound) { + /* Program wasn't found via registered resolver, fall back to the normal resolver. */ + LrLocationResolver lr; + R_TRY(lrOpenLocationResolver(static_cast(loc.storage_id), &lr)); + ON_SCOPE_EXIT { serviceClose(&lr.s); }; -Result ContentManagement::MountCodeForTidSid(Registration::TidSid *tid_sid) { - return MountCode(tid_sid->title_id, tid_sid->storage_id); -} + R_TRY(lrLrResolveProgramPath(&lr, static_cast(loc.title_id), path)); + } + } R_END_TRY_CATCH; -Result ContentManagement::ResolveContentPath(char *out_path, u64 tid, FsStorageId sid) { - char path[FS_MAX_PATH] = {0}; + std::strncpy(out_path, path, FS_MAX_PATH); + out_path[FS_MAX_PATH - 1] = '\0'; + FixFileSystemPath(out_path); + return ResultSuccess; + } - /* Try to get the path from the registered resolver. */ - LrRegisteredLocationResolver reg; - R_TRY(lrOpenRegisteredLocationResolver(®)); - ON_SCOPE_EXIT { serviceClose(®.s); }; + Result RedirectContentPath(const char *path, const ncm::TitleLocation &loc) { + LrLocationResolver lr; + R_TRY(lrOpenLocationResolver(static_cast(loc.storage_id), &lr)); + ON_SCOPE_EXIT { serviceClose(&lr.s); }; - R_TRY_CATCH(lrRegLrResolveProgramPath(®, tid, path)) { - R_CATCH(ResultLrProgramNotFound) { - /* Program wasn't found via registered resolver, fall back to the normal resolver. */ - LrLocationResolver lr; - R_TRY(lrOpenLocationResolver(sid, &lr)); - ON_SCOPE_EXIT { serviceClose(&lr.s); }; + return lrLrRedirectProgramPath(&lr, static_cast(loc.title_id), path); + } - R_TRY(lrLrResolveProgramPath(&lr, tid, path)); + Result RedirectHtmlDocumentPathForHbl(const ncm::TitleLocation &loc) { + char path[FS_MAX_PATH]; - strncpy(out_path, path, FS_MAX_PATH); + /* Open a locaiton resolver. */ + LrLocationResolver lr; + R_TRY(lrOpenLocationResolver(static_cast(loc.storage_id), &lr)); + ON_SCOPE_EXIT { serviceClose(&lr.s); }; + + /* If there's already a Html Document path, we don't need to set one. */ + if (R_SUCCEEDED(lrLrResolveApplicationHtmlDocumentPath(&lr, static_cast(loc.title_id), path))) { return ResultSuccess; } - } R_END_TRY_CATCH; - strncpy(out_path, path, FS_MAX_PATH); - return ResultSuccess; -} + /* We just need to set this to any valid NCA path. Let's use the executable path. */ + R_TRY(lrLrResolveProgramPath(&lr, static_cast(loc.title_id), path)); + R_TRY(lrLrRedirectApplicationHtmlDocumentPath(&lr, static_cast(loc.title_id), path)); -Result ContentManagement::ResolveContentPathForTidSid(char *out_path, Registration::TidSid *tid_sid) { - return ResolveContentPath(out_path, tid_sid->title_id, tid_sid->storage_id); -} - -Result ContentManagement::RedirectContentPath(const char *path, u64 tid, FsStorageId sid) { - LrLocationResolver lr; - R_TRY(lrOpenLocationResolver(sid, &lr)); - ON_SCOPE_EXIT { serviceClose(&lr.s); }; - - return lrLrRedirectProgramPath(&lr, tid, path); -} - -Result ContentManagement::RedirectContentPathForTidSid(const char *path, Registration::TidSid *tid_sid) { - return RedirectContentPath(path, tid_sid->title_id, tid_sid->storage_id); -} - -void ContentManagement::RedirectHtmlDocumentPathForHbl(u64 tid, FsStorageId sid) { - LrLocationResolver lr; - char path[FS_MAX_PATH] = {0}; - - /* Open resolver. */ - if (R_FAILED(lrOpenLocationResolver(sid, &lr))) { - return; + return ResultSuccess; } - /* Ensure close on exit. */ - ON_SCOPE_EXIT { serviceClose(&lr.s); }; - - /* Only redirect the HTML document path if there is not one already. */ - if (R_SUCCEEDED(lrLrResolveApplicationHtmlDocumentPath(&lr, tid, path))) { - return; - } - - /* We just need to set this to any valid NCA path. Let's use the executable path. */ - if (R_FAILED(lrLrResolveProgramPath(&lr, tid, path))) { - return; - } - - lrLrRedirectApplicationHtmlDocumentPath(&lr, tid, path); -} - -bool ContentManagement::HasCreatedTitle(u64 tid) { - return std::find(g_created_titles.begin(), g_created_titles.end(), tid) != g_created_titles.end(); -} - -void ContentManagement::SetCreatedTitle(u64 tid) { - if (!HasCreatedTitle(tid)) { - g_created_titles.push_back(tid); - } -} - -static OverrideKey ParseOverrideKey(const char *value) { - OverrideKey cfg; - - /* Parse on by default. */ - if (value[0] == '!') { - cfg.override_by_default = true; - value++; - } else { - cfg.override_by_default = false; - } - - /* Parse key combination. */ - if (strcasecmp(value, "A") == 0) { - cfg.key_combination = KEY_A; - } else if (strcasecmp(value, "B") == 0) { - cfg.key_combination = KEY_B; - } else if (strcasecmp(value, "X") == 0) { - cfg.key_combination = KEY_X; - } else if (strcasecmp(value, "Y") == 0) { - cfg.key_combination = KEY_Y; - } else if (strcasecmp(value, "LS") == 0) { - cfg.key_combination = KEY_LSTICK; - } else if (strcasecmp(value, "RS") == 0) { - cfg.key_combination = KEY_RSTICK; - } else if (strcasecmp(value, "L") == 0) { - cfg.key_combination = KEY_L; - } else if (strcasecmp(value, "R") == 0) { - cfg.key_combination = KEY_R; - } else if (strcasecmp(value, "ZL") == 0) { - cfg.key_combination = KEY_ZL; - } else if (strcasecmp(value, "ZR") == 0) { - cfg.key_combination = KEY_ZR; - } else if (strcasecmp(value, "PLUS") == 0) { - cfg.key_combination = KEY_PLUS; - } else if (strcasecmp(value, "MINUS") == 0) { - cfg.key_combination = KEY_MINUS; - } else if (strcasecmp(value, "DLEFT") == 0) { - cfg.key_combination = KEY_DLEFT; - } else if (strcasecmp(value, "DUP") == 0) { - cfg.key_combination = KEY_DUP; - } else if (strcasecmp(value, "DRIGHT") == 0) { - cfg.key_combination = KEY_DRIGHT; - } else if (strcasecmp(value, "DDOWN") == 0) { - cfg.key_combination = KEY_DDOWN; - } else if (strcasecmp(value, "SL") == 0) { - cfg.key_combination = KEY_SL; - } else if (strcasecmp(value, "SR") == 0) { - cfg.key_combination = KEY_SR; - } else { - cfg.key_combination = 0; - } - - return cfg; -} - -static int LoaderIniHandler(void *user, const char *section, const char *name, const char *value) { - /* Taken and modified, with love, from Rajkosto's implementation. */ - if (strcasecmp(section, "hbl_config") == 0) { - if (strcasecmp(name, "title_id") == 0) { - if (strcasecmp(value, "app") == 0) { - /* DEPRECATED */ - g_hbl_override_config.override_any_app = true; - g_hbl_override_config.title_id = 0; - } else { - u64 override_tid = strtoul(value, NULL, 16); - if (override_tid != 0) { - g_hbl_override_config.title_id = override_tid; - } - } - } else if (strcasecmp(name, "path") == 0) { - while (*value == '/' || *value == '\\') { - value++; - } - snprintf(g_hbl_sd_path, FS_MAX_PATH, "@Sdcard:/%s", value); - g_hbl_sd_path[FS_MAX_PATH] = 0; - } else if (strcasecmp(name, "override_key") == 0) { - g_hbl_override_config.override_key = ParseOverrideKey(value); - } else if (strcasecmp(name, "override_any_app") == 0) { - if (strcasecmp(value, "true") == 0 || strcasecmp(value, "1") == 0) { - g_hbl_override_config.override_any_app = true; - } else if (strcasecmp(value, "false") == 0 || strcasecmp(value, "0") == 0) { - g_hbl_override_config.override_any_app = false; - } else { - /* I guess we default to not changing the value? */ - } - } - } else if (strcasecmp(section, "default_config") == 0) { - if (strcasecmp(name, "override_key") == 0) { - g_default_override_key = ParseOverrideKey(value); - } - } else { - return 0; - } - return 1; -} - -static int LoaderTitleSpecificIniHandler(void *user, const char *section, const char *name, const char *value) { - /* We'll output an override key when relevant. */ - OverrideKey *user_cfg = reinterpret_cast(user); - - if (strcasecmp(section, "override_config") == 0) { - if (strcasecmp(name, "override_key") == 0) { - *user_cfg = ParseOverrideKey(value); - } - } else { - return 0; - } - return 1; -} - -void ContentManagement::RefreshConfigurationData() { - FILE *config = fopen("sdmc:/atmosphere/loader.ini", "r"); - if (config == NULL) { - return; - } - - std::fill(g_config_ini_data, g_config_ini_data + 0x800, 0); - fread(g_config_ini_data, 1, 0x7FF, config); - fclose(config); - - ini_parse_string(g_config_ini_data, LoaderIniHandler, NULL); -} - -void ContentManagement::TryMountSdCard() { - /* Mount SD card, if psc, bus, and pcv have been created. */ - if (!g_has_initialized_fs_dev && HasCreatedTitle(TitleId_Psc) && HasCreatedTitle(TitleId_Bus) && HasCreatedTitle(TitleId_Pcv)) { - bool can_mount = true; - DoWithSmSession([&]() { - Handle tmp_hnd = 0; - static const char * const required_active_services[] = {"pcv", "gpio", "pinmux", "psc:c"}; - for (unsigned int i = 0; i < sizeof(required_active_services) / sizeof(required_active_services[0]); i++) { - if (R_FAILED(smGetServiceOriginal(&tmp_hnd, smEncodeName(required_active_services[i])))) { - can_mount = false; - break; - } else { - svcCloseHandle(tmp_hnd); - } - } - }); - - if (can_mount && R_SUCCEEDED(fsdevMountSdmc())) { - g_has_initialized_fs_dev = true; - } - } -} - -static bool IsHBLTitleId(u64 tid) { - return ((g_hbl_override_config.override_any_app && TitleIdIsApplication(tid)) || (tid == g_hbl_override_config.title_id)); -} - -OverrideKey ContentManagement::GetTitleOverrideKey(u64 tid) { - OverrideKey cfg = g_default_override_key; - char path[FS_MAX_PATH+1] = {0}; - snprintf(path, FS_MAX_PATH, "sdmc:/atmosphere/titles/%016lx/config.ini", tid); - - - FILE *config = fopen(path, "r"); - if (config != NULL) { - ON_SCOPE_EXIT { fclose(config); }; - - /* Parse current title ini. */ - ini_parse_file(config, LoaderTitleSpecificIniHandler, &cfg); - } - - return cfg; -} - -static bool ShouldOverrideContents(OverrideKey *cfg) { - u64 kDown = 0; - bool keys_triggered = (R_SUCCEEDED(HidManagement::GetKeysHeld(&kDown)) && ((kDown & cfg->key_combination) != 0)); - return g_has_initialized_fs_dev && (cfg->override_by_default ^ keys_triggered); -} - -bool ContentManagement::ShouldOverrideContentsWithHBL(u64 tid) { - if (g_mounted_hbl_nsp && tid >= TitleId_AppletStart && HasCreatedTitle(TitleId_AppletQlaunch)) { - /* Return whether we should override contents with HBL. */ - return IsHBLTitleId(tid) && ShouldOverrideContents(&g_hbl_override_config.override_key); - } else { - /* Don't override if we failed to mount HBL or haven't launched qlaunch. */ - return false; - } -} - -bool ContentManagement::ShouldOverrideContentsWithSD(u64 tid) { - if (g_has_initialized_fs_dev) { - if (tid >= TitleId_AppletStart && HasCreatedTitle(TitleId_AppletQlaunch)) { - /* Check whether we should override with non-HBL. */ - OverrideKey title_cfg = GetTitleOverrideKey(tid); - return ShouldOverrideContents(&title_cfg); - } else { - /* Always redirect before qlaunch. */ - return true; - } - } else { - /* Never redirect before we can do so. */ - return false; - } -} - -/* SetExternalContentSource extension */ -ContentManagement::ExternalContentSource *ContentManagement::GetExternalContentSource(u64 tid) { - auto i = g_external_content_sources.find(tid); - if (i == g_external_content_sources.end()) { - return nullptr; - } else { - return &i->second; - } -} - -Result ContentManagement::SetExternalContentSource(u64 tid, FsFileSystem filesystem) { - if (g_external_content_sources.size() >= 16) { - return ResultLoaderTooManyArguments; /* TODO: Is this an appropriate error? */ - } - - /* Remove any existing ECS for this title. */ - ClearExternalContentSource(tid); - - char mountpoint[32]; - ExternalContentSource::GenerateMountpointName(tid, mountpoint, sizeof(mountpoint)); - if (fsdevMountDevice(mountpoint, filesystem) == -1) { - return ResultFsMountNameAlreadyExists; - } - g_external_content_sources.emplace( - std::piecewise_construct, - std::make_tuple(tid), - std::make_tuple(tid, mountpoint)); - - return ResultSuccess; -} - -void ContentManagement::ClearExternalContentSource(u64 tid) { - auto i = g_external_content_sources.find(tid); - if (i != g_external_content_sources.end()) { - g_external_content_sources.erase(i); - } -} - -void ContentManagement::ExternalContentSource::GenerateMountpointName(u64 tid, char *out, size_t max_length) { - snprintf(out, max_length, "ecs-%016lx", tid); -} - -ContentManagement::ExternalContentSource::ExternalContentSource(u64 tid, const char *mountpoint) : tid(tid) { - strncpy(this->mountpoint, mountpoint, sizeof(this->mountpoint)); - NpdmUtils::InvalidateCache(tid); -} - -ContentManagement::ExternalContentSource::~ExternalContentSource() { - fsdevUnmountDevice(mountpoint); } diff --git a/stratosphere/loader/source/ldr_content_management.hpp b/stratosphere/loader/source/ldr_content_management.hpp index 4fd326e0b..694d02a54 100644 --- a/stratosphere/loader/source/ldr_content_management.hpp +++ b/stratosphere/loader/source/ldr_content_management.hpp @@ -16,55 +16,64 @@ #pragma once #include +#include +#include -#include "ldr_registration.hpp" +namespace sts::ldr { -struct OverrideKey { - u64 key_combination; - bool override_by_default; -}; + /* Utility reference to make code mounting automatic. */ + class ScopedCodeMount { + NON_COPYABLE(ScopedCodeMount); + private: + bool is_code_mounted; + bool is_hbl_mounted; + public: + ScopedCodeMount() : is_code_mounted(false), is_hbl_mounted(false) { /* ... */ } + ScopedCodeMount(bool c, bool h) : is_code_mounted(c), is_hbl_mounted(h) { /* ... */ } + ~ScopedCodeMount(); -class ContentManagement { - public: - static Result MountCode(u64 tid, FsStorageId sid); - static Result MountCodeNspOnSd(u64 tid); - static void TryMountHblNspOnSd(); - static Result UnmountCode(); - static Result MountCodeForTidSid(Registration::TidSid *tid_sid); + ScopedCodeMount(ScopedCodeMount&& rhs) { + this->is_code_mounted = rhs.is_code_mounted; + this->is_hbl_mounted = rhs.is_hbl_mounted; + rhs.is_code_mounted = false; + rhs.is_hbl_mounted = false; + } - static Result ResolveContentPath(char *out_path, u64 tid, FsStorageId sid); - static Result RedirectContentPath(const char *path, u64 tid, FsStorageId sid); - static Result ResolveContentPathForTidSid(char *out_path, Registration::TidSid *tid_sid); - static Result RedirectContentPathForTidSid(const char *path, Registration::TidSid *tid_sid); + ScopedCodeMount& operator=(ScopedCodeMount&& rhs) { + rhs.Swap(*this); + return *this; + } - static void RedirectHtmlDocumentPathForHbl(u64 tid, FsStorageId sid); + void Swap(ScopedCodeMount& rhs) { + std::swap(this->is_code_mounted, rhs.is_code_mounted); + std::swap(this->is_hbl_mounted, rhs.is_hbl_mounted); + } - static bool HasCreatedTitle(u64 tid); - static void SetCreatedTitle(u64 tid); - static void RefreshConfigurationData(); - static void TryMountSdCard(); + void SetCodeMounted() { + this->is_code_mounted = true; + } - static OverrideKey GetTitleOverrideKey(u64 tid); - static bool ShouldOverrideContentsWithSD(u64 tid); - static bool ShouldOverrideContentsWithHBL(u64 tid); + void SetHblMounted() { + this->is_hbl_mounted = true; + } - /* SetExternalContentSource extension */ - class ExternalContentSource { - public: - static void GenerateMountpointName(u64 tid, char *out, size_t max_length); + bool IsCodeMounted() const { + return this->is_code_mounted; + } - ExternalContentSource(u64 tid, const char *mountpoint); - ~ExternalContentSource(); + bool IsHblMounted() const { + return this->is_hbl_mounted; + } + }; - ExternalContentSource(const ExternalContentSource &other) = delete; - ExternalContentSource(ExternalContentSource &&other) = delete; - ExternalContentSource &operator=(const ExternalContentSource &other) = delete; - ExternalContentSource &operator=(ExternalContentSource &&other) = delete; + /* Content Management API. */ + Result MountCode(ScopedCodeMount &out, const ncm::TitleLocation &loc); + Result OpenCodeFile(FILE *&out, ncm::TitleId title_id, const char *relative_path); + Result OpenCodeFileFromBaseExefs(FILE *&out, ncm::TitleId title_id, const char *relative_path); - const u64 tid; - char mountpoint[32]; - }; - static ExternalContentSource *GetExternalContentSource(u64 tid); /* returns nullptr if no ECS is set */ - static Result SetExternalContentSource(u64 tid, FsFileSystem filesystem); /* takes ownership of filesystem */ - static void ClearExternalContentSource(u64 tid); -}; + /* Redirection API. */ + Result ResolveContentPath(char *out_path, const ncm::TitleLocation &loc); + Result RedirectContentPath(const char *path, const ncm::TitleLocation &loc); + Result RedirectHtmlDocumentPathForHbl(const ncm::TitleLocation &loc); + +} diff --git a/stratosphere/loader/source/ldr_debug_monitor.cpp b/stratosphere/loader/source/ldr_debug_monitor.cpp deleted file mode 100644 index f4f7babea..000000000 --- a/stratosphere/loader/source/ldr_debug_monitor.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2018-2019 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 -#include -#include -#include "ldr_debug_monitor.hpp" -#include "ldr_launch_queue.hpp" -#include "ldr_registration.hpp" - -Result DebugMonitorService::AddTitleToLaunchQueue(u64 tid, InPointer args, u32 args_size) { - if (args.num_elements < args_size) args_size = args.num_elements; - return LaunchQueue::Add(tid, args.pointer, args_size); -} - -void DebugMonitorService::ClearLaunchQueue() { - LaunchQueue::Clear(); -} - -Result DebugMonitorService::GetProcessModuleInfo(Out count, OutPointerWithClientSize out, u64 pid) { - /* Zero out the output memory. */ - std::memset(out.pointer, 0, out.num_elements * sizeof(LoaderModuleInfo)); - /* Actually return the nso infos. */ - return Registration::GetProcessModuleInfo(out.pointer, out.num_elements, pid, count.GetPointer()); -} diff --git a/stratosphere/loader/source/ldr_debug_monitor.hpp b/stratosphere/loader/source/ldr_debug_monitor.hpp deleted file mode 100644 index 3155f0b0d..000000000 --- a/stratosphere/loader/source/ldr_debug_monitor.hpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2018-2019 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 - -#include - -enum DebugMonitorServiceCmd { - Dmnt_Cmd_AddTitleToLaunchQueue = 0, - Dmnt_Cmd_ClearLaunchQueue = 1, - Dmnt_Cmd_GetProcessModuleInfo = 2 -}; - -class DebugMonitorService final : public IServiceObject { - private: - /* Actual commands. */ - Result AddTitleToLaunchQueue(u64 tid, InPointer args, u32 args_size); - void ClearLaunchQueue(); - Result GetProcessModuleInfo(Out count, OutPointerWithClientSize out, u64 pid); - public: - DEFINE_SERVICE_DISPATCH_TABLE { - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - }; -}; diff --git a/stratosphere/loader/source/ldr_ecs.cpp b/stratosphere/loader/source/ldr_ecs.cpp new file mode 100644 index 000000000..88c360545 --- /dev/null +++ b/stratosphere/loader/source/ldr_ecs.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2018-2019 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 "ldr_ecs.hpp" + +namespace sts::ldr::ecs { + + namespace { + + /* Convenience definition. */ + constexpr size_t DeviceNameSizeMax = 0x20; + constexpr size_t MaxExternalContentSourceCount = 0x10; + + /* Types. */ + class ExternalContentSource { + NON_COPYABLE(ExternalContentSource); + NON_MOVEABLE(ExternalContentSource); + private: + char device_name[DeviceNameSizeMax]; + public: + ExternalContentSource(const char *dn){ + std::strncpy(this->device_name, dn, sizeof(this->device_name) - 1); + this->device_name[sizeof(this->device_name) - 1] = '\0'; + } + + ~ExternalContentSource() { + fsdevUnmountDevice(this->device_name); + } + + const char *GetDeviceName() const { + return this->device_name; + } + }; + + /* Global storage. */ + std::unordered_map g_map; + } + + /* API. */ + const char *Get(ncm::TitleId title_id) { + auto it = g_map.find(static_cast(title_id)); + if (it == g_map.end()) { + return nullptr; + } + return it->second.GetDeviceName(); + } + + Result Set(Handle *out, ncm::TitleId title_id) { + if (g_map.size() >= MaxExternalContentSourceCount) { + /* TODO: Is this an appropriate error? */ + return ResultLoaderTooManyArguments; + } + + /* Clear any sources. */ + R_ASSERT(Clear(title_id)); + + /* Generate mountpoint. */ + char device_name[DeviceNameSizeMax]; + std::snprintf(device_name, DeviceNameSizeMax, "ecs-%016lx", static_cast(title_id)); + + /* Create session. */ + AutoHandle server, client; + { + Handle s_h, c_h; + R_TRY(svcCreateSession(&s_h, &c_h, 0, 0)); + server.Reset(s_h); + client.Reset(c_h); + } + + /* Create service. */ + Service srv; + serviceCreate(&srv, client.Move()); + FsFileSystem fs = { srv }; + + /* Try to mount. */ + if (fsdevMountDevice(device_name, fs) == -1) { + serviceClose(&srv); + return ResultFsMountNameAlreadyExists; + } + + /* Add to map. */ + g_map.emplace(static_cast(title_id), device_name); + *out = server.Move(); + return ResultSuccess; + } + + Result Clear(ncm::TitleId title_id) { + /* Delete if present. */ + auto it = g_map.find(static_cast(title_id)); + if (it != g_map.end()) { + g_map.erase(it); + } + return ResultSuccess; + } + +} diff --git a/stratosphere/loader/source/ldr_ecs.hpp b/stratosphere/loader/source/ldr_ecs.hpp new file mode 100644 index 000000000..34044caa3 --- /dev/null +++ b/stratosphere/loader/source/ldr_ecs.hpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018-2019 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 +#include +#include + +namespace sts::ldr::ecs { + + /* External Content Source API. */ + const char *Get(ncm::TitleId title_id); + Result Set(Handle *out, ncm::TitleId title_id); + Result Clear(ncm::TitleId title_id); + +} diff --git a/stratosphere/loader/source/ldr_hid.cpp b/stratosphere/loader/source/ldr_hid.cpp deleted file mode 100644 index 20ad55995..000000000 --- a/stratosphere/loader/source/ldr_hid.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2018-2019 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 - -#include "ldr_content_management.hpp" -#include "ldr_hid.hpp" - -Result HidManagement::GetKeysHeld(u64 *keys) { - if (!ContentManagement::HasCreatedTitle(TitleId_Hid)) { - return MAKERESULT(Module_Libnx, LibnxError_InitFail_HID); - } - - if (!serviceIsActive(hidGetSessionService())) { - bool initialized = false; - DoWithSmSession([&]() { - if (R_SUCCEEDED(hidInitialize())) { - initialized = true; - } - }); - if (!initialized) { - return MAKERESULT(Module_Libnx, LibnxError_InitFail_HID); - } - } - - hidScanInput(); - *keys = 0; - - for (int controller = 0; controller < 10; controller++) { - *keys |= hidKeysHeld((HidControllerID) controller); - } - - return ResultSuccess; -} diff --git a/stratosphere/loader/source/ldr_launch_queue.cpp b/stratosphere/loader/source/ldr_launch_queue.cpp deleted file mode 100644 index dad20e93b..000000000 --- a/stratosphere/loader/source/ldr_launch_queue.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2018-2019 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 -#include -#include -#include -#include "ldr_launch_queue.hpp" -#include "meta_tools.hpp" - -static std::array g_launch_queue = {}; - -Result LaunchQueue::Add(u64 tid, const char *args, u64 arg_size) { - if (arg_size > LAUNCH_QUEUE_ARG_SIZE_MAX) { - return ResultLoaderTooLongArgument; - } - - int idx = GetFreeIndex(tid); - if (idx == LAUNCH_QUEUE_FULL) { - return ResultLoaderTooManyArguments; - } - - g_launch_queue[idx].tid = tid; - g_launch_queue[idx].arg_size = arg_size; - - std::copy(args, args + arg_size, g_launch_queue[idx].args); - return ResultSuccess; -} - -Result LaunchQueue::AddCopy(u64 tid_base, u64 tid) { - int idx = GetIndex(tid_base); - if (idx == LAUNCH_QUEUE_FULL) { - return ResultSuccess; - } - - return Add(tid, g_launch_queue[idx].args, g_launch_queue[idx].arg_size); -} - - -Result LaunchQueue::AddItem(const LaunchItem *item) { - if (item->arg_size > LAUNCH_QUEUE_ARG_SIZE_MAX) { - return ResultLoaderTooLongArgument; - } - - int idx = GetFreeIndex(item->tid); - if (idx == LAUNCH_QUEUE_FULL) { - return ResultLoaderTooManyArguments; - } - - g_launch_queue[idx] = *item; - return ResultSuccess; -} - -int LaunchQueue::GetIndex(u64 tid) { - auto it = std::find_if(g_launch_queue.begin(), g_launch_queue.end(), member_equals_fn(&LaunchQueue::LaunchItem::tid, tid)); - if (it == g_launch_queue.end()) { - return LAUNCH_QUEUE_FULL; - } - return std::distance(g_launch_queue.begin(), it); -} - -int LaunchQueue::GetFreeIndex(u64 tid) { - for (unsigned int i = 0; i < LAUNCH_QUEUE_SIZE; i++) { - if (g_launch_queue[i].tid == tid || g_launch_queue[i].tid == 0x0) { - return i; - } - } - return LAUNCH_QUEUE_FULL; -} - -bool LaunchQueue::Contains(u64 tid) { - return GetIndex(tid) != LAUNCH_QUEUE_FULL; -} - -void LaunchQueue::Clear() { - for (unsigned int i = 0; i < LAUNCH_QUEUE_SIZE; i++) { - g_launch_queue[i].tid = 0; - } -} - - -LaunchQueue::LaunchItem *LaunchQueue::GetItem(u64 tid) { - int idx = GetIndex(tid); - if (idx == LAUNCH_QUEUE_FULL) { - return NULL; - } - return &g_launch_queue[idx]; -} diff --git a/stratosphere/loader/source/ldr_launch_record.cpp b/stratosphere/loader/source/ldr_launch_record.cpp new file mode 100644 index 000000000..79b85c26b --- /dev/null +++ b/stratosphere/loader/source/ldr_launch_record.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018-2019 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 + +#include "ldr_launch_record.hpp" + +namespace sts::ldr { + + namespace { + + /* Global cache. */ + std::set g_launched_titles; + + } + + /* Launch Record API. */ + bool HasLaunchedTitle(ncm::TitleId title_id) { + return g_launched_titles.find(static_cast(title_id)) != g_launched_titles.end(); + } + + void SetLaunchedTitle(ncm::TitleId title_id) { + g_launched_titles.insert(static_cast(title_id)); + } + +} + +/* Loader wants to override this libstratosphere function, which is weakly linked. */ +/* This is necessary to prevent circular dependencies. */ +namespace sts::pm::info { + + Result HasLaunchedTitle(bool *out, u64 title_id) { + *out = ldr::HasLaunchedTitle(ncm::TitleId{title_id}); + return ResultSuccess; + } + +} diff --git a/stratosphere/loader/source/ldr_hid.hpp b/stratosphere/loader/source/ldr_launch_record.hpp similarity index 76% rename from stratosphere/loader/source/ldr_hid.hpp rename to stratosphere/loader/source/ldr_launch_record.hpp index af4188e9f..9c4c3806e 100644 --- a/stratosphere/loader/source/ldr_hid.hpp +++ b/stratosphere/loader/source/ldr_launch_record.hpp @@ -16,8 +16,13 @@ #pragma once #include +#include +#include -class HidManagement { - public: - static Result GetKeysHeld(u64 *keys); -}; +namespace sts::ldr { + + /* Launch Record API. */ + bool HasLaunchedTitle(ncm::TitleId title_id); + void SetLaunchedTitle(ncm::TitleId title_id); + +} \ No newline at end of file diff --git a/stratosphere/loader/source/ldr_loader_service.cpp b/stratosphere/loader/source/ldr_loader_service.cpp new file mode 100644 index 000000000..58a870d5d --- /dev/null +++ b/stratosphere/loader/source/ldr_loader_service.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2018-2019 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 "ldr_arguments.hpp" +#include "ldr_content_management.hpp" +#include "ldr_ecs.hpp" +#include "ldr_process_creation.hpp" +#include "ldr_launch_record.hpp" +#include "ldr_loader_service.hpp" +#include "ldr_ro_manager.hpp" + +namespace sts::ldr { + + /* Official commands. */ + Result LoaderService::CreateProcess(Out proc_h, PinId id, u32 flags, CopiedHandle reslimit) { + AutoHandle reslimit_holder(reslimit.GetValue()); + ncm::TitleLocation loc; + char path[FS_MAX_PATH]; + + /* Get location. */ + R_TRY(ldr::ro::GetTitleLocation(&loc, id)); + + if (loc.storage_id != static_cast(ncm::StorageId::None)) { + R_TRY(ResolveContentPath(path, loc)); + } + + return ldr::CreateProcess(proc_h.GetHandlePointer(), id, loc, path, flags, reslimit_holder.Get()); + } + + Result LoaderService::GetProgramInfo(OutPointerWithServerSize out_program_info, ncm::TitleLocation loc) { + /* Zero output. */ + ProgramInfo *out = out_program_info.pointer; + std::memset(out, 0, sizeof(*out)); + + R_TRY(ldr::GetProgramInfo(out, loc)); + + if (loc.storage_id != static_cast(ncm::StorageId::None) && loc.title_id != out->title_id) { + char path[FS_MAX_PATH]; + const ncm::TitleLocation new_loc = ncm::MakeTitleLocation(out->title_id, static_cast(loc.storage_id)); + + R_TRY(ResolveContentPath(path, loc)); + R_TRY(RedirectContentPath(path, new_loc)); + + const auto arg_info = args::Get(loc.title_id); + if (arg_info != nullptr) { + R_TRY(args::Set(new_loc.title_id, arg_info->args, arg_info->args_size)); + } + } + + return ResultSuccess; + } + + Result LoaderService::PinTitle(Out out_id, ncm::TitleLocation loc) { + return ldr::ro::PinTitle(out_id.GetPointer(), loc); + } + + Result LoaderService::UnpinTitle(PinId id) { + return ldr::ro::UnpinTitle(id); + } + + Result LoaderService::SetTitleArguments(ncm::TitleId title_id, InPointer args, u32 args_size) { + return args::Set(title_id, args.pointer, std::min(args.num_elements, size_t(args_size))); + } + + Result LoaderService::ClearArguments() { + return args::Clear(); + } + + Result LoaderService::GetProcessModuleInfo(Out count, OutPointerWithClientSize out, u64 process_id) { + if (out.num_elements > std::numeric_limits::max()) { + return ResultLoaderInvalidSize; + } + + return ldr::ro::GetProcessModuleInfo(count.GetPointer(), out.pointer, out.num_elements, process_id); + } + + /* Atmosphere commands. */ + Result LoaderService::AtmosphereSetExternalContentSource(Out out, ncm::TitleId title_id) { + return ecs::Set(out.GetHandlePointer(), title_id); + } + + void LoaderService::AtmosphereClearExternalContentSource(ncm::TitleId title_id) { + R_ASSERT(ecs::Clear(title_id)); + } + + void LoaderService::AtmosphereHasLaunchedTitle(Out out, ncm::TitleId title_id) { + out.SetValue(ldr::HasLaunchedTitle(title_id)); + } + +} diff --git a/stratosphere/loader/source/ldr_loader_service.hpp b/stratosphere/loader/source/ldr_loader_service.hpp new file mode 100644 index 000000000..456a3dc57 --- /dev/null +++ b/stratosphere/loader/source/ldr_loader_service.hpp @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2018-2019 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 +#include +#include + +namespace sts::ldr { + + class LoaderService : public IServiceObject { + protected: + /* Official commands. */ + virtual Result CreateProcess(Out proc_h, PinId id, u32 flags, CopiedHandle reslimit_h); + virtual Result GetProgramInfo(OutPointerWithServerSize out_program_info, ncm::TitleLocation loc); + virtual Result PinTitle(Out out_id, ncm::TitleLocation loc); + virtual Result UnpinTitle(PinId id); + virtual Result SetTitleArguments(ncm::TitleId title_id, InPointer args, u32 args_size); + virtual Result ClearArguments(); + virtual Result GetProcessModuleInfo(Out count, OutPointerWithClientSize out, u64 process_id); + + /* Atmosphere commands. */ + virtual Result AtmosphereSetExternalContentSource(Out out, ncm::TitleId title_id); + virtual void AtmosphereClearExternalContentSource(ncm::TitleId title_id); + virtual void AtmosphereHasLaunchedTitle(Out out, ncm::TitleId title_id); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + /* No commands callable, as LoaderService is abstract. */ + }; + }; + + namespace pm { + + class ProcessManagerInterface final : public LoaderService { + protected: + enum class CommandId { + CreateProcess = 0, + GetProgramInfo = 1, + PinTitle = 2, + UnpinTitle = 3, + + AtmosphereHasLaunchedTitle = 65000, + }; + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + + MakeServiceCommandMetaEx(), + }; + }; + + } + + namespace dmnt { + + class DebugMonitorInterface final : public LoaderService { + protected: + enum class CommandId { + SetTitleArguments = 0, + ClearArguments = 1, + GetProcessModuleInfo = 2, + + AtmosphereHasLaunchedTitle = 65000, + }; + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + + MakeServiceCommandMetaEx(), + }; + }; + + } + + namespace shell { + + class ShellInterface final : public LoaderService { + protected: + enum class CommandId { + SetTitleArguments = 0, + ClearArguments = 1, + + AtmosphereSetExternalContentSource = 65000, + AtmosphereClearExternalContentSource = 65001, + }; + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + }; + }; + + } + +} diff --git a/stratosphere/loader/source/ldr_main.cpp b/stratosphere/loader/source/ldr_main.cpp index badf71771..5e969630f 100644 --- a/stratosphere/loader/source/ldr_main.cpp +++ b/stratosphere/loader/source/ldr_main.cpp @@ -22,10 +22,10 @@ #include #include #include +#include +#include -#include "ldr_process_manager.hpp" -#include "ldr_debug_monitor.hpp" -#include "ldr_shell.hpp" +#include "ldr_loader_service.hpp" extern "C" { extern u32 __start__; @@ -68,14 +68,13 @@ void __libnx_initheap(void) { void __appInit(void) { SetFirmwareVersionForLibnx(); - /* Initialize services we need (TODO: SPL) */ + /* Initialize services we need. */ DoWithSmSession([&]() { R_ASSERT(fsInitialize()); R_ASSERT(lrInitialize()); R_ASSERT(fsldrInitialize()); }); - CheckAtmosphereVersion(CURRENT_ATMOSPHERE_VERSION); } @@ -95,14 +94,12 @@ struct LoaderServerOptions { int main(int argc, char **argv) { - consoleDebugInit(debugDevice_SVC); - static auto s_server_manager = WaitableManager(1); /* Add services to manager. */ - s_server_manager.AddWaitable(new ServiceServer("ldr:pm", 1)); - s_server_manager.AddWaitable(new ServiceServer("ldr:shel", 3)); - s_server_manager.AddWaitable(new ServiceServer("ldr:dmnt", 2)); + s_server_manager.AddWaitable(new ServiceServer("ldr:pm", 1)); + s_server_manager.AddWaitable(new ServiceServer("ldr:shel", 3)); + s_server_manager.AddWaitable(new ServiceServer("ldr:dmnt", 2)); /* Loop forever, servicing our services. */ s_server_manager.Process(); diff --git a/stratosphere/loader/source/ldr_map.cpp b/stratosphere/loader/source/ldr_map.cpp deleted file mode 100644 index 3d8b95eae..000000000 --- a/stratosphere/loader/source/ldr_map.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (c) 2018-2019 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 - -#include "ldr_map.hpp" - -Result MapUtils::LocateSpaceForMap(u64 *out, u64 out_size) { - if (GetRuntimeFirmwareVersion() >= FirmwareVersion_200) { - return LocateSpaceForMapModern(out, out_size); - } else { - return LocateSpaceForMapDeprecated(out, out_size); - } -} - -Result MapUtils::LocateSpaceForMapModern(u64 *out, u64 out_size) { - MemoryInfo mem_info = {}; - AddressSpaceInfo address_space = {}; - u32 page_info = 0; - u64 cur_base = 0, cur_end = 0; - - R_TRY(GetAddressSpaceInfo(&address_space, CUR_PROCESS_HANDLE)); - - cur_base = address_space.addspace_base; - - cur_end = cur_base + out_size; - if (cur_end <= cur_base) { - return ResultKernelOutOfMemory; - } - - while (true) { - if (address_space.heap_size && (address_space.heap_base <= cur_end - 1 && cur_base <= address_space.heap_end - 1)) { - /* If we overlap the heap region, go to the end of the heap region. */ - if (cur_base == address_space.heap_end) { - return ResultKernelOutOfMemory; - } - cur_base = address_space.heap_end; - } else if (address_space.map_size && (address_space.map_base <= cur_end - 1 && cur_base <= address_space.map_end - 1)) { - /* If we overlap the map region, go to the end of the map region. */ - if (cur_base == address_space.map_end) { - return ResultKernelOutOfMemory; - } - cur_base = address_space.map_end; - } else { - R_ASSERT(svcQueryMemory(&mem_info, &page_info, cur_base)); - if (mem_info.type == 0 && mem_info.addr - cur_base + mem_info.size >= out_size) { - *out = cur_base; - return ResultSuccess; - } - if (mem_info.addr + mem_info.size <= cur_base) { - return ResultKernelOutOfMemory; - } - cur_base = mem_info.addr + mem_info.size; - if (cur_base >= address_space.addspace_end) { - return ResultKernelOutOfMemory; - } - } - cur_end = cur_base + out_size; - if (cur_base + out_size <= cur_base) { - return ResultKernelOutOfMemory; - } - } -} - -Result MapUtils::LocateSpaceForMapDeprecated(u64 *out, u64 out_size) { - MemoryInfo mem_info = {}; - u32 page_info = 0; - - u64 cur_base = 0x8000000ULL; - do { - R_TRY(svcQueryMemory(&mem_info, &page_info, cur_base)); - - if (mem_info.type == 0 && mem_info.addr - cur_base + mem_info.size >= out_size) { - *out = cur_base; - return ResultSuccess; - } - - const u64 mem_end = mem_info.addr + mem_info.size; - if (mem_info.type == 0x10 || mem_end < cur_base || (mem_end >> 31)) { - return ResultKernelOutOfMemory; - } - - cur_base = mem_end; - } while (true); -} - -Result MapUtils::GetAddressSpaceInfo(AddressSpaceInfo *out, Handle process_h) { - R_TRY(svcGetInfo(&out->heap_base, 4, process_h, 0)); - R_TRY(svcGetInfo(&out->heap_size, 5, process_h, 0)); - R_TRY(svcGetInfo(&out->map_base, 2, process_h, 0)); - R_TRY(svcGetInfo(&out->map_size, 3, process_h, 0)); - R_TRY(svcGetInfo(&out->addspace_base, 12, process_h, 0)); - R_TRY(svcGetInfo(&out->addspace_size, 13, process_h, 0)); - - out->heap_end = out->heap_base + out->heap_size; - out->map_end = out->map_base + out->map_size; - out->addspace_end = out->addspace_base + out->addspace_size; - return ResultSuccess; -} diff --git a/stratosphere/loader/source/ldr_map.hpp b/stratosphere/loader/source/ldr_map.hpp deleted file mode 100644 index 8cdb0ccab..000000000 --- a/stratosphere/loader/source/ldr_map.hpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2018-2019 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 -#include - -class MapUtils { - public: - struct AddressSpaceInfo { - u64 heap_base; - u64 heap_size; - u64 heap_end; - u64 map_base; - u64 map_size; - u64 map_end; - u64 addspace_base; - u64 addspace_size; - u64 addspace_end; - }; - static Result GetAddressSpaceInfo(AddressSpaceInfo *out, Handle process_h); - static Result LocateSpaceForMapDeprecated(u64 *out, u64 out_size); - static Result LocateSpaceForMapModern(u64 *out, u64 out_size); - static Result LocateSpaceForMap(u64 *out, u64 out_size); -}; - -class AutoCloseMap { - private: - void *mapped_address = nullptr; - u64 base_address = 0; - u64 size = 0; - Handle process_handle = 0; - public: - ~AutoCloseMap() { - Close(); - } - - void *GetMappedAddress() { - return this->mapped_address; - } - - Result Open(Handle process_h, u64 address, u64 size) { - u64 try_address; - - /* Find an address to map at. */ - R_TRY(MapUtils::LocateSpaceForMap(&try_address, size)); - - /* Actually map at address. */ - void *try_map_address = reinterpret_cast(try_address); - R_TRY(svcMapProcessMemory(try_map_address, process_h, address, size)); - - this->mapped_address = try_map_address; - this->process_handle = process_h; - this->base_address = address; - this->size = size; - return ResultSuccess; - } - - void Close() { - if (this->mapped_address) { - R_ASSERT(svcUnmapProcessMemory(this->mapped_address, this->process_handle, this->base_address, this->size)); - this->mapped_address = NULL; - } - } -}; diff --git a/stratosphere/loader/source/ldr_meta.cpp b/stratosphere/loader/source/ldr_meta.cpp new file mode 100644 index 000000000..38660fff7 --- /dev/null +++ b/stratosphere/loader/source/ldr_meta.cpp @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2018-2019 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 "ldr_capabilities.hpp" +#include "ldr_content_management.hpp" +#include "ldr_meta.hpp" + +namespace sts::ldr { + + namespace { + + /* Convenience definitions. */ + constexpr size_t MetaCacheBufferSize = 0x8000; + constexpr const char *MetaFilePath = "/main.npdm"; + + /* Types. */ + struct MetaCache { + Meta meta; + u8 buffer[MetaCacheBufferSize]; + }; + + /* Global storage. */ + ncm::TitleId g_cached_title_id; + MetaCache g_meta_cache; + MetaCache g_original_meta_cache; + + /* Helpers. */ + Result ValidateSubregion(size_t allowed_start, size_t allowed_end, size_t start, size_t size, size_t min_size = 0) { + if (!(size >= min_size && allowed_start <= start && start <= allowed_end && start + size <= allowed_end)) { + return ResultLoaderInvalidMeta; + } + return ResultSuccess; + } + + Result ValidateNpdm(const Npdm *npdm, size_t size) { + /* Validate magic. */ + if (npdm->magic != Npdm::Magic) { + return ResultLoaderInvalidMeta; + } + + /* Validate flags. */ + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_700) { + /* 7.0.0 added 0x10 as a valid bit to NPDM flags. */ + if (npdm->flags & ~0x1F) { + return ResultLoaderInvalidMeta; + } + } else { + if (npdm->flags & ~0xF) { + return ResultLoaderInvalidMeta; + } + } + + /* Validate Acid extents. */ + R_TRY(ValidateSubregion(sizeof(Npdm), size, npdm->acid_offset, npdm->acid_size, sizeof(Acid))); + + /* Validate Aci extends. */ + R_TRY(ValidateSubregion(sizeof(Npdm), size, npdm->aci_offset, npdm->aci_size, sizeof(Aci))); + + return ResultSuccess; + } + + Result ValidateAcid(const Acid *acid, size_t size) { + /* Validate magic. */ + if (acid->magic != Acid::Magic) { + return ResultLoaderInvalidMeta; + } + + /* TODO: Check if retail flag is set if not development hardware. */ + + /* Validate Fac, Sac, Kac. */ + R_TRY(ValidateSubregion(sizeof(Acid), size, acid->fac_offset, acid->fac_size)); + R_TRY(ValidateSubregion(sizeof(Acid), size, acid->sac_offset, acid->sac_size)); + R_TRY(ValidateSubregion(sizeof(Acid), size, acid->kac_offset, acid->kac_size)); + + return ResultSuccess; + } + + Result ValidateAci(const Aci *aci, size_t size) { + /* Validate magic. */ + if (aci->magic != Aci::Magic) { + return ResultLoaderInvalidMeta; + } + + /* Validate Fah, Sac, Kac. */ + R_TRY(ValidateSubregion(sizeof(Aci), size, aci->fah_offset, aci->fah_size)); + R_TRY(ValidateSubregion(sizeof(Aci), size, aci->sac_offset, aci->sac_size)); + R_TRY(ValidateSubregion(sizeof(Aci), size, aci->kac_offset, aci->kac_size)); + + return ResultSuccess; + } + + Result LoadMetaFromFile(FILE *f, MetaCache *cache) { + /* Reset cache. */ + cache->meta = {}; + + /* Read from file. */ + size_t npdm_size = 0; + { + /* Get file size. */ + fseek(f, 0, SEEK_END); + npdm_size = ftell(f); + fseek(f, 0, SEEK_SET); + + /* Read data into cache buffer. */ + if (npdm_size > MetaCacheBufferSize || fread(cache->buffer, npdm_size, 1, f) != 1) { + return ResultLoaderTooLargeMeta; + } + } + + /* Ensure size is big enough. */ + if (npdm_size < sizeof(Npdm)) { + return ResultLoaderInvalidMeta; + } + + /* Validate the meta. */ + { + Meta *meta = &cache->meta; + + Npdm *npdm = reinterpret_cast(cache->buffer); + R_TRY(ValidateNpdm(npdm, npdm_size)); + + Acid *acid = reinterpret_cast(cache->buffer + npdm->acid_offset); + Aci *aci = reinterpret_cast(cache->buffer + npdm->aci_offset); + R_TRY(ValidateAcid(acid, npdm->acid_size)); + R_TRY(ValidateAci(aci, npdm->aci_size)); + + /* Set Meta members. */ + meta->npdm = npdm; + meta->acid = acid; + meta->aci = aci; + + meta->acid_fac = reinterpret_cast(acid) + acid->fac_offset; + meta->acid_sac = reinterpret_cast(acid) + acid->sac_offset; + meta->acid_kac = reinterpret_cast(acid) + acid->kac_offset; + + meta->aci_fah = reinterpret_cast(aci) + aci->fah_offset; + meta->aci_sac = reinterpret_cast(aci) + aci->sac_offset; + meta->aci_kac = reinterpret_cast(aci) + aci->kac_offset; + } + + return ResultSuccess; + } + + } + + /* API. */ + Result LoadMeta(Meta *out_meta, ncm::TitleId title_id) { + FILE *f = nullptr; + + /* Try to load meta from file. */ + R_TRY(OpenCodeFile(f, title_id, MetaFilePath)); + { + ON_SCOPE_EXIT { fclose(f); }; + R_TRY(LoadMetaFromFile(f, &g_meta_cache)); + } + + /* Patch meta. Start by setting all title ids to the current title id. */ + Meta *meta = &g_meta_cache.meta; + meta->acid->title_id_min = title_id; + meta->acid->title_id_max = title_id; + meta->aci->title_id = title_id; + + /* For HBL, we need to copy some information from the base meta. */ + if (cfg::IsHblOverrideKeyHeld(title_id)) { + if (R_SUCCEEDED(OpenCodeFileFromBaseExefs(f, title_id, MetaFilePath))) { + ON_SCOPE_EXIT { fclose(f); }; + if (R_SUCCEEDED(LoadMetaFromFile(f, &g_original_meta_cache))) { + Meta *o_meta = &g_original_meta_cache.meta; + + /* Fix pool partition. */ + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) { + meta->acid->flags = (meta->acid->flags & 0xFFFFFFC3) | (o_meta->acid->flags & 0x0000003C); + } + + /* Fix flags. */ + const u16 program_info_flags = GetProgramInfoFlags(o_meta->aci_kac, o_meta->aci->kac_size); + SetProgramInfoFlags(program_info_flags, meta->acid_kac, meta->acid->kac_size); + SetProgramInfoFlags(program_info_flags, meta->aci_kac, meta->aci->kac_size); + } + } + } + + /* Set output. */ + g_cached_title_id = title_id; + *out_meta = *meta; + + return ResultSuccess; + } + + Result LoadMetaFromCache(Meta *out_meta, ncm::TitleId title_id) { + if (g_cached_title_id != title_id) { + return LoadMeta(out_meta, title_id); + } + *out_meta = g_meta_cache.meta; + return ResultSuccess; + } + + void InvalidateMetaCache() { + /* Set the cached title id back to zero. */ + g_cached_title_id = {}; + } + +} diff --git a/stratosphere/loader/source/ldr_meta.hpp b/stratosphere/loader/source/ldr_meta.hpp new file mode 100644 index 000000000..ecb978fdc --- /dev/null +++ b/stratosphere/loader/source/ldr_meta.hpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018-2019 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 +#include +#include + +namespace sts::ldr { + + struct Meta { + Npdm *npdm; + Acid *acid; + Aci *aci; + + void *acid_fac; + void *acid_sac; + void *acid_kac; + + void *aci_fah; + void *aci_sac; + void *aci_kac; + }; + + /* Meta API. */ + Result LoadMeta(Meta *out_meta, ncm::TitleId title_id); + Result LoadMetaFromCache(Meta *out_meta, ncm::TitleId title_id); + void InvalidateMetaCache(); + +} diff --git a/stratosphere/loader/source/ldr_npdm.cpp b/stratosphere/loader/source/ldr_npdm.cpp deleted file mode 100644 index d227f3679..000000000 --- a/stratosphere/loader/source/ldr_npdm.cpp +++ /dev/null @@ -1,500 +0,0 @@ -/* - * Copyright (c) 2018-2019 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 -#include -#include "ldr_npdm.hpp" -#include "ldr_registration.hpp" -#include "ldr_content_management.hpp" - -static NpdmUtils::NpdmCache g_npdm_cache = {0}; -static NpdmUtils::NpdmCache g_original_npdm_cache = {0}; -static char g_npdm_path[FS_MAX_PATH] = {0}; - -Result NpdmUtils::LoadNpdmFromCache(u64 tid, NpdmInfo *out) { - if (g_npdm_cache.info.title_id != tid) { - return LoadNpdm(tid, out); - } - *out = g_npdm_cache.info; - return ResultSuccess; -} - -FILE *NpdmUtils::OpenNpdmFromECS(ContentManagement::ExternalContentSource *ecs) { - std::fill(g_npdm_path, g_npdm_path + FS_MAX_PATH, 0); - snprintf(g_npdm_path, FS_MAX_PATH, "%s:/main.npdm", ecs->mountpoint); - return fopen(g_npdm_path, "rb"); -} - -FILE *NpdmUtils::OpenNpdmFromHBL() { - std::fill(g_npdm_path, g_npdm_path + FS_MAX_PATH, 0); - snprintf(g_npdm_path, FS_MAX_PATH, "hbl:/main.npdm"); - return fopen(g_npdm_path, "rb"); -} - -FILE *NpdmUtils::OpenNpdmFromExeFS() { - std::fill(g_npdm_path, g_npdm_path + FS_MAX_PATH, 0); - snprintf(g_npdm_path, FS_MAX_PATH, "code:/main.npdm"); - return fopen(g_npdm_path, "rb"); -} - -FILE *NpdmUtils::OpenNpdmFromSdCard(u64 title_id) { - std::fill(g_npdm_path, g_npdm_path + FS_MAX_PATH, 0); - snprintf(g_npdm_path, FS_MAX_PATH, "sdmc:/atmosphere/titles/%016lx/exefs/main.npdm", title_id); - return fopen(g_npdm_path, "rb"); -} - - -FILE *NpdmUtils::OpenNpdm(u64 title_id) { - ContentManagement::ExternalContentSource *ecs = nullptr; - if ((ecs = ContentManagement::GetExternalContentSource(title_id)) != nullptr) { - return OpenNpdmFromECS(ecs); - } - - /* First, check HBL. */ - if (ContentManagement::ShouldOverrideContentsWithHBL(title_id)) { - return OpenNpdmFromHBL(); - } - - /* Next, check other override. */ - if (ContentManagement::ShouldOverrideContentsWithSD(title_id)) { - FILE *f_out = OpenNpdmFromSdCard(title_id); - if (f_out != NULL) { - return f_out; - } - } - - /* Last resort: real exefs. */ - return OpenNpdmFromExeFS(); -} - -Result NpdmUtils::LoadNpdmInternal(FILE *f_npdm, NpdmUtils::NpdmCache *cache) { - cache->info = {}; - - if (f_npdm == NULL) { - /* For generic "Couldn't open the file" error, just say the file doesn't exist. */ - return ResultFsPathNotFound; - } - - fseek(f_npdm, 0, SEEK_END); - size_t npdm_size = ftell(f_npdm); - fseek(f_npdm, 0, SEEK_SET); - - if ((npdm_size > sizeof(cache->buffer)) || (fread(cache->buffer, 1, npdm_size, f_npdm) != npdm_size)) { - fclose(f_npdm); - return ResultLoaderTooLargeMeta; - } - - fclose(f_npdm); - - if (npdm_size < sizeof(NpdmUtils::NpdmHeader)) { - return ResultLoaderInvalidMeta; - } - - /* For ease of access... */ - cache->info.header = (NpdmUtils::NpdmHeader *)(cache->buffer); - NpdmInfo *info = &cache->info; - - if (info->header->magic != MAGIC_META) { - return ResultLoaderInvalidMeta; - } - - /* 7.0.0 added 0x10 as a valid bit to NPDM flags. */ - if (GetRuntimeFirmwareVersion() >= FirmwareVersion_700) { - if (info->header->mmu_flags > 0x1F) { - return ResultLoaderInvalidMeta; - } - } else { - if (info->header->mmu_flags > 0xF) { - return ResultLoaderInvalidMeta; - } - } - - if (info->header->aci0_offset < sizeof(NpdmUtils::NpdmHeader) || info->header->aci0_size < sizeof(NpdmUtils::NpdmAci0) || info->header->aci0_offset + info->header->aci0_size > npdm_size) { - return ResultLoaderInvalidMeta; - } - - info->aci0 = (NpdmAci0 *)(cache->buffer + info->header->aci0_offset); - - if (info->aci0->magic != MAGIC_ACI0) { - return ResultLoaderInvalidMeta; - } - - if (info->aci0->fah_size > info->header->aci0_size || info->aci0->fah_offset < sizeof(NpdmUtils::NpdmAci0) || info->aci0->fah_offset + info->aci0->fah_size > info->header->aci0_size) { - return ResultLoaderInvalidMeta; - } - - info->aci0_fah = (void *)((uintptr_t)info->aci0 + info->aci0->fah_offset); - - if (info->aci0->sac_size > info->header->aci0_size || info->aci0->sac_offset < sizeof(NpdmUtils::NpdmAci0) || info->aci0->sac_offset + info->aci0->sac_size > info->header->aci0_size) { - return ResultLoaderInvalidMeta; - } - - info->aci0_sac = (void *)((uintptr_t)info->aci0 + info->aci0->sac_offset); - - if (info->aci0->kac_size > info->header->aci0_size || info->aci0->kac_offset < sizeof(NpdmUtils::NpdmAci0) || info->aci0->kac_offset + info->aci0->kac_size > info->header->aci0_size) { - return ResultLoaderInvalidMeta; - } - - info->aci0_kac = (void *)((uintptr_t)info->aci0 + info->aci0->kac_offset); - - if (info->header->acid_offset < sizeof(NpdmUtils::NpdmHeader) || info->header->acid_size < sizeof(NpdmUtils::NpdmAcid) || info->header->acid_offset + info->header->acid_size > npdm_size) { - return ResultLoaderInvalidMeta; - } - - info->acid = (NpdmAcid *)(cache->buffer + info->header->acid_offset); - - if (info->acid->magic != MAGIC_ACID) { - return ResultLoaderInvalidMeta; - } - - /* TODO: Check if retail flag is set if not development hardware. */ - - if (info->acid->fac_size > info->header->acid_size || info->acid->fac_offset < sizeof(NpdmUtils::NpdmAcid) || info->acid->fac_offset + info->acid->fac_size > info->header->acid_size) { - return ResultLoaderInvalidMeta; - } - - info->acid_fac = (void *)((uintptr_t)info->acid + info->acid->fac_offset); - - if (info->acid->sac_size > info->header->acid_size || info->acid->sac_offset < sizeof(NpdmUtils::NpdmAcid) || info->acid->sac_offset + info->acid->sac_size > info->header->acid_size) { - return ResultLoaderInvalidMeta; - } - - info->acid_sac = (void *)((uintptr_t)info->acid + info->acid->sac_offset); - - if (info->acid->kac_size > info->header->acid_size || info->acid->kac_offset < sizeof(NpdmUtils::NpdmAcid) || info->acid->kac_offset + info->acid->kac_size > info->header->acid_size) { - return ResultLoaderInvalidMeta; - } - - info->acid_kac = (void *)((uintptr_t)info->acid + info->acid->kac_offset); - - return ResultSuccess; -} - -Result NpdmUtils::LoadNpdm(u64 tid, NpdmInfo *out) { - /* Load and validate the NPDM. */ - R_TRY(LoadNpdmInternal(OpenNpdm(tid), &g_npdm_cache)); - - NpdmInfo *info = &g_npdm_cache.info; - /* Override the ACID/ACI0 title ID, in order to facilitate HBL takeover of any title. */ - info->acid->title_id_range_min = tid; - info->acid->title_id_range_max = tid; - info->aci0->title_id = tid; - - if (ContentManagement::ShouldOverrideContentsWithHBL(tid) && R_SUCCEEDED(LoadNpdmInternal(OpenNpdmFromExeFS(), &g_original_npdm_cache))) { - NpdmInfo *original_info = &g_original_npdm_cache.info; - /* Fix pool partition. */ - if ((GetRuntimeFirmwareVersion() >= FirmwareVersion_500)) { - info->acid->flags = (info->acid->flags & 0xFFFFFFC3) | (original_info->acid->flags & 0x0000003C); - } - /* Fix application type. */ - const u32 original_application_type = GetApplicationTypeRaw((u32 *)original_info->aci0_kac, original_info->aci0->kac_size/sizeof(u32)) & 7; - u32 *caps = (u32 *)info->aci0_kac; - for (unsigned int i = 0; i < info->aci0->kac_size/sizeof(u32); i++) { - if ((caps[i] & 0x3FFF) == 0x1FFF) { - caps[i] = (caps[i] & 0xFFFE3FFF) | (original_application_type << 14); - } - } - caps = (u32 *)info->acid_kac; - for (unsigned int i = 0; i < info->acid->kac_size/sizeof(u32); i++) { - if ((caps[i] & 0x3FFF) == 0x1FFF) { - caps[i] = (caps[i] & 0xFFFE3FFF) | (original_application_type << 14); - } - } - } - - /* We validated! */ - info->title_id = tid; - *out = *info; - - return ResultSuccess; -} - -Result NpdmUtils::ValidateCapabilityAgainstRestrictions(const u32 *restrict_caps, size_t num_restrict_caps, const u32 *&cur_cap, size_t &caps_remaining) { - u32 desc = *cur_cap++; - caps_remaining--; - unsigned int low_bits = 0; - while (desc & 1) { - desc >>= 1; - low_bits++; - } - desc >>= 1; - u32 r_desc = 0; - switch (low_bits) { - case 3: /* Kernel flags. */ - for (size_t i = 0; i < num_restrict_caps; i++) { - if ((restrict_caps[i] & 0xF) == 0x7) { - r_desc = restrict_caps[i] >> 4; - u32 highest_thread_prio = desc & 0x3F; - u32 r_highest_thread_prio = r_desc & 0x3F; - desc >>= 6; - r_desc >>= 6; - u32 lowest_thread_prio = desc & 0x3F; - u32 r_lowest_thread_prio = r_desc & 0x3F; - desc >>= 6; - r_desc >>= 6; - u32 lowest_cpu_id = desc & 0xFF; - u32 r_lowest_cpu_id = r_desc & 0xFF; - desc >>= 8; - r_desc >>= 8; - u32 highest_cpu_id = desc & 0xFF; - u32 r_highest_cpu_id = r_desc & 0xFF; - if (highest_thread_prio > r_highest_thread_prio) { - break; - } - if (lowest_thread_prio > highest_thread_prio) { - break; - } - if (lowest_thread_prio < r_lowest_thread_prio) { - break; - } - if (lowest_cpu_id < r_lowest_cpu_id) { - break; - } - if (lowest_cpu_id > r_highest_cpu_id) { - break; - } - if (highest_cpu_id > r_highest_cpu_id) { - break; - } - /* Valid! */ - return ResultSuccess; - } - } - return ResultLoaderInvalidCapabilityKernelFlags; - case 4: /* Syscall mask. */ - for (size_t i = 0; i < num_restrict_caps; i++) { - if ((restrict_caps[i] & 0x1F) == 0xF) { - r_desc = restrict_caps[i] >> 5; - u32 syscall_base = (desc >> 24); - u32 r_syscall_base = (r_desc >> 24); - if (syscall_base != r_syscall_base) { - continue; - } - u32 syscall_mask = desc & 0xFFFFFF; - u32 r_syscall_mask = r_desc & 0xFFFFFF; - if ((r_syscall_mask & syscall_mask) != syscall_mask) { - break; - } - /* Valid! */ - return ResultSuccess; - } - } - return ResultLoaderInvalidCapabilitySyscallMask; - case 6: /* Map IO/Normal. */ - { - if (caps_remaining == 0) { - return ResultLoaderInvalidCapabilityMapRange; - } - u32 next_cap = *cur_cap++; - caps_remaining--; - if ((next_cap & 0x7F) != 0x3F) { - return ResultLoaderInvalidCapabilityMapRange; - } - u32 next_desc = next_cap >> 7; - u32 base_addr = desc & 0xFFFFFF; - u32 base_size = next_desc & 0xFFFFFF; - /* Size check the mapping. */ - if (base_size >> 20) { - return ResultLoaderInvalidCapabilityMapRange; - } - u32 base_end = base_addr + base_size; - /* Validate it's possible to validate this mapping. */ - if (num_restrict_caps < 2) { - return ResultLoaderInvalidCapabilityMapRange; - } - for (size_t i = 0; i < num_restrict_caps - 1; i++) { - if ((restrict_caps[i] & 0x7F) == 0x3F) { - r_desc = restrict_caps[i] >> 7; - if ((restrict_caps[i+1] & 0x7F) != 0x3F) { - break; - } - u32 r_next_desc = restrict_caps[++i] >> 7; - u32 r_base_addr = r_desc & 0xFFFFFF; - u32 r_base_size = r_next_desc & 0xFFFFFF; - /* Size check the mapping. */ - if (r_base_size >> 20) { - break; - } - u32 r_base_end = r_base_addr + r_base_size; - /* Validate is_io matches. */ - if (((r_desc >> 24) & 1) ^ ((desc >> 24) & 1)) { - continue; - } - /* Validate is_ro matches. */ - if (((r_next_desc >> 24) & 1) ^ ((next_desc >> 24) & 1)) { - continue; - } - /* Validate bounds. */ - if (base_addr < r_base_addr || base_end > r_base_end) { - continue; - } - /* Valid! */ - return ResultSuccess; - } - } - } - return ResultLoaderInvalidCapabilityMapRange; - case 7: /* Map Normal Page. */ - for (size_t i = 0; i < num_restrict_caps; i++) { - if ((restrict_caps[i] & 0xFF) == 0x7F) { - r_desc = restrict_caps[i] >> 8; - if (r_desc != desc) { - continue; - } - /* Valid! */ - return ResultSuccess; - } - } - return ResultLoaderInvalidCapabilityMapPage; - case 11: /* IRQ Pair. */ - for (unsigned int irq_i = 0; irq_i < 2; irq_i++) { - u32 irq = desc & 0x3FF; - desc >>= 10; - if (irq != 0x3FF) { - bool found = false; - for (size_t i = 0; i < num_restrict_caps && !found; i++) { - if ((restrict_caps[i] & 0xFFF) == 0x7FF) { - r_desc = restrict_caps[i] >> 12; - u32 r_irq_0 = r_desc & 0x3FF; - r_desc >>= 10; - u32 r_irq_1 = r_desc & 0x3FF; - found |= irq == r_irq_0 || irq == r_irq_1; - found |= r_irq_0 == 0x3FF && r_irq_1 == 0x3FF; - } - } - if (!found) { - return ResultLoaderInvalidCapabilityInterruptPair; - } - } - } - return ResultSuccess; - case 13: /* App Type. */ - if (num_restrict_caps) { - for (size_t i = 0; i < num_restrict_caps; i++) { - if ((restrict_caps[i] & 0x3FFF) == 0x1FFF) { - r_desc = restrict_caps[i] >> 14; - break; - } - } - } else { - r_desc = 0; - } - if (desc == r_desc) { - /* Valid! */ - return ResultSuccess; - } - return ResultLoaderInvalidCapabilityApplicationType; - case 14: /* Kernel Release Version. */ - if (num_restrict_caps) { - for (size_t i = 0; i < num_restrict_caps; i++) { - if ((restrict_caps[i] & 0x7FFF) == 0x3FFF) { - r_desc = restrict_caps[i] >> 15; - break; - } - } - } else { - r_desc = 0; - } - if (desc == r_desc) { - /* Valid! */ - return ResultSuccess; - } - return ResultLoaderInvalidCapabilityKernelVersion; - case 15: /* Handle Table Size. */ - for (size_t i = 0; i < num_restrict_caps; i++) { - if ((restrict_caps[i] & 0xFFFF) == 0x7FFF) { - r_desc = restrict_caps[i] >> 16; - desc &= 0x3FF; - r_desc &= 0x3FF; - if (desc > r_desc) { - break; - } - /* Valid! */ - return ResultSuccess; - } - } - return ResultLoaderInvalidCapabilityHandleTable; - case 16: /* Debug Flags. */ - if (num_restrict_caps) { - for (size_t i = 0; i < num_restrict_caps; i++) { - if ((restrict_caps[i] & 0x1FFFF) == 0xFFFF) { - r_desc = restrict_caps[i] >> 17; - break; - } - } - } else { - r_desc = 0; - } - if ((desc & ~r_desc) == 0) { - /* Valid! */ - return ResultSuccess; - } - return ResultLoaderInvalidCapabilityDebugFlags; - case 32: /* Empty Descriptor. */ - return ResultSuccess; - default: /* Unrecognized Descriptor. */ - return ResultLoaderUnknownCapability; - } -} - -Result NpdmUtils::ValidateCapabilities(const u32 *acid_caps, size_t num_acid_caps, const u32 *aci0_caps, size_t num_aci0_caps) { - const u32 *cur_cap = aci0_caps; - size_t remaining = num_aci0_caps; - - while (remaining) { - /* Validate, update capabilities. cur_cap and remaining passed by reference. */ - R_TRY(ValidateCapabilityAgainstRestrictions(acid_caps, num_acid_caps, cur_cap, remaining)); - } - - return ResultSuccess; -} - -u32 NpdmUtils::GetApplicationType(const u32 *caps, size_t num_caps) { - u32 application_type = 0; - for (unsigned int i = 0; i < num_caps; i++) { - if ((caps[i] & 0x3FFF) == 0x1FFF) { - u16 app_type = (caps[i] >> 14) & 7; - if (app_type == 1) { - application_type |= 1; - } else if (app_type == 2) { - application_type |= 2; - } - } - /* After 1.0.0, allow_debug is used as bit 4. */ - if ((GetRuntimeFirmwareVersion() >= FirmwareVersion_200) && (caps[i] & 0x1FFFF) == 0xFFFF) { - application_type |= (caps[i] >> 15) & 4; - } - } - return application_type; -} - -/* Like GetApplicationType, except this returns the raw kac descriptor value. */ -u32 NpdmUtils::GetApplicationTypeRaw(const u32 *caps, size_t num_caps) { - u32 application_type = 0; - for (unsigned int i = 0; i < num_caps; i++) { - if ((caps[i] & 0x3FFF) == 0x1FFF) { - return (caps[i] >> 14) & 7; - } - } - return application_type; -} - -void NpdmUtils::InvalidateCache(u64 tid) { - if (g_npdm_cache.info.title_id == tid) { - g_npdm_cache.info = {}; - } -} diff --git a/stratosphere/loader/source/ldr_npdm.hpp b/stratosphere/loader/source/ldr_npdm.hpp deleted file mode 100644 index c8c18f76a..000000000 --- a/stratosphere/loader/source/ldr_npdm.hpp +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2018-2019 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 -#include - -#include "ldr_registration.hpp" -#include "ldr_content_management.hpp" /* for ExternalContentSource */ - -#define MAGIC_META 0x4154454D -#define MAGIC_ACI0 0x30494341 -#define MAGIC_ACID 0x44494341 - -class NpdmUtils { - public: - struct NpdmHeader { - u32 magic; - u32 _0x4; - u32 _0x8; - u8 mmu_flags; - u8 _0xD; - u8 main_thread_prio; - u8 default_cpuid; - u32 _0x10; - u32 system_resource_size; - u32 version; - u32 main_stack_size; - char title_name[0x50]; - u32 aci0_offset; - u32 aci0_size; - u32 acid_offset; - u32 acid_size; - }; - struct NpdmAcid { - u8 signature[0x100]; - u8 modulus[0x100]; - u32 magic; - u32 size; - u32 _0x208; - u32 flags; - u64 title_id_range_min; - u64 title_id_range_max; - u32 fac_offset; - u32 fac_size; - u32 sac_offset; - u32 sac_size; - u32 kac_offset; - u32 kac_size; - u64 padding; - }; - struct NpdmAci0 { - u32 magic; - u8 _0x4[0xC]; - u64 title_id; - u64 _0x18; - u32 fah_offset; - u32 fah_size; - u32 sac_offset; - u32 sac_size; - u32 kac_offset; - u32 kac_size; - u64 padding; - }; - struct NpdmInfo { - NpdmHeader *header; - NpdmAcid *acid; - NpdmAci0 *aci0; - void *acid_fac; - void *acid_sac; - void *acid_kac; - void *aci0_fah; - void *aci0_sac; - void *aci0_kac; - u64 title_id; - }; - struct NpdmCache { - NpdmInfo info; - u8 buffer[0x8000]; - }; - - static_assert(sizeof(NpdmHeader) == 0x80, "Incorrectly defined NpdmHeader!"); - static_assert(sizeof(NpdmAcid) == 0x240, "Incorrectly defined NpdmAcid!"); - static_assert(sizeof(NpdmAci0) == 0x40, "Incorrectly defined NpdmAci0!"); - - static u32 GetApplicationType(const u32 *caps, size_t num_caps); - static u32 GetApplicationTypeRaw(const u32 *caps, size_t num_caps); - - static Result ValidateCapabilityAgainstRestrictions(const u32 *restrict_caps, size_t num_restrict_caps, const u32 *&cur_cap, size_t &caps_remaining); - static Result ValidateCapabilities(const u32 *acid_caps, size_t num_acid_caps, const u32 *aci0_caps, size_t num_aci0_caps); - - static FILE *OpenNpdmFromECS(ContentManagement::ExternalContentSource *ecs); - static FILE *OpenNpdmFromHBL(); - static FILE *OpenNpdmFromExeFS(); - static FILE *OpenNpdmFromSdCard(u64 tid); - static FILE *OpenNpdm(u64 tid); - static Result LoadNpdm(u64 tid, NpdmInfo *out); - static Result LoadNpdmFromCache(u64 tid, NpdmInfo *out); - - static void InvalidateCache(u64 tid); - private: - static Result LoadNpdmInternal(FILE *f_npdm, NpdmCache *cache); -}; diff --git a/stratosphere/loader/source/ldr_nso.cpp b/stratosphere/loader/source/ldr_nso.cpp deleted file mode 100644 index c95fba65b..000000000 --- a/stratosphere/loader/source/ldr_nso.cpp +++ /dev/null @@ -1,357 +0,0 @@ -/* - * Copyright (c) 2018-2019 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 -#include -#include -#include "lz4.h" -#include "ldr_nso.hpp" -#include "ldr_map.hpp" -#include "ldr_patcher.hpp" -#include "ldr_content_management.hpp" - -static NsoUtils::NsoHeader g_nso_headers[NSO_NUM_MAX] = {0}; -static bool g_nso_present[NSO_NUM_MAX] = {0}; - -static char g_nso_path[FS_MAX_PATH] = {0}; - -FILE *NsoUtils::OpenNsoFromECS(unsigned int index, ContentManagement::ExternalContentSource *ecs) { - std::fill(g_nso_path, g_nso_path + FS_MAX_PATH, 0); - snprintf(g_nso_path, FS_MAX_PATH, "%s:/%s", ecs->mountpoint, NsoUtils::GetNsoFileName(index)); - return fopen(g_nso_path, "rb"); -} - -FILE *NsoUtils::OpenNsoFromHBL(unsigned int index) { - std::fill(g_nso_path, g_nso_path + FS_MAX_PATH, 0); - snprintf(g_nso_path, FS_MAX_PATH, "hbl:/%s", NsoUtils::GetNsoFileName(index)); - return fopen(g_nso_path, "rb"); -} - -FILE *NsoUtils::OpenNsoFromExeFS(unsigned int index) { - std::fill(g_nso_path, g_nso_path + FS_MAX_PATH, 0); - snprintf(g_nso_path, FS_MAX_PATH, "code:/%s", NsoUtils::GetNsoFileName(index)); - return fopen(g_nso_path, "rb"); -} - -FILE *NsoUtils::OpenNsoFromSdCard(unsigned int index, u64 title_id) { - std::fill(g_nso_path, g_nso_path + FS_MAX_PATH, 0); - snprintf(g_nso_path, FS_MAX_PATH, "sdmc:/atmosphere/titles/%016lx/exefs/%s", title_id, NsoUtils::GetNsoFileName(index)); - return fopen(g_nso_path, "rb"); -} - -bool NsoUtils::CheckNsoStubbed(unsigned int index, u64 title_id) { - std::fill(g_nso_path, g_nso_path + FS_MAX_PATH, 0); - snprintf(g_nso_path, FS_MAX_PATH, "sdmc:/atmosphere/titles/%016lx/exefs/%s.stub", title_id, NsoUtils::GetNsoFileName(index)); - FILE *f = fopen(g_nso_path, "rb"); - bool ret = (f != NULL); - if (ret) { - fclose(f); - } - return ret; -} - -FILE *NsoUtils::OpenNso(unsigned int index, u64 title_id) { - ContentManagement::ExternalContentSource *ecs = nullptr; - if ((ecs = ContentManagement::GetExternalContentSource(title_id)) != nullptr) { - return OpenNsoFromECS(index, ecs); - } - - /* First, check HBL. */ - if (ContentManagement::ShouldOverrideContentsWithHBL(title_id)) { - return OpenNsoFromHBL(index); - } - - /* Next, check secondary override. */ - if (ContentManagement::ShouldOverrideContentsWithSD(title_id)) { - FILE *f_out = OpenNsoFromSdCard(index, title_id); - if (f_out != NULL) { - return f_out; - } else if (CheckNsoStubbed(index, title_id)) { - return NULL; - } - } - - /* Finally, default to exefs. */ - return OpenNsoFromExeFS(index); -} - -bool NsoUtils::IsNsoPresent(unsigned int index) { - return g_nso_present[index]; -} - - -unsigned char *NsoUtils::GetNsoBuildId(unsigned int index) { - if (g_nso_present[index]) { - return g_nso_headers[index].build_id; - } - return NULL; -} - -Result NsoUtils::LoadNsoHeaders(u64 title_id) { - FILE *f_nso; - - /* Zero out the cache. */ - std::fill(g_nso_present, g_nso_present + NSO_NUM_MAX, false); - std::fill(g_nso_headers, g_nso_headers + NSO_NUM_MAX, NsoUtils::NsoHeader{}); - - for (unsigned int i = 0; i < NSO_NUM_MAX; i++) { - f_nso = OpenNso(i, title_id); - if (f_nso != NULL) { - if (fread(&g_nso_headers[i], 1, sizeof(NsoUtils::NsoHeader), f_nso) != sizeof(NsoUtils::NsoHeader)) { - return ResultLoaderInvalidNso; - } - g_nso_present[i] = true; - fclose(f_nso); - f_nso = NULL; - continue; - } - if (1 < i && i < 12) { - /* If we failed to open a subsdk, there are no more subsdks. */ - i = 11; - } - } - - return ResultSuccess; -} - -Result NsoUtils::ValidateNsoLoadSet() { - /* We *must* have a "main" NSO. */ - if (!g_nso_present[1]) { - return ResultLoaderInvalidNso; - } - - /* Behavior switches depending on whether we have an rtld. */ - if (g_nso_present[0]) { - /* If we have an rtld, dst offset for .text must be 0 for all other NSOs. */ - for (unsigned int i = 0; i < NSO_NUM_MAX; i++) { - if (g_nso_present[i] && g_nso_headers[i].segments[0].dst_offset != 0) { - return ResultLoaderInvalidNso; - } - } - } else { - /* If we don't have an rtld, we must ONLY have a main. */ - for (unsigned int i = 2; i < NSO_NUM_MAX; i++) { - if (g_nso_present[i]) { - return ResultLoaderInvalidNso; - } - } - /* That main's .text must be at dst_offset 0. */ - if (g_nso_headers[1].segments[0].dst_offset != 0) { - return ResultLoaderInvalidNso; - } - } - - return ResultSuccess; -} - - -Result NsoUtils::CalculateNsoLoadExtents(u32 addspace_type, u32 args_size, NsoLoadExtents *extents) { - *extents = {}; - - /* Calculate base offsets. */ - for (unsigned int i = 0; i < NSO_NUM_MAX; i++) { - if (g_nso_present[i]) { - extents->nso_addresses[i] = extents->total_size; - u32 text_end = g_nso_headers[i].segments[0].dst_offset + g_nso_headers[i].segments[0].decomp_size; - u32 ro_end = g_nso_headers[i].segments[1].dst_offset + g_nso_headers[i].segments[1].decomp_size; - u32 rw_end = g_nso_headers[i].segments[2].dst_offset + g_nso_headers[i].segments[2].decomp_size + g_nso_headers[i].segments[2].align_or_total_size; - extents->nso_sizes[i] = text_end; - if (extents->nso_sizes[i] < ro_end) { - extents->nso_sizes[i] = ro_end; - } - if (extents->nso_sizes[i] < rw_end) { - extents->nso_sizes[i] = rw_end; - } - extents->nso_sizes[i] += 0xFFF; - extents->nso_sizes[i] &= ~0xFFFULL; - extents->total_size += extents->nso_sizes[i]; - if (args_size && !extents->args_size) { - extents->args_address = extents->total_size; - /* What the fuck? Where does 0x9007 come from? */ - extents->args_size = (2 * args_size + 0x9007); - extents->args_size &= ~0xFFFULL; - extents->total_size += extents->args_size; - } - } - } - - /* Calculate ASLR extents for address space type. */ - u64 addspace_start, addspace_size; - if ((GetRuntimeFirmwareVersion() >= FirmwareVersion_200)) { - switch (addspace_type & 0xE) { - case 0: - case 4: - addspace_start = 0x200000ULL; - addspace_size = 0x3FE00000ULL; - break; - case 2: - addspace_start = 0x8000000ULL; - addspace_size = 0x78000000ULL; - break; - case 6: - addspace_start = 0x8000000ULL; - addspace_size = 0x7FF8000000ULL; - break; - default: - std::abort(); - } - } else { - if (addspace_type & 2) { - addspace_start = 0x8000000ULL; - addspace_size = 0x78000000ULL; - } else { - addspace_start = 0x200000ULL; - addspace_size = 0x3FE00000ULL; - } - } - if (extents->total_size > addspace_size) { - return ResultKernelOutOfMemory; - } - - u64 aslr_slide = 0; - if (addspace_type & 0x20) { - aslr_slide = sts::rnd::GenerateRandomU64((addspace_size - extents->total_size) >> 21) << 21; - } - - extents->base_address = addspace_start + aslr_slide; - for (unsigned int i = 0; i < NSO_NUM_MAX; i++) { - if (g_nso_present[i]) { - extents->nso_addresses[i] += extents->base_address; - } - } - if (extents->args_address) { - extents->args_address += extents->base_address; - } - - return ResultSuccess; -} - - - -Result NsoUtils::LoadNsoSegment(u64 title_id, unsigned int index, unsigned int segment, FILE *f_nso, u8 *map_base, u8 *map_end) { - bool is_compressed = ((g_nso_headers[index].flags >> segment) & 1) != 0; - bool check_hash = ((g_nso_headers[index].flags >> (segment + 3)) & 1) != 0; - size_t out_size = g_nso_headers[index].segments[segment].decomp_size; - size_t size = is_compressed ? g_nso_headers[index].compressed_sizes[segment] : out_size; - if (size > out_size) { - return ResultLoaderInvalidNso; - } - if ((u32)(size | out_size) >> 31) { - return ResultLoaderInvalidNso; - } - - u8 *dst_addr = map_base + g_nso_headers[index].segments[segment].dst_offset; - u8 *load_addr = is_compressed ? map_end - size : dst_addr; - - - fseek(f_nso, g_nso_headers[index].segments[segment].file_offset, SEEK_SET); - if (fread(load_addr, 1, size, f_nso) != size) { - return ResultLoaderInvalidNso; - } - - - if (is_compressed) { - if (LZ4_decompress_safe((char *)load_addr, (char *)dst_addr, size, out_size) != (int)out_size) { - return ResultLoaderInvalidNso; - } - } - - - if (check_hash) { - u8 hash[0x20] = {0}; - sha256CalculateHash(hash, dst_addr, out_size); - - if (std::memcmp(g_nso_headers[index].section_hashes[segment], hash, sizeof(hash))) { - return ResultLoaderInvalidNso; - } - } - - return ResultSuccess; -} - -Result NsoUtils::LoadNsosIntoProcessMemory(Handle process_h, u64 title_id, NsoLoadExtents *extents, const u8 *args, u32 args_size) { - for (unsigned int i = 0; i < NSO_NUM_MAX; i++) { - if (g_nso_present[i]) { - AutoCloseMap nso_map; - R_TRY(nso_map.Open(process_h, extents->nso_addresses[i], extents->nso_sizes[i])); - - u8 *map_base = (u8 *)nso_map.GetMappedAddress(); - - /* Load NSO segments from file. */ - { - FILE *f_nso = OpenNso(i, title_id); - if (f_nso == NULL) { - /* TODO: Is there a better error to return here? */ - return ResultLoaderInvalidNso; - } - ON_SCOPE_EXIT { fclose(f_nso); }; - - for (unsigned int seg = 0; seg < 3; seg++) { - R_TRY(LoadNsoSegment(title_id, i, seg, f_nso, map_base, map_base + extents->nso_sizes[i])); - } - } - - /* Zero out memory before .text. */ - u64 text_base = 0, text_start = g_nso_headers[i].segments[0].dst_offset; - std::fill(map_base + text_base, map_base + text_start, 0); - /* Zero out memory before .rodata. */ - u64 ro_base = text_start + g_nso_headers[i].segments[0].decomp_size, ro_start = g_nso_headers[i].segments[1].dst_offset; - std::fill(map_base + ro_base, map_base + ro_start, 0); - /* Zero out memory before .rwdata. */ - u64 rw_base = ro_start + g_nso_headers[i].segments[1].decomp_size, rw_start = g_nso_headers[i].segments[2].dst_offset; - std::fill(map_base + rw_base, map_base + rw_start, 0); - /* Zero out .bss. */ - u64 bss_base = rw_start + g_nso_headers[i].segments[2].decomp_size, bss_size = g_nso_headers[i].segments[2].align_or_total_size; - std::fill(map_base + bss_base, map_base + bss_base + bss_size, 0); - - /* Apply patches to loaded module. */ - PatchUtils::ApplyPatches(&g_nso_headers[i], map_base, bss_base); - - nso_map.Close(); - - for (unsigned int seg = 0; seg < 3; seg++) { - u64 size = g_nso_headers[i].segments[seg].decomp_size; - if (seg == 2) { - size += g_nso_headers[i].segments[2].align_or_total_size; - } - size += 0xFFF; - size &= ~0xFFFULL; - const static unsigned int segment_perms[3] = {5, 1, 3}; - R_TRY(svcSetProcessMemoryPermission(process_h, extents->nso_addresses[i] + g_nso_headers[i].segments[seg].dst_offset, size, segment_perms[seg])); - } - } - } - - /* Map in arguments. */ - if (args != nullptr && args_size) { - AutoCloseMap args_map; - R_TRY(args_map.Open(process_h, extents->args_address, extents->args_size)); - - NsoArgument *arg_map_base = (NsoArgument *)args_map.GetMappedAddress(); - - arg_map_base->allocated_space = extents->args_size; - arg_map_base->args_size = args_size; - std::fill(arg_map_base->_0x8, arg_map_base->_0x8 + sizeof(arg_map_base->_0x8), 0); - std::copy(args, args + args_size, arg_map_base->arguments); - - args_map.Close(); - - R_TRY(svcSetProcessMemoryPermission(process_h, extents->args_address, extents->args_size, 3)); - } - - return ResultSuccess; -} diff --git a/stratosphere/loader/source/ldr_nso.hpp b/stratosphere/loader/source/ldr_nso.hpp deleted file mode 100644 index eee9853f2..000000000 --- a/stratosphere/loader/source/ldr_nso.hpp +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2018-2019 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 -#include - -#include "ldr_content_management.hpp" /* for ExternalContentSource */ - -#define MAGIC_NSO0 0x304F534E -#define NSO_NUM_MAX 13 - -class NsoUtils { - public: - struct NsoSegment { - u32 file_offset; - u32 dst_offset; - u32 decomp_size; - u32 align_or_total_size; - }; - - struct NsoHeader { - u32 magic; - u32 _0x4; - u32 _0x8; - u32 flags; - NsoSegment segments[3]; - u8 build_id[0x20]; - u32 compressed_sizes[3]; - u8 _0x6C[0x24]; - u64 dynstr_extents; - u64 dynsym_extents; - u8 section_hashes[3][0x20]; - }; - - struct NsoLoadExtents { - u64 base_address; - u64 total_size; - u64 args_address; - u64 args_size; - u64 nso_addresses[NSO_NUM_MAX]; - u64 nso_sizes[NSO_NUM_MAX]; - }; - - struct NsoArgument { - u32 allocated_space; - u32 args_size; - u8 _0x8[0x18]; - u8 arguments[]; - }; - - - static_assert(sizeof(NsoHeader) == 0x100, "Incorrectly defined NsoHeader!"); - - static const char *GetNsoFileName(unsigned int index) { - switch (index) { - case 0: - return "rtld"; - case 1: - return "main"; - case 2: - return "subsdk0"; - case 3: - return "subsdk1"; - case 4: - return "subsdk2"; - case 5: - return "subsdk3"; - case 6: - return "subsdk4"; - case 7: - return "subsdk5"; - case 8: - return "subsdk6"; - case 9: - return "subsdk7"; - case 10: - return "subsdk8"; - case 11: - return "subsdk9"; - case 12: - return "sdk"; - default: - std::abort(); - } - } - - static FILE *OpenNsoFromECS(unsigned int index, ContentManagement::ExternalContentSource *ecs); - static FILE *OpenNsoFromHBL(unsigned int index); - static FILE *OpenNsoFromExeFS(unsigned int index); - static FILE *OpenNsoFromSdCard(unsigned int index, u64 title_id); - static bool CheckNsoStubbed(unsigned int index, u64 title_id); - static FILE *OpenNso(unsigned int index, u64 title_id); - - static bool IsNsoPresent(unsigned int index); - static unsigned char *GetNsoBuildId(unsigned int index); - static Result LoadNsoHeaders(u64 title_id); - static Result ValidateNsoLoadSet(); - static Result CalculateNsoLoadExtents(u32 addspace_type, u32 args_size, NsoLoadExtents *extents); - - static Result LoadNsoSegment(u64 title_id, unsigned int index, unsigned int segment, FILE *f_nso, u8 *map_base, u8 *map_end); - static Result LoadNsosIntoProcessMemory(Handle process_h, u64 title_id, NsoLoadExtents *extents, const u8 *args, u32 args_size); -}; \ No newline at end of file diff --git a/stratosphere/loader/source/ldr_patcher.cpp b/stratosphere/loader/source/ldr_patcher.cpp index 9b753072f..0582bd7a6 100644 --- a/stratosphere/loader/source/ldr_patcher.cpp +++ b/stratosphere/loader/source/ldr_patcher.cpp @@ -14,174 +14,30 @@ * along with this program. If not, see . */ -#include -#include -#include #include -#include -#include +#include +#include -#include #include "ldr_patcher.hpp" -/* IPS Patching adapted from Luma3DS (https://github.com/AuroraWright/Luma3DS/blob/master/sysmodules/loader/source/patcher.c) */ +namespace sts::ldr { -#define IPS_MAGIC "PATCH" -#define IPS_TAIL "EOF" + namespace { -#define IPS32_MAGIC "IPS32" -#define IPS32_TAIL "EEOF" + constexpr const char *NsoPatchesDirectory = "exefs_patches"; -static inline u8 HexNybbleToU8(const char nybble) { - if ('0' <= nybble && nybble <= '9') { - return nybble - '0'; - } else if ('a' <= nybble && nybble <= 'f') { - return nybble - 'a' + 0xa; - } else { - return nybble - 'A' + 0xA; - } -} + /* Exefs patches want to prevent modification of header, */ + /* and also want to adjust offset relative to mapped location. */ + constexpr size_t NsoPatchesProtectedSize = sizeof(NsoHeader); + constexpr size_t NsoPatchesProtectedOffset = sizeof(NsoHeader); -static bool MatchesBuildId(const char *name, size_t name_len, const u8 *build_id) { - /* Validate name is hex build id. */ - for (unsigned int i = 0; i < name_len - 4; i++) { - if (isxdigit(name[i]) == 0) { - return false; - } } - /* Read build id from name. */ - u8 build_id_from_name[0x20] = {0}; - for (unsigned int name_ofs = 0, id_ofs = 0; name_ofs < name_len - 4; id_ofs++) { - build_id_from_name[id_ofs] |= HexNybbleToU8(name[name_ofs++]) << 4; - build_id_from_name[id_ofs] |= HexNybbleToU8(name[name_ofs++]); + /* Apply IPS patches. */ + void LocateAndApplyIpsPatchesToModule(const u8 *build_id, uintptr_t mapped_nso, size_t mapped_size) { + ro::ModuleId module_id; + std::memcpy(&module_id.build_id, build_id, sizeof(module_id.build_id)); + sts::patcher::LocateAndApplyIpsPatchesToModule(NsoPatchesDirectory, NsoPatchesProtectedSize, NsoPatchesProtectedOffset, &module_id, reinterpret_cast(mapped_nso), mapped_size); } - return memcmp(build_id, build_id_from_name, sizeof(build_id_from_name)) == 0; -} - -static void ApplyIpsPatch(u8 *mapped_nso, size_t mapped_size, bool is_ips32, FILE *f_ips) { - u8 buffer[4]; - while (fread(buffer, is_ips32 ? 4 : 3, 1, f_ips) == 1) { - if (is_ips32 && memcmp(buffer, IPS32_TAIL, 4) == 0) { - break; - } else if (!is_ips32 && memcmp(buffer, IPS_TAIL, 3) == 0) { - break; - } - - /* Offset of patch. */ - u32 patch_offset; - if (is_ips32) { - patch_offset = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; - } else { - patch_offset = (buffer[0] << 16) | (buffer[1] << 8) | (buffer[2]); - } - - /* Size of patch. */ - if (fread(buffer, 2, 1, f_ips) != 1) { - break; - } - u32 patch_size = (buffer[0] << 8) | (buffer[1]); - - /* Check for RLE encoding. */ - if (patch_size == 0) { - /* Size of RLE. */ - if (fread(buffer, 2, 1, f_ips) != 1) { - break; - } - - u32 rle_size = (buffer[0] << 8) | (buffer[1]); - - /* Value for RLE. */ - if (fread(buffer, 1, 1, f_ips) != 1) { - break; - } - - if (patch_offset < sizeof(NsoUtils::NsoHeader)) { - if (patch_offset + rle_size > sizeof(NsoUtils::NsoHeader)) { - u32 diff = sizeof(NsoUtils::NsoHeader) - patch_offset; - patch_offset += diff; - rle_size -= diff; - goto IPS_RLE_PATCH_OFFSET_WITHIN_BOUNDS; - } - } else { - IPS_RLE_PATCH_OFFSET_WITHIN_BOUNDS: - patch_offset -= sizeof(NsoUtils::NsoHeader); - if (patch_offset + rle_size > mapped_size) { - rle_size = mapped_size - patch_offset; - } - memset(mapped_nso + patch_offset, buffer[0], rle_size); - } - } else { - if (patch_offset < sizeof(NsoUtils::NsoHeader)) { - if (patch_offset + patch_size > sizeof(NsoUtils::NsoHeader)) { - u32 diff = sizeof(NsoUtils::NsoHeader) - patch_offset; - patch_offset += diff; - patch_size -= diff; - fseek(f_ips, diff, SEEK_CUR); - goto IPS_DATA_PATCH_OFFSET_WITHIN_BOUNDS; - } else { - fseek(f_ips, patch_size, SEEK_CUR); - } - } else { - IPS_DATA_PATCH_OFFSET_WITHIN_BOUNDS: - patch_offset -= sizeof(NsoUtils::NsoHeader); - u32 read_size = patch_size; - if (patch_offset + read_size > mapped_size) { - read_size = mapped_size - patch_offset; - } - if (fread(mapped_nso + patch_offset, read_size, 1, f_ips) != 1) { - break; - } - if (patch_size > read_size) { - fseek(f_ips, patch_size - read_size, SEEK_CUR); - } - } - } - } -} - -void PatchUtils::ApplyPatches(const NsoUtils::NsoHeader *header, u8 *mapped_nso, size_t mapped_size) { - /* Inspect all patches from /atmosphere/exefs_patches/<*>/<*>.ips */ - char path[FS_MAX_PATH+1] = {0}; - snprintf(path, sizeof(path) - 1, "sdmc:/atmosphere/exefs_patches"); - DIR *patches_dir = opendir(path); - struct dirent *pdir_ent; - if (patches_dir != NULL) { - /* Iterate over the patches directory to find patch subdirectories. */ - while ((pdir_ent = readdir(patches_dir)) != NULL) { - if (strcmp(pdir_ent->d_name, ".") == 0 || strcmp(pdir_ent->d_name, "..") == 0) { - continue; - } - snprintf(path, sizeof(path) - 1, "sdmc:/atmosphere/exefs_patches/%s", pdir_ent->d_name); - DIR *patch_dir = opendir(path); - struct dirent *ent; - if (patch_dir != NULL) { - /* Iterate over the patch subdirectory to find .ips patches. */ - while ((ent = readdir(patch_dir)) != NULL) { - if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) { - continue; - } - size_t name_len = strlen(ent->d_name); - if ((4 < name_len && name_len <= 0x44) && ((name_len & 1) == 0) && strcmp(ent->d_name + name_len - 4, ".ips") == 0 && MatchesBuildId(ent->d_name, name_len, header->build_id)) { - snprintf(path, sizeof(path) - 1, "sdmc:/atmosphere/exefs_patches/%s/%s", pdir_ent->d_name, ent->d_name); - FILE *f_ips = fopen(path, "rb"); - if (f_ips != NULL) { - u8 header[5]; - if (fread(header, 5, 1, f_ips) == 1) { - if (memcmp(header, IPS_MAGIC, 5) == 0) { - ApplyIpsPatch(mapped_nso, mapped_size, false, f_ips); - } else if (memcmp(header, IPS32_MAGIC, 5) == 0) { - ApplyIpsPatch(mapped_nso, mapped_size, true, f_ips); - } - } - fclose(f_ips); - } - } - } - closedir(patch_dir); - } - } - closedir(patches_dir); - } } \ No newline at end of file diff --git a/stratosphere/loader/source/ldr_patcher.hpp b/stratosphere/loader/source/ldr_patcher.hpp index eb492c726..a5b611f11 100644 --- a/stratosphere/loader/source/ldr_patcher.hpp +++ b/stratosphere/loader/source/ldr_patcher.hpp @@ -16,11 +16,11 @@ #pragma once #include -#include +#include -#include "ldr_nso.hpp" +namespace sts::ldr { -class PatchUtils { - public: - static void ApplyPatches(const NsoUtils::NsoHeader *header, u8 *mapped_nso, size_t size); -}; \ No newline at end of file + /* Apply IPS patches. */ + void LocateAndApplyIpsPatchesToModule(const u8 *build_id, uintptr_t mapped_nso, size_t mapped_size); + +} \ No newline at end of file diff --git a/stratosphere/loader/source/ldr_process_creation.cpp b/stratosphere/loader/source/ldr_process_creation.cpp index 228150655..64b423b50 100644 --- a/stratosphere/loader/source/ldr_process_creation.cpp +++ b/stratosphere/loader/source/ldr_process_creation.cpp @@ -14,248 +14,698 @@ * along with this program. If not, see . */ -#include -#include -#include +#include +#include +#include +#include -#include "ldr_process_creation.hpp" -#include "ldr_registration.hpp" -#include "ldr_launch_queue.hpp" +#include "ldr_capabilities.hpp" #include "ldr_content_management.hpp" -#include "ldr_npdm.hpp" -#include "ldr_nso.hpp" +#include "ldr_launch_record.hpp" +#include "ldr_meta.hpp" +#include "ldr_patcher.hpp" +#include "ldr_process_creation.hpp" +#include "ldr_ro_manager.hpp" + +/* TODO: Move into libstratosphere header? */ +namespace sts::svc { + + namespace { + + enum CreateProcessFlag : u32 { + /* Is 64 bit? */ + CreateProcessFlag_Is64Bit = (1 << 0), + + /* What kind of address space? */ + CreateProcessFlag_AddressSpaceShift = 1, + CreateProcessFlag_AddressSpaceMask = (7 << CreateProcessFlag_AddressSpaceShift), + CreateProcessFlag_AddressSpace32Bit = (ldr::Npdm::AddressSpaceType_32Bit << CreateProcessFlag_AddressSpaceShift), + CreateProcessFlag_AddressSpace64BitDeprecated = (ldr::Npdm::AddressSpaceType_64BitDeprecated << CreateProcessFlag_AddressSpaceShift), + CreateProcessFlag_AddressSpace32BitWithoutAlias = (ldr::Npdm::AddressSpaceType_32BitWithoutAlias << CreateProcessFlag_AddressSpaceShift), + CreateProcessFlag_AddressSpace64Bit = (ldr::Npdm::AddressSpaceType_64Bit << CreateProcessFlag_AddressSpaceShift), + + /* Should JIT debug be done on crash? */ + CreateProcessFlag_EnableDebug = (1 << 4), + + /* Should ASLR be enabled for the process? */ + CreateProcessFlag_EnableAslr = (1 << 5), + + /* Is the process an application? */ + CreateProcessFlag_IsApplication = (1 << 6), + + /* 4.x deprecated: Should use secure memory? */ + CreateProcessFlag_DeprecatedUseSecureMemory = (1 << 7), + + /* 5.x+ Pool partition type. */ + CreateProcessFlag_PoolPartitionShift = 7, + CreateProcessFlag_PoolPartitionMask = (0xF << CreateProcessFlag_PoolPartitionShift), + CreateProcessFlag_PoolPartitionApplication = (ldr::Acid::PoolPartition_Application << CreateProcessFlag_PoolPartitionShift), + CreateProcessFlag_PoolPartitionApplet = (ldr::Acid::PoolPartition_Applet << CreateProcessFlag_PoolPartitionShift), + CreateProcessFlag_PoolPartitionSystem = (ldr::Acid::PoolPartition_System << CreateProcessFlag_PoolPartitionShift), + CreateProcessFlag_PoolPartitionSystemNonSecure = (ldr::Acid::PoolPartition_SystemNonSecure << CreateProcessFlag_PoolPartitionShift), + + /* 7.x+ Should memory allocation be optimized? This requires IsApplication. */ + CreateProcessFlag_OptimizeMemoryAllocation = (1 << 11), + }; + + } -static inline bool IsDisallowedVersion810(const u64 title_id, const u32 version) { - return version == 0 && - (title_id == TitleId_Settings || - title_id == TitleId_Bus || - title_id == TitleId_Audio || - title_id == TitleId_NvServices || - title_id == TitleId_Ns || - title_id == TitleId_Ssl || - title_id == TitleId_Es || - title_id == TitleId_Creport || - title_id == TitleId_Ro); } -Result ProcessCreation::ValidateProcessVersion(u64 title_id, u32 version) { - if (GetRuntimeFirmwareVersion() < FirmwareVersion_810) { - return ResultSuccess; - } else { -#ifdef LDR_VALIDATE_PROCESS_VERSION - if (IsDisallowedVersion810(title_id, version)) { - return ResultLoaderInvalidVersion; - } else { +namespace sts::ldr { + + namespace { + + /* Convenience defines. */ + constexpr size_t BaseAddressAlignment = 0x200000; + constexpr size_t SystemResourceSizeAlignment = 0x200000; + constexpr size_t SystemResourceSizeMax = 0x1FE00000; + + /* Types. */ + enum NsoIndex { + Nso_Rtld = 0, + Nso_Main = 1, + Nso_SubSdk0 = 2, + Nso_SubSdk1 = 3, + Nso_SubSdk2 = 4, + Nso_SubSdk3 = 5, + Nso_SubSdk4 = 6, + Nso_SubSdk5 = 7, + Nso_SubSdk6 = 8, + Nso_SubSdk7 = 9, + Nso_SubSdk8 = 10, + Nso_SubSdk9 = 11, + Nso_Sdk = 12, + Nso_Count, + }; + + constexpr const char *GetNsoName(size_t idx) { + if (idx >= Nso_Count) { + std::abort(); + } + + constexpr const char *NsoNames[Nso_Count] = { + "rtld", + "main", + "subsdk0", + "subsdk1", + "subsdk2", + "subsdk3", + "subsdk4", + "subsdk5", + "subsdk6", + "subsdk7", + "subsdk8", + "subsdk9", + "sdk", + }; + return NsoNames[idx]; + } + + struct CreateProcessInfo { + char name[12]; + u32 version; + ncm::TitleId title_id; + u64 code_address; + u32 code_num_pages; + u32 flags; + Handle reslimit; + u32 system_resource_num_pages; + }; + static_assert(sizeof(CreateProcessInfo) == 0x30, "CreateProcessInfo definition!"); + + struct ProcessInfo { + AutoHandle process_handle; + uintptr_t args_address; + size_t args_size; + uintptr_t nso_address[Nso_Count]; + size_t nso_size[Nso_Count]; + }; + + /* Global NSO header cache. */ + bool g_has_nso[Nso_Count]; + NsoHeader g_nso_headers[Nso_Count]; + + /* Helpers. */ + Result GetProgramInfoFromMeta(ProgramInfo *out, const Meta *meta) { + /* Copy basic info. */ + out->main_thread_priority = meta->npdm->main_thread_priority; + out->default_cpu_id = meta->npdm->default_cpu_id; + out->main_thread_stack_size = meta->npdm->main_thread_stack_size; + out->title_id = meta->aci->title_id; + + /* Copy access controls. */ + size_t offset = 0; +#define COPY_ACCESS_CONTROL(source, which) \ + ({ \ + const size_t size = meta->source->which##_size; \ + if (offset + size >= sizeof(out->ac_buffer)) { \ + return ResultLoaderInternalError; \ + } \ + out->source##_##which##_size = size; \ + std::memcpy(out->ac_buffer + offset, meta->source##_##which, size); \ + offset += size; \ + }) + + /* Copy all access controls to buffer. */ + COPY_ACCESS_CONTROL(acid, sac); + COPY_ACCESS_CONTROL(aci, sac); + COPY_ACCESS_CONTROL(acid, fac); + COPY_ACCESS_CONTROL(aci, fah); +#undef COPY_ACCESS_CONTROL + + /* Copy flags. */ + out->flags = GetProgramInfoFlags(meta->acid_kac, meta->acid->kac_size); return ResultSuccess; } -#else - return ResultSuccess; -#endif - } -} -Result ProcessCreation::InitializeProcessInfo(NpdmUtils::NpdmInfo *npdm, Handle reslimit_h, u64 arg_flags, ProcessInfo *out_proc_info) { - /* Initialize a ProcessInfo using an npdm. */ - *out_proc_info = {}; - - /* Copy all but last char of name, insert NULL terminator. */ - std::copy(npdm->header->title_name, npdm->header->title_name + sizeof(out_proc_info->name) - 1, out_proc_info->name); - out_proc_info->name[sizeof(out_proc_info->name) - 1] = 0; - - /* Set title id. */ - out_proc_info->title_id = npdm->aci0->title_id; - - /* Set version. */ - out_proc_info->version = npdm->header->version; - - /* Copy reslimit handle raw. */ - out_proc_info->reslimit_h = reslimit_h; - - /* Set IsAddressSpace64Bit, AddressSpaceType. */ - if (npdm->header->mmu_flags & 8) { - /* Invalid Address Space Type. */ - return ResultLoaderInvalidMeta; - } - out_proc_info->process_flags = (npdm->header->mmu_flags & 0xF); - - /* Set Bit 4 (?) and EnableAslr based on argument flags. */ - out_proc_info->process_flags |= ((arg_flags & 3) << 4) ^ 0x20; - /* Set UseSystemMemBlocks if application type is 1. */ - u32 application_type = NpdmUtils::GetApplicationType((u32 *)npdm->aci0_kac, npdm->aci0->kac_size / sizeof(u32)); - if ((application_type & 3) == 1) { - out_proc_info->process_flags |= 0x40; - /* 7.0.0+: Set unknown bit related to system resource heap if relevant. */ - if (GetRuntimeFirmwareVersion() >= FirmwareVersion_700) { - if ((npdm->header->mmu_flags & 0x10)) { - out_proc_info->process_flags |= 0x800; - } + bool IsApplet(const Meta *meta) { + return (GetProgramInfoFlags(meta->aci_kac, meta->aci->kac_size) & ProgramInfoFlag_ApplicationTypeMask) == ProgramInfoFlag_Applet; } - } - /* 3.0.0+ System Resource Size. */ - if ((GetRuntimeFirmwareVersion() >= FirmwareVersion_300)) { - if (npdm->header->system_resource_size & 0x1FFFFF) { - return ResultLoaderInvalidSize; + bool IsApplication(const Meta *meta) { + return (GetProgramInfoFlags(meta->aci_kac, meta->aci->kac_size) & ProgramInfoFlag_ApplicationTypeMask) == ProgramInfoFlag_Application; } - if (npdm->header->system_resource_size) { - if ((out_proc_info->process_flags & 6) == 0) { - return ResultLoaderInvalidMeta; - } - if (!(((application_type & 3) == 1) || ((GetRuntimeFirmwareVersion() >= FirmwareVersion_600) && (application_type & 3) == 2))) { - return ResultLoaderInvalidMeta; - } - if (npdm->header->system_resource_size > 0x1FE00000) { - return ResultLoaderInvalidMeta; - } - } - out_proc_info->system_resource_num_pages = npdm->header->system_resource_size >> 12; - } else { - out_proc_info->system_resource_num_pages = 0; - } - /* 5.0.0+ Pool Partition. */ - if ((GetRuntimeFirmwareVersion() >= FirmwareVersion_500)) { - u32 pool_partition_id = (npdm->acid->flags >> 2) & 0xF; - switch (pool_partition_id) { - case 0: /* Application. */ - if ((application_type & 3) == 2) { - out_proc_info->process_flags |= 0x80; + Npdm::AddressSpaceType GetAddressSpaceType(const Meta *meta) { + return static_cast((meta->npdm->flags & Npdm::MetaFlag_AddressSpaceTypeMask) >> Npdm::MetaFlag_AddressSpaceTypeShift); + } + + Acid::PoolPartition GetPoolPartition(const Meta *meta) { + return static_cast((meta->acid->flags & Acid::AcidFlag_PoolPartitionMask) >> Acid::AcidFlag_PoolPartitionShift); + } + + constexpr bool IsDisallowedVersion810(const u64 title_id, const u32 version) { + return version == 0 && + (title_id == TitleId_Settings || + title_id == TitleId_Bus || + title_id == TitleId_Audio || + title_id == TitleId_NvServices || + title_id == TitleId_Ns || + title_id == TitleId_Ssl || + title_id == TitleId_Es || + title_id == TitleId_Creport || + title_id == TitleId_Ro); + } + + Result ValidateTitleVersion(ncm::TitleId title_id, u32 version) { + if (GetRuntimeFirmwareVersion() < FirmwareVersion_810) { + return ResultSuccess; + } else { +#ifdef LDR_VALIDATE_PROCESS_VERSION + if (IsDisallowedVersion810(static_cast(title_id), version)) { + return ResultLoaderInvalidVersion; + } else { + return ResultSuccess; } - break; - case 1: /* Applet. */ - out_proc_info->process_flags |= 0x80; - break; - case 2: /* Sysmodule. */ - out_proc_info->process_flags |= 0x100; - break; - case 3: /* nvservices. */ - out_proc_info->process_flags |= 0x180; - break; - default: - return ResultLoaderInvalidMeta; - } - } - - return ResultSuccess; -} - -Result ProcessCreation::CreateProcess(Handle *out_process_h, u64 index, char *nca_path, LaunchQueue::LaunchItem *launch_item, u64 arg_flags, Handle reslimit_h) { - NpdmUtils::NpdmInfo npdm_info = {}; - ProcessInfo process_info = {}; - NsoUtils::NsoLoadExtents nso_extents = {}; - Registration::Process *target_process; - Handle process_h = 0; - u64 process_id = 0; - - /* Get the process from the registration queue. */ - target_process = Registration::GetProcess(index); - if (target_process == nullptr) { - return ResultLoaderProcessNotRegistered; - } - - /* Mount the title's exefs. */ - bool mounted_code = false; - if (target_process->tid_sid.storage_id != FsStorageId_None) { - R_TRY(ContentManagement::MountCodeForTidSid(&target_process->tid_sid)); - mounted_code = true; - } else { - if (R_SUCCEEDED(ContentManagement::MountCodeNspOnSd(target_process->tid_sid.title_id))) { - mounted_code = true; - } - } - ON_SCOPE_EXIT { - if (mounted_code) { - const Result unmount_res = ContentManagement::UnmountCode(); - if (target_process->tid_sid.storage_id != FsStorageId_None) { - R_ASSERT(unmount_res); +#else + return ResultSuccess; +#endif } } - }; - /* Load the process's NPDM. */ - R_TRY(NpdmUtils::LoadNpdmFromCache(target_process->tid_sid.title_id, &npdm_info)); + Result LoadNsoHeaders(ncm::TitleId title_id, NsoHeader *nso_headers, bool *has_nso) { + /* Clear NSOs. */ + std::memset(nso_headers, 0, sizeof(*nso_headers) * Nso_Count); + std::memset(has_nso, 0, sizeof(*has_nso) * Nso_Count); - /* Validate version. */ - R_TRY(ValidateProcessVersion(target_process->tid_sid.title_id, npdm_info.header->version)); + for (size_t i = 0; i < Nso_Count; i++) { + FILE *f = nullptr; + if (R_SUCCEEDED(OpenCodeFile(f, title_id, GetNsoName(i)))) { + ON_SCOPE_EXIT { fclose(f); }; + /* Read NSO header. */ + if (fread(nso_headers + i, sizeof(*nso_headers), 1, f) != 1) { + return ResultLoaderInvalidNso; + } + has_nso[i] = true; + } + } - /* Validate the title we're loading is what we expect. */ - if (npdm_info.aci0->title_id < npdm_info.acid->title_id_range_min || npdm_info.aci0->title_id > npdm_info.acid->title_id_range_max) { - return ResultLoaderInvalidProgramId; - } - - /* Validate that the ACI0 Kernel Capabilities are valid and restricted by the ACID Kernel Capabilities. */ - const u32 *acid_caps = reinterpret_cast(npdm_info.acid_kac); - const u32 *aci0_caps = reinterpret_cast(npdm_info.aci0_kac); - const size_t num_acid_caps = npdm_info.acid->kac_size / sizeof(*acid_caps); - const size_t num_aci0_caps = npdm_info.aci0->kac_size / sizeof(*aci0_caps); - R_TRY(NpdmUtils::ValidateCapabilities(acid_caps, num_acid_caps, aci0_caps, num_aci0_caps)); - - /* Read in all NSO headers, see what NSOs are present. */ - R_TRY(NsoUtils::LoadNsoHeaders(npdm_info.aci0->title_id)); - - /* Validate that the set of NSOs to be loaded is correct. */ - R_TRY(NsoUtils::ValidateNsoLoadSet()); - - /* Initialize the ProcessInfo. */ - R_TRY(ProcessCreation::InitializeProcessInfo(&npdm_info, reslimit_h, arg_flags, &process_info)); - - /* Figure out where NSOs will be mapped, and how much space they (and arguments) will take up. */ - R_TRY(NsoUtils::CalculateNsoLoadExtents(process_info.process_flags, launch_item != nullptr ? launch_item->arg_size : 0, &nso_extents)); - - /* Set Address Space information in ProcessInfo. */ - process_info.code_addr = nso_extents.base_address; - process_info.code_num_pages = nso_extents.total_size + 0xFFF; - process_info.code_num_pages >>= 12; - - /* Call svcCreateProcess(). */ - R_TRY(svcCreateProcess(&process_h, &process_info, (u32 *)npdm_info.aci0_kac, npdm_info.aci0->kac_size/sizeof(u32))); - auto proc_handle_guard = SCOPE_GUARD { - svcCloseHandle(process_h); - }; - - - /* Load all NSOs into Process memory, and set permissions accordingly. */ - { - const u8 *launch_args = nullptr; - size_t launch_args_size = 0; - if (launch_item != nullptr) { - launch_args = reinterpret_cast(launch_item->args); - launch_args_size = launch_item->arg_size; + return ResultSuccess; } - R_TRY(NsoUtils::LoadNsosIntoProcessMemory(process_h, npdm_info.aci0->title_id, &nso_extents, launch_args, launch_args_size)); - } + Result ValidateNsoHeaders(const NsoHeader *nso_headers, const bool *has_nso) { + /* We must always have a main. */ + if (!has_nso[Nso_Main]) { + return ResultLoaderInvalidNso; + } - /* Update the list of registered processes with the new process. */ - svcGetProcessId(&process_id, process_h); - bool is_64_bit_addspace; - if ((GetRuntimeFirmwareVersion() >= FirmwareVersion_200)) { - is_64_bit_addspace = (((npdm_info.header->mmu_flags >> 1) & 5) | 2) == 3; - } else { - is_64_bit_addspace = (npdm_info.header->mmu_flags & 0xE) == 0x2; - } - Registration::SetProcessIdTidAndIs64BitAddressSpace(index, process_id, npdm_info.aci0->title_id, is_64_bit_addspace); - for (unsigned int i = 0; i < NSO_NUM_MAX; i++) { - if (NsoUtils::IsNsoPresent(i)) { - Registration::AddModuleInfo(index, nso_extents.nso_addresses[i], nso_extents.nso_sizes[i], NsoUtils::GetNsoBuildId(i)); + /* If we don't have an RTLD, we must only have a main. */ + if (!has_nso[Nso_Rtld]) { + for (size_t i = Nso_Main + 1; i < Nso_Count; i++) { + if (has_nso[i]) { + return ResultLoaderInvalidNso; + } + } + } + + /* All NSOs must have zero text offset. */ + for (size_t i = 0; i < Nso_Count; i++) { + if (nso_headers[i].text_dst_offset != 0) { + return ResultLoaderInvalidNso; + } + } + + return ResultSuccess; } + + Result ValidateMeta(const Meta *meta, const ncm::TitleLocation &loc) { + /* Validate version. */ + R_TRY(ValidateTitleVersion(loc.title_id, meta->npdm->version)); + + /* Validate title id. */ + if (meta->aci->title_id < meta->acid->title_id_min || meta->aci->title_id > meta->acid->title_id_max) { + return ResultLoaderInvalidProgramId; + } + + /* Validate the kernel capacilities. */ + R_TRY(ValidateCapabilities(meta->acid_kac, meta->acid->kac_size, meta->aci_kac, meta->aci->kac_size)); + + /* All good. */ + return ResultSuccess; + } + + Result GetCreateProcessFlags(u32 *out, const Meta *meta, const u32 ldr_flags) { + const u8 meta_flags = meta->npdm->flags; + + u32 flags = 0; + + /* Set Is64Bit. */ + if (meta_flags & Npdm::MetaFlag_Is64Bit) { + flags |= svc::CreateProcessFlag_Is64Bit; + } + + /* Set AddressSpaceType. */ + switch (GetAddressSpaceType(meta)) { + case Npdm::AddressSpaceType_32Bit: + flags |= svc::CreateProcessFlag_AddressSpace32Bit; + break; + case Npdm::AddressSpaceType_64BitDeprecated: + flags |= svc::CreateProcessFlag_AddressSpace64BitDeprecated; + break; + case Npdm::AddressSpaceType_32BitWithoutAlias: + flags |= svc::CreateProcessFlag_AddressSpace32BitWithoutAlias; + break; + case Npdm::AddressSpaceType_64Bit: + flags |= svc::CreateProcessFlag_AddressSpace64Bit; + break; + default: + return ResultLoaderInvalidMeta; + } + + /* Set Enable Debug. */ + if (ldr_flags & CreateProcessFlag_EnableDebug) { + flags |= svc::CreateProcessFlag_EnableDebug; + } + + /* Set Enable ASLR. */ + if (!(ldr_flags & CreateProcessFlag_DisableAslr)) { + flags |= svc::CreateProcessFlag_EnableAslr; + } + + /* Set Is Application. */ + if (IsApplication(meta)) { + flags |= svc::CreateProcessFlag_IsApplication; + + /* 7.0.0+: Set OptimizeMemoryAllocation if relevant. */ + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_700) { + if (meta_flags & Npdm::MetaFlag_OptimizeMemoryAllocation) { + flags |= svc::CreateProcessFlag_OptimizeMemoryAllocation; + } + } + } + + /* 5.0.0+ Set Pool Partition. */ + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) { + switch (GetPoolPartition(meta)) { + case Acid::PoolPartition_Application: + if (IsApplet(meta)) { + flags |= svc::CreateProcessFlag_PoolPartitionApplet; + } else { + flags |= svc::CreateProcessFlag_PoolPartitionApplication; + } + break; + case Acid::PoolPartition_Applet: + flags |= svc::CreateProcessFlag_PoolPartitionApplet; + break; + case Acid::PoolPartition_System: + flags |= svc::CreateProcessFlag_PoolPartitionSystem; + break; + case Acid::PoolPartition_SystemNonSecure: + flags |= svc::CreateProcessFlag_PoolPartitionSystemNonSecure; + break; + default: + return ResultLoaderInvalidMeta; + } + } else if (GetRuntimeFirmwareVersion() >= FirmwareVersion_400) { + /* On 4.0.0+, the corresponding bit was simply "UseSecureMemory". */ + if (meta->acid->flags & Acid::AcidFlag_DeprecatedUseSecureMemory) { + flags |= svc::CreateProcessFlag_DeprecatedUseSecureMemory; + } + } + + *out = flags; + return ResultSuccess; + } + + Result GetCreateProcessInfo(CreateProcessInfo *out, const Meta *meta, u32 flags, Handle reslimit_h) { + /* Clear output. */ + std::memset(out, 0, sizeof(*out)); + + /* Set name, version, title id, resource limit handle. */ + std::memcpy(out->name, meta->npdm->title_name, sizeof(out->name) - 1); + out->version = meta->npdm->version; + out->title_id = meta->aci->title_id; + out->reslimit = reslimit_h; + + /* Set flags. */ + R_TRY(GetCreateProcessFlags(&out->flags, meta, flags)); + + /* 3.0.0+ System Resource Size. */ + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_300) { + /* Validate size is aligned. */ + if (meta->npdm->system_resource_size & (SystemResourceSizeAlignment - 1)) { + return ResultLoaderInvalidSize; + } + /* Validate system resource usage. */ + if (meta->npdm->system_resource_size) { + /* Process must be 64-bit. */ + if (!(out->flags & svc::CreateProcessFlag_AddressSpace64Bit)) { + return ResultLoaderInvalidMeta; + } + + /* Process must be application or applet. */ + if (!IsApplication(meta) && !IsApplet(meta)) { + return ResultLoaderInvalidMeta; + } + + /* Size must be less than or equal to max. */ + if (meta->npdm->system_resource_size > SystemResourceSizeMax) { + return ResultLoaderInvalidMeta; + } + } + out->system_resource_num_pages = meta->npdm->system_resource_size >> 12; + } + + return ResultSuccess; + } + + Result DecideAddressSpaceLayout(ProcessInfo *out, CreateProcessInfo *out_cpi, const NsoHeader *nso_headers, const bool *has_nso, const args::ArgumentInfo *arg_info) { + /* Clear output. */ + out->args_address = 0; + out->args_size = 0; + std::memset(out->nso_address, 0, sizeof(out->nso_address)); + std::memset(out->nso_size, 0, sizeof(out->nso_size)); + + size_t total_size = 0; + + /* Calculate base offsets. */ + for (size_t i = 0; i < Nso_Count; i++) { + if (has_nso[i]) { + out->nso_address[i] = total_size; + const size_t text_end = nso_headers[i].text_dst_offset + nso_headers[i].text_size; + const size_t ro_end = nso_headers[i].ro_dst_offset + nso_headers[i].ro_size; + const size_t rw_end = nso_headers[i].rw_dst_offset + nso_headers[i].rw_size + nso_headers[i].bss_size; + out->nso_size[i] = text_end; + out->nso_size[i] = std::max(out->nso_size[i], ro_end); + out->nso_size[i] = std::max(out->nso_size[i], rw_end); + out->nso_size[i] = (out->nso_size[i] + size_t(0xFFFul)) & ~size_t(0xFFFul); + + total_size += out->nso_size[i]; + + if (arg_info != nullptr && arg_info->args_size && !out->args_size) { + out->args_address = total_size; + out->args_size = 2 * arg_info->args_size + args::ArgumentSizeMax + 2 * sizeof(u32); + out->args_size = (out->args_size + size_t(0xFFFul)) & ~size_t(0xFFFul); + total_size += out->args_size; + } + } + } + + /* Calculate ASLR. */ + uintptr_t aslr_start = 0; + uintptr_t aslr_size = 0; + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_200) { + switch (out_cpi->flags & svc::CreateProcessFlag_AddressSpaceMask) { + case svc::CreateProcessFlag_AddressSpace32Bit: + case svc::CreateProcessFlag_AddressSpace32BitWithoutAlias: + aslr_start = map::AslrBase32Bit; + aslr_size = map::AslrSize32Bit; + break; + case svc::CreateProcessFlag_AddressSpace64BitDeprecated: + aslr_start = map::AslrBase64BitDeprecated; + aslr_size = map::AslrSize64BitDeprecated; + break; + case svc::CreateProcessFlag_AddressSpace64Bit: + aslr_start = map::AslrBase64Bit; + aslr_size = map::AslrSize64Bit; + break; + default: + std::abort(); + } + } else { + /* On 1.0.0, only 2 address space types existed. */ + if (out_cpi->flags & svc::CreateProcessFlag_AddressSpace64BitDeprecated) { + aslr_start = map::AslrBase64BitDeprecated; + aslr_size = map::AslrSize64BitDeprecated; + } else { + aslr_start = map::AslrBase32Bit; + aslr_size = map::AslrSize32Bit; + } + } + if (total_size > aslr_size) { + return ResultKernelOutOfMemory; + } + + /* Set Create Process output. */ + uintptr_t aslr_slide = 0; + uintptr_t unused_size = (aslr_size - total_size); + if (out_cpi->flags & svc::CreateProcessFlag_EnableAslr) { + aslr_slide = sts::rnd::GenerateRandomU64(unused_size / BaseAddressAlignment) * BaseAddressAlignment; + } + + /* Set out. */ + aslr_start += aslr_slide; + for (size_t i = 0; i < Nso_Count; i++) { + if (has_nso[i]) { + out->nso_address[i] += aslr_start; + } + } + if (out->args_address) { + out->args_address += aslr_start; + } + + out_cpi->code_address = aslr_start; + out_cpi->code_num_pages = total_size >> 12; + + return ResultSuccess; + } + + Result CreateProcessImpl(ProcessInfo *out, const Meta *meta, const NsoHeader *nso_headers, const bool *has_nso, const args::ArgumentInfo *arg_info, u32 flags, Handle reslimit_h) { + /* Get CreateProcessInfo. */ + CreateProcessInfo cpi; + R_TRY(GetCreateProcessInfo(&cpi, meta, flags, reslimit_h)); + + /* Decide on an NSO layout. */ + R_TRY(DecideAddressSpaceLayout(out, &cpi, nso_headers, has_nso, arg_info)); + + /* Actually create process. const_cast necessary because libnx doesn't declare svcCreateProcess with const u32*. */ + return svcCreateProcess(out->process_handle.GetPointer(), &cpi, const_cast(reinterpret_cast(meta->aci_kac)), meta->aci->kac_size / sizeof(u32)); + } + + Result LoadNsoSegment(FILE *f, const NsoHeader::SegmentInfo *segment, size_t file_size, const u8 *file_hash, bool is_compressed, bool check_hash, uintptr_t map_base, uintptr_t map_end) { + /* Select read size based on compression. */ + if (!is_compressed) { + file_size = segment->size; + } + + /* Validate size. */ + if (file_size > segment->size || file_size > std::numeric_limits::max() || segment->size > std::numeric_limits::max()) { + return ResultLoaderInvalidNso; + } + + /* Load data from file. */ + uintptr_t load_address = is_compressed ? map_end - file_size : map_base; + fseek(f, segment->file_offset, SEEK_SET); + if (fread(reinterpret_cast(load_address), file_size, 1, f) != 1) { + return ResultLoaderInvalidNso; + } + + /* Uncompress if necessary. */ + if (is_compressed) { + if (util::DecompressLZ4(reinterpret_cast(map_base), segment->size, reinterpret_cast(load_address), file_size) != static_cast(segment->size)) { + return ResultLoaderInvalidNso; + } + } + + /* Check hash if necessary. */ + if (check_hash) { + u8 hash[SHA256_HASH_SIZE]; + sha256CalculateHash(hash, reinterpret_cast(map_base), segment->size); + + if (std::memcmp(hash, file_hash, sizeof(hash)) != 0) { + return ResultLoaderInvalidNso; + } + } + + return ResultSuccess; + } + + Result LoadNsoIntoProcessMemory(Handle process_handle, FILE *f, uintptr_t map_address, const NsoHeader *nso_header, uintptr_t nso_address, size_t nso_size) { + /* Map and read data from file. */ + { + map::AutoCloseMap mapper(map_address, process_handle, nso_address, nso_size); + R_TRY(mapper.GetResult()); + + /* Load NSO segments. */ + R_TRY(LoadNsoSegment(f, &nso_header->segments[NsoHeader::Segment_Text], nso_header->text_compressed_size, nso_header->text_hash, (nso_header->flags & NsoHeader::Flag_CompressedText) != 0, + (nso_header->flags & NsoHeader::Flag_CheckHashText) != 0, map_address + nso_header->text_dst_offset, map_address + nso_size)); + R_TRY(LoadNsoSegment(f, &nso_header->segments[NsoHeader::Segment_Ro], nso_header->ro_compressed_size, nso_header->ro_hash, (nso_header->flags & NsoHeader::Flag_CompressedRo) != 0, + (nso_header->flags & NsoHeader::Flag_CheckHashRo) != 0, map_address + nso_header->ro_dst_offset, map_address + nso_size)); + R_TRY(LoadNsoSegment(f, &nso_header->segments[NsoHeader::Segment_Rw], nso_header->rw_compressed_size, nso_header->rw_hash, (nso_header->flags & NsoHeader::Flag_CompressedRw) != 0, + (nso_header->flags & NsoHeader::Flag_CheckHashRw) != 0, map_address + nso_header->rw_dst_offset, map_address + nso_size)); + + /* Clear unused space to zero. */ + const size_t text_end = nso_header->text_dst_offset + nso_header->text_size; + const size_t ro_end = nso_header->ro_dst_offset + nso_header->ro_size; + const size_t rw_end = nso_header->rw_dst_offset + nso_header->rw_size; + std::memset(reinterpret_cast(map_address), 0, nso_header->text_dst_offset); + std::memset(reinterpret_cast(map_address + text_end), 0, nso_header->ro_dst_offset - text_end); + std::memset(reinterpret_cast(map_address + ro_end), 0, nso_header->rw_dst_offset - ro_end); + std::memset(reinterpret_cast(map_address + rw_end), 0, nso_header->bss_size); + + /* Apply IPS patches. */ + LocateAndApplyIpsPatchesToModule(nso_header->build_id, map_address, nso_size); + } + + /* Set permissions. */ + const size_t text_size = (static_cast(nso_header->text_size) + size_t(0xFFFul)) & ~size_t(0xFFFul); + const size_t ro_size = (static_cast(nso_header->ro_size) + size_t(0xFFFul)) & ~size_t(0xFFFul); + const size_t rw_size = (static_cast(nso_header->rw_size + nso_header->bss_size) + size_t(0xFFFul)) & ~size_t(0xFFFul); + if (text_size) { + R_TRY(svcSetProcessMemoryPermission(process_handle, nso_address + nso_header->text_dst_offset, text_size, Perm_Rx)); + } + if (ro_size) { + R_TRY(svcSetProcessMemoryPermission(process_handle, nso_address + nso_header->ro_dst_offset, ro_size, Perm_R)); + } + if (rw_size) { + R_TRY(svcSetProcessMemoryPermission(process_handle, nso_address + nso_header->rw_dst_offset, rw_size, Perm_Rw)); + } + + return ResultSuccess; + } + + Result LoadNsosIntoProcessMemory(const ProcessInfo *process_info, const ncm::TitleId title_id, const NsoHeader *nso_headers, const bool *has_nso, const args::ArgumentInfo *arg_info) { + const Handle process_handle = process_info->process_handle.Get(); + + /* Load each NSO. */ + for (size_t i = 0; i < Nso_Count; i++) { + if (has_nso[i]) { + FILE *f = nullptr; + R_TRY(OpenCodeFile(f, title_id, GetNsoName(i))); + ON_SCOPE_EXIT { fclose(f); }; + + uintptr_t map_address = 0; + R_TRY(map::LocateMappableSpace(&map_address, process_info->nso_size[i])); + + R_TRY(LoadNsoIntoProcessMemory(process_handle, f, map_address, nso_headers + i, process_info->nso_address[i], process_info->nso_size[i])); + } + } + + /* Load arguments, if present. */ + if (arg_info != nullptr) { + /* Write argument data into memory. */ + { + uintptr_t map_address = 0; + R_TRY(map::LocateMappableSpace(&map_address, process_info->args_size)); + + map::AutoCloseMap mapper(map_address, process_handle, process_info->args_address, process_info->args_size); + R_TRY(mapper.GetResult()); + + ProgramArguments *args = reinterpret_cast(map_address); + std::memset(args, 0, sizeof(*args)); + args->allocated_size = process_info->args_size; + args->arguments_size = arg_info->args_size; + std::memcpy(args->arguments, arg_info->args, arg_info->args_size); + } + + /* Set argument region permissions. */ + R_TRY(svcSetProcessMemoryPermission(process_handle, process_info->args_address, process_info->args_size, Perm_Rw)); + } + + return ResultSuccess; + } + } - /* Send the pid/tid pair to anyone interested in man-in-the-middle-attacking it. */ - Registration::AssociatePidTidForMitM(index); + /* Process Creation API. */ + Result CreateProcess(Handle *out, PinId pin_id, const ncm::TitleLocation &loc, const char *path, u32 flags, Handle reslimit_h) { + /* Use global storage for NSOs. */ + NsoHeader *nso_headers = g_nso_headers; + bool *has_nso = g_has_nso; + const auto arg_info = args::Get(loc.title_id); - /* If HBL, override HTML document path. */ - if (ContentManagement::ShouldOverrideContentsWithHBL(target_process->tid_sid.title_id)) { - ContentManagement::RedirectHtmlDocumentPathForHbl(target_process->tid_sid.title_id, target_process->tid_sid.storage_id); + { + /* Mount code. */ + ScopedCodeMount mount; + R_TRY(MountCode(mount, loc)); + + /* Load meta, possibly from cache. */ + Meta meta; + R_TRY(LoadMetaFromCache(&meta, loc.title_id)); + + /* Validate meta. */ + R_TRY(ValidateMeta(&meta, loc)); + + /* Load, validate NSOs. */ + R_TRY(LoadNsoHeaders(loc.title_id, nso_headers, has_nso)); + R_TRY(ValidateNsoHeaders(nso_headers, has_nso)); + + /* Actually create process. */ + ProcessInfo info; + R_TRY(CreateProcessImpl(&info, &meta, nso_headers, has_nso, arg_info, flags, reslimit_h)); + + /* Load NSOs into process memory. */ + R_TRY(LoadNsosIntoProcessMemory(&info, loc.title_id, nso_headers, has_nso, arg_info)); + + /* Register NSOs with ro manager. */ + { + /* Nintendo doesn't validate this result, but we will. */ + u64 process_id; + R_ASSERT(svcGetProcessId(&process_id, info.process_handle.Get())); + + /* Register new process. */ + ldr::ro::RegisterProcess(pin_id, process_id, loc.title_id); + + /* Register all NSOs. */ + for (size_t i = 0; i < Nso_Count; i++) { + if (has_nso[i]) { + ldr::ro::RegisterModule(pin_id, nso_headers[i].build_id, info.nso_address[i], info.nso_size[i]); + } + } + } + + /* Move the process handle to output. */ + *out = info.process_handle.Move(); + } + + /* Note that we've created the title. */ + SetLaunchedTitle(loc.title_id); + + return ResultSuccess; } - /* ECS is a one-shot operation, but we don't clear on failure. */ - ContentManagement::ClearExternalContentSource(target_process->tid_sid.title_id); + Result GetProgramInfo(ProgramInfo *out, const ncm::TitleLocation &loc) { + Meta meta; - /* Cancel the process handle guard. */ - proc_handle_guard.Cancel(); + /* Load Meta. */ + { + ScopedCodeMount mount; + R_TRY(MountCode(mount, loc)); + R_TRY(LoadMeta(&meta, loc.title_id)); + } + + return GetProgramInfoFromMeta(out, &meta); + } - /* Write process handle to output. */ - *out_process_h = process_h; - return ResultSuccess; } diff --git a/stratosphere/loader/source/ldr_process_creation.hpp b/stratosphere/loader/source/ldr_process_creation.hpp index df975d063..f7fde6d55 100644 --- a/stratosphere/loader/source/ldr_process_creation.hpp +++ b/stratosphere/loader/source/ldr_process_creation.hpp @@ -16,26 +16,15 @@ #pragma once #include +#include +#include -#include "ldr_registration.hpp" -#include "ldr_launch_queue.hpp" -#include "ldr_npdm.hpp" +#include "ldr_arguments.hpp" -/* Utilities for Process Creation, for Loader. */ +namespace sts::ldr { -class ProcessCreation { - public: - struct ProcessInfo { - u8 name[12]; - u32 version; - u64 title_id; - u64 code_addr; - u32 code_num_pages; - u32 process_flags; - Handle reslimit_h; - u32 system_resource_num_pages; - }; - static Result ValidateProcessVersion(u64 title_id, u32 version); - static Result InitializeProcessInfo(NpdmUtils::NpdmInfo *npdm, Handle reslimit_h, u64 arg_flags, ProcessInfo *out_proc_info); - static Result CreateProcess(Handle *out_process_h, u64 index, char *nca_path, LaunchQueue::LaunchItem *launch_item, u64 arg_flags, Handle reslimit_h); -}; \ No newline at end of file + /* Process Creation API. */ + Result CreateProcess(Handle *out, PinId pin_id, const ncm::TitleLocation &loc, const char *path, u32 flags, Handle reslimit_h); + Result GetProgramInfo(ProgramInfo *out, const ncm::TitleLocation &loc); + +} diff --git a/stratosphere/loader/source/ldr_process_manager.cpp b/stratosphere/loader/source/ldr_process_manager.cpp deleted file mode 100644 index abe233422..000000000 --- a/stratosphere/loader/source/ldr_process_manager.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (c) 2018-2019 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 -#include "ldr_process_manager.hpp" -#include "ldr_registration.hpp" -#include "ldr_launch_queue.hpp" -#include "ldr_content_management.hpp" -#include "ldr_npdm.hpp" - -Result ProcessManagerService::CreateProcess(Out proc_h, u64 index, u32 flags, CopiedHandle reslimit_h) { - Registration::TidSid tid_sid; - LaunchQueue::LaunchItem *launch_item; - char nca_path[FS_MAX_PATH] = {0}; - - ON_SCOPE_EXIT { - /* Loader doesn't persist the copied resource limit handle. */ - svcCloseHandle(reslimit_h.handle); - }; - - R_TRY(Registration::GetRegisteredTidSid(index, &tid_sid)); - - if (tid_sid.storage_id != FsStorageId_None) { - R_TRY(ContentManagement::ResolveContentPathForTidSid(nca_path, &tid_sid)); - } - - launch_item = LaunchQueue::GetItem(tid_sid.title_id); - R_TRY(ProcessCreation::CreateProcess(proc_h.GetHandlePointer(), index, nca_path, launch_item, flags, reslimit_h.handle)); - - ContentManagement::SetCreatedTitle(tid_sid.title_id); - return ResultSuccess; -} - -Result ProcessManagerService::GetProgramInfo(OutPointerWithServerSize out_program_info, Registration::TidSid tid_sid) { - char nca_path[FS_MAX_PATH] = {0}; - - /* Zero output. */ - std::fill(out_program_info.pointer, out_program_info.pointer + out_program_info.num_elements, ProcessManagerService::ProgramInfo{}); - - R_TRY(PopulateProgramInfoBuffer(out_program_info.pointer, &tid_sid)); - - if (tid_sid.storage_id != FsStorageId_None && tid_sid.title_id != out_program_info.pointer->title_id) { - R_TRY(ContentManagement::ResolveContentPathForTidSid(nca_path, &tid_sid)); - R_TRY(ContentManagement::RedirectContentPath(nca_path, out_program_info.pointer->title_id, tid_sid.storage_id)); - R_TRY(LaunchQueue::AddCopy(tid_sid.title_id, out_program_info.pointer->title_id)); - } - - return ResultSuccess; -} - -Result ProcessManagerService::RegisterTitle(Out index, Registration::TidSid tid_sid) { - return Registration::RegisterTidSid(&tid_sid, index.GetPointer()) ? 0 : ResultLoaderTooManyProcesses; -} - -Result ProcessManagerService::UnregisterTitle(u64 index) { - return Registration::UnregisterIndex(index) ? 0 : ResultLoaderProcessNotRegistered; -} - - -Result ProcessManagerService::PopulateProgramInfoBuffer(ProcessManagerService::ProgramInfo *out, Registration::TidSid *tid_sid) { - NpdmUtils::NpdmInfo info; - - /* Mount code, load NPDM. */ - { - bool mounted_code = false; - if (tid_sid->storage_id != FsStorageId_None) { - R_TRY(ContentManagement::MountCodeForTidSid(tid_sid)); - mounted_code = true; - } else if (R_SUCCEEDED(ContentManagement::MountCodeNspOnSd(tid_sid->title_id))) { - mounted_code = true; - } - ON_SCOPE_EXIT { - if (mounted_code) { - ContentManagement::UnmountCode(); - } - }; - - R_TRY(NpdmUtils::LoadNpdm(tid_sid->title_id, &info)); - } - - out->main_thread_priority = info.header->main_thread_prio; - out->default_cpu_id = info.header->default_cpuid; - out->main_thread_stack_size = info.header->main_stack_size; - out->title_id = info.aci0->title_id; - - out->acid_fac_size = info.acid->fac_size; - out->aci0_sac_size = info.aci0->sac_size; - out->aci0_fah_size = info.aci0->fah_size; - - size_t offset = 0; - - /* Copy ACID Service Access Control. */ - if (offset + info.acid->sac_size >= sizeof(out->ac_buffer)) { - return ResultLoaderInternalError; - } - out->acid_sac_size = info.acid->sac_size; - std::memcpy(out->ac_buffer + offset, info.acid_sac, out->acid_sac_size); - offset += out->acid_sac_size; - - /* Copy ACI0 Service Access Control. */ - if (offset + info.aci0->sac_size >= sizeof(out->ac_buffer)) { - return ResultLoaderInternalError; - } - out->aci0_sac_size = info.aci0->sac_size; - std::memcpy(out->ac_buffer + offset, info.aci0_sac, out->aci0_sac_size); - offset += out->aci0_sac_size; - - /* Copy ACID Filesystem Access Control. */ - if (offset + info.acid->fac_size >= sizeof(out->ac_buffer)) { - return ResultLoaderInternalError; - } - out->acid_fac_size = info.acid->fac_size; - std::memcpy(out->ac_buffer + offset, info.acid_fac, out->acid_fac_size); - offset += out->acid_fac_size; - - /* Copy ACI0 Filesystem Access Header. */ - if (offset + info.aci0->fah_size >= sizeof(out->ac_buffer)) { - return ResultLoaderInternalError; - } - out->aci0_fah_size = info.aci0->fah_size; - std::memcpy(out->ac_buffer + offset, info.aci0_fah, out->aci0_fah_size); - offset += out->aci0_fah_size; - - /* Parse application type. */ - out->application_type = NpdmUtils::GetApplicationType(reinterpret_cast(info.acid_kac), info.acid->kac_size / sizeof(u32)); - - return ResultSuccess; -} diff --git a/stratosphere/loader/source/ldr_process_manager.hpp b/stratosphere/loader/source/ldr_process_manager.hpp deleted file mode 100644 index 7686b7783..000000000 --- a/stratosphere/loader/source/ldr_process_manager.hpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2018-2019 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 -#include - -#include "ldr_registration.hpp" -#include "ldr_process_creation.hpp" - -enum ProcessManagerServiceCmd { - Pm_Cmd_CreateProcess = 0, - Pm_Cmd_GetProgramInfo = 1, - Pm_Cmd_RegisterTitle = 2, - Pm_Cmd_UnregisterTitle = 3 -}; - -class ProcessManagerService final : public IServiceObject { - struct ProgramInfo { - u8 main_thread_priority; - u8 default_cpu_id; - u16 application_type; - u32 main_thread_stack_size; - u64 title_id; - u32 acid_sac_size; - u32 aci0_sac_size; - u32 acid_fac_size; - u32 aci0_fah_size; - u8 ac_buffer[0x3E0]; - }; - - static_assert(sizeof(ProcessManagerService::ProgramInfo) == 0x400, "Incorrect ProgramInfo definition."); - private: - /* Actual commands. */ - Result CreateProcess(Out proc_h, u64 index, u32 flags, CopiedHandle reslimit_h); - Result GetProgramInfo(OutPointerWithServerSize out_program_info, Registration::TidSid tid_sid); - Result RegisterTitle(Out index, Registration::TidSid tid_sid); - Result UnregisterTitle(u64 index); - - /* Utilities */ - Result PopulateProgramInfoBuffer(ProcessManagerService::ProgramInfo *out, Registration::TidSid *tid_sid); - public: - DEFINE_SERVICE_DISPATCH_TABLE { - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - }; -}; diff --git a/stratosphere/loader/source/ldr_registration.cpp b/stratosphere/loader/source/ldr_registration.cpp deleted file mode 100644 index 29be5f842..000000000 --- a/stratosphere/loader/source/ldr_registration.cpp +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright (c) 2018-2019 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 -#include -#include -#include -#include "ldr_registration.hpp" - -static Registration::List g_registration_list = {}; -static u64 g_num_registered = 1; - -Registration::Process *Registration::GetFreeProcess() { - auto process_it = std::find_if_not(g_registration_list.processes.begin(), g_registration_list.processes.end(), std::mem_fn(&Registration::Process::in_use)); - if (process_it == g_registration_list.processes.end()) { - return nullptr; - } - return &*process_it; -} - -Registration::Process *Registration::GetProcess(u64 index) { - for (unsigned int i = 0; i < Registration::MaxProcesses; i++) { - if (g_registration_list.processes[i].in_use && g_registration_list.processes[i].index == index) { - return &g_registration_list.processes[i]; - } - } - return NULL; -} - -Registration::Process *Registration::GetProcessByProcessId(u64 pid) { - for (unsigned int i = 0; i < Registration::MaxProcesses; i++) { - if (g_registration_list.processes[i].in_use && g_registration_list.processes[i].process_id == pid) { - return &g_registration_list.processes[i]; - } - } - return NULL; -} - -bool Registration::RegisterTidSid(const TidSid *tid_sid, u64 *out_index) { - Registration::Process *free_process = GetFreeProcess(); - if (free_process == NULL) { - return false; - } - - /* Reset the process. */ - *free_process = {}; - free_process->tid_sid = *tid_sid; - free_process->in_use = true; - free_process->index = g_num_registered++; - *out_index = free_process->index; - return true; -} - -bool Registration::UnregisterIndex(u64 index) { - Registration::Process *target_process = GetProcess(index); - if (target_process == NULL) { - return false; - } - - /* Reset the process. */ - *target_process = {}; - return true; -} - - -Result Registration::GetRegisteredTidSid(u64 index, Registration::TidSid *out) { - Registration::Process *target_process = GetProcess(index); - if (target_process == NULL) { - return ResultLoaderProcessNotRegistered; - } - - *out = target_process->tid_sid; - - return ResultSuccess; -} - -void Registration::SetProcessIdTidAndIs64BitAddressSpace(u64 index, u64 process_id, u64 tid, bool is_64_bit_addspace) { - Registration::Process *target_process = GetProcess(index); - if (target_process == NULL) { - return; - } - - target_process->process_id = process_id; - target_process->title_id = tid; - target_process->is_64_bit_addspace = is_64_bit_addspace; -} - -void Registration::AddModuleInfo(u64 index, u64 base_address, u64 size, const unsigned char *build_id) { - Registration::Process *target_process = GetProcess(index); - if (target_process == NULL) { - return; - } - - auto nso_info_it = std::find_if_not(target_process->module_infos.begin(), target_process->module_infos.end(), std::mem_fn(&Registration::ModuleInfoHolder::in_use)); - if (nso_info_it != target_process->module_infos.end()) { - nso_info_it->info.base_address = base_address; - nso_info_it->info.size = size; - memcpy(nso_info_it->info.build_id, build_id, sizeof(nso_info_it->info.build_id)); - nso_info_it->in_use = true; - } -} - -Result Registration::GetProcessModuleInfo(LoaderModuleInfo *out, u32 max_out, u64 process_id, u32 *num_written) { - Registration::Process *target_process = GetProcessByProcessId(process_id); - if (target_process == NULL) { - return ResultLoaderProcessNotRegistered; - } - u32 cur = 0; - - for (unsigned int i = 0; i < Registration::MaxModuleInfos && cur < max_out; i++) { - if (target_process->module_infos[i].in_use) { - out[cur++] = target_process->module_infos[i].info; - } - } - - *num_written = cur; - - return ResultSuccess; -} - -void Registration::AssociatePidTidForMitM(u64 index) { - Registration::Process *target_process = GetProcess(index); - if (target_process == NULL) { - return; - } - - Handle sm_hnd; - if (R_SUCCEEDED(svcConnectToNamedPort(&sm_hnd, "sm:"))) { - ON_SCOPE_EXIT { svcCloseHandle(sm_hnd); }; - - /* Initialize. */ - { - IpcCommand c; - ipcInitialize(&c); - ipcSendPid(&c); - - struct { - u64 magic; - u64 cmd_id; - u64 zero; - u64 reserved[2]; - } *raw = (decltype(raw))ipcPrepareHeader(&c, sizeof(*raw)); - - raw->magic = SFCI_MAGIC; - raw->cmd_id = 0; - raw->zero = 0; - - if (R_FAILED(ipcDispatch(sm_hnd))) { - return; - } - - IpcParsedCommand r; - ipcParse(&r); - - struct { - u64 magic; - u64 result; - } *resp = (decltype(resp))r.Raw; - - if (R_FAILED(resp->result)) { - return; - } - } - - /* Associate. */ - { - IpcCommand c; - ipcInitialize(&c); - struct { - u64 magic; - u64 cmd_id; - u64 process_id; - u64 title_id; - } *raw = (decltype(raw))ipcPrepareHeader(&c, sizeof(*raw)); - - raw->magic = SFCI_MAGIC; - raw->cmd_id = 65002; - raw->process_id = target_process->process_id; - raw->title_id = target_process->tid_sid.title_id; - - ipcDispatch(sm_hnd); - } - } -} \ No newline at end of file diff --git a/stratosphere/loader/source/ldr_registration.hpp b/stratosphere/loader/source/ldr_registration.hpp deleted file mode 100644 index 67315bc5d..000000000 --- a/stratosphere/loader/source/ldr_registration.hpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2018-2019 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 -#include - -#include "ldr_map.hpp" - -class Registration { - public: - static constexpr size_t MaxProcesses = 0x40; - static constexpr size_t MaxModuleInfos = 0x20; - public: - struct ModuleInfoHolder { - bool in_use; - LoaderModuleInfo info; - }; - - struct TidSid { - u64 title_id; - FsStorageId storage_id; - }; - - struct Process { - bool in_use; - bool is_64_bit_addspace; - u64 index; - u64 process_id; - u64 title_id; - Registration::TidSid tid_sid; - std::array module_infos; - }; - - struct List { - std::array processes; - u64 num_processes; - }; - - static Registration::Process *GetFreeProcess(); - static Registration::Process *GetProcess(u64 index); - static Registration::Process *GetProcessByProcessId(u64 pid); - static Result GetRegisteredTidSid(u64 index, Registration::TidSid *out); - static bool RegisterTidSid(const TidSid *tid_sid, u64 *out_index); - static bool UnregisterIndex(u64 index); - static void SetProcessIdTidAndIs64BitAddressSpace(u64 index, u64 process_id, u64 tid, bool is_64_bit_addspace); - static void AddModuleInfo(u64 index, u64 base_address, u64 size, const unsigned char *build_id); - static Result GetProcessModuleInfo(LoaderModuleInfo *out, u32 max_out, u64 process_id, u32 *num_written); - - /* Atmosphere MitM Extension. */ - static void AssociatePidTidForMitM(u64 index); -}; diff --git a/stratosphere/loader/source/ldr_ro_manager.cpp b/stratosphere/loader/source/ldr_ro_manager.cpp new file mode 100644 index 000000000..c1d9ce65d --- /dev/null +++ b/stratosphere/loader/source/ldr_ro_manager.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2018-2019 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 "ldr_ro_manager.hpp" + +namespace sts::ldr::ro { + + namespace { + + /* Convenience definitions. */ + constexpr PinId InvalidPinId = {}; + constexpr size_t ProcessCountMax = 0x40; + constexpr size_t ModuleCountMax = 0x20; + + /* Types. */ + struct ModuleInfo { + ldr::ModuleInfo info; + bool in_use; + }; + + struct ProcessInfo { + PinId pin_id; + u64 process_id; + ncm::TitleId title_id; + ncm::TitleLocation loc; + ModuleInfo modules[ModuleCountMax]; + bool in_use; + }; + + /* Globals. */ + ProcessInfo g_process_infos[ProcessCountMax]; + + /* Helpers. */ + ProcessInfo *GetProcessInfo(PinId pin_id) { + for (size_t i = 0; i < ProcessCountMax; i++) { + ProcessInfo *info = &g_process_infos[i]; + if (info->in_use && info->pin_id == pin_id) { + return info; + } + } + return nullptr; + } + + ProcessInfo *GetProcessInfo(u64 process_id) { + for (size_t i = 0; i < ProcessCountMax; i++) { + ProcessInfo *info = &g_process_infos[i]; + if (info->in_use && info->process_id == process_id) { + return info; + } + } + return nullptr; + } + + ProcessInfo *GetFreeProcessInfo() { + for (size_t i = 0; i < ProcessCountMax; i++) { + ProcessInfo *info = &g_process_infos[i]; + if (!info->in_use) { + return info; + } + } + return nullptr; + } + + } + + /* RO Manager API. */ + Result PinTitle(PinId *out, const ncm::TitleLocation &loc) { + *out = InvalidPinId; + ProcessInfo *info = GetFreeProcessInfo(); + if (info == nullptr) { + return ResultLoaderTooManyProcesses; + } + + static u64 s_cur_pin_id = 1; + + std::memset(info, 0, sizeof(*info)); + info->pin_id = { s_cur_pin_id++ }; + info->loc = loc; + info->in_use = true; + *out = info->pin_id; + return ResultSuccess; + } + + Result UnpinTitle(PinId id) { + ProcessInfo *info = GetProcessInfo(id); + if (info == nullptr) { + return ResultLoaderNotPinned; + } + + info->in_use = false; + return ResultSuccess; + } + + + Result GetTitleLocation(ncm::TitleLocation *out, PinId id) { + ProcessInfo *info = GetProcessInfo(id); + if (info == nullptr) { + return ResultLoaderNotPinned; + } + + *out = info->loc; + return ResultSuccess; + } + + Result RegisterProcess(PinId id, u64 process_id, ncm::TitleId title_id) { + ProcessInfo *info = GetProcessInfo(id); + if (info == nullptr) { + return ResultLoaderNotPinned; + } + + info->title_id = title_id; + info->process_id = process_id; + return ResultSuccess; + } + + Result RegisterModule(PinId id, const u8 *build_id, uintptr_t address, size_t size) { + ProcessInfo *info = GetProcessInfo(id); + if (info == nullptr) { + return ResultLoaderNotPinned; + } + + /* Nintendo doesn't actually care about successful allocation. */ + for (size_t i = 0; i < ModuleCountMax; i++) { + ModuleInfo *module = &info->modules[i]; + if (!module->in_use) { + continue; + } + + std::memcpy(module->info.build_id, build_id, sizeof(module->info.build_id)); + module->info.base_address = address; + module->info.size = size; + break; + } + + return ResultSuccess; + } + + Result GetProcessModuleInfo(u32 *out_count, ldr::ModuleInfo *out, size_t max_out_count, u64 process_id) { + const ProcessInfo *info = GetProcessInfo(process_id); + if (info == nullptr) { + return ResultLoaderNotPinned; + } + + size_t count = 0; + for (size_t i = 0; i < ModuleCountMax && count < max_out_count; i++) { + const ModuleInfo *module = &info->modules[i]; + if (!module->in_use) { + continue; + } + + out[count++] = module->info; + } + + *out_count = static_cast(count); + return ResultSuccess; + } + +} diff --git a/stratosphere/loader/source/ldr_ro_manager.hpp b/stratosphere/loader/source/ldr_ro_manager.hpp new file mode 100644 index 000000000..a59430719 --- /dev/null +++ b/stratosphere/loader/source/ldr_ro_manager.hpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018-2019 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 +#include +#include + +namespace sts::ldr::ro { + + /* RO Manager API. */ + Result PinTitle(PinId *out, const ncm::TitleLocation &loc); + Result UnpinTitle(PinId id); + Result GetTitleLocation(ncm::TitleLocation *out, PinId id); + Result RegisterProcess(PinId id, u64 process_id, ncm::TitleId title_id); + Result RegisterModule(PinId id, const u8 *build_id, uintptr_t address, size_t size); + Result GetProcessModuleInfo(u32 *out_count, ModuleInfo *out, size_t max_out_count, u64 process_id); + +} diff --git a/stratosphere/loader/source/ldr_shell.cpp b/stratosphere/loader/source/ldr_shell.cpp deleted file mode 100644 index b27d761ab..000000000 --- a/stratosphere/loader/source/ldr_shell.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2018-2019 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 -#include "ldr_shell.hpp" -#include "ldr_launch_queue.hpp" -#include "ldr_content_management.hpp" - -Result ShellService::AddTitleToLaunchQueue(u64 tid, InPointer args, u32 args_size) { - if (args.num_elements < args_size) args_size = args.num_elements; - return LaunchQueue::Add(tid, args.pointer, args_size); -} - -void ShellService::ClearLaunchQueue() { - LaunchQueue::Clear(); -} - -/* SetExternalContentSource extension */ -Result ShellService::SetExternalContentSource(Out out, u64 tid) { - Handle server_h; - Handle client_h; - - R_TRY(svcCreateSession(&server_h, &client_h, 0, 0)); - - Service service; - serviceCreate(&service, client_h); - ContentManagement::SetExternalContentSource(tid, FsFileSystem {service}); - out.SetValue(server_h); - return ResultSuccess; -} - -void ShellService::ClearExternalContentSource(u64 tid) { - ContentManagement::ClearExternalContentSource(tid); -} diff --git a/stratosphere/loader/source/ldr_shell.hpp b/stratosphere/loader/source/ldr_shell.hpp deleted file mode 100644 index 6dd7869d6..000000000 --- a/stratosphere/loader/source/ldr_shell.hpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018-2019 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 -#include - -enum ShellServiceCmd { - Shell_Cmd_AddTitleToLaunchQueue = 0, - Shell_Cmd_ClearLaunchQueue = 1, - - Shell_Cmd_AtmosphereSetExternalContentSource = 65000, - Shell_Cmd_AtmosphereClearExternalContentSource = 65001, -}; - -class ShellService final : public IServiceObject { - private: - /* Actual commands. */ - Result AddTitleToLaunchQueue(u64 tid, InPointer args, u32 args_size); - void ClearLaunchQueue(); - - /* Atmosphere commands. */ - Result SetExternalContentSource(Out out, u64 tid); - void ClearExternalContentSource(u64 tid); - public: - DEFINE_SERVICE_DISPATCH_TABLE { - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - }; -}; diff --git a/stratosphere/loader/source/lz4.c b/stratosphere/loader/source/lz4.c deleted file mode 100644 index 33aa5c7d3..000000000 --- a/stratosphere/loader/source/lz4.c +++ /dev/null @@ -1,1857 +0,0 @@ -/* - LZ4 - Fast LZ compression algorithm - Copyright (C) 2011-2017, Yann Collet. - - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following disclaimer - in the documentation and/or other materials provided with the - distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - You can contact the author at : - - LZ4 homepage : http://www.lz4.org - - LZ4 source repository : https://github.com/lz4/lz4 -*/ - - -/*-************************************ -* Tuning parameters -**************************************/ -/* - * LZ4_HEAPMODE : - * Select how default compression functions will allocate memory for their hash table, - * in memory stack (0:default, fastest), or in memory heap (1:requires malloc()). - */ -#ifndef LZ4_HEAPMODE -# define LZ4_HEAPMODE 0 -#endif - -/* - * ACCELERATION_DEFAULT : - * Select "acceleration" for LZ4_compress_fast() when parameter value <= 0 - */ -#define ACCELERATION_DEFAULT 1 - - -/*-************************************ -* CPU Feature Detection -**************************************/ -/* LZ4_FORCE_MEMORY_ACCESS - * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. - * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. - * The below switch allow to select different access method for improved performance. - * Method 0 (default) : use `memcpy()`. Safe and portable. - * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable). - * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. - * Method 2 : direct access. This method is portable but violate C standard. - * It can generate buggy code on targets which assembly generation depends on alignment. - * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6) - * See https://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details. - * Prefer these methods in priority order (0 > 1 > 2) - */ -#ifndef LZ4_FORCE_MEMORY_ACCESS /* can be defined externally */ -# if defined(__GNUC__) && ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) ) -# define LZ4_FORCE_MEMORY_ACCESS 2 -# elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || defined(__GNUC__) -# define LZ4_FORCE_MEMORY_ACCESS 1 -# endif -#endif - -/* - * LZ4_FORCE_SW_BITCOUNT - * Define this parameter if your target system or compiler does not support hardware bit count - */ -#if defined(_MSC_VER) && defined(_WIN32_WCE) /* Visual Studio for Windows CE does not support Hardware bit count */ -# define LZ4_FORCE_SW_BITCOUNT -#endif - - - -/*-************************************ -* Dependency -**************************************/ -#define LZ4_STATIC_LINKING_ONLY -#include "lz4.h" -/* see also "memory routines" below */ - - -/*-************************************ -* Compiler Options -**************************************/ -#ifdef _MSC_VER /* Visual Studio */ -# include -# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ -# pragma warning(disable : 4293) /* disable: C4293: too large shift (32-bits) */ -#endif /* _MSC_VER */ - -#ifndef LZ4_FORCE_INLINE -# ifdef _MSC_VER /* Visual Studio */ -# define LZ4_FORCE_INLINE static __forceinline -# else -# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ -# ifdef __GNUC__ -# define LZ4_FORCE_INLINE static inline __attribute__((always_inline)) -# else -# define LZ4_FORCE_INLINE static inline -# endif -# else -# define LZ4_FORCE_INLINE static -# endif /* __STDC_VERSION__ */ -# endif /* _MSC_VER */ -#endif /* LZ4_FORCE_INLINE */ - -/* LZ4_FORCE_O2_GCC_PPC64LE and LZ4_FORCE_O2_INLINE_GCC_PPC64LE - * Gcc on ppc64le generates an unrolled SIMDized loop for LZ4_wildCopy, - * together with a simple 8-byte copy loop as a fall-back path. - * However, this optimization hurts the decompression speed by >30%, - * because the execution does not go to the optimized loop - * for typical compressible data, and all of the preamble checks - * before going to the fall-back path become useless overhead. - * This optimization happens only with the -O3 flag, and -O2 generates - * a simple 8-byte copy loop. - * With gcc on ppc64le, all of the LZ4_decompress_* and LZ4_wildCopy - * functions are annotated with __attribute__((optimize("O2"))), - * and also LZ4_wildCopy is forcibly inlined, so that the O2 attribute - * of LZ4_wildCopy does not affect the compression speed. - */ -#if defined(__PPC64__) && defined(__LITTLE_ENDIAN__) && defined(__GNUC__) -# define LZ4_FORCE_O2_GCC_PPC64LE __attribute__((optimize("O2"))) -# define LZ4_FORCE_O2_INLINE_GCC_PPC64LE __attribute__((optimize("O2"))) LZ4_FORCE_INLINE -#else -# define LZ4_FORCE_O2_GCC_PPC64LE -# define LZ4_FORCE_O2_INLINE_GCC_PPC64LE static -#endif - -#if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__) -# define expect(expr,value) (__builtin_expect ((expr),(value)) ) -#else -# define expect(expr,value) (expr) -#endif - -#define likely(expr) expect((expr) != 0, 1) -#define unlikely(expr) expect((expr) != 0, 0) - - -/*-************************************ -* Memory routines -**************************************/ -#include /* malloc, calloc, free */ -#define ALLOC(s) malloc(s) -#define ALLOC_AND_ZERO(s) calloc(1,s) -#define FREEMEM free -#include /* memset, memcpy */ -#define MEM_INIT memset - - -/*-************************************ -* Basic Types -**************************************/ -#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) -# include - typedef uint8_t BYTE; - typedef uint16_t U16; - typedef uint32_t U32; - typedef int32_t S32; - typedef uint64_t U64; - typedef uintptr_t uptrval; -#else - typedef unsigned char BYTE; - typedef unsigned short U16; - typedef unsigned int U32; - typedef signed int S32; - typedef unsigned long long U64; - typedef size_t uptrval; /* generally true, except OpenVMS-64 */ -#endif - -#if defined(__x86_64__) - typedef U64 reg_t; /* 64-bits in x32 mode */ -#else - typedef size_t reg_t; /* 32-bits in x32 mode */ -#endif - -/*-************************************ -* Reading and writing into memory -**************************************/ -static unsigned LZ4_isLittleEndian(void) -{ - const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */ - return one.c[0]; -} - - -#if defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==2) -/* lie to the compiler about data alignment; use with caution */ - -static U16 LZ4_read16(const void* memPtr) { return *(const U16*) memPtr; } -static U32 LZ4_read32(const void* memPtr) { return *(const U32*) memPtr; } -static reg_t LZ4_read_ARCH(const void* memPtr) { return *(const reg_t*) memPtr; } - -static void LZ4_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; } -static void LZ4_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; } - -#elif defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==1) - -/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ -/* currently only defined for gcc and icc */ -typedef union { U16 u16; U32 u32; reg_t uArch; } __attribute__((packed)) unalign; - -static U16 LZ4_read16(const void* ptr) { return ((const unalign*)ptr)->u16; } -static U32 LZ4_read32(const void* ptr) { return ((const unalign*)ptr)->u32; } -static reg_t LZ4_read_ARCH(const void* ptr) { return ((const unalign*)ptr)->uArch; } - -static void LZ4_write16(void* memPtr, U16 value) { ((unalign*)memPtr)->u16 = value; } -static void LZ4_write32(void* memPtr, U32 value) { ((unalign*)memPtr)->u32 = value; } - -#else /* safe and portable access through memcpy() */ - -static U16 LZ4_read16(const void* memPtr) -{ - U16 val; memcpy(&val, memPtr, sizeof(val)); return val; -} - -static U32 LZ4_read32(const void* memPtr) -{ - U32 val; memcpy(&val, memPtr, sizeof(val)); return val; -} - -static reg_t LZ4_read_ARCH(const void* memPtr) -{ - reg_t val; memcpy(&val, memPtr, sizeof(val)); return val; -} - -static void LZ4_write16(void* memPtr, U16 value) -{ - memcpy(memPtr, &value, sizeof(value)); -} - -static void LZ4_write32(void* memPtr, U32 value) -{ - memcpy(memPtr, &value, sizeof(value)); -} - -#endif /* LZ4_FORCE_MEMORY_ACCESS */ - - -static U16 LZ4_readLE16(const void* memPtr) -{ - if (LZ4_isLittleEndian()) { - return LZ4_read16(memPtr); - } else { - const BYTE* p = (const BYTE*)memPtr; - return (U16)((U16)p[0] + (p[1]<<8)); - } -} - -static void LZ4_writeLE16(void* memPtr, U16 value) -{ - if (LZ4_isLittleEndian()) { - LZ4_write16(memPtr, value); - } else { - BYTE* p = (BYTE*)memPtr; - p[0] = (BYTE) value; - p[1] = (BYTE)(value>>8); - } -} - -/* customized variant of memcpy, which can overwrite up to 8 bytes beyond dstEnd */ -LZ4_FORCE_O2_INLINE_GCC_PPC64LE -void LZ4_wildCopy(void* dstPtr, const void* srcPtr, void* dstEnd) -{ - BYTE* d = (BYTE*)dstPtr; - const BYTE* s = (const BYTE*)srcPtr; - BYTE* const e = (BYTE*)dstEnd; - - do { memcpy(d,s,8); d+=8; s+=8; } while (d=1) -# include -#else -# ifndef assert -# define assert(condition) ((void)0) -# endif -#endif - -#define LZ4_STATIC_ASSERT(c) { enum { LZ4_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */ - -#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=2) -# include -static int g_debuglog_enable = 1; -# define DEBUGLOG(l, ...) { \ - if ((g_debuglog_enable) && (l<=LZ4_DEBUG)) { \ - fprintf(stderr, __FILE__ ": "); \ - fprintf(stderr, __VA_ARGS__); \ - fprintf(stderr, " \n"); \ - } } -#else -# define DEBUGLOG(l, ...) {} /* disabled */ -#endif - - -/*-************************************ -* Common functions -**************************************/ -static unsigned LZ4_NbCommonBytes (reg_t val) -{ - if (LZ4_isLittleEndian()) { - if (sizeof(val)==8) { -# if defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT) - unsigned long r = 0; - _BitScanForward64( &r, (U64)val ); - return (int)(r>>3); -# elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT) - return (__builtin_ctzll((U64)val) >> 3); -# else - static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2, - 0, 3, 1, 3, 1, 4, 2, 7, - 0, 2, 3, 6, 1, 5, 3, 5, - 1, 3, 4, 4, 2, 5, 6, 7, - 7, 0, 1, 2, 3, 3, 4, 6, - 2, 6, 5, 5, 3, 4, 5, 6, - 7, 1, 2, 4, 6, 4, 4, 5, - 7, 2, 6, 5, 7, 6, 7, 7 }; - return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58]; -# endif - } else /* 32 bits */ { -# if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT) - unsigned long r; - _BitScanForward( &r, (U32)val ); - return (int)(r>>3); -# elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT) - return (__builtin_ctz((U32)val) >> 3); -# else - static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0, - 3, 2, 2, 1, 3, 2, 0, 1, - 3, 3, 1, 2, 2, 2, 2, 0, - 3, 1, 2, 0, 1, 0, 1, 1 }; - return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27]; -# endif - } - } else /* Big Endian CPU */ { - if (sizeof(val)==8) { /* 64-bits */ -# if defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT) - unsigned long r = 0; - _BitScanReverse64( &r, val ); - return (unsigned)(r>>3); -# elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT) - return (__builtin_clzll((U64)val) >> 3); -# else - static const U32 by32 = sizeof(val)*4; /* 32 on 64 bits (goal), 16 on 32 bits. - Just to avoid some static analyzer complaining about shift by 32 on 32-bits target. - Note that this code path is never triggered in 32-bits mode. */ - unsigned r; - if (!(val>>by32)) { r=4; } else { r=0; val>>=by32; } - if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; } - r += (!val); - return r; -# endif - } else /* 32 bits */ { -# if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT) - unsigned long r = 0; - _BitScanReverse( &r, (unsigned long)val ); - return (unsigned)(r>>3); -# elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT) - return (__builtin_clz((U32)val) >> 3); -# else - unsigned r; - if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; } - r += (!val); - return r; -# endif - } - } -} - -#define STEPSIZE sizeof(reg_t) -LZ4_FORCE_INLINE -unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit) -{ - const BYTE* const pStart = pIn; - - if (likely(pIn < pInLimit-(STEPSIZE-1))) { - reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn); - if (!diff) { - pIn+=STEPSIZE; pMatch+=STEPSIZE; - } else { - return LZ4_NbCommonBytes(diff); - } } - - while (likely(pIn < pInLimit-(STEPSIZE-1))) { - reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn); - if (!diff) { pIn+=STEPSIZE; pMatch+=STEPSIZE; continue; } - pIn += LZ4_NbCommonBytes(diff); - return (unsigned)(pIn - pStart); - } - - if ((STEPSIZE==8) && (pIn<(pInLimit-3)) && (LZ4_read32(pMatch) == LZ4_read32(pIn))) { pIn+=4; pMatch+=4; } - if ((pIn<(pInLimit-1)) && (LZ4_read16(pMatch) == LZ4_read16(pIn))) { pIn+=2; pMatch+=2; } - if ((pIn compression run slower on incompressible data */ - - -/*-************************************ -* Local Structures and types -**************************************/ -typedef enum { notLimited = 0, limitedOutput = 1 } limitedOutput_directive; -typedef enum { clearedTable = 0, byPtr, byU32, byU16 } tableType_t; - -/** - * This enum distinguishes several different modes of accessing previous - * content in the stream. - * - * - noDict : There is no preceding content. - * - withPrefix64k : Table entries up to ctx->dictSize before the current blob - * blob being compressed are valid and refer to the preceding - * content (of length ctx->dictSize), which is available - * contiguously preceding in memory the content currently - * being compressed. - * - usingExtDict : Like withPrefix64k, but the preceding content is somewhere - * else in memory, starting at ctx->dictionary with length - * ctx->dictSize. - * - usingDictCtx : Like usingExtDict, but everything concerning the preceding - * content is in a separate context, pointed to by - * ctx->dictCtx. ctx->dictionary, ctx->dictSize, and table - * entries in the current context that refer to positions - * preceding the beginning of the current compression are - * ignored. Instead, ctx->dictCtx->dictionary and ctx->dictCtx - * ->dictSize describe the location and size of the preceding - * content, and matches are found by looking in the ctx - * ->dictCtx->hashTable. - */ -typedef enum { noDict = 0, withPrefix64k, usingExtDict, usingDictCtx } dict_directive; -typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive; - -typedef enum { endOnOutputSize = 0, endOnInputSize = 1 } endCondition_directive; -typedef enum { full = 0, partial = 1 } earlyEnd_directive; - - -/*-************************************ -* Local Utils -**************************************/ -int LZ4_versionNumber (void) { return LZ4_VERSION_NUMBER; } -const char* LZ4_versionString(void) { return LZ4_VERSION_STRING; } -int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); } -int LZ4_sizeofState() { return LZ4_STREAMSIZE; } - - -/*-****************************** -* Compression functions -********************************/ -static U32 LZ4_hash4(U32 sequence, tableType_t const tableType) -{ - if (tableType == byU16) - return ((sequence * 2654435761U) >> ((MINMATCH*8)-(LZ4_HASHLOG+1))); - else - return ((sequence * 2654435761U) >> ((MINMATCH*8)-LZ4_HASHLOG)); -} - -static U32 LZ4_hash5(U64 sequence, tableType_t const tableType) -{ - static const U64 prime5bytes = 889523592379ULL; - static const U64 prime8bytes = 11400714785074694791ULL; - const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG+1 : LZ4_HASHLOG; - if (LZ4_isLittleEndian()) - return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog)); - else - return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog)); -} - -LZ4_FORCE_INLINE U32 LZ4_hashPosition(const void* const p, tableType_t const tableType) -{ - if ((sizeof(reg_t)==8) && (tableType != byU16)) return LZ4_hash5(LZ4_read_ARCH(p), tableType); - return LZ4_hash4(LZ4_read32(p), tableType); -} - -static void LZ4_putIndexOnHash(U32 idx, U32 h, void* tableBase, tableType_t const tableType) -{ - switch (tableType) - { - default: /* fallthrough */ - case clearedTable: /* fallthrough */ - case byPtr: { /* illegal! */ assert(0); return; } - case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = idx; return; } - case byU16: { U16* hashTable = (U16*) tableBase; assert(idx < 65536); hashTable[h] = (U16)idx; return; } - } -} - -static void LZ4_putPositionOnHash(const BYTE* p, U32 h, void* tableBase, tableType_t const tableType, const BYTE* srcBase) -{ - switch (tableType) - { - case clearedTable: { /* illegal! */ assert(0); return; } - case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = p; return; } - case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = (U32)(p-srcBase); return; } - case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = (U16)(p-srcBase); return; } - } -} - -LZ4_FORCE_INLINE void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase) -{ - U32 const h = LZ4_hashPosition(p, tableType); - LZ4_putPositionOnHash(p, h, tableBase, tableType, srcBase); -} - -/* LZ4_getIndexOnHash() : - * Index of match position registered in hash table. - * hash position must be calculated by using base+index, or dictBase+index. - * Assumption 1 : only valid if tableType == byU32 or byU16. - * Assumption 2 : h is presumed valid (within limits of hash table) - */ -static U32 LZ4_getIndexOnHash(U32 h, const void* tableBase, tableType_t tableType) -{ - LZ4_STATIC_ASSERT(LZ4_MEMORY_USAGE > 2); - if (tableType == byU32) { const U32* const hashTable = (const U32*) tableBase; assert(h < (1U << (LZ4_MEMORY_USAGE-2))); return hashTable[h]; } - if (tableType == byU16) { const U16* const hashTable = (const U16*) tableBase; assert(h < (1U << (LZ4_MEMORY_USAGE-1))); return hashTable[h]; } - assert(0); return 0; /* forbidden case */ -} - -static const BYTE* LZ4_getPositionOnHash(U32 h, const void* tableBase, tableType_t tableType, const BYTE* srcBase) -{ - if (tableType == byPtr) { const BYTE* const* hashTable = (const BYTE* const*) tableBase; return hashTable[h]; } - if (tableType == byU32) { const U32* const hashTable = (const U32*) tableBase; return hashTable[h] + srcBase; } - { const U16* const hashTable = (const U16*) tableBase; return hashTable[h] + srcBase; } /* default, to ensure a return */ -} - -LZ4_FORCE_INLINE const BYTE* LZ4_getPosition(const BYTE* p, const void* tableBase, tableType_t tableType, const BYTE* srcBase) -{ - U32 const h = LZ4_hashPosition(p, tableType); - return LZ4_getPositionOnHash(h, tableBase, tableType, srcBase); -} - -LZ4_FORCE_INLINE void LZ4_prepareTable( - LZ4_stream_t_internal* const cctx, - const int inputSize, - const tableType_t tableType) { - /* If the table hasn't been used, it's guaranteed to be zeroed out, and is - * therefore safe to use no matter what mode we're in. Otherwise, we figure - * out if it's safe to leave as is or whether it needs to be reset. - */ - if (cctx->tableType != clearedTable) { - if (cctx->tableType != tableType - || (tableType == byU16 && cctx->currentOffset + inputSize >= 0xFFFFU) - || (tableType == byU32 && cctx->currentOffset > 1 GB) - || tableType == byPtr - || inputSize >= 4 KB) - { - DEBUGLOG(4, "LZ4_prepareTable: Resetting table in %p", cctx); - MEM_INIT(cctx->hashTable, 0, LZ4_HASHTABLESIZE); - cctx->currentOffset = 0; - cctx->tableType = clearedTable; - } else { - DEBUGLOG(4, "LZ4_prepareTable: Re-use hash table (no reset)"); - } - } - - /* Adding a gap, so all previous entries are > MAX_DISTANCE back, is faster - * than compressing without a gap. However, compressing with - * currentOffset == 0 is faster still, so we preserve that case. - */ - if (cctx->currentOffset != 0 && tableType == byU32) { - DEBUGLOG(5, "LZ4_prepareTable: adding 64KB to currentOffset"); - cctx->currentOffset += 64 KB; - } - - /* Finally, clear history */ - cctx->dictCtx = NULL; - cctx->dictionary = NULL; - cctx->dictSize = 0; -} - -/** LZ4_compress_generic() : - inlined, to ensure branches are decided at compilation time */ -LZ4_FORCE_INLINE int LZ4_compress_generic( - LZ4_stream_t_internal* const cctx, - const char* const source, - char* const dest, - const int inputSize, - const int maxOutputSize, - const limitedOutput_directive outputLimited, - const tableType_t tableType, - const dict_directive dictDirective, - const dictIssue_directive dictIssue, - const U32 acceleration) -{ - const BYTE* ip = (const BYTE*) source; - - U32 const startIndex = cctx->currentOffset; - const BYTE* base = (const BYTE*) source - startIndex; - const BYTE* lowLimit; - - const LZ4_stream_t_internal* dictCtx = (const LZ4_stream_t_internal*) cctx->dictCtx; - const BYTE* const dictionary = - dictDirective == usingDictCtx ? dictCtx->dictionary : cctx->dictionary; - const U32 dictSize = - dictDirective == usingDictCtx ? dictCtx->dictSize : cctx->dictSize; - const U32 dictDelta = (dictDirective == usingDictCtx) ? startIndex - dictCtx->currentOffset : 0; /* make indexes in dictCtx comparable with index in current context */ - - int const maybe_extMem = (dictDirective == usingExtDict) || (dictDirective == usingDictCtx); - U32 const prefixIdxLimit = startIndex - dictSize; /* used when dictDirective == dictSmall */ - const BYTE* const dictEnd = dictionary + dictSize; - const BYTE* anchor = (const BYTE*) source; - const BYTE* const iend = ip + inputSize; - const BYTE* const mflimitPlusOne = iend - MFLIMIT + 1; - const BYTE* const matchlimit = iend - LASTLITERALS; - - /* the dictCtx currentOffset is indexed on the start of the dictionary, - * while a dictionary in the current context precedes the currentOffset */ - const BYTE* dictBase = dictDirective == usingDictCtx ? - dictionary + dictSize - dictCtx->currentOffset : /* is it possible that dictCtx->currentOffset != dictCtx->dictSize ? Yes if the dictionary context is not reset */ - dictionary + dictSize - startIndex; - - BYTE* op = (BYTE*) dest; - BYTE* const olimit = op + maxOutputSize; - - U32 offset = 0; - U32 forwardH; - - DEBUGLOG(5, "LZ4_compress_generic: srcSize=%i, tableType=%u", inputSize, tableType); - /* Init conditions */ - if ((U32)inputSize > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported inputSize, too large (or negative) */ - if (tableType==byPtr) assert(dictDirective==noDict); /* only supported use case with byPtr */ - assert(acceleration >= 1); - - lowLimit = (const BYTE*)source - (dictDirective == withPrefix64k ? dictSize : 0); - - if ((tableType == byU16) && (inputSize>=LZ4_64Klimit)) return 0; /* Size too large (not within 64K limit) */ - - /* Update context state */ - if (dictDirective == usingDictCtx) { - /* Subsequent linked blocks can't use the dictionary. */ - /* Instead, they use the block we just compressed. */ - cctx->dictCtx = NULL; - cctx->dictSize = (U32)inputSize; - } else { - cctx->dictSize += (U32)inputSize; - } - cctx->currentOffset += (U32)inputSize; - cctx->tableType = tableType; - - if (inputSizehashTable, tableType, base); - ip++; forwardH = LZ4_hashPosition(ip, tableType); - - /* Main Loop */ - for ( ; ; ) { - const BYTE* match; - BYTE* token; - - /* Find a match */ - if (tableType == byPtr) { - const BYTE* forwardIp = ip; - unsigned step = 1; - unsigned searchMatchNb = acceleration << LZ4_skipTrigger; - do { - U32 const h = forwardH; - ip = forwardIp; - forwardIp += step; - step = (searchMatchNb++ >> LZ4_skipTrigger); - - if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals; - assert(ip < mflimitPlusOne); - - match = LZ4_getPositionOnHash(h, cctx->hashTable, tableType, base); - forwardH = LZ4_hashPosition(forwardIp, tableType); - LZ4_putPositionOnHash(ip, h, cctx->hashTable, tableType, base); - - } while ( (match+MAX_DISTANCE < ip) - || (LZ4_read32(match) != LZ4_read32(ip)) ); - - } else { /* byU32, byU16 */ - - const BYTE* forwardIp = ip; - unsigned step = 1; - unsigned searchMatchNb = acceleration << LZ4_skipTrigger; - do { - U32 const h = forwardH; - U32 const current = (U32)(forwardIp - base); - U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType); - assert(matchIndex <= current); - assert(forwardIp - base < (ptrdiff_t)(2 GB - 1)); - ip = forwardIp; - forwardIp += step; - step = (searchMatchNb++ >> LZ4_skipTrigger); - - if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals; - assert(ip < mflimitPlusOne); - - if (dictDirective == usingDictCtx) { - if (matchIndex < startIndex) { - /* there was no match, try the dictionary */ - assert(tableType == byU32); - matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32); - match = dictBase + matchIndex; - matchIndex += dictDelta; /* make dictCtx index comparable with current context */ - lowLimit = dictionary; - } else { - match = base + matchIndex; - lowLimit = (const BYTE*)source; - } - } else if (dictDirective==usingExtDict) { - if (matchIndex < startIndex) { - DEBUGLOG(7, "extDict candidate: matchIndex=%5u < startIndex=%5u", matchIndex, startIndex); - match = dictBase + matchIndex; - lowLimit = dictionary; - } else { - match = base + matchIndex; - lowLimit = (const BYTE*)source; - } - } else { /* single continuous memory segment */ - match = base + matchIndex; - } - forwardH = LZ4_hashPosition(forwardIp, tableType); - LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType); - - if ((dictIssue == dictSmall) && (matchIndex < prefixIdxLimit)) continue; /* match outside of valid area */ - if ((tableType != byU16) && (current - matchIndex > MAX_DISTANCE)) continue; /* too far - note: works even if matchIndex overflows */ - if (tableType == byU16) assert((current - matchIndex) <= MAX_DISTANCE); /* too_far presumed impossible with byU16 */ - - if (LZ4_read32(match) == LZ4_read32(ip)) { - if (maybe_extMem) offset = current - matchIndex; - break; /* match found */ - } - - } while(1); - } - - /* Catch up */ - while (((ip>anchor) & (match > lowLimit)) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; } - - /* Encode Literals */ - { unsigned const litLength = (unsigned)(ip - anchor); - token = op++; - if ((outputLimited) && /* Check output buffer overflow */ - (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength/255) > olimit))) - return 0; - if (litLength >= RUN_MASK) { - int len = (int)litLength-RUN_MASK; - *token = (RUN_MASK<= 255 ; len-=255) *op++ = 255; - *op++ = (BYTE)len; - } - else *token = (BYTE)(litLength< %i)", offset, (int)(ip - (const BYTE*)source)); - assert(offset <= MAX_DISTANCE && offset > 0); - LZ4_writeLE16(op, (U16)offset); op+=2; - } else { - DEBUGLOG(6, " with offset=%u (same segment)", (U32)(ip - match)); - assert(ip-match <= MAX_DISTANCE); - LZ4_writeLE16(op, (U16)(ip - match)); op+=2; - } - - /* Encode MatchLength */ - { unsigned matchCode; - - if ( (dictDirective==usingExtDict || dictDirective==usingDictCtx) - && (lowLimit==dictionary) /* match within extDict */ ) { - const BYTE* limit = ip + (dictEnd-match); - assert(dictEnd > match); - if (limit > matchlimit) limit = matchlimit; - matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, limit); - ip += MINMATCH + matchCode; - if (ip==limit) { - unsigned const more = LZ4_count(limit, (const BYTE*)source, matchlimit); - matchCode += more; - ip += more; - } - DEBUGLOG(6, " with matchLength=%u starting in extDict", matchCode+MINMATCH); - } else { - matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit); - ip += MINMATCH + matchCode; - DEBUGLOG(6, " with matchLength=%u", matchCode+MINMATCH); - } - - if ( outputLimited && /* Check output buffer overflow */ - (unlikely(op + (1 + LASTLITERALS) + (matchCode>>8) > olimit)) ) - return 0; - if (matchCode >= ML_MASK) { - *token += ML_MASK; - matchCode -= ML_MASK; - LZ4_write32(op, 0xFFFFFFFF); - while (matchCode >= 4*255) { - op+=4; - LZ4_write32(op, 0xFFFFFFFF); - matchCode -= 4*255; - } - op += matchCode / 255; - *op++ = (BYTE)(matchCode % 255); - } else - *token += (BYTE)(matchCode); - } - - anchor = ip; - - /* Test end of chunk */ - if (ip >= mflimitPlusOne) break; - - /* Fill table */ - LZ4_putPosition(ip-2, cctx->hashTable, tableType, base); - - /* Test next position */ - if (tableType == byPtr) { - - match = LZ4_getPosition(ip, cctx->hashTable, tableType, base); - LZ4_putPosition(ip, cctx->hashTable, tableType, base); - if ( (match+MAX_DISTANCE >= ip) - && (LZ4_read32(match) == LZ4_read32(ip)) ) - { token=op++; *token=0; goto _next_match; } - - } else { /* byU32, byU16 */ - - U32 const h = LZ4_hashPosition(ip, tableType); - U32 const current = (U32)(ip-base); - U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType); - assert(matchIndex < current); - if (dictDirective == usingDictCtx) { - if (matchIndex < startIndex) { - /* there was no match, try the dictionary */ - matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32); - match = dictBase + matchIndex; - lowLimit = dictionary; /* required for match length counter */ - matchIndex += dictDelta; - } else { - match = base + matchIndex; - lowLimit = (const BYTE*)source; /* required for match length counter */ - } - } else if (dictDirective==usingExtDict) { - if (matchIndex < startIndex) { - match = dictBase + matchIndex; - lowLimit = dictionary; /* required for match length counter */ - } else { - match = base + matchIndex; - lowLimit = (const BYTE*)source; /* required for match length counter */ - } - } else { /* single memory segment */ - match = base + matchIndex; - } - LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType); - if ( ((dictIssue==dictSmall) ? (matchIndex >= prefixIdxLimit) : 1) - && ((tableType==byU16) ? 1 : (current - matchIndex <= MAX_DISTANCE)) - && (LZ4_read32(match) == LZ4_read32(ip)) ) { - token=op++; - *token=0; - if (maybe_extMem) offset = current - matchIndex; - DEBUGLOG(6, "seq.start:%i, literals=%u, match.start:%i", (int)(anchor-(const BYTE*)source), 0, (int)(ip-(const BYTE*)source)); - goto _next_match; - } - } - - /* Prepare next loop */ - forwardH = LZ4_hashPosition(++ip, tableType); - - } - -_last_literals: - /* Encode Last Literals */ - { size_t const lastRun = (size_t)(iend - anchor); - if ( (outputLimited) && /* Check output buffer overflow */ - ((op - (BYTE*)dest) + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > (U32)maxOutputSize) ) - return 0; - if (lastRun >= RUN_MASK) { - size_t accumulator = lastRun - RUN_MASK; - *op++ = RUN_MASK << ML_BITS; - for(; accumulator >= 255 ; accumulator-=255) *op++ = 255; - *op++ = (BYTE) accumulator; - } else { - *op++ = (BYTE)(lastRun<internal_donotuse; - if (acceleration < 1) acceleration = ACCELERATION_DEFAULT; - LZ4_resetStream((LZ4_stream_t*)state); - if (maxOutputSize >= LZ4_compressBound(inputSize)) { - if (inputSize < LZ4_64Klimit) { - return LZ4_compress_generic(ctx, source, dest, inputSize, 0, notLimited, byU16, noDict, noDictIssue, acceleration); - } else { - const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > MAX_DISTANCE)) ? byPtr : byU32; - return LZ4_compress_generic(ctx, source, dest, inputSize, 0, notLimited, tableType, noDict, noDictIssue, acceleration); - } - } else { - if (inputSize < LZ4_64Klimit) {; - return LZ4_compress_generic(ctx, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration); - } else { - const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > MAX_DISTANCE)) ? byPtr : byU32; - return LZ4_compress_generic(ctx, source, dest, inputSize, maxOutputSize, limitedOutput, tableType, noDict, noDictIssue, acceleration); - } - } -} - -/** - * LZ4_compress_fast_extState_fastReset() : - * A variant of LZ4_compress_fast_extState(). - * - * Using this variant avoids an expensive initialization step. It is only safe - * to call if the state buffer is known to be correctly initialized already - * (see comment in lz4.h on LZ4_resetStream_fast() for a definition of - * "correctly initialized"). - */ -int LZ4_compress_fast_extState_fastReset(void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration) -{ - LZ4_stream_t_internal* ctx = &((LZ4_stream_t*)state)->internal_donotuse; - if (acceleration < 1) acceleration = ACCELERATION_DEFAULT; - - if (dstCapacity >= LZ4_compressBound(srcSize)) { - if (srcSize < LZ4_64Klimit) { - const tableType_t tableType = byU16; - LZ4_prepareTable(ctx, srcSize, tableType); - if (ctx->currentOffset) { - return LZ4_compress_generic(ctx, src, dst, srcSize, 0, notLimited, tableType, noDict, dictSmall, acceleration); - } else { - return LZ4_compress_generic(ctx, src, dst, srcSize, 0, notLimited, tableType, noDict, noDictIssue, acceleration); - } - } else { - const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > MAX_DISTANCE)) ? byPtr : byU32; - LZ4_prepareTable(ctx, srcSize, tableType); - return LZ4_compress_generic(ctx, src, dst, srcSize, 0, notLimited, tableType, noDict, noDictIssue, acceleration); - } - } else { - if (srcSize < LZ4_64Klimit) { - const tableType_t tableType = byU16; - LZ4_prepareTable(ctx, srcSize, tableType); - if (ctx->currentOffset) { - return LZ4_compress_generic(ctx, src, dst, srcSize, dstCapacity, limitedOutput, tableType, noDict, dictSmall, acceleration); - } else { - return LZ4_compress_generic(ctx, src, dst, srcSize, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration); - } - } else { - const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > MAX_DISTANCE)) ? byPtr : byU32; - LZ4_prepareTable(ctx, srcSize, tableType); - return LZ4_compress_generic(ctx, src, dst, srcSize, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration); - } - } -} - - -int LZ4_compress_fast(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) -{ - int result; -#if (LZ4_HEAPMODE) - LZ4_stream_t* ctxPtr = ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ - if (ctxPtr == NULL) return 0; -#else - LZ4_stream_t ctx; - LZ4_stream_t* const ctxPtr = &ctx; -#endif - result = LZ4_compress_fast_extState(ctxPtr, source, dest, inputSize, maxOutputSize, acceleration); - -#if (LZ4_HEAPMODE) - FREEMEM(ctxPtr); -#endif - return result; -} - - -int LZ4_compress_default(const char* source, char* dest, int inputSize, int maxOutputSize) -{ - return LZ4_compress_fast(source, dest, inputSize, maxOutputSize, 1); -} - - -/* hidden debug function */ -/* strangely enough, gcc generates faster code when this function is uncommented, even if unused */ -int LZ4_compress_fast_force(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) -{ - LZ4_stream_t ctx; - LZ4_resetStream(&ctx); - - if (inputSize < LZ4_64Klimit) - return LZ4_compress_generic(&ctx.internal_donotuse, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration); - else - return LZ4_compress_generic(&ctx.internal_donotuse, source, dest, inputSize, maxOutputSize, limitedOutput, sizeof(void*)==8 ? byU32 : byPtr, noDict, noDictIssue, acceleration); -} - - -/*-****************************** -* *_destSize() variant -********************************/ - -static int LZ4_compress_destSize_generic( - LZ4_stream_t_internal* const ctx, - const char* const src, - char* const dst, - int* const srcSizePtr, - const int targetDstSize, - const tableType_t tableType) -{ - const BYTE* ip = (const BYTE*) src; - const BYTE* base = (const BYTE*) src; - const BYTE* lowLimit = (const BYTE*) src; - const BYTE* anchor = ip; - const BYTE* const iend = ip + *srcSizePtr; - const BYTE* const mflimit = iend - MFLIMIT; - const BYTE* const matchlimit = iend - LASTLITERALS; - - BYTE* op = (BYTE*) dst; - BYTE* const oend = op + targetDstSize; - BYTE* const oMaxLit = op + targetDstSize - 2 /* offset */ - 8 /* because 8+MINMATCH==MFLIMIT */ - 1 /* token */; - BYTE* const oMaxMatch = op + targetDstSize - (LASTLITERALS + 1 /* token */); - BYTE* const oMaxSeq = oMaxLit - 1 /* token */; - - U32 forwardH; - - - /* Init conditions */ - if (targetDstSize < 1) return 0; /* Impossible to store anything */ - if ((U32)*srcSizePtr > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported input size, too large (or negative) */ - if ((tableType == byU16) && (*srcSizePtr>=LZ4_64Klimit)) return 0; /* Size too large (not within 64K limit) */ - if (*srcSizePtrhashTable, tableType, base); - ip++; forwardH = LZ4_hashPosition(ip, tableType); - - /* Main Loop */ - for ( ; ; ) { - const BYTE* match; - BYTE* token; - - /* Find a match */ - { const BYTE* forwardIp = ip; - unsigned step = 1; - unsigned searchMatchNb = 1 << LZ4_skipTrigger; - - do { - U32 h = forwardH; - ip = forwardIp; - forwardIp += step; - step = (searchMatchNb++ >> LZ4_skipTrigger); - - if (unlikely(forwardIp > mflimit)) goto _last_literals; - - match = LZ4_getPositionOnHash(h, ctx->hashTable, tableType, base); - forwardH = LZ4_hashPosition(forwardIp, tableType); - LZ4_putPositionOnHash(ip, h, ctx->hashTable, tableType, base); - - } while ( ((tableType==byU16) ? 0 : (match + MAX_DISTANCE < ip)) - || (LZ4_read32(match) != LZ4_read32(ip)) ); - } - - /* Catch up */ - while ((ip>anchor) && (match > lowLimit) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; } - - /* Encode Literal length */ - { unsigned litLength = (unsigned)(ip - anchor); - token = op++; - if (op + ((litLength+240)/255) + litLength > oMaxLit) { - /* Not enough space for a last match */ - op--; - goto _last_literals; - } - if (litLength>=RUN_MASK) { - unsigned len = litLength - RUN_MASK; - *token=(RUN_MASK<= 255 ; len-=255) *op++ = 255; - *op++ = (BYTE)len; - } - else *token = (BYTE)(litLength< oMaxMatch) { - /* Match description too long : reduce it */ - matchLength = (15-1) + (oMaxMatch-op) * 255; - } - ip += MINMATCH + matchLength; - - if (matchLength>=ML_MASK) { - *token += ML_MASK; - matchLength -= ML_MASK; - while (matchLength >= 255) { matchLength-=255; *op++ = 255; } - *op++ = (BYTE)matchLength; - } - else *token += (BYTE)(matchLength); - } - - anchor = ip; - - /* Test end of block */ - if (ip > mflimit) break; - if (op > oMaxSeq) break; - - /* Fill table */ - LZ4_putPosition(ip-2, ctx->hashTable, tableType, base); - - /* Test next position */ - match = LZ4_getPosition(ip, ctx->hashTable, tableType, base); - LZ4_putPosition(ip, ctx->hashTable, tableType, base); - if ( (match+MAX_DISTANCE>=ip) - && (LZ4_read32(match)==LZ4_read32(ip)) ) - { token=op++; *token=0; goto _next_match; } - - /* Prepare next loop */ - forwardH = LZ4_hashPosition(++ip, tableType); - } - -_last_literals: - /* Encode Last Literals */ - { size_t lastRunSize = (size_t)(iend - anchor); - if (op + 1 /* token */ + ((lastRunSize+240)/255) /* litLength */ + lastRunSize /* literals */ > oend) { - /* adapt lastRunSize to fill 'dst' */ - lastRunSize = (oend-op) - 1; - lastRunSize -= (lastRunSize+240)/255; - } - ip = anchor + lastRunSize; - - if (lastRunSize >= RUN_MASK) { - size_t accumulator = lastRunSize - RUN_MASK; - *op++ = RUN_MASK << ML_BITS; - for(; accumulator >= 255 ; accumulator-=255) *op++ = 255; - *op++ = (BYTE) accumulator; - } else { - *op++ = (BYTE)(lastRunSize<= LZ4_compressBound(*srcSizePtr)) { /* compression success is guaranteed */ - return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, 1); - } else { - if (*srcSizePtr < LZ4_64Klimit) { - return LZ4_compress_destSize_generic(&state->internal_donotuse, src, dst, srcSizePtr, targetDstSize, byU16); - } else { - tableType_t const tableType = ((sizeof(void*)==4) && ((uptrval)src > MAX_DISTANCE)) ? byPtr : byU32; - return LZ4_compress_destSize_generic(&state->internal_donotuse, src, dst, srcSizePtr, targetDstSize, tableType); - } } -} - - -int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize) -{ -#if (LZ4_HEAPMODE) - LZ4_stream_t* ctx = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ - if (ctx == NULL) return 0; -#else - LZ4_stream_t ctxBody; - LZ4_stream_t* ctx = &ctxBody; -#endif - - int result = LZ4_compress_destSize_extState(ctx, src, dst, srcSizePtr, targetDstSize); - -#if (LZ4_HEAPMODE) - FREEMEM(ctx); -#endif - return result; -} - - - -/*-****************************** -* Streaming functions -********************************/ - -LZ4_stream_t* LZ4_createStream(void) -{ - LZ4_stream_t* lz4s = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); - LZ4_STATIC_ASSERT(LZ4_STREAMSIZE >= sizeof(LZ4_stream_t_internal)); /* A compilation error here means LZ4_STREAMSIZE is not large enough */ - DEBUGLOG(4, "LZ4_createStream %p", lz4s); - if (lz4s == NULL) return NULL; - LZ4_resetStream(lz4s); - return lz4s; -} - -void LZ4_resetStream (LZ4_stream_t* LZ4_stream) -{ - DEBUGLOG(5, "LZ4_resetStream (ctx:%p)", LZ4_stream); - MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t)); -} - -void LZ4_resetStream_fast(LZ4_stream_t* ctx) { - LZ4_prepareTable(&(ctx->internal_donotuse), 0, byU32); -} - -int LZ4_freeStream (LZ4_stream_t* LZ4_stream) -{ - if (!LZ4_stream) return 0; /* support free on NULL */ - DEBUGLOG(5, "LZ4_freeStream %p", LZ4_stream); - FREEMEM(LZ4_stream); - return (0); -} - - -#define HASH_UNIT sizeof(reg_t) -int LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize) -{ - LZ4_stream_t_internal* dict = &LZ4_dict->internal_donotuse; - const tableType_t tableType = byU32; - const BYTE* p = (const BYTE*)dictionary; - const BYTE* const dictEnd = p + dictSize; - const BYTE* base; - - DEBUGLOG(4, "LZ4_loadDict (%i bytes from %p into %p)", dictSize, dictionary, LZ4_dict); - - LZ4_prepareTable(dict, 0, tableType); - - /* We always increment the offset by 64 KB, since, if the dict is longer, - * we truncate it to the last 64k, and if it's shorter, we still want to - * advance by a whole window length so we can provide the guarantee that - * there are only valid offsets in the window, which allows an optimization - * in LZ4_compress_fast_continue() where it uses noDictIssue even when the - * dictionary isn't a full 64k. */ - - if ((dictEnd - p) > 64 KB) p = dictEnd - 64 KB; - base = dictEnd - 64 KB - dict->currentOffset; - dict->dictionary = p; - dict->dictSize = (U32)(dictEnd - p); - dict->currentOffset += 64 KB; - dict->tableType = tableType; - - if (dictSize < (int)HASH_UNIT) { - return 0; - } - - while (p <= dictEnd-HASH_UNIT) { - LZ4_putPosition(p, dict->hashTable, tableType, base); - p+=3; - } - - return dict->dictSize; -} - -void LZ4_attach_dictionary(LZ4_stream_t *working_stream, const LZ4_stream_t *dictionary_stream) { - if (dictionary_stream != NULL) { - /* If the current offset is zero, we will never look in the - * external dictionary context, since there is no value a table - * entry can take that indicate a miss. In that case, we need - * to bump the offset to something non-zero. - */ - if (working_stream->internal_donotuse.currentOffset == 0) { - working_stream->internal_donotuse.currentOffset = 64 KB; - } - working_stream->internal_donotuse.dictCtx = &(dictionary_stream->internal_donotuse); - } else { - working_stream->internal_donotuse.dictCtx = NULL; - } -} - - -static void LZ4_renormDictT(LZ4_stream_t_internal* LZ4_dict, int nextSize) -{ - if (LZ4_dict->currentOffset + nextSize > 0x80000000) { /* potential ptrdiff_t overflow (32-bits mode) */ - /* rescale hash table */ - U32 const delta = LZ4_dict->currentOffset - 64 KB; - const BYTE* dictEnd = LZ4_dict->dictionary + LZ4_dict->dictSize; - int i; - DEBUGLOG(4, "LZ4_renormDictT"); - for (i=0; ihashTable[i] < delta) LZ4_dict->hashTable[i]=0; - else LZ4_dict->hashTable[i] -= delta; - } - LZ4_dict->currentOffset = 64 KB; - if (LZ4_dict->dictSize > 64 KB) LZ4_dict->dictSize = 64 KB; - LZ4_dict->dictionary = dictEnd - LZ4_dict->dictSize; - } -} - - -int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) -{ - const tableType_t tableType = byU32; - LZ4_stream_t_internal* streamPtr = &LZ4_stream->internal_donotuse; - const BYTE* const dictEnd = streamPtr->dictionary + streamPtr->dictSize; - - if (streamPtr->initCheck) return 0; /* Uninitialized structure detected */ - LZ4_renormDictT(streamPtr, inputSize); /* avoid index overflow */ - if (acceleration < 1) acceleration = ACCELERATION_DEFAULT; - - /* Check overlapping input/dictionary space */ - { const BYTE* sourceEnd = (const BYTE*) source + inputSize; - if ((sourceEnd > streamPtr->dictionary) && (sourceEnd < dictEnd)) { - streamPtr->dictSize = (U32)(dictEnd - sourceEnd); - if (streamPtr->dictSize > 64 KB) streamPtr->dictSize = 64 KB; - if (streamPtr->dictSize < 4) streamPtr->dictSize = 0; - streamPtr->dictionary = dictEnd - streamPtr->dictSize; - } - } - - /* prefix mode : source data follows dictionary */ - if (dictEnd == (const BYTE*)source) { - if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) - return LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, tableType, withPrefix64k, dictSmall, acceleration); - else - return LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, tableType, withPrefix64k, noDictIssue, acceleration); - } - - /* external dictionary mode */ - { int result; - if (streamPtr->dictCtx) { - /* We depend here on the fact that dictCtx'es (produced by - * LZ4_loadDict) guarantee that their tables contain no references - * to offsets between dictCtx->currentOffset - 64 KB and - * dictCtx->currentOffset - dictCtx->dictSize. This makes it safe - * to use noDictIssue even when the dict isn't a full 64 KB. - */ - if (inputSize > 4 KB) { - /* For compressing large blobs, it is faster to pay the setup - * cost to copy the dictionary's tables into the active context, - * so that the compression loop is only looking into one table. - */ - memcpy(streamPtr, streamPtr->dictCtx, sizeof(LZ4_stream_t)); - result = LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration); - } else { - result = LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, tableType, usingDictCtx, noDictIssue, acceleration); - } - } else { - if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) { - result = LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, tableType, usingExtDict, dictSmall, acceleration); - } else { - result = LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration); - } - } - streamPtr->dictionary = (const BYTE*)source; - streamPtr->dictSize = (U32)inputSize; - return result; - } -} - - -/* Hidden debug function, to force-test external dictionary mode */ -int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize) -{ - LZ4_stream_t_internal* streamPtr = &LZ4_dict->internal_donotuse; - int result; - - LZ4_renormDictT(streamPtr, srcSize); - - if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) { - result = LZ4_compress_generic(streamPtr, source, dest, srcSize, 0, notLimited, byU32, usingExtDict, dictSmall, 1); - } else { - result = LZ4_compress_generic(streamPtr, source, dest, srcSize, 0, notLimited, byU32, usingExtDict, noDictIssue, 1); - } - - streamPtr->dictionary = (const BYTE*)source; - streamPtr->dictSize = (U32)srcSize; - - return result; -} - - -/*! LZ4_saveDict() : - * If previously compressed data block is not guaranteed to remain available at its memory location, - * save it into a safer place (char* safeBuffer). - * Note : you don't need to call LZ4_loadDict() afterwards, - * dictionary is immediately usable, you can therefore call LZ4_compress_fast_continue(). - * Return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error. - */ -int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize) -{ - LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse; - const BYTE* const previousDictEnd = dict->dictionary + dict->dictSize; - - if ((U32)dictSize > 64 KB) dictSize = 64 KB; /* useless to define a dictionary > 64 KB */ - if ((U32)dictSize > dict->dictSize) dictSize = dict->dictSize; - - memmove(safeBuffer, previousDictEnd - dictSize, dictSize); - - dict->dictionary = (const BYTE*)safeBuffer; - dict->dictSize = (U32)dictSize; - - return dictSize; -} - - - -/*-***************************** -* Decompression functions -*******************************/ -/*! LZ4_decompress_generic() : - * This generic decompression function covers all use cases. - * It shall be instantiated several times, using different sets of directives. - * Note that it is important for performance that this function really get inlined, - * in order to remove useless branches during compilation optimization. - */ -LZ4_FORCE_O2_GCC_PPC64LE -LZ4_FORCE_INLINE int LZ4_decompress_generic( - const char* const src, - char* const dst, - int srcSize, - int outputSize, /* If endOnInput==endOnInputSize, this value is `dstCapacity` */ - - int endOnInput, /* endOnOutputSize, endOnInputSize */ - int partialDecoding, /* full, partial */ - int targetOutputSize, /* only used if partialDecoding==partial */ - int dict, /* noDict, withPrefix64k, usingExtDict */ - const BYTE* const lowPrefix, /* always <= dst, == dst when no prefix */ - const BYTE* const dictStart, /* only if dict==usingExtDict */ - const size_t dictSize /* note : = 0 if noDict */ - ) -{ - const BYTE* ip = (const BYTE*) src; - const BYTE* const iend = ip + srcSize; - - BYTE* op = (BYTE*) dst; - BYTE* const oend = op + outputSize; - BYTE* cpy; - BYTE* oexit = op + targetOutputSize; - - const BYTE* const dictEnd = (const BYTE*)dictStart + dictSize; - const unsigned inc32table[8] = {0, 1, 2, 1, 0, 4, 4, 4}; - const int dec64table[8] = {0, 0, 0, -1, -4, 1, 2, 3}; - - const int safeDecode = (endOnInput==endOnInputSize); - const int checkOffset = ((safeDecode) && (dictSize < (int)(64 KB))); - - - /* Special cases */ - if ((partialDecoding) && (oexit > oend-MFLIMIT)) oexit = oend-MFLIMIT; /* targetOutputSize too high => just decode everything */ - if ((endOnInput) && (unlikely(outputSize==0))) return ((srcSize==1) && (*ip==0)) ? 0 : -1; /* Empty output buffer */ - if ((!endOnInput) && (unlikely(outputSize==0))) return (*ip==0?1:-1); - - /* Main Loop : decode sequences */ - while (1) { - size_t length; - const BYTE* match; - size_t offset; - - unsigned const token = *ip++; - - /* shortcut for common case : - * in most circumstances, we expect to decode small matches (<= 18 bytes) separated by few literals (<= 14 bytes). - * this shortcut was tested on x86 and x64, where it improves decoding speed. - * it has not yet been benchmarked on ARM, Power, mips, etc. */ - if (((ip + 14 /*maxLL*/ + 2 /*offset*/ <= iend) - & (op + 14 /*maxLL*/ + 18 /*maxML*/ <= oend)) - & ((token < (15<> ML_BITS; - size_t const off = LZ4_readLE16(ip+ll); - const BYTE* const matchPtr = op + ll - off; /* pointer underflow risk ? */ - if ((off >= 8) /* do not deal with overlapping matches */ & (matchPtr >= lowPrefix)) { - size_t const ml = (token & ML_MASK) + MINMATCH; - memcpy(op, ip, 16); op += ll; ip += ll + 2 /*offset*/; - memcpy(op + 0, matchPtr + 0, 8); - memcpy(op + 8, matchPtr + 8, 8); - memcpy(op +16, matchPtr +16, 2); - op += ml; - continue; - } - } - - /* decode literal length */ - if ((length=(token>>ML_BITS)) == RUN_MASK) { - unsigned s; - do { - s = *ip++; - length += s; - } while ( likely(endOnInput ? ip(partialDecoding?oexit:oend-MFLIMIT)) || (ip+length>iend-(2+1+LASTLITERALS))) ) - || ((!endOnInput) && (cpy>oend-WILDCOPYLENGTH)) ) - { - if (partialDecoding) { - if (cpy > oend) goto _output_error; /* Error : write attempt beyond end of output buffer */ - if ((endOnInput) && (ip+length > iend)) goto _output_error; /* Error : read attempt beyond end of input buffer */ - } else { - if ((!endOnInput) && (cpy != oend)) goto _output_error; /* Error : block decoding must stop exactly there */ - if ((endOnInput) && ((ip+length != iend) || (cpy > oend))) goto _output_error; /* Error : input must be consumed */ - } - memcpy(op, ip, length); - ip += length; - op += length; - break; /* Necessarily EOF, due to parsing restrictions */ - } - LZ4_wildCopy(op, ip, cpy); - ip += length; op = cpy; - - /* get offset */ - offset = LZ4_readLE16(ip); ip+=2; - match = op - offset; - if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) goto _output_error; /* Error : offset outside buffers */ - LZ4_write32(op, (U32)offset); /* costs ~1%; silence an msan warning when offset==0 */ - - /* get matchlength */ - length = token & ML_MASK; - if (length == ML_MASK) { - unsigned s; - do { - s = *ip++; - if ((endOnInput) && (ip > iend-LASTLITERALS)) goto _output_error; - length += s; - } while (s==255); - if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)op)) goto _output_error; /* overflow detection */ - } - length += MINMATCH; - - /* check external dictionary */ - if ((dict==usingExtDict) && (match < lowPrefix)) { - if (unlikely(op+length > oend-LASTLITERALS)) goto _output_error; /* doesn't respect parsing restriction */ - - if (length <= (size_t)(lowPrefix-match)) { - /* match can be copied as a single segment from external dictionary */ - memmove(op, dictEnd - (lowPrefix-match), length); - op += length; - } else { - /* match encompass external dictionary and current block */ - size_t const copySize = (size_t)(lowPrefix-match); - size_t const restSize = length - copySize; - memcpy(op, dictEnd - copySize, copySize); - op += copySize; - if (restSize > (size_t)(op-lowPrefix)) { /* overlap copy */ - BYTE* const endOfMatch = op + restSize; - const BYTE* copyFrom = lowPrefix; - while (op < endOfMatch) *op++ = *copyFrom++; - } else { - memcpy(op, lowPrefix, restSize); - op += restSize; - } } - continue; - } - - /* copy match within block */ - cpy = op + length; - if (unlikely(offset<8)) { - op[0] = match[0]; - op[1] = match[1]; - op[2] = match[2]; - op[3] = match[3]; - match += inc32table[offset]; - memcpy(op+4, match, 4); - match -= dec64table[offset]; - } else { memcpy(op, match, 8); match+=8; } - op += 8; - - if (unlikely(cpy>oend-12)) { - BYTE* const oCopyLimit = oend-(WILDCOPYLENGTH-1); - if (cpy > oend-LASTLITERALS) goto _output_error; /* Error : last LASTLITERALS bytes must be literals (uncompressed) */ - if (op < oCopyLimit) { - LZ4_wildCopy(op, match, oCopyLimit); - match += oCopyLimit - op; - op = oCopyLimit; - } - while (op16) LZ4_wildCopy(op+8, match+8, cpy); - } - op = cpy; /* correction */ - } - - /* end of decoding */ - if (endOnInput) - return (int) (((char*)op)-dst); /* Nb of output bytes decoded */ - else - return (int) (((const char*)ip)-src); /* Nb of input bytes read */ - - /* Overflow error detected */ -_output_error: - return (int) (-(((const char*)ip)-src))-1; -} - - -LZ4_FORCE_O2_GCC_PPC64LE -int LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize) -{ - return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, endOnInputSize, full, 0, noDict, (BYTE*)dest, NULL, 0); -} - -LZ4_FORCE_O2_GCC_PPC64LE -int LZ4_decompress_safe_partial(const char* source, char* dest, int compressedSize, int targetOutputSize, int maxDecompressedSize) -{ - return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, endOnInputSize, partial, targetOutputSize, noDict, (BYTE*)dest, NULL, 0); -} - -LZ4_FORCE_O2_GCC_PPC64LE -int LZ4_decompress_fast(const char* source, char* dest, int originalSize) -{ - return LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, withPrefix64k, (BYTE*)(dest - 64 KB), NULL, 64 KB); -} - - -/*===== streaming decompression functions =====*/ - -LZ4_streamDecode_t* LZ4_createStreamDecode(void) -{ - LZ4_streamDecode_t* lz4s = (LZ4_streamDecode_t*) ALLOC_AND_ZERO(sizeof(LZ4_streamDecode_t)); - return lz4s; -} - -int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream) -{ - if (!LZ4_stream) return 0; /* support free on NULL */ - FREEMEM(LZ4_stream); - return 0; -} - -/*! - * LZ4_setStreamDecode() : - * Use this function to instruct where to find the dictionary. - * This function is not necessary if previous data is still available where it was decoded. - * Loading a size of 0 is allowed (same effect as no dictionary). - * Return : 1 if OK, 0 if error - */ -int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize) -{ - LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse; - lz4sd->prefixSize = (size_t) dictSize; - lz4sd->prefixEnd = (const BYTE*) dictionary + dictSize; - lz4sd->externalDict = NULL; - lz4sd->extDictSize = 0; - return 1; -} - -/* -*_continue() : - These decoding functions allow decompression of multiple blocks in "streaming" mode. - Previously decoded blocks must still be available at the memory position where they were decoded. - If it's not possible, save the relevant part of decoded data into a safe buffer, - and indicate where it stands using LZ4_setStreamDecode() -*/ -LZ4_FORCE_O2_GCC_PPC64LE -int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxOutputSize) -{ - LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse; - int result; - - if (lz4sd->prefixEnd == (BYTE*)dest) { - result = LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, - endOnInputSize, full, 0, - usingExtDict, lz4sd->prefixEnd - lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize); - if (result <= 0) return result; - lz4sd->prefixSize += result; - lz4sd->prefixEnd += result; - } else { - lz4sd->extDictSize = lz4sd->prefixSize; - lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize; - result = LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, - endOnInputSize, full, 0, - usingExtDict, (BYTE*)dest, lz4sd->externalDict, lz4sd->extDictSize); - if (result <= 0) return result; - lz4sd->prefixSize = result; - lz4sd->prefixEnd = (BYTE*)dest + result; - } - - return result; -} - -LZ4_FORCE_O2_GCC_PPC64LE -int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize) -{ - LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse; - int result; - - if (lz4sd->prefixEnd == (BYTE*)dest) { - result = LZ4_decompress_generic(source, dest, 0, originalSize, - endOnOutputSize, full, 0, - usingExtDict, lz4sd->prefixEnd - lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize); - if (result <= 0) return result; - lz4sd->prefixSize += originalSize; - lz4sd->prefixEnd += originalSize; - } else { - lz4sd->extDictSize = lz4sd->prefixSize; - lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize; - result = LZ4_decompress_generic(source, dest, 0, originalSize, - endOnOutputSize, full, 0, - usingExtDict, (BYTE*)dest, lz4sd->externalDict, lz4sd->extDictSize); - if (result <= 0) return result; - lz4sd->prefixSize = originalSize; - lz4sd->prefixEnd = (BYTE*)dest + originalSize; - } - - return result; -} - - -/* -Advanced decoding functions : -*_usingDict() : - These decoding functions work the same as "_continue" ones, - the dictionary must be explicitly provided within parameters -*/ - -LZ4_FORCE_O2_GCC_PPC64LE -LZ4_FORCE_INLINE int LZ4_decompress_usingDict_generic(const char* source, char* dest, int compressedSize, int maxOutputSize, int safe, const char* dictStart, int dictSize) -{ - if (dictSize==0) - return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, noDict, (BYTE*)dest, NULL, 0); - if (dictStart+dictSize == dest) { - if (dictSize >= (int)(64 KB - 1)) - return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, withPrefix64k, (BYTE*)dest-64 KB, NULL, 0); - return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, noDict, (BYTE*)dest-dictSize, NULL, 0); - } - return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, usingExtDict, (BYTE*)dest, (const BYTE*)dictStart, dictSize); -} - -LZ4_FORCE_O2_GCC_PPC64LE -int LZ4_decompress_safe_usingDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize) -{ - return LZ4_decompress_usingDict_generic(source, dest, compressedSize, maxOutputSize, 1, dictStart, dictSize); -} - -LZ4_FORCE_O2_GCC_PPC64LE -int LZ4_decompress_fast_usingDict(const char* source, char* dest, int originalSize, const char* dictStart, int dictSize) -{ - return LZ4_decompress_usingDict_generic(source, dest, 0, originalSize, 0, dictStart, dictSize); -} - -/* debug function */ -LZ4_FORCE_O2_GCC_PPC64LE -int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize) -{ - return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, usingExtDict, (BYTE*)dest, (const BYTE*)dictStart, dictSize); -} - - -/*=************************************************* -* Obsolete Functions -***************************************************/ -/* obsolete compression functions */ -int LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize) { return LZ4_compress_default(source, dest, inputSize, maxOutputSize); } -int LZ4_compress(const char* source, char* dest, int inputSize) { return LZ4_compress_default(source, dest, inputSize, LZ4_compressBound(inputSize)); } -int LZ4_compress_limitedOutput_withState (void* state, const char* src, char* dst, int srcSize, int dstSize) { return LZ4_compress_fast_extState(state, src, dst, srcSize, dstSize, 1); } -int LZ4_compress_withState (void* state, const char* src, char* dst, int srcSize) { return LZ4_compress_fast_extState(state, src, dst, srcSize, LZ4_compressBound(srcSize), 1); } -int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_stream, const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_fast_continue(LZ4_stream, src, dst, srcSize, maxDstSize, 1); } -int LZ4_compress_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize) { return LZ4_compress_fast_continue(LZ4_stream, source, dest, inputSize, LZ4_compressBound(inputSize), 1); } - -/* -These function names are deprecated and should no longer be used. -They are only provided here for compatibility with older user programs. -- LZ4_uncompress is totally equivalent to LZ4_decompress_fast -- LZ4_uncompress_unknownOutputSize is totally equivalent to LZ4_decompress_safe -*/ -int LZ4_uncompress (const char* source, char* dest, int outputSize) { return LZ4_decompress_fast(source, dest, outputSize); } -int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize) { return LZ4_decompress_safe(source, dest, isize, maxOutputSize); } - -/* Obsolete Streaming functions */ - -int LZ4_sizeofStreamState() { return LZ4_STREAMSIZE; } - -int LZ4_resetStreamState(void* state, char* inputBuffer) -{ - (void)inputBuffer; - LZ4_resetStream((LZ4_stream_t*)state); - return 0; -} - -void* LZ4_create (char* inputBuffer) -{ - (void)inputBuffer; - return LZ4_createStream(); -} - -char* LZ4_slideInputBuffer (void* state) -{ - /* avoid const char * -> char * conversion warning */ - return (char *)(uptrval)((LZ4_stream_t*)state)->internal_donotuse.dictionary; -} - -/* Obsolete streaming decompression functions */ - -int LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize) -{ - return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, withPrefix64k, (BYTE*)dest - 64 KB, NULL, 64 KB); -} - -int LZ4_decompress_fast_withPrefix64k(const char* source, char* dest, int originalSize) -{ - return LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, withPrefix64k, (BYTE*)dest - 64 KB, NULL, 64 KB); -} - -#endif /* LZ4_COMMONDEFS_ONLY */ diff --git a/stratosphere/loader/source/lz4.h b/stratosphere/loader/source/lz4.h deleted file mode 100644 index 0dfa19e00..000000000 --- a/stratosphere/loader/source/lz4.h +++ /dev/null @@ -1,569 +0,0 @@ -/* - * LZ4 - Fast LZ compression algorithm - * Header File - * Copyright (C) 2011-2017, Yann Collet. - - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following disclaimer - in the documentation and/or other materials provided with the - distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - You can contact the author at : - - LZ4 homepage : http://www.lz4.org - - LZ4 source repository : https://github.com/lz4/lz4 -*/ -#if defined (__cplusplus) -extern "C" { -#endif - -#ifndef LZ4_H_2983827168210 -#define LZ4_H_2983827168210 - -/* --- Dependency --- */ -#include /* size_t */ - - -/** - Introduction - - LZ4 is lossless compression algorithm, providing compression speed at 400 MB/s per core, - scalable with multi-cores CPU. It features an extremely fast decoder, with speed in - multiple GB/s per core, typically reaching RAM speed limits on multi-core systems. - - The LZ4 compression library provides in-memory compression and decompression functions. - Compression can be done in: - - a single step (described as Simple Functions) - - a single step, reusing a context (described in Advanced Functions) - - unbounded multiple steps (described as Streaming compression) - - lz4.h provides block compression functions. It gives full buffer control to user. - Decompressing an lz4-compressed block also requires metadata (such as compressed size). - Each application is free to encode such metadata in whichever way it wants. - - An additional format, called LZ4 frame specification (doc/lz4_Frame_format.md), - take care of encoding standard metadata alongside LZ4-compressed blocks. - If your application requires interoperability, it's recommended to use it. - A library is provided to take care of it, see lz4frame.h. -*/ - -/*^*************************************************************** -* Export parameters -*****************************************************************/ -/* -* LZ4_DLL_EXPORT : -* Enable exporting of functions when building a Windows DLL -* LZ4LIB_VISIBILITY : -* Control library symbols visibility. -*/ -#ifndef LZ4LIB_VISIBILITY -# if defined(__GNUC__) && (__GNUC__ >= 4) -# define LZ4LIB_VISIBILITY __attribute__ ((visibility ("default"))) -# else -# define LZ4LIB_VISIBILITY -# endif -#endif -#if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1) -# define LZ4LIB_API __declspec(dllexport) LZ4LIB_VISIBILITY -#elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1) -# define LZ4LIB_API __declspec(dllimport) LZ4LIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/ -#else -# define LZ4LIB_API LZ4LIB_VISIBILITY -#endif - -/*------ Version ------*/ -#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */ -#define LZ4_VERSION_MINOR 8 /* for new (non-breaking) interface capabilities */ -#define LZ4_VERSION_RELEASE 2 /* for tweaks, bug-fixes, or development */ - -#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE) - -#define LZ4_LIB_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE -#define LZ4_QUOTE(str) #str -#define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str) -#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION) - -LZ4LIB_API int LZ4_versionNumber (void); /**< library version number; useful to check dll version */ -LZ4LIB_API const char* LZ4_versionString (void); /**< library version string; unseful to check dll version */ - - -/*-************************************ -* Tuning parameter -**************************************/ -/*! - * LZ4_MEMORY_USAGE : - * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.) - * Increasing memory usage improves compression ratio - * Reduced memory usage may improve speed, thanks to cache effect - * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache - */ -#ifndef LZ4_MEMORY_USAGE -# define LZ4_MEMORY_USAGE 14 -#endif - -/*-************************************ -* Simple Functions -**************************************/ -/*! LZ4_compress_default() : - Compresses 'srcSize' bytes from buffer 'src' - into already allocated 'dst' buffer of size 'dstCapacity'. - Compression is guaranteed to succeed if 'dstCapacity' >= LZ4_compressBound(srcSize). - It also runs faster, so it's a recommended setting. - If the function cannot compress 'src' into a more limited 'dst' budget, - compression stops *immediately*, and the function result is zero. - Note : as a consequence, 'dst' content is not valid. - Note 2 : This function is protected against buffer overflow scenarios (never writes outside 'dst' buffer, nor read outside 'source' buffer). - srcSize : max supported value is LZ4_MAX_INPUT_SIZE. - dstCapacity : size of buffer 'dst' (which must be already allocated) - return : the number of bytes written into buffer 'dst' (necessarily <= dstCapacity) - or 0 if compression fails */ -LZ4LIB_API int LZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacity); - -/*! LZ4_decompress_safe() : - compressedSize : is the exact complete size of the compressed block. - dstCapacity : is the size of destination buffer, which must be already allocated. - return : the number of bytes decompressed into destination buffer (necessarily <= dstCapacity) - If destination buffer is not large enough, decoding will stop and output an error code (negative value). - If the source stream is detected malformed, the function will stop decoding and return a negative result. - This function is protected against malicious data packets. -*/ -LZ4LIB_API int LZ4_decompress_safe (const char* src, char* dst, int compressedSize, int dstCapacity); - - -/*-************************************ -* Advanced Functions -**************************************/ -#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */ -#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16) - -/*! -LZ4_compressBound() : - Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible) - This function is primarily useful for memory allocation purposes (destination buffer size). - Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example). - Note that LZ4_compress_default() compresses faster when dstCapacity is >= LZ4_compressBound(srcSize) - inputSize : max supported value is LZ4_MAX_INPUT_SIZE - return : maximum output size in a "worst case" scenario - or 0, if input size is incorrect (too large or negative) -*/ -LZ4LIB_API int LZ4_compressBound(int inputSize); - -/*! -LZ4_compress_fast() : - Same as LZ4_compress_default(), but allows selection of "acceleration" factor. - The larger the acceleration value, the faster the algorithm, but also the lesser the compression. - It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed. - An acceleration value of "1" is the same as regular LZ4_compress_default() - Values <= 0 will be replaced by ACCELERATION_DEFAULT (currently == 1, see lz4.c). -*/ -LZ4LIB_API int LZ4_compress_fast (const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); - - -/*! -LZ4_compress_fast_extState() : - Same compression function, just using an externally allocated memory space to store compression state. - Use LZ4_sizeofState() to know how much memory must be allocated, - and allocate it on 8-bytes boundaries (using malloc() typically). - Then, provide it as 'void* state' to compression function. -*/ -LZ4LIB_API int LZ4_sizeofState(void); -LZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); - - -/*! -LZ4_compress_destSize() : - Reverse the logic : compresses as much data as possible from 'src' buffer - into already allocated buffer 'dst' of size 'targetDestSize'. - This function either compresses the entire 'src' content into 'dst' if it's large enough, - or fill 'dst' buffer completely with as much data as possible from 'src'. - *srcSizePtr : will be modified to indicate how many bytes where read from 'src' to fill 'dst'. - New value is necessarily <= old value. - return : Nb bytes written into 'dst' (necessarily <= targetDestSize) - or 0 if compression fails -*/ -LZ4LIB_API int LZ4_compress_destSize (const char* src, char* dst, int* srcSizePtr, int targetDstSize); - - -/*! -LZ4_decompress_fast() : **unsafe!** -This function is a bit faster than LZ4_decompress_safe(), -but doesn't provide any security guarantee. - originalSize : is the uncompressed size to regenerate - Destination buffer must be already allocated, and its size must be >= 'originalSize' bytes. - return : number of bytes read from source buffer (== compressed size). - If the source stream is detected malformed, the function stops decoding and return a negative result. - note : This function respects memory boundaries for *properly formed* compressed data. - However, it does not provide any protection against malicious input. - It also doesn't know 'src' size, and implies it's >= compressed size. - Use this function in trusted environment **only**. -*/ -LZ4LIB_API int LZ4_decompress_fast (const char* src, char* dst, int originalSize); - -/*! -LZ4_decompress_safe_partial() : - This function decompress a compressed block of size 'srcSize' at position 'src' - into destination buffer 'dst' of size 'dstCapacity'. - The function will decompress a minimum of 'targetOutputSize' bytes, and stop after that. - However, it's not accurate, and may write more than 'targetOutputSize' (but always <= dstCapacity). - @return : the number of bytes decoded in the destination buffer (necessarily <= dstCapacity) - Note : this number can also be < targetOutputSize, if compressed block contains less data. - Therefore, always control how many bytes were decoded. - If source stream is detected malformed, function returns a negative result. - This function is protected against malicious data packets. -*/ -LZ4LIB_API int LZ4_decompress_safe_partial (const char* src, char* dst, int srcSize, int targetOutputSize, int dstCapacity); - - -/*-********************************************* -* Streaming Compression Functions -***********************************************/ -typedef union LZ4_stream_u LZ4_stream_t; /* incomplete type (defined later) */ - -/*! LZ4_createStream() and LZ4_freeStream() : - * LZ4_createStream() will allocate and initialize an `LZ4_stream_t` structure. - * LZ4_freeStream() releases its memory. - */ -LZ4LIB_API LZ4_stream_t* LZ4_createStream(void); -LZ4LIB_API int LZ4_freeStream (LZ4_stream_t* streamPtr); - -/*! LZ4_resetStream() : - * An LZ4_stream_t structure can be allocated once and re-used multiple times. - * Use this function to start compressing a new stream. - */ -LZ4LIB_API void LZ4_resetStream (LZ4_stream_t* streamPtr); - -/*! LZ4_loadDict() : - * Use this function to load a static dictionary into LZ4_stream_t. - * Any previous data will be forgotten, only 'dictionary' will remain in memory. - * Loading a size of 0 is allowed, and is the same as reset. - * @return : dictionary size, in bytes (necessarily <= 64 KB) - */ -LZ4LIB_API int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize); - -/*! LZ4_compress_fast_continue() : - * Compress 'src' content using data from previously compressed blocks, for better compression ratio. - * 'dst' buffer must be already allocated. - * If dstCapacity >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster. - * - * Important : The previous 64KB of compressed data is assumed to remain present and unmodified in memory! - * - * Special 1 : When input is a double-buffer, they can have any size, including < 64 KB. - * Make sure that buffers are separated by at least one byte. - * This way, each block only depends on previous block. - * Special 2 : If input buffer is a ring-buffer, it can have any size, including < 64 KB. - * - * @return : size of compressed block - * or 0 if there is an error (typically, cannot fit into 'dst'). - * After an error, the stream status is invalid, it can only be reset or freed. - */ -LZ4LIB_API int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); - -/*! LZ4_saveDict() : - * If last 64KB data cannot be guaranteed to remain available at its current memory location, - * save it into a safer place (char* safeBuffer). - * This is schematically equivalent to a memcpy() followed by LZ4_loadDict(), - * but is much faster, because LZ4_saveDict() doesn't need to rebuild tables. - * @return : saved dictionary size in bytes (necessarily <= maxDictSize), or 0 if error. - */ -LZ4LIB_API int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int maxDictSize); - - -/*-********************************************** -* Streaming Decompression Functions -* Bufferless synchronous API -************************************************/ -typedef union LZ4_streamDecode_u LZ4_streamDecode_t; /* incomplete type (defined later) */ - -/*! LZ4_createStreamDecode() and LZ4_freeStreamDecode() : - * creation / destruction of streaming decompression tracking structure. - * A tracking structure can be re-used multiple times sequentially. */ -LZ4LIB_API LZ4_streamDecode_t* LZ4_createStreamDecode(void); -LZ4LIB_API int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream); - -/*! LZ4_setStreamDecode() : - * An LZ4_streamDecode_t structure can be allocated once and re-used multiple times. - * Use this function to start decompression of a new stream of blocks. - * A dictionary can optionnally be set. Use NULL or size 0 for a reset order. - * @return : 1 if OK, 0 if error - */ -LZ4LIB_API int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize); - -/*! LZ4_decompress_*_continue() : - * These decoding functions allow decompression of consecutive blocks in "streaming" mode. - * A block is an unsplittable entity, it must be presented entirely to a decompression function. - * Decompression functions only accept one block at a time. - * The last 64KB of previously decoded data *must* remain available and unmodified at the memory position where they were decoded. - * If less than 64KB of data has been decoded all the data must be present. - * - * Special : if application sets a ring buffer for decompression, it must respect one of the following conditions : - * - Exactly same size as encoding buffer, with same update rule (block boundaries at same positions) - * In which case, the decoding & encoding ring buffer can have any size, including very small ones ( < 64 KB). - * - Larger than encoding buffer, by a minimum of maxBlockSize more bytes. - * maxBlockSize is implementation dependent. It's the maximum size of any single block. - * In which case, encoding and decoding buffers do not need to be synchronized, - * and encoding ring buffer can have any size, including small ones ( < 64 KB). - * - _At least_ 64 KB + 8 bytes + maxBlockSize. - * In which case, encoding and decoding buffers do not need to be synchronized, - * and encoding ring buffer can have any size, including larger than decoding buffer. - * Whenever these conditions are not possible, save the last 64KB of decoded data into a safe buffer, - * and indicate where it is saved using LZ4_setStreamDecode() before decompressing next block. -*/ -LZ4LIB_API int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int srcSize, int dstCapacity); -LZ4LIB_API int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int originalSize); - - -/*! LZ4_decompress_*_usingDict() : - * These decoding functions work the same as - * a combination of LZ4_setStreamDecode() followed by LZ4_decompress_*_continue() - * They are stand-alone, and don't need an LZ4_streamDecode_t structure. - */ -LZ4LIB_API int LZ4_decompress_safe_usingDict (const char* src, char* dst, int srcSize, int dstCapcity, const char* dictStart, int dictSize); -LZ4LIB_API int LZ4_decompress_fast_usingDict (const char* src, char* dst, int originalSize, const char* dictStart, int dictSize); - - -/*^********************************************** - * !!!!!! STATIC LINKING ONLY !!!!!! - ***********************************************/ - -/*-************************************ - * Unstable declarations - ************************************** - * Declarations in this section should be considered unstable. - * Use at your own peril, etc., etc. - * They may be removed in the future. - * Their signatures may change. - **************************************/ - -#ifdef LZ4_STATIC_LINKING_ONLY - -/*! LZ4_resetStream_fast() : - * When an LZ4_stream_t is known to be in a internally coherent state, - * it can often be prepared for a new compression with almost no work, only - * sometimes falling back to the full, expensive reset that is always required - * when the stream is in an indeterminate state (i.e., the reset performed by - * LZ4_resetStream()). - * - * LZ4_streams are guaranteed to be in a valid state when: - * - returned from LZ4_createStream() - * - reset by LZ4_resetStream() - * - memset(stream, 0, sizeof(LZ4_stream_t)) - * - the stream was in a valid state and was reset by LZ4_resetStream_fast() - * - the stream was in a valid state and was then used in any compression call - * that returned success - * - the stream was in an indeterminate state and was used in a compression - * call that fully reset the state (LZ4_compress_fast_extState()) and that - * returned success - */ -LZ4LIB_API void LZ4_resetStream_fast (LZ4_stream_t* streamPtr); - -/*! LZ4_compress_fast_extState_fastReset() : - * A variant of LZ4_compress_fast_extState(). - * - * Using this variant avoids an expensive initialization step. It is only safe - * to call if the state buffer is known to be correctly initialized already - * (see above comment on LZ4_resetStream_fast() for a definition of "correctly - * initialized"). From a high level, the difference is that this function - * initializes the provided state with a call to LZ4_resetStream_fast() while - * LZ4_compress_fast_extState() starts with a call to LZ4_resetStream(). - */ -LZ4LIB_API int LZ4_compress_fast_extState_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); - -/*! LZ4_attach_dictionary() : - * This is an experimental API that allows for the efficient use of a - * static dictionary many times. - * - * Rather than re-loading the dictionary buffer into a working context before - * each compression, or copying a pre-loaded dictionary's LZ4_stream_t into a - * working LZ4_stream_t, this function introduces a no-copy setup mechanism, - * in which the working stream references the dictionary stream in-place. - * - * Several assumptions are made about the state of the dictionary stream. - * Currently, only streams which have been prepared by LZ4_loadDict() should - * be expected to work. - * - * Alternatively, the provided dictionary stream pointer may be NULL, in which - * case any existing dictionary stream is unset. - * - * If a dictionary is provided, it replaces any pre-existing stream history. - * The dictionary contents are the only history that can be referenced and - * logically immediately precede the data compressed in the first subsequent - * compression call. - * - * The dictionary will only remain attached to the working stream through the - * first compression call, at the end of which it is cleared. The dictionary - * stream (and source buffer) must remain in-place / accessible / unchanged - * through the completion of the first compression call on the stream. - */ -LZ4LIB_API void LZ4_attach_dictionary(LZ4_stream_t *working_stream, const LZ4_stream_t *dictionary_stream); - -#endif - -/*-************************************ - * Private definitions - ************************************** - * Do not use these definitions. - * They are exposed to allow static allocation of `LZ4_stream_t` and `LZ4_streamDecode_t`. - * Using these definitions will expose code to API and/or ABI break in future versions of the library. - **************************************/ -#define LZ4_HASHLOG (LZ4_MEMORY_USAGE-2) -#define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE) -#define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */ - -#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) -#include - -typedef struct LZ4_stream_t_internal LZ4_stream_t_internal; -struct LZ4_stream_t_internal { - uint32_t hashTable[LZ4_HASH_SIZE_U32]; - uint32_t currentOffset; - uint16_t initCheck; - uint16_t tableType; - const uint8_t* dictionary; - const LZ4_stream_t_internal* dictCtx; - uint32_t dictSize; -}; - -typedef struct { - const uint8_t* externalDict; - size_t extDictSize; - const uint8_t* prefixEnd; - size_t prefixSize; -} LZ4_streamDecode_t_internal; - -#else - -typedef struct LZ4_stream_t_internal LZ4_stream_t_internal; -struct LZ4_stream_t_internal { - unsigned int hashTable[LZ4_HASH_SIZE_U32]; - unsigned int currentOffset; - unsigned short initCheck; - unsigned short tableType; - const unsigned char* dictionary; - const LZ4_stream_t_internal* dictCtx; - unsigned int dictSize; -}; - -typedef struct { - const unsigned char* externalDict; - size_t extDictSize; - const unsigned char* prefixEnd; - size_t prefixSize; -} LZ4_streamDecode_t_internal; - -#endif - -/*! - * LZ4_stream_t : - * information structure to track an LZ4 stream. - * init this structure before first use. - * note : only use in association with static linking ! - * this definition is not API/ABI safe, - * it may change in a future version ! - */ -#define LZ4_STREAMSIZE_U64 ((1 << (LZ4_MEMORY_USAGE-3)) + 4) -#define LZ4_STREAMSIZE (LZ4_STREAMSIZE_U64 * sizeof(unsigned long long)) -union LZ4_stream_u { - unsigned long long table[LZ4_STREAMSIZE_U64]; - LZ4_stream_t_internal internal_donotuse; -} ; /* previously typedef'd to LZ4_stream_t */ - - -/*! - * LZ4_streamDecode_t : - * information structure to track an LZ4 stream during decompression. - * init this structure using LZ4_setStreamDecode (or memset()) before first use - * note : only use in association with static linking ! - * this definition is not API/ABI safe, - * and may change in a future version ! - */ -#define LZ4_STREAMDECODESIZE_U64 4 -#define LZ4_STREAMDECODESIZE (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long)) -union LZ4_streamDecode_u { - unsigned long long table[LZ4_STREAMDECODESIZE_U64]; - LZ4_streamDecode_t_internal internal_donotuse; -} ; /* previously typedef'd to LZ4_streamDecode_t */ - - -/*-************************************ -* Obsolete Functions -**************************************/ - -/*! Deprecation warnings - Should deprecation warnings be a problem, - it is generally possible to disable them, - typically with -Wno-deprecated-declarations for gcc - or _CRT_SECURE_NO_WARNINGS in Visual. - Otherwise, it's also possible to define LZ4_DISABLE_DEPRECATE_WARNINGS */ -#ifdef LZ4_DISABLE_DEPRECATE_WARNINGS -# define LZ4_DEPRECATED(message) /* disable deprecation warnings */ -#else -# define LZ4_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -# if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */ -# define LZ4_DEPRECATED(message) [[deprecated(message)]] -# elif (LZ4_GCC_VERSION >= 405) || defined(__clang__) -# define LZ4_DEPRECATED(message) __attribute__((deprecated(message))) -# elif (LZ4_GCC_VERSION >= 301) -# define LZ4_DEPRECATED(message) __attribute__((deprecated)) -# elif defined(_MSC_VER) -# define LZ4_DEPRECATED(message) __declspec(deprecated(message)) -# else -# pragma message("WARNING: You need to implement LZ4_DEPRECATED for this compiler") -# define LZ4_DEPRECATED(message) -# endif -#endif /* LZ4_DISABLE_DEPRECATE_WARNINGS */ - -/* Obsolete compression functions */ -LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress (const char* source, char* dest, int sourceSize); -LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress_limitedOutput (const char* source, char* dest, int sourceSize, int maxOutputSize); -LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize); -LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize); -LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize); -LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize); - -/* Obsolete decompression functions */ -LZ4_DEPRECATED("use LZ4_decompress_fast() instead") LZ4LIB_API int LZ4_uncompress (const char* source, char* dest, int outputSize); -LZ4_DEPRECATED("use LZ4_decompress_safe() instead") LZ4LIB_API int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize); - -/* Obsolete streaming functions; degraded functionality; do not use! - * - * In order to perform streaming compression, these functions depended on data - * that is no longer tracked in the state. They have been preserved as well as - * possible: using them will still produce a correct output. However, they don't - * actually retain any history between compression calls. The compression ratio - * achieved will therefore be no better than compressing each chunk - * independently. - */ -LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API void* LZ4_create (char* inputBuffer); -LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API int LZ4_sizeofStreamState(void); -LZ4_DEPRECATED("Use LZ4_resetStream() instead") LZ4LIB_API int LZ4_resetStreamState(void* state, char* inputBuffer); -LZ4_DEPRECATED("Use LZ4_saveDict() instead") LZ4LIB_API char* LZ4_slideInputBuffer (void* state); - -/* Obsolete streaming decoding functions */ -LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") LZ4LIB_API int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize); -LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") LZ4LIB_API int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize); - -#endif /* LZ4_H_2983827168210 */ - - -#if defined (__cplusplus) -} -#endif diff --git a/stratosphere/pm/source/pm_boot2.cpp b/stratosphere/pm/source/pm_boot2.cpp index abca134ce..dfa89f4ad 100644 --- a/stratosphere/pm/source/pm_boot2.cpp +++ b/stratosphere/pm/source/pm_boot2.cpp @@ -29,7 +29,6 @@ #include "pm_registration.hpp" #include "pm_boot_mode.hpp" -static std::vector g_launched_titles; static bool IsHexadecimal(const char *str) { while (*str) { @@ -42,23 +41,11 @@ static bool IsHexadecimal(const char *str) { return true; } -static bool HasLaunchedTitle(u64 title_id) { - return std::find(g_launched_titles.begin(), g_launched_titles.end(), title_id) != g_launched_titles.end(); -} - -static void SetLaunchedTitle(u64 title_id) { - g_launched_titles.push_back(title_id); -} - -static void ClearLaunchedTitles() { - g_launched_titles.clear(); -} - static void LaunchTitle(u64 title_id, FsStorageId storage_id, u32 launch_flags, u64 *pid) { u64 local_pid = 0; /* Don't launch a title twice during boot2. */ - if (HasLaunchedTitle(title_id)) { + if (Registration::HasLaunchedTitle(title_id)) { return; } @@ -80,8 +67,6 @@ static void LaunchTitle(u64 title_id, FsStorageId storage_id, u32 launch_flags, if (pid) { *pid = local_pid; } - - SetLaunchedTitle(title_id); } static bool GetGpioPadLow(GpioPadName pad) { @@ -199,9 +184,6 @@ void EmbeddedBoot2::Main() { /* Wait until fs.mitm has installed itself. We want this to happen as early as possible. */ WaitForMitm("fsp-srv"); - /* Clear titles. */ - ClearLaunchedTitles(); - /* psc, bus, pcv is the minimal set of required titles to get SD card. */ /* bus depends on pcie, and pcv depends on settings. */ /* Launch psc. */ @@ -260,7 +242,7 @@ void EmbeddedBoot2::Main() { while ((ent = readdir(titles_dir)) != NULL) { if (strlen(ent->d_name) == 0x10 && IsHexadecimal(ent->d_name)) { u64 title_id = (u64)strtoul(ent->d_name, NULL, 16); - if (HasLaunchedTitle(title_id)) { + if (Registration::HasLaunchedTitle(title_id)) { continue; } char title_path[FS_MAX_PATH] = {0}; @@ -290,7 +272,4 @@ void EmbeddedBoot2::Main() { /* We no longer need the SD card. */ fsdevUnmountAll(); - - /* Free the memory used to track what boot2 launches. */ - ClearLaunchedTitles(); } diff --git a/stratosphere/pm/source/pm_info.cpp b/stratosphere/pm/source/pm_info.cpp index 83d409cda..d40888225 100644 --- a/stratosphere/pm/source/pm_info.cpp +++ b/stratosphere/pm/source/pm_info.cpp @@ -15,6 +15,9 @@ */ #include +#include +#include + #include "pm_registration.hpp" #include "pm_info.hpp" @@ -22,9 +25,26 @@ Result InformationService::GetTitleId(Out tid, u64 pid) { std::scoped_lock lk(Registration::GetProcessList()); std::shared_ptr proc = Registration::GetProcess(pid); - if (proc != NULL) { - tid.SetValue(proc->tid_sid.title_id); - return ResultSuccess; + if (proc == NULL) { + return ResultPmProcessNotFound; } - return ResultPmProcessNotFound; + + tid.SetValue(proc->tid_sid.title_id); + return ResultSuccess; +} + +Result InformationService::AtmosphereGetProcessId(Out pid, u64 tid) { + std::scoped_lock lk(Registration::GetProcessList()); + + std::shared_ptr proc = Registration::GetProcessByTitleId(tid); + if (proc == nullptr) { + return ResultPmProcessNotFound; + } + + pid.SetValue(proc->pid); + return ResultSuccess; +} + +Result InformationService::AtmosphereHasLaunchedTitle(Out out, u64 tid) { + return sts::ldr::pm::HasLaunchedTitle(out.GetPointer(), sts::ncm::TitleId{tid}); } diff --git a/stratosphere/pm/source/pm_info.hpp b/stratosphere/pm/source/pm_info.hpp index 8643e2a9c..9420bc5b6 100644 --- a/stratosphere/pm/source/pm_info.hpp +++ b/stratosphere/pm/source/pm_info.hpp @@ -20,14 +20,23 @@ enum InformationCmd { Information_Cmd_GetTitleId = 0, + + Information_Cmd_AtmosphereGetProcessId = 65000, + Information_Cmd_AtmosphereHasCreatedTitle = 65001, }; class InformationService final : public IServiceObject { private: /* Actual commands. */ Result GetTitleId(Out tid, u64 pid); + + /* Atmosphere commands. */ + Result AtmosphereGetProcessId(Out pid, u64 tid); + Result AtmosphereHasLaunchedTitle(Out out, u64 tid); public: DEFINE_SERVICE_DISPATCH_TABLE { MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), }; }; diff --git a/stratosphere/pm/source/pm_main.cpp b/stratosphere/pm/source/pm_main.cpp index df6507b14..8ef2b8770 100644 --- a/stratosphere/pm/source/pm_main.cpp +++ b/stratosphere/pm/source/pm_main.cpp @@ -36,7 +36,7 @@ extern "C" { u32 __nx_applet_type = AppletType_None; - #define INNER_HEAP_SIZE 0x30000 + #define INNER_HEAP_SIZE 0x40000 size_t nx_inner_heap_size = INNER_HEAP_SIZE; char nx_inner_heap[INNER_HEAP_SIZE]; @@ -100,12 +100,12 @@ void __appInit(void) { DoWithSmSession([&]() { R_ASSERT(fsprInitialize()); - R_ASSERT(smManagerInitialize()); /* This works around a bug with process permissions on < 4.0.0. */ RegisterPrivilegedProcessesWithFs(); /* Use AMS manager extension to tell SM that FS has been worked around. */ + R_ASSERT(smManagerInitialize()); R_ASSERT(sts::sm::manager::EndInitialDefers()); R_ASSERT(lrInitialize()); @@ -145,7 +145,7 @@ int main(int argc, char **argv) s_server_manager.AddWaitable(new ServiceServer("pm:shell", 3)); s_server_manager.AddWaitable(new ServiceServer("pm:dmnt", 3)); s_server_manager.AddWaitable(new ServiceServer("pm:bm", 6)); - s_server_manager.AddWaitable(new ServiceServer("pm:info", 3)); + s_server_manager.AddWaitable(new ServiceServer("pm:info", 19)); /* Loop forever, servicing our services. */ s_server_manager.Process(); diff --git a/stratosphere/pm/source/pm_registration.cpp b/stratosphere/pm/source/pm_registration.cpp index f5943addd..fd572272e 100644 --- a/stratosphere/pm/source/pm_registration.cpp +++ b/stratosphere/pm/source/pm_registration.cpp @@ -13,9 +13,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + +#include #include #include -#include +#include +#include #include "pm_registration.hpp" #include "pm_resource_limits.hpp" @@ -492,6 +495,12 @@ Handle Registration::GetBootFinishedEventHandle() { return g_boot_finished_event->GetHandle(); } +bool Registration::HasLaunchedTitle(u64 title_id) { + bool has_launched = false; + R_ASSERT(sts::ldr::pm::HasLaunchedTitle(&has_launched, sts::ncm::TitleId{title_id})); + return has_launched; +} + void Registration::SignalBootFinished() { g_boot_finished_event->Signal(); } diff --git a/stratosphere/pm/source/pm_registration.hpp b/stratosphere/pm/source/pm_registration.hpp index 6eb07dd1b..d8efcf529 100644 --- a/stratosphere/pm/source/pm_registration.hpp +++ b/stratosphere/pm/source/pm_registration.hpp @@ -204,6 +204,8 @@ class Registration { static Result LaunchProcess(u64 title_id, FsStorageId storage_id, u64 launch_flags, u64 *out_pid); static Result LaunchProcessByTidSid(TidSid tid_sid, u64 launch_flags, u64 *out_pid); + static bool HasLaunchedTitle(u64 title_id); + static void SignalBootFinished(); static bool HasApplicationProcess(std::shared_ptr *out = nullptr);