Compare commits
7 Commits
1.2.0
...
sysupdater
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
838492c84c | ||
|
|
76fa4db2ed | ||
|
|
015537f9bf | ||
|
|
d47e9ec9fd | ||
|
|
4291d81642 | ||
|
|
dc9c9284e2 | ||
|
|
aa9ba17986 |
@@ -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>
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
19
libraries/libstratosphere/include/stratosphere/err.hpp
Normal file
19
libraries/libstratosphere/include/stratosphere/err.hpp
Normal 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>
|
||||||
@@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
185
libraries/libstratosphere/source/os/os_transfer_memory_api.cpp
Normal file
185
libraries/libstratosphere/source/os/os_transfer_memory_api.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
26
libraries/libvapours/include/vapours/results/nim_results.hpp
Normal file
26
libraries/libvapours/include/vapours/results/nim_results.hpp
Normal 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);
|
||||||
|
|
||||||
|
}
|
||||||
31
libraries/libvapours/include/vapours/results/ns_results.hpp
Normal file
31
libraries/libvapours/include/vapours/results/ns_results.hpp
Normal 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);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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>;
|
||||||
|
|||||||
@@ -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>(),
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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; }
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
|
||||||
|
}
|
||||||
247
stratosphere/ams_mitm/source/sysupdater/sysupdater_fs_utils.cpp
Normal file
247
stratosphere/ams_mitm/source/sysupdater/sysupdater_fs_utils.cpp
Normal 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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));
|
||||||
|
|
||||||
|
}
|
||||||
517
stratosphere/ams_mitm/source/sysupdater/sysupdater_service.cpp
Normal file
517
stratosphere/ams_mitm/source/sysupdater/sysupdater_service.cpp
Normal 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user