Compare commits

...

10 Commits

Author SHA1 Message Date
Michael Scire
838492c84c sysupdater: minor api fixes (now verified working on hardware) 2020-06-27 07:33:36 -07:00
Michael Scire
76fa4db2ed sysupdater: implement (untested) rest of the api 2020-06-26 19:22:50 -07:00
Michael Scire
015537f9bf sysupdater: Add ValidateUpdate, begin implementing Async logic 2020-06-26 11:36:38 -07:00
Michael Scire
d47e9ec9fd sysupdater: don't do unnecessary work when parsing update 2020-06-26 05:05:24 -07:00
Michael Scire
4291d81642 sysupdater: make GetUpdateInformation work on hardware. 2020-06-26 04:34:26 -07:00
Michael Scire
dc9c9284e2 sysupdater: implement content meta mounting 2020-06-26 02:24:52 -07:00
Michael Scire
aa9ba17986 sysupdater: begin implementing api 2020-06-25 21:59:59 -07:00
Michael Scire
f197b88dd7 fs: update IndirectStorage for 10.x, bugfixes 2020-06-25 17:36:05 -07:00
Michael Scire
f594de0081 ams: add -Wno-format-truncation to flags 2020-06-25 17:32:34 -07:00
Adubbz
9003ad6e49 ncm: fix exfat system updates 2020-06-22 16:15:56 -07:00
63 changed files with 2417 additions and 63 deletions

View File

@@ -17,8 +17,10 @@ endif
export ATMOSPHERE_DEFINES := -DATMOSPHERE export ATMOSPHERE_DEFINES := -DATMOSPHERE
export ATMOSPHERE_SETTINGS := -fPIE -g export ATMOSPHERE_SETTINGS := -fPIE -g
export ATMOSPHERE_CFLAGS := -Wall -ffunction-sections -fdata-sections -fno-strict-aliasing -fwrapv \ export ATMOSPHERE_CFLAGS := -Wall -ffunction-sections -fdata-sections -fno-strict-aliasing -fwrapv \
-fno-asynchronous-unwind-tables -fno-unwind-tables -fno-stack-protector -fno-asynchronous-unwind-tables -fno-unwind-tables -fno-stack-protector \
-Wno-format-truncation
export ATMOSPHERE_CXXFLAGS := -fno-rtti -fno-exceptions -std=gnu++20 export ATMOSPHERE_CXXFLAGS := -fno-rtti -fno-exceptions -std=gnu++20
export ATMOSPHERE_ASFLAGS := export ATMOSPHERE_ASFLAGS :=

View File

@@ -45,6 +45,7 @@
#include <stratosphere/cfg.hpp> #include <stratosphere/cfg.hpp>
#include <stratosphere/dmnt.hpp> #include <stratosphere/dmnt.hpp>
#include <stratosphere/erpt.hpp> #include <stratosphere/erpt.hpp>
#include <stratosphere/err.hpp>
#include <stratosphere/fatal.hpp> #include <stratosphere/fatal.hpp>
#include <stratosphere/hid.hpp> #include <stratosphere/hid.hpp>
#include <stratosphere/hos.hpp> #include <stratosphere/hos.hpp>

View File

@@ -57,10 +57,12 @@ namespace ams::impl {
AMS_DEFINE_SYSTEM_THREAD(-1, boot, Main); AMS_DEFINE_SYSTEM_THREAD(-1, boot, Main);
/* Mitm. */ /* Mitm. */
AMS_DEFINE_SYSTEM_THREAD(-7, mitm, InitializeThread); AMS_DEFINE_SYSTEM_THREAD(-7, mitm, InitializeThread);
AMS_DEFINE_SYSTEM_THREAD(-1, mitm_sf, QueryServerProcessThread); AMS_DEFINE_SYSTEM_THREAD(-1, mitm_sf, QueryServerProcessThread);
AMS_DEFINE_SYSTEM_THREAD(16, mitm_fs, RomFileSystemInitializeThread); AMS_DEFINE_SYSTEM_THREAD(16, mitm_fs, RomFileSystemInitializeThread);
AMS_DEFINE_SYSTEM_THREAD(21, mitm, DebugThrowThread); AMS_DEFINE_SYSTEM_THREAD(21, mitm, DebugThrowThread);
AMS_DEFINE_SYSTEM_THREAD(21, mitm_sysupdater, IpcServer);
AMS_DEFINE_SYSTEM_THREAD(21, mitm_sysupdater, AsyncPrepareSdCardUpdateTask);
/* boot2. */ /* boot2. */
AMS_DEFINE_SYSTEM_THREAD(20, boot2, Main); AMS_DEFINE_SYSTEM_THREAD(20, boot2, Main);
@@ -93,6 +95,7 @@ namespace ams::impl {
/* ns.*/ /* ns.*/
AMS_DEFINE_SYSTEM_THREAD(21, ns, ApplicationManagerIpcSession); AMS_DEFINE_SYSTEM_THREAD(21, ns, ApplicationManagerIpcSession);
AMS_DEFINE_SYSTEM_THREAD(21, nssrv, AsyncPrepareCardUpdateTask);
/* settings. */ /* settings. */
AMS_DEFINE_SYSTEM_THREAD(21, settings, Main); AMS_DEFINE_SYSTEM_THREAD(21, settings, Main);

View File

@@ -0,0 +1,19 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/err/err_error_context.hpp>

View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/sf/sf_buffer_tags.hpp>
namespace ams::err {
enum class ErrorContextType : u8 {
None = 0,
Http = 1,
FileSystem = 2,
WebMediaPlayer = 3,
LocalContentShare = 4,
};
struct PaddingErrorContext {
u8 padding[0x200 - 8];
};
struct ErrorContext : public sf::LargeData, public sf::PrefersMapAliasTransferMode {
ErrorContextType type;
u8 reserved[7];
union {
PaddingErrorContext padding;
};
};
static_assert(sizeof(ErrorContext) == 0x200);
static_assert(util::is_pod<ErrorContext>::value);
}

View File

@@ -24,6 +24,7 @@
#include <stratosphere/fs/fsa/fs_registrar.hpp> #include <stratosphere/fs/fsa/fs_registrar.hpp>
#include <stratosphere/fs/fs_remote_filesystem.hpp> #include <stratosphere/fs/fs_remote_filesystem.hpp>
#include <stratosphere/fs/fs_read_only_filesystem.hpp> #include <stratosphere/fs/fs_read_only_filesystem.hpp>
#include <stratosphere/fs/fs_shared_filesystem_holder.hpp>
#include <stratosphere/fs/fs_istorage.hpp> #include <stratosphere/fs/fs_istorage.hpp>
#include <stratosphere/fs/fs_substorage.hpp> #include <stratosphere/fs/fs_substorage.hpp>
#include <stratosphere/fs/fs_memory_storage.hpp> #include <stratosphere/fs/fs_memory_storage.hpp>

View File

@@ -0,0 +1,54 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/fs/fs_common.hpp>
#include <stratosphere/fs/impl/fs_newable.hpp>
#include <stratosphere/fs/fsa/fs_ifile.hpp>
#include <stratosphere/fs/fsa/fs_idirectory.hpp>
#include <stratosphere/fs/fsa/fs_ifilesystem.hpp>
namespace ams::fs {
class SharedFileSystemHolder : public fsa::IFileSystem, public impl::Newable {
NON_COPYABLE(SharedFileSystemHolder);
NON_MOVEABLE(SharedFileSystemHolder);
private:
std::shared_ptr<fsa::IFileSystem> fs;
public:
SharedFileSystemHolder(std::shared_ptr<fsa::IFileSystem> f) : fs(std::move(f)) { /* ... */ }
public:
virtual Result CreateFileImpl(const char *path, s64 size, int flags) override { return this->fs->CreateFile(path, size, flags); }
virtual Result DeleteFileImpl(const char *path) override { return this->fs->DeleteFile(path); }
virtual Result CreateDirectoryImpl(const char *path) override { return this->fs->CreateDirectory(path); }
virtual Result DeleteDirectoryImpl(const char *path) override { return this->fs->DeleteDirectory(path); }
virtual Result DeleteDirectoryRecursivelyImpl(const char *path) override { return this->fs->DeleteDirectoryRecursively(path); }
virtual Result RenameFileImpl(const char *old_path, const char *new_path) override { return this->fs->RenameFile(old_path, new_path); }
virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) override { return this->fs->RenameDirectory(old_path, new_path); }
virtual Result GetEntryTypeImpl(fs::DirectoryEntryType *out, const char *path) override { return this->fs->GetEntryType(out, path); }
virtual Result OpenFileImpl(std::unique_ptr<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) override { return this->fs->OpenFile(out_file, path, mode); }
virtual Result OpenDirectoryImpl(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const char *path, fs::OpenDirectoryMode mode) override { return this->fs->OpenDirectory(out_dir, path, mode); }
virtual Result CommitImpl() override { return this->fs->Commit(); }
virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) override { return this->fs->GetFreeSpaceSize(out, path); }
virtual Result GetTotalSpaceSizeImpl(s64 *out, const char *path) override { return this->fs->GetTotalSpaceSize(out, path); }
virtual Result CleanDirectoryRecursivelyImpl(const char *path) override { return this->fs->CleanDirectoryRecursively(path); }
/* These aren't accessible as commands. */
virtual Result CommitProvisionallyImpl(s64 counter) override { return this->fs->CommitProvisionally(counter); }
virtual Result RollbackImpl() override { return this->fs->Rollback(); }
virtual Result FlushImpl() override { return this->fs->Flush(); }
};
}

View File

@@ -133,7 +133,7 @@ namespace ams::fssystem {
virtual Result GetSize(s64 *out) override { virtual Result GetSize(s64 *out) override {
AMS_ASSERT(out != nullptr); AMS_ASSERT(out != nullptr);
*out = this->table.GetSize(); *out = this->table.GetEnd();
return ResultSuccess(); return ResultSuccess();
} }

View File

@@ -101,7 +101,16 @@ namespace ams::fssystem {
} }
if (needs_operate) { if (needs_operate) {
R_TRY(func(std::addressof(this->data_storage[cur_entry.storage_index]), cur_entry.GetPhysicalOffset() + data_offset, cur_offset, cur_size)); /* Get the current data storage's size. */
s64 cur_data_storage_size;
R_TRY(this->data_storage[cur_entry.storage_index].GetSize(std::addressof(cur_data_storage_size)));
/* Ensure that we remain within range. */
const auto cur_entry_phys_offset = cur_entry.GetPhysicalOffset();
R_UNLESS(0 <= cur_entry_phys_offset && cur_entry_phys_offset <= cur_data_storage_size, fs::ResultIndirectStorageCorrupted());
R_UNLESS(cur_entry_phys_offset + data_offset + cur_size <= cur_data_storage_size, fs::ResultIndirectStorageCorrupted());
R_TRY(func(std::addressof(this->data_storage[cur_entry.storage_index]), cur_entry_phys_offset + data_offset, cur_offset, cur_size));
} }
cur_offset += cur_size; cur_offset += cur_size;

View File

@@ -45,7 +45,7 @@ namespace ams::ncm {
}; };
} }
constexpr ContentMetaKey ToKey() { constexpr ContentMetaKey ToKey() const {
return ContentMetaKey::Make(this->id, this->version, this->type); return ContentMetaKey::Make(this->id, this->version, this->type);
} }
}; };

View File

@@ -318,12 +318,12 @@ namespace ams::ncm {
return reinterpret_cast<uintptr_t>(this->data); return reinterpret_cast<uintptr_t>(this->data);
} }
uintptr_t GetFirmwarVariationIdStartAddress() const { uintptr_t GetFirmwareVariationIdStartAddress() const {
return this->GetHeaderAddress() + sizeof(SystemUpdateMetaExtendedDataHeader); return this->GetHeaderAddress() + sizeof(SystemUpdateMetaExtendedDataHeader);
} }
uintptr_t GetFirmwareVariationIdAddress(size_t i) const { uintptr_t GetFirmwareVariationIdAddress(size_t i) const {
return this->GetFirmwarVariationIdStartAddress() + i * sizeof(FirmwareVariationId); return this->GetFirmwareVariationIdStartAddress() + i * sizeof(FirmwareVariationId);
} }
uintptr_t GetFirmwareVariationInfoStartAddress() const { uintptr_t GetFirmwareVariationInfoStartAddress() const {
@@ -331,7 +331,7 @@ namespace ams::ncm {
} }
uintptr_t GetFirmwareVariationInfoAddress(size_t i) const { uintptr_t GetFirmwareVariationInfoAddress(size_t i) const {
return this->GetFirmwarVariationIdStartAddress() + i * sizeof(FirmwareVariationInfo); return this->GetFirmwareVariationInfoStartAddress() + i * sizeof(FirmwareVariationInfo);
} }
uintptr_t GetContentMetaInfoStartAddress() const { uintptr_t GetContentMetaInfoStartAddress() const {

View File

@@ -23,8 +23,11 @@
namespace ams::ncm { namespace ams::ncm {
Result ReadContentMetaPath(AutoBuffer *out, const char *path); using MountContentMetaFunction = Result (*)(const char *mount_name, const char *path);
Result ReadContentMetaPath(AutoBuffer *out, const char *path);
Result ReadVariationContentMetaInfoList(s32 *out_count, std::unique_ptr<ContentMetaInfo[]> *out_meta_infos, const Path &path, FirmwareVariationId firmware_variation_id); Result ReadVariationContentMetaInfoList(s32 *out_count, std::unique_ptr<ContentMetaInfo[]> *out_meta_infos, const Path &path, FirmwareVariationId firmware_variation_id);
void SetMountContentMetaFunction(MountContentMetaFunction func);
} }

View File

@@ -29,6 +29,7 @@
#include <stratosphere/os/os_condition_variable.hpp> #include <stratosphere/os/os_condition_variable.hpp>
#include <stratosphere/os/os_sdk_mutex.hpp> #include <stratosphere/os/os_sdk_mutex.hpp>
#include <stratosphere/os/os_rw_lock.hpp> #include <stratosphere/os/os_rw_lock.hpp>
#include <stratosphere/os/os_transfer_memory.hpp>
#include <stratosphere/os/os_semaphore.hpp> #include <stratosphere/os/os_semaphore.hpp>
#include <stratosphere/os/os_event.hpp> #include <stratosphere/os/os_event.hpp>
#include <stratosphere/os/os_system_event.hpp> #include <stratosphere/os/os_system_event.hpp>

View File

@@ -70,6 +70,11 @@ namespace ams::os {
return h; return h;
} }
void Detach() {
const Handle h = this->Move();
AMS_UNUSED(h);
}
void Reset(Handle h) { void Reset(Handle h) {
ManagedHandle(h).Swap(*this); ManagedHandle(h).Swap(*this);
} }

View File

@@ -27,6 +27,8 @@ namespace ams::os {
constexpr inline s32 DefaultThreadPriority = ThreadPriorityRangeSize / 2; constexpr inline s32 DefaultThreadPriority = ThreadPriorityRangeSize / 2;
constexpr inline s32 LowestThreadPriority = ThreadPriorityRangeSize - 1; constexpr inline s32 LowestThreadPriority = ThreadPriorityRangeSize - 1;
constexpr inline s32 InvalidThreadPriority = 127;
constexpr inline s32 LowestSystemThreadPriority = 35; constexpr inline s32 LowestSystemThreadPriority = 35;
constexpr inline s32 HighestSystemThreadPriority = -12; constexpr inline s32 HighestSystemThreadPriority = -12;

View File

@@ -0,0 +1,78 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/os_transfer_memory_types.hpp>
#include <stratosphere/os/os_transfer_memory_api.hpp>
namespace ams::os {
class TransferMemory {
NON_COPYABLE(TransferMemory);
NON_MOVEABLE(TransferMemory);
private:
TransferMemoryType tmem;
public:
constexpr TransferMemory() : tmem{ .state = TransferMemoryType::State_NotInitialized } {
/* ... */
}
TransferMemory(void *address, size_t size, MemoryPermission perm) {
R_ABORT_UNLESS(CreateTransferMemory(std::addressof(this->tmem), address, size, perm));
}
TransferMemory(size_t size, Handle handle, bool managed) {
this->Attach(size, handle, managed);
}
~TransferMemory() {
if (this->tmem.state == TransferMemoryType::State_NotInitialized) {
return;
}
DestroyTransferMemory(std::addressof(this->tmem));
}
void Attach(size_t size, Handle handle, bool managed) {
AttachTransferMemory(std::addressof(this->tmem), size, handle, managed);
}
Handle Detach() {
return DetachTransferMemory(std::addressof(this->tmem));
}
Result Map(void **out, MemoryPermission owner_perm) {
return MapTransferMemory(out, std::addressof(this->tmem), owner_perm);
}
void Unmap() {
UnmapTransferMemory(std::addressof(this->tmem));
}
operator TransferMemoryType &() {
return this->tmem;
}
operator const TransferMemoryType &() const {
return this->tmem;
}
TransferMemoryType *GetBase() {
return std::addressof(this->tmem);
}
};
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/os_memory_permission.hpp>
namespace ams::os {
struct TransferMemoryType;
Result CreateTransferMemory(TransferMemoryType *tmem, void *address, size_t size, MemoryPermission perm);
Result AttachTransferMemory(TransferMemoryType *tmem, size_t size, Handle handle, bool managed);
Handle DetachTransferMemory(TransferMemoryType *tmem);
void DestroyTransferMemory(TransferMemoryType *tmem);
Result MapTransferMemory(void **out, TransferMemoryType *tmem, MemoryPermission owner_perm);
void UnmapTransferMemory(TransferMemoryType *tmem);
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/impl/os_internal_critical_section.hpp>
namespace ams::os {
struct TransferMemoryType {
enum State {
State_NotInitialized = 0,
State_Created = 1,
State_Mapped = 2,
State_Detached = 3,
};
u8 state;
bool handle_managed;
bool allocated;
void *address;
size_t size;
Handle handle;
mutable impl::InternalCriticalSectionStorage cs_transfer_memory;
};
static_assert(std::is_trivial<TransferMemoryType>::value);
}

View File

@@ -23,6 +23,7 @@
#include <stratosphere/settings/factory/settings_device_certificate.hpp> #include <stratosphere/settings/factory/settings_device_certificate.hpp>
#include <stratosphere/settings/system/settings_error_report.hpp> #include <stratosphere/settings/system/settings_error_report.hpp>
#include <stratosphere/settings/system/settings_firmware_version.hpp> #include <stratosphere/settings/system/settings_firmware_version.hpp>
#include <stratosphere/settings/system/settings_platform_region.hpp>
#include <stratosphere/settings/system/settings_product_model.hpp> #include <stratosphere/settings/system/settings_product_model.hpp>
#include <stratosphere/settings/system/settings_region.hpp> #include <stratosphere/settings/system/settings_region.hpp>
#include <stratosphere/settings/system/settings_serial_number.hpp> #include <stratosphere/settings/system/settings_serial_number.hpp>

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/settings/settings_types.hpp>
namespace ams::settings::system {
enum PlatformRegion {
PlatformRegion_Invalid = 0,
PlatformRegion_Global = 1,
PlatformRegion_China = 2,
};
PlatformRegion GetPlatformRegion();
}

View File

@@ -13,7 +13,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
namespace ams::sf { namespace ams::sf {

View File

@@ -86,22 +86,17 @@ namespace ams::emummc {
const Storage storage = static_cast<Storage>(g_exo_config.base_cfg.type); const Storage storage = static_cast<Storage>(g_exo_config.base_cfg.type);
g_is_emummc = g_exo_config.base_cfg.magic == StorageMagic && storage != Storage_Emmc; g_is_emummc = g_exo_config.base_cfg.magic == StorageMagic && storage != Storage_Emmc;
/* Format paths. Ignore string format warnings. */ /* Format paths. */
#pragma GCC diagnostic push if (storage == Storage_SdFile) {
#pragma GCC diagnostic ignored "-Wformat-truncation" std::snprintf(g_exo_config.file_cfg.path, sizeof(g_exo_config.file_cfg.path), "/%s", paths->file_path);
{ }
if (storage == Storage_SdFile) {
std::snprintf(g_exo_config.file_cfg.path, sizeof(g_exo_config.file_cfg.path), "/%s", paths->file_path); std::snprintf(g_exo_config.emu_dir_path, sizeof(g_exo_config.emu_dir_path), "/%s", paths->nintendo_path);
}
/* If we're emummc, implement default nintendo redirection path. */
std::snprintf(g_exo_config.emu_dir_path, sizeof(g_exo_config.emu_dir_path), "/%s", paths->nintendo_path); if (g_is_emummc && std::strcmp(g_exo_config.emu_dir_path, "/") == 0) {
std::snprintf(g_exo_config.emu_dir_path, sizeof(g_exo_config.emu_dir_path), "/emummc/Nintendo_%04x", g_exo_config.base_cfg.id);
/* If we're emummc, implement default nintendo redirection path. */
if (g_is_emummc && std::strcmp(g_exo_config.emu_dir_path, "/") == 0) {
std::snprintf(g_exo_config.emu_dir_path, sizeof(g_exo_config.emu_dir_path), "/emummc/Nintendo_%04x", g_exo_config.base_cfg.id);
}
} }
#pragma GCC diagnostic pop
} }
g_has_cached = true; g_has_cached = true;

View File

@@ -43,10 +43,9 @@ namespace ams::fs {
} }
Result MountSdCard(const char *name) { Result MountSdCard(const char *name) {
/* Validate the mount name. */ /* Validate the mount name. */
R_TRY(impl::CheckMountName(name)); R_TRY(impl::CheckMountNameAllowingReserved(name));
/* Open the SD card. This uses libnx bindings. */ /* Open the SD card. This uses libnx bindings. */
FsFileSystem fs; FsFileSystem fs;

View File

@@ -24,12 +24,12 @@ namespace ams::fssystem {
/* Official FS has a 4.5 MB exp heap, a 6 MB buffer pool, an 8 MB device buffer manager heap, and a 14 MB buffer manager heap. */ /* Official FS has a 4.5 MB exp heap, a 6 MB buffer pool, an 8 MB device buffer manager heap, and a 14 MB buffer manager heap. */
/* We don't need so much memory for ams.mitm (as we're servicing a much more limited context). */ /* We don't need so much memory for ams.mitm (as we're servicing a much more limited context). */
/* We'll give ourselves a 2.5 MB exp heap, a 2 MB buffer pool, a 2 MB device buffer manager heap, and a 2 MB buffer manager heap. */ /* We'll give ourselves a 1.5 MB exp heap, a 1 MB buffer pool, a 1 MB device buffer manager heap, and a 1 MB buffer manager heap. */
/* These numbers match signed-system-partition safe FS. */ /* These numbers are 1 MB less than signed-system-partition safe FS in all pools. */
constexpr size_t ExpHeapSize = 2_MB + 512_KB; constexpr size_t ExpHeapSize = 1_MB + 512_KB;
constexpr size_t BufferPoolSize = 2_MB; constexpr size_t BufferPoolSize = 1_MB;
constexpr size_t DeviceBufferSize = 2_MB; constexpr size_t DeviceBufferSize = 1_MB;
constexpr size_t BufferManagerHeapSize = 2_MB; constexpr size_t BufferManagerHeapSize = 1_MB;
constexpr size_t MaxCacheCount = 1024; constexpr size_t MaxCacheCount = 1024;
constexpr size_t BlockSize = 16_KB; constexpr size_t BlockSize = 16_KB;

View File

@@ -888,7 +888,7 @@ namespace ams::fssystem {
/* Set the storage holder's storages. */ /* Set the storage holder's storages. */
storage->SetStorage(0, original_storage.get(), 0, original_data_size); storage->SetStorage(0, original_storage.get(), 0, original_data_size);
storage->SetStorage(1, indirect_table_storage.get(), 0, indirect_data_size); storage->SetStorage(1, indirect_data_storage.get(), 0, indirect_data_size);
storage->Set(std::move(base_storage), std::move(original_storage), std::move(indirect_table_storage), std::move(indirect_data_storage)); storage->Set(std::move(base_storage), std::move(original_storage), std::move(indirect_table_storage), std::move(indirect_data_storage));
/* Set the indirect storage. */ /* Set the indirect storage. */

View File

@@ -26,12 +26,18 @@ namespace ams::ncm {
return impl::PathView(name).HasSuffix(".cnmt"); return impl::PathView(name).HasSuffix(".cnmt");
} }
Result MountContentMetaByRemoteFileSystemProxy(const char *mount_name, const char *path) {
return fs::MountContent(mount_name, path, fs::ContentType_Meta);
}
constinit MountContentMetaFunction g_mount_content_meta_func = MountContentMetaByRemoteFileSystemProxy;
} }
Result ReadContentMetaPath(AutoBuffer *out, const char *path) { Result ReadContentMetaPath(AutoBuffer *out, const char *path) {
/* Mount the content. */ /* Mount the content. */
auto mount_name = impl::CreateUniqueMountName(); auto mount_name = impl::CreateUniqueMountName();
R_TRY(fs::MountContent(mount_name.str, path, fs::ContentType_Meta)); R_TRY(g_mount_content_meta_func(mount_name.str, path));
ON_SCOPE_EXIT { fs::Unmount(mount_name.str); }; ON_SCOPE_EXIT { fs::Unmount(mount_name.str); };
/* Open the root directory. */ /* Open the root directory. */
@@ -156,4 +162,8 @@ namespace ams::ncm {
return ResultSuccess(); return ResultSuccess();
} }
void SetMountContentMetaFunction(MountContentMetaFunction func) {
g_mount_content_meta_func = func;
}
} }

View File

@@ -58,7 +58,7 @@ namespace ams::ncm {
R_TRY(this->data.Initialize(context_path)); R_TRY(this->data.Initialize(context_path));
/* Initialize PackageInstallTaskBase. */ /* Initialize PackageInstallTaskBase. */
u32 config = requires_exfat_driver ? InstallConfig_SystemUpdate : InstallConfig_SystemUpdate | InstallConfig_RequiresExFatDriver; u32 config = !requires_exfat_driver ? InstallConfig_SystemUpdate : InstallConfig_SystemUpdate | InstallConfig_RequiresExFatDriver;
R_TRY(PackageInstallTaskBase::Initialize(package_root, buffer, buffer_size, StorageId::BuiltInSystem, std::addressof(this->data), config)); R_TRY(PackageInstallTaskBase::Initialize(package_root, buffer, buffer_size, StorageId::BuiltInSystem, std::addressof(this->data), config));
/* Cancel guards. */ /* Cancel guards. */

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::os::impl {
class TransferMemoryImpl {
public:
static Result Create(Handle *out, void *address, size_t size, MemoryPermission perm);
static void Close(Handle handle);
static Result Map(void **out, Handle handle, void *address, size_t size, MemoryPermission owner_perm);
static void Unmap(Handle handle, void *address, size_t size);
};
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "os_transfer_memory_impl.hpp"
namespace ams::os::impl {
namespace {
svc::MemoryPermission ConvertToSvcMemoryPermission(os::MemoryPermission perm) {
switch (perm) {
case os::MemoryPermission_None: return svc::MemoryPermission_None;
case os::MemoryPermission_ReadOnly: return svc::MemoryPermission_Read;
case os::MemoryPermission_WriteOnly: return svc::MemoryPermission_Write;
case os::MemoryPermission_ReadWrite: return svc::MemoryPermission_ReadWrite;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
}
Result TransferMemoryImpl::Create(Handle *out, void *address, size_t size, MemoryPermission perm) {
/* Convert memory permission. */
auto svc_perm = ConvertToSvcMemoryPermission(perm);
/* Create the memory. */
svc::Handle handle;
R_TRY_CATCH(svc::CreateTransferMemory(std::addressof(handle), reinterpret_cast<uintptr_t>(address), size, svc_perm)) {
R_CONVERT(svc::ResultOutOfHandles, os::ResultOutOfHandles())
R_CONVERT(svc::ResultOutOfResource, os::ResultOutOfTransferMemory())
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
*out = handle;
return ResultSuccess();
}
void TransferMemoryImpl::Close(Handle handle) {
R_ABORT_UNLESS(svc::CloseHandle(handle));
}
Result TransferMemoryImpl::Map(void **out, Handle handle, void *address, size_t size, MemoryPermission owner_perm) {
AMS_ASSERT(address != nullptr);
/* Convert memory permission. */
auto svc_owner_perm = ConvertToSvcMemoryPermission(owner_perm);
/* Map the memory. */
R_TRY_CATCH(svc::MapTransferMemory(handle, reinterpret_cast<uintptr_t>(address), size, svc_owner_perm)) {
R_CONVERT(svc::ResultInvalidHandle, os::ResultInvalidHandle())
R_CONVERT(svc::ResultInvalidSize, os::ResultInvalidTransferMemorySize())
R_CONVERT(svc::ResultInvalidState, os::ResultInvalidTransferMemoryState())
R_CONVERT(svc::ResultInvalidCurrentMemory, os::ResultInvalidCurrentMemoryState())
R_CONVERT(svc::ResultInvalidMemoryRegion, os::ResultInvalidCurrentMemoryState())
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
*out = address;
return ResultSuccess();
}
void TransferMemoryImpl::Unmap(Handle handle, void *address, size_t size) {
R_ABORT_UNLESS(svc::UnmapTransferMemory(handle, reinterpret_cast<uintptr_t>(address), size));
}
}

View File

@@ -0,0 +1,185 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "impl/os_thread_manager.hpp"
#include "impl/os_transfer_memory_impl.hpp"
namespace ams::os {
namespace {
Result MapTransferMemoryWithAddressUnsafe(TransferMemoryType *tmem, void *address, os::MemoryPermission owner_perm) {
/* Map the transfer memory. */
void *mapped_address = nullptr;
R_TRY(impl::TransferMemoryImpl::Map(std::addressof(mapped_address), tmem->handle, address, tmem->size, owner_perm));
/* Set fields now that we've mapped. */
tmem->address = mapped_address;
tmem->state = TransferMemoryType::State_Mapped;
return ResultSuccess();
}
inline void SetupTransferMemoryType(TransferMemoryType *tmem, size_t size, Handle handle, bool managed) {
/* Set members. */
tmem->handle = handle;
tmem->size = size;
tmem->address = nullptr;
tmem->allocated = false;
/* Set managed. */
tmem->handle_managed = managed;
/* Create the critical section. */
new (GetPointer(tmem->cs_transfer_memory)) impl::InternalCriticalSection;
}
}
Result CreateTransferMemory(TransferMemoryType *tmem, void *address, size_t size, MemoryPermission perm) {
/* Validate pre-conditions. */
AMS_ASSERT(size > 0);
AMS_ASSERT(util::IsAligned(size, os::MemoryPageSize));
AMS_ASSERT(address != nullptr);
AMS_ASSERT(util::IsAligned(reinterpret_cast<uintptr_t>(address), os::MemoryPageSize));
/* Create the memory. */
Handle handle;
R_TRY(impl::TransferMemoryImpl::Create(std::addressof(handle), address, size, perm));
/* Setup the object. */
SetupTransferMemoryType(tmem, size, handle, true);
return ResultSuccess();
}
Result AttachTransferMemory(TransferMemoryType *tmem, size_t size, Handle handle, bool managed) {
AMS_ASSERT(size > 0);
AMS_ASSERT(util::IsAligned(size, os::MemoryPageSize));
AMS_ASSERT(handle != svc::InvalidHandle);
/* Setup the object. */
SetupTransferMemoryType(tmem, size, handle, managed);
return ResultSuccess();
}
Handle DetachTransferMemory(TransferMemoryType *tmem) {
AMS_ASSERT(tmem->state == TransferMemoryType::State_Created);
/* Set state to detached. */
tmem->state = TransferMemoryType::State_Detached;
/* Clear handle. */
Handle handle = tmem->handle;
tmem->handle = svc::InvalidHandle;
tmem->handle_managed = false;
return handle;
}
void DestroyTransferMemory(TransferMemoryType *tmem) {
/* Unmap the transfer memory, if required. */
if (tmem->state == TransferMemoryType::State_Mapped) {
UnmapTransferMemory(tmem);
}
/* Check the state is valid. */
AMS_ASSERT(tmem->state == TransferMemoryType::State_Created || tmem->state == TransferMemoryType::State_Detached);
/* Set state to not initialized. */
tmem->state = TransferMemoryType::State_NotInitialized;
/* Close the handle, if it's managed. */
if (tmem->handle_managed) {
impl::TransferMemoryImpl::Close(tmem->handle);
}
tmem->handle_managed = false;
/* Clear members. */
tmem->address = nullptr;
tmem->size = 0;
tmem->handle = svc::InvalidHandle;
/* Destroy the critical section. */
GetReference(tmem->cs_transfer_memory).~InternalCriticalSection();
}
Result MapTransferMemory(void **out, TransferMemoryType *tmem, MemoryPermission owner_perm) {
/* Lock the current thread, and then the transfer memory. */
std::scoped_lock thread_lk(GetReference(impl::GetCurrentThread()->cs_thread));
std::scoped_lock lk(GetReference(tmem->cs_transfer_memory));
/* Ensure we're in a mappable state. */
AMS_ASSERT(tmem->state == TransferMemoryType::State_Created);
/* Try to map up to 64 times. */
for (int i = 0; i < 64; ++i) {
/* Reserve space to map the memory. */
/* TODO: os::AslrSpaceManager */
void *map_address = ::virtmemReserve(tmem->size);
R_UNLESS(map_address != nullptr, os::ResultOutOfAddressSpace());
/* Mark allocated. */
tmem->allocated = true;
auto alloc_guard = SCOPE_GUARD { tmem->allocated = false; };
/* Try to map. */
R_TRY_CATCH(MapTransferMemoryWithAddressUnsafe(tmem, map_address, owner_perm)) {
/* If we failed to map at the address, retry. */
R_CATCH(os::ResultInvalidCurrentMemoryState) { continue; }
} R_END_TRY_CATCH;
/* TODO: Check guard space via aslr manager. */
if (false /* !impl::GetAslrSpaceManager()->CheckGuardSpace(reinterpret_cast<uintptr_t>(tmem->address), tmem->size) */) {
impl::TransferMemoryImpl::Unmap(tmem->handle, tmem->address, tmem->size);
continue;
}
/* We mapped successfully. */
alloc_guard.Cancel();
*out = tmem->address;
return ResultSuccess();
}
/* We failed to map. */
return os::ResultOutOfAddressSpace();
}
void UnmapTransferMemory(TransferMemoryType *tmem) {
/* Lock the memory. */
std::scoped_lock lk(GetReference(tmem->cs_transfer_memory));
/* If the memory isn't mapped, we can't unmap it. */
if (tmem->state != TransferMemoryType::State_Mapped) {
return;
}
/* Unmap the memory. */
impl::TransferMemoryImpl::Unmap(tmem->handle, tmem->address, tmem->size);
/* Unmapped memory is necessarily not allocated. */
if (tmem->allocated) {
tmem->allocated = false;
}
/* Clear the address. */
tmem->address = nullptr;
tmem->state = TransferMemoryType::State_Created;
}
}

View File

@@ -237,10 +237,7 @@ namespace ams::patcher {
} }
/* Print the path for this directory. */ /* Print the path for this directory. */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-truncation"
std::snprintf(path + patches_dir_path_len, sizeof(path) - patches_dir_path_len, "/%s", entry.name); std::snprintf(path + patches_dir_path_len, sizeof(path) - patches_dir_path_len, "/%s", entry.name);
#pragma GCC diagnostic pop
const size_t patch_dir_path_len = patches_dir_path_len + 1 + std::strlen(entry.name); const size_t patch_dir_path_len = patches_dir_path_len + 1 + std::strlen(entry.name);
/* Open the patch directory. */ /* Open the patch directory. */

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "settings_platform_region_impl.hpp"
namespace ams::settings::impl {
Result GetPlatformRegion(s32 *out) {
static_assert(sizeof(*out) == sizeof(::SetSysPlatformRegion));
return ::setsysGetPlatformRegion(reinterpret_cast<::SetSysPlatformRegion *>(out));
}
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::settings::impl {
Result GetPlatformRegion(s32 *out);
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "impl/settings_platform_region_impl.hpp"
namespace ams::settings::system {
PlatformRegion GetPlatformRegion() {
if (hos::GetVersion() >= hos::Version_9_0_0) {
s32 region = 0;
R_ABORT_UNLESS(settings::impl::GetPlatformRegion(std::addressof(region)));
return static_cast<PlatformRegion>(region);
} else {
return PlatformRegion_Global;
}
}
}

View File

@@ -37,8 +37,10 @@
#include <vapours/results/kvdb_results.hpp> #include <vapours/results/kvdb_results.hpp>
#include <vapours/results/loader_results.hpp> #include <vapours/results/loader_results.hpp>
#include <vapours/results/lr_results.hpp> #include <vapours/results/lr_results.hpp>
#include <vapours/results/os_results.hpp>
#include <vapours/results/ncm_results.hpp> #include <vapours/results/ncm_results.hpp>
#include <vapours/results/nim_results.hpp>
#include <vapours/results/ns_results.hpp>
#include <vapours/results/os_results.hpp>
#include <vapours/results/pgl_results.hpp> #include <vapours/results/pgl_results.hpp>
#include <vapours/results/pm_results.hpp> #include <vapours/results/pm_results.hpp>
#include <vapours/results/psc_results.hpp> #include <vapours/results/psc_results.hpp>

View File

@@ -78,6 +78,8 @@ namespace ams::fs {
R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemA, 3247); R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemA, 3247);
R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemB, 3248); R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemB, 3248);
R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemC, 3249); R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemC, 3249);
R_DEFINE_ERROR_RESULT(AllocationFailureInFileSystemProxyCoreImplD, 3256);
R_DEFINE_ERROR_RESULT(AllocationFailureInFileSystemProxyCoreImplE, 3257);
R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemCreatorA, 3280); R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemCreatorA, 3280);
R_DEFINE_ERROR_RESULT(AllocationFailureInRomFileSystemCreatorA, 3281); R_DEFINE_ERROR_RESULT(AllocationFailureInRomFileSystemCreatorA, 3281);
R_DEFINE_ERROR_RESULT(AllocationFailureInStorageOnNcaCreatorA, 3288); R_DEFINE_ERROR_RESULT(AllocationFailureInStorageOnNcaCreatorA, 3288);

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::nim {
R_DEFINE_NAMESPACE_RESULT_MODULE(137);
R_DEFINE_ERROR_RESULT(HttpConnectionCanceled, 70);
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::ns {
R_DEFINE_NAMESPACE_RESULT_MODULE(16);
R_DEFINE_ERROR_RESULT(Canceled, 90);
R_DEFINE_ERROR_RESULT(OutOfMaxRunningTask, 110);
R_DEFINE_ERROR_RESULT(CardUpdateNotSetup, 270);
R_DEFINE_ERROR_RESULT(CardUpdateNotPrepared, 280);
R_DEFINE_ERROR_RESULT(CardUpdateAlreadySetup, 290);
R_DEFINE_ERROR_RESULT(PrepareCardUpdateAlreadyRequested, 460);
}

View File

@@ -21,12 +21,20 @@ namespace ams::os {
R_DEFINE_NAMESPACE_RESULT_MODULE(3); R_DEFINE_NAMESPACE_RESULT_MODULE(3);
R_DEFINE_ERROR_RESULT(Busy, 4); R_DEFINE_ERROR_RESULT(Busy, 4);
R_DEFINE_ERROR_RESULT(OutOfMemory, 8); R_DEFINE_ERROR_RESULT(OutOfMemory, 8);
R_DEFINE_ERROR_RESULT(OutOfResource, 9); R_DEFINE_ERROR_RESULT(OutOfResource, 9);
R_DEFINE_ERROR_RESULT(OutOfVirtualAddressSpace, 12); R_DEFINE_ERROR_RESULT(OutOfVirtualAddressSpace, 12);
R_DEFINE_ERROR_RESULT(ResourceLimit, 13); R_DEFINE_ERROR_RESULT(ResourceLimit, 13);
R_DEFINE_ERROR_RESULT(OutOfHandles, 500);
R_DEFINE_ERROR_RESULT(InvalidHandle, 501);
R_DEFINE_ERROR_RESULT(InvalidCurrentMemoryState, 502);
R_DEFINE_ERROR_RESULT(InvalidTransferMemoryState, 503);
R_DEFINE_ERROR_RESULT(InvalidTransferMemorySize, 504);
R_DEFINE_ERROR_RESULT(OutOfTransferMemory, 505);
R_DEFINE_ERROR_RESULT(OutOfAddressSpace, 506);
} }

View File

@@ -20,6 +20,54 @@
namespace ams::util { namespace ams::util {
template<typename T>
constexpr T ToLower(T c) {
return ('A' <= c && c <= 'Z') ? (c - 'A' + 'a') : c;
}
template<typename T>
constexpr T ToUpper(T c) {
return ('a' <= c && c <= 'z') ? (c - 'a' + 'A') : c;
}
template<typename T>
int Strncmp(const T *lhs, const T *rhs, int count) {
AMS_ASSERT(lhs != nullptr);
AMS_ASSERT(rhs != nullptr);
AMS_ABORT_UNLESS(count >= 0);
if (count == 0) {
return 0;
}
T l, r;
do {
l = *(lhs++);
r = *(rhs++);
} while (l && (l == r) && (--count));
return l - r;
}
template<typename T>
int Strnicmp(const T *lhs, const T *rhs, int count) {
AMS_ASSERT(lhs != nullptr);
AMS_ASSERT(rhs != nullptr);
AMS_ABORT_UNLESS(count >= 0);
if (count == 0) {
return 0;
}
T l, r;
do {
l = ::ams::util::ToLower(*(lhs++));
r = ::ams::util::ToLower(*(rhs++));
} while (l && (l == r) && (--count));
return l - r;
}
template<typename T> template<typename T>
constexpr int Strlcpy(T *dst, const T *src, int count) { constexpr int Strlcpy(T *dst, const T *src, int count) {
AMS_ASSERT(dst != nullptr); AMS_ASSERT(dst != nullptr);

View File

@@ -62,6 +62,8 @@
"svcReplyAndReceive": "0x43", "svcReplyAndReceive": "0x43",
"svcReplyAndReceiveWithUserBuffer": "0x44", "svcReplyAndReceiveWithUserBuffer": "0x44",
"svcCreateEvent": "0x45", "svcCreateEvent": "0x45",
"svcMapTransferMemory": "0x51",
"svcUnmapTransferMemory": "0x52",
"svcCreateInterruptEvent": "0x53", "svcCreateInterruptEvent": "0x53",
"svcReadWriteRegister": "0x4E", "svcReadWriteRegister": "0x4E",
"svcQueryIoMapping": "0x55", "svcQueryIoMapping": "0x55",

View File

@@ -139,6 +139,9 @@ namespace ams::mitm {
/* Open global SD card file system, so that other threads can begin using the SD. */ /* Open global SD card file system, so that other threads can begin using the SD. */
mitm::fs::OpenGlobalSdCardFileSystem(); mitm::fs::OpenGlobalSdCardFileSystem();
/* Mount the sd card at a convenient mountpoint. */
ams::fs::MountSdCard(ams::fs::impl::SdCardFileSystemMountName);
/* Initialize the reboot manager (load a payload off the SD). */ /* Initialize the reboot manager (load a payload off the SD). */
/* Discard result, since it doesn't need to succeed. */ /* Discard result, since it doesn't need to succeed. */
mitm::bpc::LoadRebootPayload(); mitm::bpc::LoadRebootPayload();
@@ -157,6 +160,7 @@ namespace ams::mitm {
/* Connect to set:sys. */ /* Connect to set:sys. */
sm::DoWithSession([]() { sm::DoWithSession([]() {
R_ABORT_UNLESS(setInitialize());
R_ABORT_UNLESS(setsysInitialize()); R_ABORT_UNLESS(setsysInitialize());
}); });

View File

@@ -17,6 +17,7 @@
#include "amsmitm_initialization.hpp" #include "amsmitm_initialization.hpp"
#include "amsmitm_module_management.hpp" #include "amsmitm_module_management.hpp"
#include "bpc_mitm/bpc_ams_power_utils.hpp" #include "bpc_mitm/bpc_ams_power_utils.hpp"
#include "sysupdater/sysupdater_fs_utils.hpp"
extern "C" { extern "C" {
extern u32 __start__; extern u32 __start__;
@@ -81,15 +82,23 @@ void __appInit(void) {
R_ABORT_UNLESS(fsInitialize()); R_ABORT_UNLESS(fsInitialize());
R_ABORT_UNLESS(pmdmntInitialize()); R_ABORT_UNLESS(pmdmntInitialize());
R_ABORT_UNLESS(pminfoInitialize()); R_ABORT_UNLESS(pminfoInitialize());
ncm::Initialize();
spl::InitializeForFs(); spl::InitializeForFs();
}); });
/* Initialize fssystem library. */
fssystem::InitializeForFileSystemProxy();
/* Configure ncm to use fssystem library to mount content from the sd card. */
ncm::SetMountContentMetaFunction(mitm::sysupdater::MountSdCardContentMeta);
ams::CheckApiVersion(); ams::CheckApiVersion();
} }
void __appExit(void) { void __appExit(void) {
/* Cleanup services. */ /* Cleanup services. */
spl::Finalize(); spl::Finalize();
ncm::Finalize();
pminfoExit(); pminfoExit();
pmdmntExit(); pmdmntExit();
fsExit(); fsExit();

View File

@@ -18,7 +18,6 @@
namespace ams::mitm { namespace ams::mitm {
/* TODO: C++20 Concepts will make this a lot less stupid. */
template<typename T> template<typename T>
concept IsModule = requires(T, void *arg) { concept IsModule = requires(T, void *arg) {
{ T::ThreadPriority } -> std::convertible_to<s32>; { T::ThreadPriority } -> std::convertible_to<s32>;

View File

@@ -23,6 +23,7 @@
#include "bpc_mitm/bpc_ams_module.hpp" #include "bpc_mitm/bpc_ams_module.hpp"
#include "ns_mitm/nsmitm_module.hpp" #include "ns_mitm/nsmitm_module.hpp"
#include "hid_mitm/hidmitm_module.hpp" #include "hid_mitm/hidmitm_module.hpp"
#include "sysupdater/sysupdater_module.hpp"
namespace ams::mitm { namespace ams::mitm {
@@ -35,6 +36,7 @@ namespace ams::mitm {
ModuleId_BpcAms, ModuleId_BpcAms,
ModuleId_NsMitm, ModuleId_NsMitm,
ModuleId_HidMitm, ModuleId_HidMitm,
ModuleId_Sysupdater,
ModuleId_Count, ModuleId_Count,
}; };
@@ -67,6 +69,7 @@ namespace ams::mitm {
GetModuleDefinition<bpc_ams::MitmModule>(), GetModuleDefinition<bpc_ams::MitmModule>(),
GetModuleDefinition<ns::MitmModule>(), GetModuleDefinition<ns::MitmModule>(),
GetModuleDefinition<hid::MitmModule>(), GetModuleDefinition<hid::MitmModule>(),
GetModuleDefinition<sysupdater::MitmModule>(),
}; };
} }

View File

@@ -13,7 +13,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <stratosphere.hpp> #include <stratosphere.hpp>

View File

@@ -56,15 +56,11 @@ namespace ams::mitm::settings {
const auto api_info = exosphere::GetApiInfo(); const auto api_info = exosphere::GetApiInfo();
const char emummc_char = emummc::IsActive() ? 'E' : 'S'; const char emummc_char = emummc::IsActive() ? 'E' : 'S';
/* GCC complains about the following snprintf possibly truncating, but this is not a problem and has been carefully accounted for. */ /* NOTE: We have carefully accounted for the size of the string we print. */
#pragma GCC diagnostic push /* No truncation occurs assuming two-digits for all version number components. */
#pragma GCC diagnostic ignored "-Wformat-truncation" char display_version[sizeof(g_ams_firmware_version.display_version)];
{ std::snprintf(display_version, sizeof(display_version), "%s|AMS %u.%u.%u|%c", g_ams_firmware_version.display_version, api_info.GetMajorVersion(), api_info.GetMinorVersion(), api_info.GetMicroVersion(), emummc_char);
char display_version[sizeof(g_ams_firmware_version.display_version)]; std::memcpy(g_ams_firmware_version.display_version, display_version, sizeof(display_version));
std::snprintf(display_version, sizeof(display_version), "%s|AMS %u.%u.%u|%c", g_ams_firmware_version.display_version, api_info.GetMajorVersion(), api_info.GetMinorVersion(), api_info.GetMicroVersion(), emummc_char);
std::memcpy(g_ams_firmware_version.display_version, display_version, sizeof(display_version));
}
#pragma GCC diagnostic pop
} }
g_cached_firmware_version = true; g_cached_firmware_version = true;

View File

@@ -0,0 +1,102 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "sysupdater_apply_manager.hpp"
namespace ams::mitm::sysupdater {
namespace {
alignas(os::MemoryPageSize) u8 g_boot_image_update_buffer[64_KB];
updater::BootImageUpdateType GetBootImageUpdateType() {
int boot_image_update_type;
auto size = settings::fwdbg::GetSettingsItemValue(std::addressof(boot_image_update_type), sizeof(boot_image_update_type), "systeminitializer", "boot_image_update_type");
if (size != sizeof(boot_image_update_type)) {
return updater::BootImageUpdateType::Erista;
}
return updater::GetBootImageUpdateType(boot_image_update_type);
}
Result MarkPreCommitForBootImages() {
/* Set verification required for both normal and safe mode. */
R_TRY(updater::MarkVerifyingRequired(updater::BootModeType::Normal, g_boot_image_update_buffer, sizeof(g_boot_image_update_buffer)));
R_TRY(updater::MarkVerifyingRequired(updater::BootModeType::Safe, g_boot_image_update_buffer, sizeof(g_boot_image_update_buffer)));
/* Pre-commit is now marked. */
return ResultSuccess();
}
Result UpdateBootImages() {
/* Define a helper to update the images. */
auto UpdateBootImageImpl = [](updater::BootModeType boot_mode, updater::BootImageUpdateType boot_image_update_type) -> Result {
/* Get the boot image package id. */
ncm::SystemDataId boot_image_package_id = {};
R_TRY_CATCH(updater::GetBootImagePackageId(std::addressof(boot_image_package_id), boot_mode, g_boot_image_update_buffer, sizeof(g_boot_image_update_buffer))) {
R_CATCH(updater::ResultBootImagePackageNotFound) {
/* Nintendo simply falls through when the package is not found. */
}
} R_END_TRY_CATCH;
/* Update the boot images. */
R_TRY_CATCH(updater::UpdateBootImagesFromPackage(boot_image_package_id, boot_mode, g_boot_image_update_buffer, sizeof(g_boot_image_update_buffer), boot_image_update_type)) {
R_CATCH(updater::ResultBootImagePackageNotFound) {
/* Nintendo simply falls through when the package is not found. */
}
} R_END_TRY_CATCH;
/* Mark the images verified. */
R_TRY(updater::MarkVerified(boot_mode, g_boot_image_update_buffer, sizeof(g_boot_image_update_buffer)));
/* The boot images are updated. */
return ResultSuccess();
};
/* Get the boot image update type. */
auto boot_image_update_type = GetBootImageUpdateType();
/* Update boot images for safe mode. */
R_TRY(UpdateBootImageImpl(updater::BootModeType::Safe, boot_image_update_type));
/* Update boot images for normal mode. */
R_TRY(UpdateBootImageImpl(updater::BootModeType::Normal, boot_image_update_type));
/* Both sets of images are updated. */
return ResultSuccess();
}
}
Result SystemUpdateApplyManager::ApplyPackageTask(ncm::PackageSystemDowngradeTask *task) {
/* Lock the apply mutex. */
std::scoped_lock lk(this->apply_mutex);
/* NOTE: Here, Nintendo creates a system report for the update. */
/* Mark boot images to note that we're updating. */
R_TRY(MarkPreCommitForBootImages());
/* Commit the task. */
R_TRY(task->Commit());
/* Update the boot images. */
R_TRY(UpdateBootImages());
return ResultSuccess();
}
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::mitm::sysupdater {
class SystemUpdateApplyManager {
private:
os::Mutex apply_mutex;
public:
constexpr SystemUpdateApplyManager() : apply_mutex(false) { /* ... */ }
Result ApplyPackageTask(ncm::PackageSystemDowngradeTask *task);
};
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "sysupdater_async_impl.hpp"
#include "sysupdater_async_thread_allocator.hpp"
namespace ams::mitm::sysupdater {
Result AsyncBase::ToAsyncResult(Result result) {
R_TRY_CATCH(result) {
R_CONVERT(nim::ResultHttpConnectionCanceled, ns::ResultCanceled());
R_CONVERT(ncm::ResultInstallTaskCancelled, ns::ResultCanceled());
} R_END_TRY_CATCH;
return ResultSuccess();
}
AsyncPrepareSdCardUpdateImpl::~AsyncPrepareSdCardUpdateImpl() {
if (this->thread_info) {
os::WaitThread(this->thread_info->thread);
os::DestroyThread(this->thread_info->thread);
GetAsyncThreadAllocator()->Free(*this->thread_info);
}
}
Result AsyncPrepareSdCardUpdateImpl::Run() {
/* Get a thread info. */
ThreadInfo info;
R_TRY(GetAsyncThreadAllocator()->Allocate(std::addressof(info)));
/* Set the thread info's priority. */
info.priority = AMS_GET_SYSTEM_THREAD_PRIORITY(mitm_sysupdater, AsyncPrepareSdCardUpdateTask);
/* Ensure that we clean up appropriately. */
ON_SCOPE_EXIT {
if (!this->thread_info) {
GetAsyncThreadAllocator()->Free(info);
}
};
/* Create a thread for the task. */
R_TRY(os::CreateThread(info.thread, [](void *arg) {
auto *_this = reinterpret_cast<AsyncPrepareSdCardUpdateImpl *>(arg);
_this->result = _this->Execute();
_this->event.Signal();
}, this, info.stack, info.stack_size, info.priority));
/* Set the thread name. */
os::SetThreadNamePointer(info.thread, AMS_GET_SYSTEM_THREAD_NAME(mitm_sysupdater, AsyncPrepareSdCardUpdateTask));
/* Start the thread. */
os::StartThread(info.thread);
/* Set our thread info. */
this->thread_info = info;
return ResultSuccess();
}
Result AsyncPrepareSdCardUpdateImpl::Execute() {
return this->task->PrepareAndExecute();
}
void AsyncPrepareSdCardUpdateImpl::CancelImpl() {
this->task->Cancel();
}
}

View File

@@ -0,0 +1,132 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "sysupdater_i_async_result.hpp"
#include "sysupdater_thread_allocator.hpp"
namespace ams::mitm::sysupdater {
class ErrorContextHolder {
private:
err::ErrorContext error_context;
public:
constexpr ErrorContextHolder() : error_context{} { /* ... */ }
virtual ~ErrorContextHolder() { /* ... */ }
template<typename T>
Result SaveErrorContextIfFailed(T &async, Result result) {
if (R_FAILED(result)) {
async.GetErrorContext(std::addressof(this->error_context));
return result;
}
return ResultSuccess();
}
template<typename T>
Result GetAndSaveErrorContext(T &async) {
R_TRY(this->SaveErrorContextIfFailed(async, async.Get()));
return ResultSuccess();
}
template<typename T>
Result SaveInternalTaskErrorContextIfFailed(T &async, Result result) {
if (R_FAILED(result)) {
async.CreateErrorContext(std::addressof(this->error_context));
return result;
}
return ResultSuccess();
}
const err::ErrorContext &GetErrorContextImpl() {
return this->error_context;
}
};
class AsyncBase : public IAsyncBase {
public:
virtual ~AsyncBase() { /* ... */ }
static Result ToAsyncResult(Result result);
virtual Result Cancel() override final {
this->CancelImpl();
return ResultSuccess();
}
virtual Result GetErrorContext(sf::Out<err::ErrorContext> out) override {
*out = {};
return ResultSuccess();
}
private:
virtual void CancelImpl() = 0;
};
class AsyncResultBase : public IAsyncResult {
public:
virtual ~AsyncResultBase() { /* ... */ }
static Result ToAsyncResult(Result result) { return AsyncBase::ToAsyncResult(result); }
virtual Result Cancel() override final {
this->CancelImpl();
return ResultSuccess();
}
virtual Result Get() override final {
return ToAsyncResult(this->GetImpl());
}
virtual Result GetErrorContext(sf::Out<err::ErrorContext> out) override {
*out = {};
return ResultSuccess();
}
private:
virtual void CancelImpl() = 0;
virtual Result GetImpl() = 0;
};
/* NOTE: Based off of ns AsyncPrepareCardUpdateImpl. */
/* We don't implement the RequestServer::ManagedStop details, as we don't implement stoppable request list. */
class AsyncPrepareSdCardUpdateImpl : public AsyncResultBase, private ErrorContextHolder {
private:
Result result;
os::SystemEvent event;
std::optional<ThreadInfo> thread_info;
ncm::InstallTaskBase *task;
public:
AsyncPrepareSdCardUpdateImpl(ncm::InstallTaskBase *task) : result(ResultSuccess()), event(os::EventClearMode_ManualClear, true), thread_info(), task(task) { /* ... */ }
virtual ~AsyncPrepareSdCardUpdateImpl();
os::SystemEvent &GetEvent() { return this->event; }
virtual Result GetErrorContext(sf::Out<err::ErrorContext> out) override {
*out = ErrorContextHolder::GetErrorContextImpl();
return ResultSuccess();
}
Result Run();
private:
Result Execute();
virtual void CancelImpl() override;
virtual Result GetImpl() override { return this->result; }
};
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "sysupdater_async_thread_allocator.hpp"
namespace ams::mitm::sysupdater {
namespace {
constexpr inline int AsyncThreadCount = 1;
constexpr inline size_t AsyncThreadStackSize = 16_KB;
os::ThreadType g_async_threads[AsyncThreadCount];
alignas(os::ThreadStackAlignment) u8 g_async_thread_stack_heap[AsyncThreadCount * AsyncThreadStackSize];
constinit ThreadAllocator g_async_thread_allocator(g_async_threads, AsyncThreadCount, os::InvalidThreadPriority, g_async_thread_stack_heap, sizeof(g_async_thread_stack_heap), AsyncThreadStackSize);
}
ThreadAllocator *GetAsyncThreadAllocator() {
return std::addressof(g_async_thread_allocator);
}
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "sysupdater_thread_allocator.hpp"
namespace ams::mitm::sysupdater {
ThreadAllocator *GetAsyncThreadAllocator();
}

View File

@@ -0,0 +1,247 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "sysupdater_fs_utils.hpp"
namespace ams::mitm::sysupdater {
namespace {
constexpr inline const char * const NcaExtension = ".nca";
constexpr inline const char * const NspExtension = ".nsp";
constexpr inline const size_t NcaExtensionSize = 4;
constexpr inline const size_t NspExtensionSize = 4;
static_assert(NcaExtensionSize == NspExtensionSize);
constexpr inline const size_t NcaNspExtensionSize = NcaExtensionSize;
constexpr inline std::underlying_type<fssrv::PathNormalizer::Option>::type SdCardContentMetaPathNormalizeOption = fssrv::PathNormalizer::Option_PreserveTailSeparator |
fssrv::PathNormalizer::Option_HasMountName;
Result CheckNcaOrNsp(const char **path) {
/* Ensure that the path is currently at the mount name delimeter. */
R_UNLESS(std::strncmp(*path, ams::fs::impl::MountNameDelimiter, strnlen(ams::fs::impl::MountNameDelimiter, ams::fs::EntryNameLengthMax)) == 0, fs::ResultPathNotFound());
/* Advance past the :. */
static_assert(ams::fs::impl::MountNameDelimiter[0] == ':');
*path += 1;
/* Ensure path is long enough for the extension. */
const auto path_len = strnlen(*path, ams::fs::EntryNameLengthMax);
R_UNLESS(path_len > NcaNspExtensionSize, fs::ResultPathNotFound());
/* Get the extension. */
const char * const extension = *path + path_len - NcaNspExtensionSize;
/* Ensure nca or nsp. */
const bool is_nca = util::Strnicmp(extension, NcaExtension, NcaNspExtensionSize) == 0;
const bool is_nsp = util::Strnicmp(extension, NspExtension, NcaNspExtensionSize) == 0;
R_UNLESS(is_nca || is_nsp, fs::ResultPathNotFound());
return ResultSuccess();
}
Result ParseMountName(const char **path, std::shared_ptr<ams::fs::fsa::IFileSystem> *out) {
/* The equivalent function here supports all the common mount names; we'll only support the SD card, system content storage. */
if (const auto mount_len = strnlen(ams::fs::impl::SdCardFileSystemMountName, ams::fs::MountNameLengthMax); std::strncmp(*path, ams::fs::impl::SdCardFileSystemMountName, mount_len) == 0) {
/* Advance the path. */
*path += mount_len;
/* Open the SD card. This uses libnx bindings. */
FsFileSystem fs;
R_TRY(fsOpenSdCardFileSystem(std::addressof(fs)));
/* Allocate a new filesystem wrapper. */
auto fsa = std::make_shared<ams::fs::RemoteFileSystem>(fs);
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInSdCardA());
/* Set the output fs. */
*out = std::move(fsa);
} else if (const auto mount_len = strnlen(ams::fs::impl::ContentStorageSystemMountName, ams::fs::MountNameLengthMax); std::strncmp(*path, ams::fs::impl::ContentStorageSystemMountName, mount_len) == 0) {
/* Advance the path. */
*path += mount_len;
/* Open the system content storage. This uses libnx bindings. */
FsFileSystem fs;
R_TRY(fsOpenContentStorageFileSystem(std::addressof(fs), FsContentStorageId_System));
/* Allocate a new filesystem wrapper. */
auto fsa = std::make_shared<ams::fs::RemoteFileSystem>(fs);
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInContentStorageA());
/* Set the output fs. */
*out = std::move(fsa);
} else {
return fs::ResultPathNotFound();
}
/* Ensure that there's something that could be a mount name delimiter. */
R_UNLESS(strnlen(*path, fs::EntryNameLengthMax) != 0, fs::ResultPathNotFound());
return ResultSuccess();
}
Result ParseNsp(const char **path, std::shared_ptr<ams::fs::fsa::IFileSystem> *out, std::shared_ptr<ams::fs::fsa::IFileSystem> base_fs) {
const char *work_path = *path;
/* Advance to the nsp extension. */
while (true) {
if (util::Strnicmp(work_path, NspExtension, NspExtensionSize) == 0) {
if (work_path[NspExtensionSize] == '\x00' || work_path[NspExtensionSize] == '/') {
break;
}
work_path += NspExtensionSize;
} else {
R_UNLESS(*work_path != '\x00', fs::ResultPathNotFound());
work_path += 1;
}
}
/* Advance past the extension. */
work_path += NspExtensionSize;
/* Get the nsp path. */
char nsp_path[fs::EntryNameLengthMax + 1];
R_UNLESS(static_cast<size_t>(work_path - *path) <= sizeof(nsp_path), fs::ResultTooLongPath());
std::memcpy(nsp_path, *path, work_path - *path);
nsp_path[work_path - *path] = '\x00';
/* Open the file storage. */
std::shared_ptr<ams::fs::FileStorageBasedFileSystem> file_storage = fssystem::AllocateShared<ams::fs::FileStorageBasedFileSystem>();
R_UNLESS(file_storage != nullptr, fs::ResultAllocationFailureInFileSystemProxyCoreImplD());
R_TRY(file_storage->Initialize(std::move(base_fs), nsp_path, ams::fs::OpenMode_Read));
/* Create a partition fs. */
R_TRY(fssystem::GetFileSystemCreatorInterfaces()->partition_fs_creator->Create(out, std::move(file_storage)));
/* Update the path. */
*path = work_path;
return ResultSuccess();
}
Result ParseNca(const char **path, std::shared_ptr<fssystem::NcaReader> *out, std::shared_ptr<ams::fs::fsa::IFileSystem> base_fs) {
/* Open the file storage. */
std::shared_ptr<ams::fs::FileStorageBasedFileSystem> file_storage = fssystem::AllocateShared<ams::fs::FileStorageBasedFileSystem>();
R_UNLESS(file_storage != nullptr, fs::ResultAllocationFailureInFileSystemProxyCoreImplE());
R_TRY(file_storage->Initialize(std::move(base_fs), *path, ams::fs::OpenMode_Read));
/* Create the nca reader. */
std::shared_ptr<fssystem::NcaReader> nca_reader;
R_TRY(fssystem::GetFileSystemCreatorInterfaces()->storage_on_nca_creator->CreateNcaReader(std::addressof(nca_reader), file_storage));
/* NOTE: Here Nintendo validates program ID, but this does not need checking in the meta case. */
/* Set output reader. */
*out = std::move(nca_reader);
return ResultSuccess();
}
Result OpenMetaStorage(std::shared_ptr<ams::fs::IStorage> *out, std::shared_ptr<fssystem::NcaReader> nca_reader, fssystem::NcaFsHeader::FsType *out_fs_type) {
/* Ensure the nca is a meta nca. */
R_UNLESS(nca_reader->GetContentType() == fssystem::NcaHeader::ContentType::Meta, fs::ResultPreconditionViolation());
/* We only support SD card ncas, so ensure this isn't a gamecard nca. */
R_UNLESS(nca_reader->GetDistributionType() != fssystem::NcaHeader::DistributionType::GameCard, fs::ResultPermissionDenied());
/* Here Nintendo would call GetPartitionIndex(), but we don't need to, because it's meta. */
constexpr int MetaPartitionIndex = 0;
/* Open fs header reader. */
fssystem::NcaFsHeaderReader fs_header_reader;
R_TRY(fssystem::GetFileSystemCreatorInterfaces()->storage_on_nca_creator->Create(out, std::addressof(fs_header_reader), std::move(nca_reader), MetaPartitionIndex, false));
/* Set the output fs type. */
*out_fs_type = fs_header_reader.GetFsType();
return ResultSuccess();
}
Result OpenContentMetaFileSystem(std::shared_ptr<ams::fs::fsa::IFileSystem> *out, const char *path) {
/* Parse the mount name to get a filesystem. */
const char *cur_path = path;
std::shared_ptr<ams::fs::fsa::IFileSystem> base_fs;
R_TRY(ParseMountName(std::addressof(cur_path), std::addressof(base_fs)));
/* Ensure the path is an nca or nsp. */
R_TRY(CheckNcaOrNsp(std::addressof(cur_path)));
/* Try to parse as nsp. */
std::shared_ptr<ams::fs::fsa::IFileSystem> nsp_fs;
if (R_SUCCEEDED(ParseNsp(std::addressof(cur_path), std::addressof(nsp_fs), base_fs))) {
/* nsp target is only allowed for type package, and we're assuming type meta. */
R_UNLESS(*path != '\x00', fs::ResultInvalidArgument());
/* Use the nsp fs as the base fs. */
base_fs = std::move(nsp_fs);
}
/* Parse as nca. */
std::shared_ptr<fssystem::NcaReader> nca_reader;
R_TRY(ParseNca(std::addressof(cur_path), std::addressof(nca_reader), std::move(base_fs)));
/* Open meta storage. */
std::shared_ptr<ams::fs::IStorage> storage;
fssystem::NcaFsHeader::FsType fs_type;
R_TRY(OpenMetaStorage(std::addressof(storage), std::move(nca_reader), std::addressof(fs_type)));
/* Open the appropriate interface. */
const auto * const creator_intfs = fssystem::GetFileSystemCreatorInterfaces();
switch (fs_type) {
case fssystem::NcaFsHeader::FsType::PartitionFs: return creator_intfs->partition_fs_creator->Create(out, std::move(storage));
case fssystem::NcaFsHeader::FsType::RomFs: return creator_intfs->rom_fs_creator->Create(out, std::move(storage));
default:
return fs::ResultInvalidNcaFileSystemType();
}
}
}
bool PathView::HasPrefix(std::string_view prefix) const {
return this->path.compare(0, prefix.length(), prefix) == 0;
}
bool PathView::HasSuffix(std::string_view suffix) const {
return this->path.compare(this->path.length() - suffix.length(), suffix.length(), suffix) == 0;
}
std::string_view PathView::GetFileName() const {
auto pos = this->path.find_last_of("/");
return pos != std::string_view::npos ? this->path.substr(pos + 1) : this->path;
}
Result MountSdCardContentMeta(const char *mount_name, const char *path) {
/* Sanitize input. */
/* NOTE: This is an internal API, so we won't bother with mount name sanitization. */
R_UNLESS(path != nullptr, fs::ResultInvalidPath());
/* Normalize the path. */
fssrv::PathNormalizer normalized_path(path, SdCardContentMetaPathNormalizeOption);
R_TRY(normalized_path.GetResult());
/* Open the filesystem. */
std::shared_ptr<ams::fs::fsa::IFileSystem> fs;
R_TRY(OpenContentMetaFileSystem(std::addressof(fs), normalized_path.GetPath()));
/* Create a holder for the fs. */
std::unique_ptr unique_fs = std::make_unique<ams::fs::SharedFileSystemHolder>(std::move(fs));
R_UNLESS(unique_fs != nullptr, fs::ResultAllocationFailureInNew());
/* Register the fs. */
return ams::fs::fsa::Register(mount_name, std::move(unique_fs));
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::mitm::sysupdater {
class PathView {
private:
std::string_view path; /* Nintendo uses util::string_view here. */
public:
PathView(std::string_view p) : path(p) { /* ...*/ }
bool HasPrefix(std::string_view prefix) const;
bool HasSuffix(std::string_view suffix) const;
std::string_view GetFileName() const;
};
Result MountSdCardContentMeta(const char *mount_name, const char *path);
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::mitm::sysupdater {
class IAsyncBase : public sf::IServiceObject {
public:
virtual Result Cancel() = 0;
virtual Result GetErrorContext(sf::Out<err::ErrorContext> out) = 0;
};
class IAsyncResult : public IAsyncBase {
private:
enum class CommandId {
Get = 0,
Cancel = 1,
GetErrorContext = 2,
};
public:
virtual Result Get() = 0;
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(Get),
MAKE_SERVICE_COMMAND_META(Cancel),
MAKE_SERVICE_COMMAND_META(GetErrorContext),
};
};
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "../amsmitm_initialization.hpp"
#include "sysupdater_module.hpp"
#include "sysupdater_service.hpp"
#include "sysupdater_async_impl.hpp"
namespace ams::mitm::sysupdater {
namespace {
constexpr sm::ServiceName SystemUpdateServiceName = sm::ServiceName::Encode("ams:su");
constexpr size_t SystemUpdateMaxSessions = 1;
constexpr size_t MaxServers = 1;
constexpr size_t MaxSessions = SystemUpdateMaxSessions + 3;
struct ServerOptions {
static constexpr size_t PointerBufferSize = 1_KB;
static constexpr size_t MaxDomains = 0;
static constexpr size_t MaxDomainObjects = 0;
};
sf::hipc::ServerManager<MaxServers, ServerOptions, MaxSessions> g_server_manager;
constinit sysupdater::SystemUpdateService g_system_update_service_object;
}
void MitmModule::ThreadFunction(void *arg) {
/* Wait until initialization is complete. */
mitm::WaitInitialized();
/* Connect to nim. */
sm::DoWithSession([]() { nim::InitializeForNetworkInstallManager(); });
ON_SCOPE_EXIT { nim::FinalizeForNetworkInstallManager(); };
/* Register ams:su. */
R_ABORT_UNLESS((g_server_manager.RegisterServer<sysupdater::SystemUpdateService>(SystemUpdateServiceName, SystemUpdateMaxSessions, sf::ServiceObjectTraits<sysupdater::SystemUpdateService>::SharedPointerHelper::GetEmptyDeleteSharedPointer(std::addressof(g_system_update_service_object)))));
/* Loop forever, servicing our services. */
g_server_manager.LoopProcess();
}
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "../amsmitm_module.hpp"
namespace ams::mitm::sysupdater {
DEFINE_MITM_MODULE_CLASS(0x8000, AMS_GET_SYSTEM_THREAD_PRIORITY(mitm_sysupdater, IpcServer));
}

View File

@@ -0,0 +1,517 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "sysupdater_service.hpp"
#include "sysupdater_async_impl.hpp"
#include "sysupdater_fs_utils.hpp"
namespace ams::mitm::sysupdater {
namespace {
/* ExFat NCAs prior to 2.0.0 do not actually include the exfat driver, and don't boot. */
constexpr inline u32 MinimumVersionForExFatDriver = 65536;
template<typename F>
Result ForEachFileInDirectory(const char *root_path, F f) {
/* Open the directory. */
fs::DirectoryHandle dir;
R_TRY(fs::OpenDirectory(std::addressof(dir), root_path, fs::OpenDirectoryMode_File));
ON_SCOPE_EXIT { fs::CloseDirectory(dir); };
while (true) {
/* Read the current entry. */
s64 count;
fs::DirectoryEntry entry;
R_TRY(fs::ReadDirectory(std::addressof(count), std::addressof(entry), dir, 1));
if (count == 0) {
break;
}
/* Invoke our handler on the entry. */
bool done;
R_TRY(f(std::addressof(done), entry));
R_SUCCEED_IF(done);
}
return ResultSuccess();
}
Result ConvertToFsCommonPath(char *dst, size_t dst_size, const char *package_root_path, const char *entry_path) {
char package_path[ams::fs::EntryNameLengthMax];
const size_t path_len = std::snprintf(package_path, sizeof(package_path), "%s%s", package_root_path, entry_path);
AMS_ABORT_UNLESS(path_len < ams::fs::EntryNameLengthMax);
return ams::fs::ConvertToFsCommonPath(dst, dst_size, package_path);
}
Result LoadContentMeta(ncm::AutoBuffer *out, const char *package_root_path, const fs::DirectoryEntry &entry) {
AMS_ABORT_UNLESS(PathView(entry.name).HasSuffix(".cnmt.nca"));
char path[ams::fs::EntryNameLengthMax];
R_TRY(ConvertToFsCommonPath(path, sizeof(path), package_root_path, entry.name));
return ncm::ReadContentMetaPath(out, path);
}
Result ReadContentMetaPath(ncm::AutoBuffer *out, const char *package_root, const ncm::ContentInfo &content_info) {
/* Get the .cnmt.nca path for the info. */
char cnmt_nca_name[ncm::ContentIdStringLength + 10];
ncm::GetStringFromContentId(cnmt_nca_name, sizeof(cnmt_nca_name), content_info.GetId());
std::memcpy(cnmt_nca_name + ncm::ContentIdStringLength, ".cnmt.nca", std::strlen(".cnmt.nca"));
cnmt_nca_name[sizeof(cnmt_nca_name) - 1] = '\x00';
/* Create a new path. */
ncm::Path content_path;
R_TRY(ConvertToFsCommonPath(content_path.str, sizeof(content_path.str), package_root, cnmt_nca_name));
/* Read the content meta path. */
return ncm::ReadContentMetaPath(out, content_path.str);
}
Result GetSystemUpdateUpdateContentInfoFromPackage(ncm::ContentInfo *out, const char *package_root) {
bool found_system_update = false;
/* Iterate over all files to find the system update meta. */
R_TRY(ForEachFileInDirectory(package_root, [&](bool *done, const fs::DirectoryEntry &entry) -> Result {
/* Don't early terminate by default. */
*done = false;
/* We have nothing to list if we're not looking at a meta. */
R_SUCCEED_IF(!PathView(entry.name).HasSuffix(".cnmt.nca"));
/* Read the content meta path, and build. */
ncm::AutoBuffer package_meta;
R_TRY(LoadContentMeta(std::addressof(package_meta), package_root, entry));
/* Create a reader. */
const auto reader = ncm::PackagedContentMetaReader(package_meta.Get(), package_meta.GetSize());
/* If we find a system update, we're potentially done. */
if (reader.GetHeader()->type == ncm::ContentMetaType::SystemUpdate) {
/* Try to parse a content id from the name. */
auto content_id = ncm::GetContentIdFromString(entry.name, sizeof(entry.name));
R_UNLESS(content_id, ncm::ResultInvalidPackageFormat());
/* We're done. */
*done = true;
found_system_update = true;
*out = ncm::ContentInfo::Make(*content_id, entry.file_size, ncm::ContentType::Meta);
}
return ResultSuccess();
}));
/* If we didn't find anything, error. */
R_UNLESS(found_system_update, ncm::ResultSystemUpdateNotFoundInPackage());
return ResultSuccess();
}
Result ValidateSystemUpdate(Result *out_result, UpdateValidationInfo *out_info, const ncm::PackagedContentMetaReader &update_reader, const char *package_root) {
/* Clear output. */
*out_result = ResultSuccess();
/* We want to track all content the update requires. */
const size_t num_content_metas = update_reader.GetContentMetaCount();
bool content_meta_valid[num_content_metas] = {};
/* Allocate a buffer to use for validation. */
size_t data_buffer_size = 1_MB;
void *data_buffer;
do {
data_buffer = std::malloc(data_buffer_size);
if (data_buffer != nullptr) {
break;
}
data_buffer_size /= 2;
} while (data_buffer_size >= 16_KB);
R_UNLESS(data_buffer != nullptr, fs::ResultAllocationFailureInNew());
ON_SCOPE_EXIT { std::free(data_buffer); };
/* Declare helper for result validation. */
auto ValidateResult = [&] ALWAYS_INLINE_LAMBDA (Result result) -> Result {
*out_result = result;
return result;
};
/* Iterate over all files to find all content metas. */
R_TRY(ForEachFileInDirectory(package_root, [&](bool *done, const fs::DirectoryEntry &entry) -> Result {
/* Clear output. */
*out_info = {};
/* Don't early terminate by default. */
*done = false;
/* We have nothing to list if we're not looking at a meta. */
R_SUCCEED_IF(!PathView(entry.name).HasSuffix(".cnmt.nca"));
/* Read the content meta path, and build. */
ncm::AutoBuffer package_meta;
R_TRY(LoadContentMeta(std::addressof(package_meta), package_root, entry));
/* Create a reader. */
const auto reader = ncm::PackagedContentMetaReader(package_meta.Get(), package_meta.GetSize());
/* Get the key for the reader. */
const auto key = reader.GetKey();
/* Check if we need to validate this content. */
bool need_validate = false;
size_t validation_index = 0;
for (size_t i = 0; i < num_content_metas; ++i) {
if (update_reader.GetContentMetaInfo(i)->ToKey() == key) {
need_validate = true;
validation_index = i;
break;
}
}
/* If we don't need to validate, continue. */
R_SUCCEED_IF(!need_validate);
/* We're validating. */
out_info->invalid_key = key;
/* Validate all contents. */
for (size_t i = 0; i < reader.GetContentCount(); ++i) {
const auto *content_info = reader.GetContentInfo(i);
const auto &content_id = content_info->GetId();
const s64 content_size = content_info->info.GetSize();
out_info->invalid_content_id = content_id;
/* Get the content id string. */
auto content_id_str = ncm::GetContentIdString(content_id);
/* Open the file. */
fs::FileHandle file;
{
char path[fs::EntryNameLengthMax];
std::snprintf(path, sizeof(path), "%s%s%s", package_root, content_id_str.data, content_info->GetType() == ncm::ContentType::Meta ? ".cnmt.nca" : ".nca");
if (R_FAILED(ValidateResult(fs::OpenFile(std::addressof(file), path, ams::fs::OpenMode_Read)))) {
*done = true;
return ResultSuccess();
}
}
ON_SCOPE_EXIT { fs::CloseFile(file); };
/* Validate the file size is correct. */
s64 file_size;
if (R_FAILED(ValidateResult(fs::GetFileSize(std::addressof(file_size), file)))) {
*done = true;
return ResultSuccess();
}
if (file_size != content_size) {
*out_result = ncm::ResultInvalidContentHash();
*done = true;
return ResultSuccess();
}
/* Read and hash the file in chunks. */
crypto::Sha256Generator sha;
sha.Initialize();
s64 ofs = 0;
while (ofs < content_size) {
const size_t cur_size = std::min(static_cast<size_t>(content_size - ofs), data_buffer_size);
if (R_FAILED(ValidateResult(fs::ReadFile(file, ofs, data_buffer, cur_size)))) {
*done = true;
return ResultSuccess();
}
sha.Update(data_buffer, cur_size);
ofs += cur_size;
}
/* Get the hash. */
ncm::Digest calc_digest;
sha.GetHash(std::addressof(calc_digest), sizeof(calc_digest));
/* Validate the hash. */
if (std::memcmp(std::addressof(calc_digest), std::addressof(content_info->digest), sizeof(ncm::Digest)) != 0) {
*out_result = ncm::ResultInvalidContentHash();
*done = true;
return ResultSuccess();
}
}
/* Mark the relevant content as validated. */
content_meta_valid[validation_index] = true;
*out_info = {};
return ResultSuccess();
}));
/* If we're otherwise going to succeed, ensure that every content was found. */
if (R_SUCCEEDED(*out_result)) {
for (size_t i = 0; i < num_content_metas; ++i) {
if (!content_meta_valid[i]) {
*out_result = fs::ResultPathNotFound();
*out_info = {
.invalid_key = update_reader.GetContentMetaInfo(i)->ToKey(),
};
break;
}
}
}
return ResultSuccess();
}
bool IsExFatDriverSupported(const ncm::ContentMetaInfo &info) {
return info.version >= MinimumVersionForExFatDriver && ((info.attributes & ncm::ContentMetaAttribute_IncludesExFatDriver) != 0);
}
Result FormatUserPackagePath(ncm::Path *out, const ncm::Path &user_path) {
/* Ensure that the user path is valid. */
R_UNLESS(user_path.str[0] == '/', fs::ResultInvalidPath());
/* Print as @Sdcard:<user_path>/ */
std::snprintf(out->str, sizeof(out->str), "%s:%s/", ams::fs::impl::SdCardFileSystemMountName, user_path.str);
/* Normalize, if the user provided an ending / */
const size_t len = std::strlen(out->str);
if (out->str[len - 1] == '/' && out->str[len - 2] == '/') {
out->str[len - 1] = '\x00';
}
return ResultSuccess();
}
const char *GetFirmwareVariationSettingName(settings::system::PlatformRegion region) {
switch (region) {
case settings::system::PlatformRegion_Global: return "firmware_variation";
case settings::system::PlatformRegion_China: return "t_firmware_variation";
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
ncm::FirmwareVariationId GetFirmwareVariationId() {
/* Get the firmware variation setting name. */
const char * const setting_name = GetFirmwareVariationSettingName(settings::system::GetPlatformRegion());
/* Retrieve the firmware variation id. */
ncm::FirmwareVariationId id = {};
settings::fwdbg::GetSettingsItemValue(std::addressof(id.value), sizeof(u8), "ns.systemupdate", setting_name);
return id;
}
}
Result SystemUpdateService::GetUpdateInformation(sf::Out<UpdateInformation> out, const ncm::Path &path) {
/* Adjust the path. */
ncm::Path package_root;
R_TRY(FormatUserPackagePath(std::addressof(package_root), path));
/* Create a new update information. */
UpdateInformation update_info = {};
/* Parse the update. */
{
/* Get the content info for the system update. */
ncm::ContentInfo content_info;
R_TRY(GetSystemUpdateUpdateContentInfoFromPackage(std::addressof(content_info), package_root.str));
/* Read the content meta. */
ncm::AutoBuffer content_meta_buffer;
R_TRY(ReadContentMetaPath(std::addressof(content_meta_buffer), package_root.str, content_info));
/* Create a reader. */
const auto reader = ncm::PackagedContentMetaReader(content_meta_buffer.Get(), content_meta_buffer.GetSize());
/* Get the version from the header. */
update_info.version = reader.GetHeader()->version;
/* Iterate over infos to find the system update info. */
for (size_t i = 0; i < reader.GetContentMetaCount(); ++i) {
const auto &meta_info = *reader.GetContentMetaInfo(i);
switch (meta_info.type) {
case ncm::ContentMetaType::BootImagePackage:
/* Detect exFAT support. */
update_info.exfat_supported |= IsExFatDriverSupported(meta_info);
break;
default:
break;
}
}
/* Default to no firmware variations. */
update_info.firmware_variation_count = 0;
/* Parse firmware variations if relevant. */
if (reader.GetExtendedDataSize() != 0) {
/* Get the actual firmware variation count. */
ncm::SystemUpdateMetaExtendedDataReader extended_data_reader(reader.GetExtendedData(), reader.GetExtendedDataSize());
update_info.firmware_variation_count = extended_data_reader.GetFirmwareVariationCount();
/* NOTE: Update this if Nintendo ever actually releases an update with this many variations? */
R_UNLESS(update_info.firmware_variation_count <= FirmwareVariationCountMax, ncm::ResultInvalidFirmwareVariation());
for (size_t i = 0; i < update_info.firmware_variation_count; ++i) {
update_info.firmware_variation_ids[i] = *extended_data_reader.GetFirmwareVariationId(i);
}
}
}
/* Set the parsed update info. */
out.SetValue(update_info);
return ResultSuccess();
}
Result SystemUpdateService::ValidateUpdate(sf::Out<Result> out_validate_result, sf::Out<UpdateValidationInfo> out_validate_info, const ncm::Path &path) {
/* Adjust the path. */
ncm::Path package_root;
R_TRY(FormatUserPackagePath(std::addressof(package_root), path));
/* Parse the update. */
{
/* Get the content info for the system update. */
ncm::ContentInfo content_info;
R_TRY(GetSystemUpdateUpdateContentInfoFromPackage(std::addressof(content_info), package_root.str));
/* Read the content meta. */
ncm::AutoBuffer content_meta_buffer;
R_TRY(ReadContentMetaPath(std::addressof(content_meta_buffer), package_root.str, content_info));
/* Create a reader. */
const auto reader = ncm::PackagedContentMetaReader(content_meta_buffer.Get(), content_meta_buffer.GetSize());
/* Validate the update. */
R_TRY(ValidateSystemUpdate(out_validate_result.GetPointer(), out_validate_info.GetPointer(), reader, package_root.str));
}
return ResultSuccess();
};
Result SystemUpdateService::SetupUpdate(sf::CopyHandle transfer_memory, u64 transfer_memory_size, const ncm::Path &path, bool exfat) {
return this->SetupUpdateImpl(transfer_memory.GetValue(), transfer_memory_size, path, exfat, GetFirmwareVariationId());
}
Result SystemUpdateService::SetupUpdateWithVariation(sf::CopyHandle transfer_memory, u64 transfer_memory_size, const ncm::Path &path, bool exfat, ncm::FirmwareVariationId firmware_variation_id) {
return this->SetupUpdateImpl(transfer_memory.GetValue(), transfer_memory_size, path, exfat, firmware_variation_id);
}
Result SystemUpdateService::RequestPrepareUpdate(sf::OutCopyHandle out_event_handle, sf::Out<std::shared_ptr<IAsyncResult>> out_async) {
/* Ensure the update is setup but not prepared. */
R_UNLESS(this->setup_update, ns::ResultCardUpdateNotSetup());
R_UNLESS(!this->requested_update, ns::ResultPrepareCardUpdateAlreadyRequested());
/* Create the async result. */
auto async_result = std::make_shared<AsyncPrepareSdCardUpdateImpl>(std::addressof(*this->update_task));
R_UNLESS(async_result != nullptr, ns::ResultOutOfMaxRunningTask());
/* Run the task. */
R_TRY(async_result->Run());
/* We prepared the task! */
this->requested_update = true;
out_event_handle.SetValue(async_result->GetEvent().GetReadableHandle());
out_async.SetValue(std::move(async_result));
return ResultSuccess();
}
Result SystemUpdateService::GetPrepareUpdateProgress(sf::Out<SystemUpdateProgress> out) {
/* Ensure the update is setup. */
R_UNLESS(this->setup_update, ns::ResultCardUpdateNotSetup());
/* Get the progress. */
auto install_progress = this->update_task->GetProgress();
out.SetValue({ .current_size = install_progress.installed_size, .total_size = install_progress.total_size });
return ResultSuccess();
}
Result SystemUpdateService::HasPreparedUpdate(sf::Out<bool> out) {
/* Ensure the update is setup. */
R_UNLESS(this->setup_update, ns::ResultCardUpdateNotSetup());
out.SetValue(this->update_task->GetProgress().state == ncm::InstallProgressState::Downloaded);
return ResultSuccess();
}
Result SystemUpdateService::ApplyPreparedUpdate() {
/* Ensure the update is setup. */
R_UNLESS(this->setup_update, ns::ResultCardUpdateNotSetup());
/* Ensure the update is prepared. */
R_UNLESS(this->update_task->GetProgress().state == ncm::InstallProgressState::Downloaded, ns::ResultCardUpdateNotPrepared());
/* Apply the task. */
R_TRY(this->apply_manager.ApplyPackageTask(std::addressof(*this->update_task)));
return ResultSuccess();
}
Result SystemUpdateService::SetupUpdateImpl(os::ManagedHandle transfer_memory, u64 transfer_memory_size, const ncm::Path &path, bool exfat, ncm::FirmwareVariationId firmware_variation_id) {
/* Ensure we don't already have an update set up. */
R_UNLESS(!this->setup_update, ns::ResultCardUpdateAlreadySetup());
/* Destroy any existing update tasks. */
nim::SystemUpdateTaskId id;
auto count = nim::ListSystemUpdateTask(std::addressof(id), 1);
if (count > 0) {
R_TRY(nim::DestroySystemUpdateTask(id));
}
/* Initialize the update task. */
R_TRY(InitializeUpdateTask(transfer_memory, transfer_memory_size, path, exfat, firmware_variation_id));
/* The update is now set up. */
this->setup_update = true;
return ResultSuccess();
}
Result SystemUpdateService::InitializeUpdateTask(os::ManagedHandle &transfer_memory_handle, u64 transfer_memory_size, const ncm::Path &path, bool exfat, ncm::FirmwareVariationId firmware_variation_id) {
/* Map the transfer memory. */
const size_t tmem_buffer_size = static_cast<size_t>(transfer_memory_size);
this->update_transfer_memory.emplace(tmem_buffer_size, transfer_memory_handle.Get(), true);
void *tmem_buffer;
R_TRY(this->update_transfer_memory->Map(std::addressof(tmem_buffer), os::MemoryPermission_None));
auto tmem_guard = SCOPE_GUARD {
this->update_transfer_memory->Unmap();
this->update_transfer_memory = std::nullopt;
};
/* Now that the memory is mapped, the input handle is managed and can be released. */
transfer_memory_handle.Detach();
/* Adjust the package root. */
ncm::Path package_root;
R_TRY(FormatUserPackagePath(std::addressof(package_root), path));
/* Ensure that we can create an update context. */
R_TRY(fs::EnsureDirectoryRecursively("@Sdcard:/atmosphere/update/"));
const char *context_path = "@Sdcard:/atmosphere/update/cup.ctx";
/* Create and initialize the update task. */
this->update_task.emplace();
R_TRY(this->update_task->Initialize(package_root.str, context_path, tmem_buffer, tmem_buffer_size, exfat, firmware_variation_id));
/* We successfully setup the update. */
tmem_guard.Cancel();
return ResultSuccess();
}
}

View File

@@ -0,0 +1,87 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "sysupdater_i_async_result.hpp"
#include "sysupdater_apply_manager.hpp"
namespace ams::mitm::sysupdater {
constexpr inline size_t FirmwareVariationCountMax = 16;
struct UpdateInformation {
u32 version;
bool exfat_supported;
u32 firmware_variation_count;
ncm::FirmwareVariationId firmware_variation_ids[FirmwareVariationCountMax];
};
struct UpdateValidationInfo {
ncm::ContentMetaKey invalid_key;
ncm::ContentId invalid_content_id;
};
struct SystemUpdateProgress {
s64 current_size;
s64 total_size;
};
class SystemUpdateService final : public sf::IServiceObject {
private:
enum class CommandId {
GetUpdateInformation = 0,
ValidateUpdate = 1,
SetupUpdate = 2,
SetupUpdateWithVariation = 3,
RequestPrepareUpdate = 4,
GetPrepareUpdateProgress = 5,
HasPreparedUpdate = 6,
ApplyPreparedUpdate = 7,
};
private:
SystemUpdateApplyManager apply_manager;
std::optional<ncm::PackageSystemDowngradeTask> update_task;
std::optional<os::TransferMemory> update_transfer_memory;
bool setup_update;
bool requested_update;
public:
constexpr SystemUpdateService() : apply_manager(), update_task(), update_transfer_memory(), setup_update(false), requested_update(false) { /* ... */ }
private:
Result SetupUpdateImpl(os::ManagedHandle transfer_memory, u64 transfer_memory_size, const ncm::Path &path, bool exfat, ncm::FirmwareVariationId firmware_variation_id);
Result InitializeUpdateTask(os::ManagedHandle &transfer_memory, u64 transfer_memory_size, const ncm::Path &path, bool exfat, ncm::FirmwareVariationId firmware_variation_id);
private:
Result GetUpdateInformation(sf::Out<UpdateInformation> out, const ncm::Path &path);
Result ValidateUpdate(sf::Out<Result> out_validate_result, sf::Out<UpdateValidationInfo> out_validate_info, const ncm::Path &path);
Result SetupUpdate(sf::CopyHandle transfer_memory, u64 transfer_memory_size, const ncm::Path &path, bool exfat);
Result SetupUpdateWithVariation(sf::CopyHandle transfer_memory, u64 transfer_memory_size, const ncm::Path &path, bool exfat, ncm::FirmwareVariationId firmware_variation_id);
Result RequestPrepareUpdate(sf::OutCopyHandle out_event_handle, sf::Out<std::shared_ptr<IAsyncResult>> out_async);
Result GetPrepareUpdateProgress(sf::Out<SystemUpdateProgress> out);
Result HasPreparedUpdate(sf::Out<bool> out);
Result ApplyPreparedUpdate();
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(GetUpdateInformation),
MAKE_SERVICE_COMMAND_META(ValidateUpdate),
MAKE_SERVICE_COMMAND_META(SetupUpdate),
MAKE_SERVICE_COMMAND_META(SetupUpdateWithVariation),
MAKE_SERVICE_COMMAND_META(RequestPrepareUpdate),
MAKE_SERVICE_COMMAND_META(GetPrepareUpdateProgress),
MAKE_SERVICE_COMMAND_META(HasPreparedUpdate),
MAKE_SERVICE_COMMAND_META(ApplyPreparedUpdate),
};
};
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "sysupdater_thread_allocator.hpp"
namespace ams::mitm::sysupdater {
Result ThreadAllocator::Allocate(ThreadInfo *out) {
std::scoped_lock lk(this->mutex);
for (int i = 0; i < this->thread_count; ++i) {
const u64 mask = (static_cast<u64>(1) << i);
if ((this->bitmap & mask) == 0) {
*out = {
.thread = this->thread_list + i,
.priority = this->thread_priority,
.stack = this->stack_heap + (this->stack_size * i),
.stack_size = this->stack_size,
};
this->bitmap |= mask;
return ResultSuccess();
}
}
return ns::ResultOutOfMaxRunningTask();
}
void ThreadAllocator::Free(const ThreadInfo &info) {
std::scoped_lock lk(this->mutex);
for (int i = 0; i < this->thread_count; ++i) {
if (info.thread == std::addressof(this->thread_list[i])) {
const u64 mask = (static_cast<u64>(1) << i);
this->bitmap &= ~mask;
return;
}
}
AMS_ABORT("Invalid thread passed to ThreadAllocator::Free");
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::mitm::sysupdater {
struct ThreadInfo {
os::ThreadType *thread;
int priority;
void *stack;
size_t stack_size;
};
/* NOTE: Nintendo uses a util::BitArray, but this seems excessive. */
class ThreadAllocator {
private:
os::ThreadType *thread_list;
const int thread_priority;
const int thread_count;
u8 *stack_heap;
const size_t stack_heap_size;
const size_t stack_size;
u64 bitmap;
os::SdkMutex mutex;
public:
constexpr ThreadAllocator(os::ThreadType *thread_list, int count, int priority, u8 *stack_heap, size_t stack_heap_size, size_t stack_size)
: thread_list(thread_list), thread_priority(priority), thread_count(count), stack_heap(stack_heap), stack_heap_size(stack_heap_size), stack_size(stack_size), bitmap()
{
AMS_ASSERT(count <= static_cast<int>(stack_heap_size / stack_size));
AMS_ASSERT(count <= static_cast<int>(BITSIZEOF(this->bitmap)));
}
Result Allocate(ThreadInfo *out);
void Free(const ThreadInfo &info);
};
}

View File

@@ -138,10 +138,7 @@ int main(int argc, char **argv)
settings::system::GetSerialNumber(std::addressof(serial_number)); settings::system::GetSerialNumber(std::addressof(serial_number));
char os_private[0x60]; char os_private[0x60];
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-truncation"
const auto os_priv_len = std::snprintf(os_private, sizeof(os_private), "%s (%.8s)", firmware_version.display_name, firmware_version.revision); const auto os_priv_len = std::snprintf(os_private, sizeof(os_private), "%s (%.8s)", firmware_version.display_name, firmware_version.revision);
#pragma GCC diagnostic pop
AMS_ASSERT(static_cast<size_t>(os_priv_len) < sizeof(os_private)); AMS_ASSERT(static_cast<size_t>(os_priv_len) < sizeof(os_private));
R_ABORT_UNLESS(erpt::srv::SetSerialNumberAndOsVersion(serial_number.str, R_ABORT_UNLESS(erpt::srv::SetSerialNumberAndOsVersion(serial_number.str,