Compare commits
13 Commits
1.6.1-prer
...
1.6.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f7bf379cfe | ||
|
|
9f26419b1a | ||
|
|
1b057d48c6 | ||
|
|
0c3afff4d3 | ||
|
|
274f6b63f2 | ||
|
|
2ed8450446 | ||
|
|
60974a5f4e | ||
|
|
fa384fd920 | ||
|
|
3f19db0d96 | ||
|
|
a84f725e21 | ||
|
|
7f61dfdb8d | ||
|
|
c44da84869 | ||
|
|
7f4450f930 |
@@ -84,7 +84,7 @@ dist-no-debug: package3 $(CURRENT_DIRECTORY)/$(ATMOSPHERE_OUT_DIR)
|
||||
mkdir -p $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/0100000000000034
|
||||
mkdir -p $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/0100000000000036
|
||||
mkdir -p $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/0100000000000037
|
||||
#mkdir -p $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/010000000000003c
|
||||
mkdir -p $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/010000000000003c
|
||||
mkdir -p $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/0100000000000042
|
||||
mkdir -p $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/0100000000000420
|
||||
mkdir -p $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/010000000000b240
|
||||
@@ -98,7 +98,7 @@ dist-no-debug: package3 $(CURRENT_DIRECTORY)/$(ATMOSPHERE_OUT_DIR)
|
||||
cp stratosphere/fatal/$(ATMOSPHERE_OUT_DIR)/fatal.nsp $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/0100000000000034/exefs.nsp
|
||||
cp stratosphere/creport/$(ATMOSPHERE_OUT_DIR)/creport.nsp $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/0100000000000036/exefs.nsp
|
||||
cp stratosphere/ro/$(ATMOSPHERE_OUT_DIR)/ro.nsp $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/0100000000000037/exefs.nsp
|
||||
#cp stratosphere/jpegdec/$(ATMOSPHERE_OUT_DIR)/jpegdec.nsp $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/010000000000003c/exefs.nsp
|
||||
cp stratosphere/jpegdec/$(ATMOSPHERE_OUT_DIR)/jpegdec.nsp $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/010000000000003c/exefs.nsp
|
||||
cp stratosphere/pgl/$(ATMOSPHERE_OUT_DIR)/pgl.nsp $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/0100000000000042/exefs.nsp
|
||||
cp stratosphere/LogManager/$(ATMOSPHERE_OUT_DIR)/LogManager.nsp $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/0100000000000420/exefs.nsp
|
||||
cp stratosphere/htc/$(ATMOSPHERE_OUT_DIR)/htc.nsp $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/010000000000b240/exefs.nsp
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
# Changelog
|
||||
## 1.6.2
|
||||
+ Support was finished for 17.0.0.
|
||||
+ `erpt` was updated to support the latest official behavior.
|
||||
+ `jpegdec` was updated to support the latest official behavior.
|
||||
+ `pm` was updated to support the latest official behavior.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
## 1.6.1
|
||||
+ An improved solution to [the problem that would cause consoles which had previously re-built their SYSTEM partition to brick on update-to-17.0.0](https://gist.github.com/SciresM/2ddb708c812ed585c4d99f54e25205ff) was added.
|
||||
+ In particular, booting atmosphère will now automatically detect the problem and unbrick any consoles which have fallen into this state.
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
[subrepo]
|
||||
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
|
||||
branch = master
|
||||
commit = 965e05b3cc5ea74de7e3071c3554135e828ce42e
|
||||
parent = 183f3e0d7e0c620d213be53c66d9156f55389be2
|
||||
commit = 80bf6aeeed03df0871329e1ec32da073a62bf102
|
||||
parent = 9f26419b1a669762d995ebf21ff83b6e1cf56153
|
||||
method = merge
|
||||
cmdver = 0.4.1
|
||||
|
||||
@@ -66,7 +66,7 @@ endif
|
||||
|
||||
|
||||
ifeq ($(ATMOSPHERE_BOARD),nx-hac-001)
|
||||
export LDFLAGS = -specs=$(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/stratosphere.specs -specs=$(DEVKITPRO)/libnx/switch.specs $(CXXFLAGS) $(CXXWRAPS) $(CXXREQUIRED) -Wl,-Map,$(notdir $*.map)
|
||||
export LDFLAGS = -specs=$(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/stratosphere.specs -specs=$(DEVKITPRO)/libnx/switch.specs $(CXXFLAGS) $(CXXWRAPS) $(CXXREQUIRED) -Wl,-Map,$(notdir $*.map) -Wl,-z,relro,-z,now
|
||||
else ifeq ($(ATMOSPHERE_OS_NAME),macos)
|
||||
export LDFLAGS = $(CXXFLAGS) $(CXXWRAPS) $(CXXREQUIRED) -Wl,-map,$(notdir $@.map)
|
||||
else
|
||||
|
||||
17
libraries/libstratosphere/include/stratosphere/fat.hpp
Normal file
17
libraries/libstratosphere/include/stratosphere/fat.hpp
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (c) 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/fat/fat_file_system.hpp>
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 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>
|
||||
|
||||
namespace ams::fat {
|
||||
|
||||
constexpr inline size_t FatErrorNameMaxLength = 0x10;
|
||||
|
||||
struct FatError {
|
||||
int error;
|
||||
int extra_error;
|
||||
int drive_id;
|
||||
char name[FatErrorNameMaxLength];
|
||||
u8 reserved[4];
|
||||
};
|
||||
static_assert(sizeof(FatError) == 0x20);
|
||||
static_assert(util::is_pod<FatError>::value);
|
||||
|
||||
struct FatReportInfo1 {
|
||||
u16 open_file_peak_count;
|
||||
u16 open_directory_peak_count;
|
||||
};
|
||||
static_assert(sizeof(FatReportInfo1) == 4);
|
||||
static_assert(util::is_pod<FatReportInfo1>::value);
|
||||
|
||||
struct FatReportInfo2 {
|
||||
u16 open_unique_file_entry_peak_count;
|
||||
u16 open_unique_directory_entry_peak_count;
|
||||
};
|
||||
static_assert(sizeof(FatReportInfo2) == 4);
|
||||
static_assert(util::is_pod<FatReportInfo2>::value);
|
||||
|
||||
struct FatSafeInfo {
|
||||
u32 result;
|
||||
u32 error_number;
|
||||
u32 safe_error_number;
|
||||
};
|
||||
static_assert(sizeof(FatSafeInfo) == 12);
|
||||
static_assert(util::is_pod<FatSafeInfo>::value);
|
||||
|
||||
}
|
||||
@@ -51,9 +51,11 @@
|
||||
#include <stratosphere/fs/fs_code.hpp>
|
||||
#include <stratosphere/fs/fs_content.hpp>
|
||||
#include <stratosphere/fs/fs_content_storage.hpp>
|
||||
#include <stratosphere/fs/fs_error_info.hpp>
|
||||
#include <stratosphere/fs/fs_game_card.hpp>
|
||||
#include <stratosphere/fs/fs_host.hpp>
|
||||
#include <stratosphere/fs/fs_image_directory.hpp>
|
||||
#include <stratosphere/fs/fs_mmc.hpp>
|
||||
#include <stratosphere/fs/fs_save_data_types.hpp>
|
||||
#include <stratosphere/fs/fs_save_data_management.hpp>
|
||||
#include <stratosphere/fs/fs_save_data_transaction.hpp>
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 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/fat/fat_file_system.hpp>
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
struct StorageErrorInfo {
|
||||
u32 num_activation_failures;
|
||||
u32 num_activation_error_corrections;
|
||||
u32 num_read_write_failures;
|
||||
u32 num_read_write_error_corrections;
|
||||
};
|
||||
static_assert(sizeof(StorageErrorInfo) == 0x10);
|
||||
static_assert(util::is_pod<StorageErrorInfo>::value);
|
||||
|
||||
struct FileSystemProxyErrorInfo {
|
||||
u32 rom_fs_remount_for_data_corruption_count;
|
||||
u32 rom_fs_unrecoverable_data_corruption_by_remount_count;
|
||||
fat::FatError fat_fs_error;
|
||||
u32 rom_fs_recovered_by_invalidate_cache_count;
|
||||
u32 save_data_index_count;
|
||||
fat::FatReportInfo1 bis_system_fat_report_info_1;
|
||||
fat::FatReportInfo1 bis_user_fat_report_info_1;
|
||||
fat::FatReportInfo1 sd_card_fat_report_info_1;
|
||||
fat::FatReportInfo2 bis_system_fat_report_info_2;
|
||||
fat::FatReportInfo2 bis_user_fat_report_info_2;
|
||||
fat::FatReportInfo2 sd_card_fat_report_info_2;
|
||||
u32 rom_fs_deep_retry_start_count;
|
||||
u32 rom_fs_unrecoverable_by_game_card_access_failed_count;
|
||||
fat::FatSafeInfo bis_system_fat_safe_info;
|
||||
fat::FatSafeInfo bis_user_fat_safe_info;
|
||||
|
||||
u8 reserved[0x18];
|
||||
};
|
||||
static_assert(sizeof(FileSystemProxyErrorInfo) == 0x80);
|
||||
static_assert(util::is_pod<FileSystemProxyErrorInfo>::value);
|
||||
|
||||
Result GetAndClearMmcErrorInfo(StorageErrorInfo *out_sei, size_t *out_log_size, char *out_log_buffer, size_t log_buffer_size);
|
||||
Result GetAndClearSdCardErrorInfo(StorageErrorInfo *out_sei, size_t *out_log_size, char *out_log_buffer, size_t log_buffer_size);
|
||||
|
||||
Result GetAndClearFileSystemProxyErrorInfo(FileSystemProxyErrorInfo *out);
|
||||
|
||||
}
|
||||
@@ -19,6 +19,9 @@
|
||||
namespace ams::fs {
|
||||
|
||||
/* ACCURATE_TO_VERSION: Unknown */
|
||||
constexpr inline size_t GameCardCidSize = 0x10;
|
||||
constexpr inline size_t GameCardDeviceIdSize = 0x10;
|
||||
|
||||
enum class GameCardPartition {
|
||||
Update = 0,
|
||||
Normal = 1,
|
||||
@@ -47,9 +50,44 @@ namespace ams::fs {
|
||||
Terra = 1,
|
||||
};
|
||||
|
||||
struct GameCardErrorReportInfo {
|
||||
u16 game_card_crc_error_num;
|
||||
u16 reserved1;
|
||||
u16 asic_crc_error_num;
|
||||
u16 reserved2;
|
||||
u16 refresh_num;
|
||||
u16 reserved3;
|
||||
u16 retry_limit_out_num;
|
||||
u16 timeout_retry_num;
|
||||
u16 asic_reinitialize_failure_detail;
|
||||
u16 insertion_count;
|
||||
u16 removal_count;
|
||||
u16 asic_reinitialize_num;
|
||||
u32 initialize_count;
|
||||
u16 asic_reinitialize_failure_num;
|
||||
u16 awaken_failure_num;
|
||||
u16 reserved4;
|
||||
u16 refresh_succeeded_count;
|
||||
u32 last_read_error_page_address;
|
||||
u32 last_read_error_page_count;
|
||||
u32 awaken_count;
|
||||
u32 read_count_from_insert;
|
||||
u32 read_count_from_awaken;
|
||||
u8 reserved5[8];
|
||||
};
|
||||
static_assert(util::is_pod<GameCardErrorReportInfo>::value);
|
||||
static_assert(sizeof(GameCardErrorReportInfo) == 0x40);
|
||||
|
||||
using GameCardHandle = u32;
|
||||
|
||||
Result GetGameCardHandle(GameCardHandle *out);
|
||||
Result MountGameCardPartition(const char *name, GameCardHandle handle, GameCardPartition partition);
|
||||
|
||||
Result GetGameCardCid(void *dst, size_t size);
|
||||
Result GetGameCardDeviceId(void *dst, size_t size);
|
||||
|
||||
Result GetGameCardErrorReportInfo(GameCardErrorReportInfo *out);
|
||||
|
||||
bool IsGameCardInserted();
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 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>
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
struct MemoryReportInfo {
|
||||
u64 pooled_buffer_peak_free_size;
|
||||
u64 pooled_buffer_retried_count;
|
||||
u64 pooled_buffer_reduce_allocation_count;
|
||||
u64 buffer_manager_peak_free_size;
|
||||
u64 buffer_manager_retried_count;
|
||||
u64 exp_heap_peak_free_size;
|
||||
u64 buffer_pool_peak_free_size;
|
||||
u64 patrol_read_allocate_buffer_success_count;
|
||||
u64 patrol_read_allocate_buffer_failure_count;
|
||||
u64 buffer_manager_peak_total_allocatable_size;
|
||||
u64 buffer_pool_max_allocate_size;
|
||||
u64 pooled_buffer_failed_ideal_allocation_count_on_async_access;
|
||||
|
||||
u8 reserved[0x20];
|
||||
};
|
||||
static_assert(sizeof(MemoryReportInfo) == 0x80);
|
||||
static_assert(util::is_pod<MemoryReportInfo>::value);
|
||||
|
||||
Result GetAndClearMemoryReportInfo(MemoryReportInfo *out);
|
||||
|
||||
}
|
||||
57
libraries/libstratosphere/include/stratosphere/fs/fs_mmc.hpp
Normal file
57
libraries/libstratosphere/include/stratosphere/fs/fs_mmc.hpp
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 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>
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
constexpr inline size_t MmcCidSize = 0x10;
|
||||
|
||||
constexpr inline size_t MmcExtendedCsdSize = 0x200;
|
||||
|
||||
constexpr inline int MmcExtendedCsdOffsetReEolInfo = 267;
|
||||
constexpr inline int MmcExtendedCsdOffsetDeviceLifeTimeEstTypA = 268;
|
||||
constexpr inline int MmcExtendedCsdOffsetDeviceLifeTimeEstTypB = 269;
|
||||
|
||||
enum MmcSpeedMode {
|
||||
MmcSpeedMode_Identification = 0,
|
||||
MmcSpeedMode_LegacySpeed = 1,
|
||||
MmcSpeedMode_HighSpeed = 2,
|
||||
MmcSpeedMode_Hs200 = 3,
|
||||
MmcSpeedMode_Hs400 = 4,
|
||||
MmcSpeedMode_Unknown = 5,
|
||||
};
|
||||
|
||||
enum class MmcPartition {
|
||||
UserData = 0,
|
||||
BootPartition1 = 1,
|
||||
BootPartition2 = 2,
|
||||
};
|
||||
|
||||
Result GetMmcCid(void *dst, size_t size);
|
||||
|
||||
inline void ClearMmcCidSerialNumber(u8 *cid) {
|
||||
/* Clear the serial number from the cid. */
|
||||
std::memset(cid + 1, 0, 4);
|
||||
}
|
||||
|
||||
Result GetMmcSpeedMode(MmcSpeedMode *out);
|
||||
|
||||
Result GetMmcPatrolCount(u32 *out);
|
||||
|
||||
Result GetMmcExtendedCsd(void *dst, size_t size);
|
||||
|
||||
}
|
||||
@@ -21,12 +21,38 @@ namespace ams::fs {
|
||||
/* ACCURATE_TO_VERSION: Unknown */
|
||||
class IEventNotifier;
|
||||
|
||||
constexpr inline size_t SdCardCidSize = 0x10;
|
||||
|
||||
enum SdCardSpeedMode {
|
||||
SdCardSpeedMode_Identification = 0,
|
||||
SdCardSpeedMode_DefaultSpeed = 1,
|
||||
SdCardSpeedMode_HighSpeed = 2,
|
||||
SdCardSpeedMode_Sdr12 = 3,
|
||||
SdCardSpeedMode_Sdr25 = 4,
|
||||
SdCardSpeedMode_Sdr50 = 5,
|
||||
SdCardSpeedMode_Sdr104 = 6,
|
||||
SdCardSpeedMode_Ddr50 = 7,
|
||||
SdCardSpeedMode_Unknown = 8,
|
||||
};
|
||||
|
||||
struct EncryptionSeed {
|
||||
char value[0x10];
|
||||
};
|
||||
static_assert(util::is_pod<EncryptionSeed>::value);
|
||||
static_assert(sizeof(EncryptionSeed) == 0x10);
|
||||
|
||||
Result GetSdCardCid(void *dst, size_t size);
|
||||
|
||||
inline void ClearSdCardCidSerialNumber(u8 *cid) {
|
||||
/* Clear the serial number from the cid. */
|
||||
std::memset(cid + 2, 0, 4);
|
||||
}
|
||||
|
||||
Result GetSdCardUserAreaSize(s64 *out);
|
||||
Result GetSdCardProtectedAreaSize(s64 *out);
|
||||
|
||||
Result GetSdCardSpeedMode(SdCardSpeedMode *out);
|
||||
|
||||
Result MountSdCard(const char *name);
|
||||
|
||||
Result MountSdCardErrorReportDirectoryForAtmosphere(const char *name);
|
||||
|
||||
@@ -121,6 +121,7 @@ namespace ams::fssrv {
|
||||
Result IsSignedSystemPartitionOnSdCardValid(ams::sf::Out<bool> out);
|
||||
Result OpenAccessFailureDetectionEventNotifier();
|
||||
/* ... */
|
||||
Result GetAndClearErrorInfo(ams::sf::Out<fs::FileSystemProxyErrorInfo> out);
|
||||
Result RegisterProgramIndexMapInfo(const ams::sf::InBuffer &buffer, s32 count);
|
||||
Result SetBisRootForHost(u32 id, const fssrv::sf::FspPath &path);
|
||||
Result SetSaveDataSize(s64 size, s64 journal_size);
|
||||
@@ -131,6 +132,7 @@ namespace ams::fssrv {
|
||||
Result OutputAccessLogToSdCard(const ams::sf::InBuffer &buf);
|
||||
Result RegisterUpdatePartition();
|
||||
Result OpenRegisteredUpdatePartition(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out);
|
||||
Result GetAndClearMemoryReportInfo(ams::sf::Out<fs::MemoryReportInfo> out);
|
||||
/* ... */
|
||||
Result GetProgramIndexForAccessLog(ams::sf::Out<u32> out_idx, ams::sf::Out<u32> out_count);
|
||||
Result GetFsStackUsage(ams::sf::Out<u32> out, u32 type);
|
||||
|
||||
@@ -16,12 +16,27 @@
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/sf.hpp>
|
||||
#include <stratosphere/fs/fs_error_info.hpp>
|
||||
#include <stratosphere/fs/fs_game_card.hpp>
|
||||
|
||||
/* TODO */
|
||||
/* ACCURATE_TO_VERSION: 13.4.0.0 */
|
||||
#define AMS_FSSRV_I_DEVICE_OPERATOR_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 0, Result, IsSdCardInserted, (ams::sf::Out<bool> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 200, Result, IsGameCardInserted, (ams::sf::Out<bool> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 202, Result, GetGameCardHandle, (ams::sf::Out<u32> out), (out))
|
||||
AMS_SF_METHOD_INFO(C, H, 0, Result, IsSdCardInserted, (ams::sf::Out<bool> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 1, Result, GetSdCardSpeedMode, (ams::sf::Out<s64> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 2, Result, GetSdCardCid, (ams::sf::OutBuffer out, s64 size), (out, size)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 3, Result, GetSdCardUserAreaSize, (ams::sf::Out<s64> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 4, Result, GetSdCardProtectedAreaSize, (ams::sf::Out<s64> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 5, Result, GetAndClearSdCardErrorInfo, (ams::sf::Out<fs::StorageErrorInfo> out_sei, ams::sf::Out<s64> out_size, ams::sf::OutBuffer out_buf, s64 size), (out_sei, out_size, out_buf, size)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 100, Result, GetMmcCid, (ams::sf::OutBuffer out, s64 size), (out, size)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 101, Result, GetMmcSpeedMode, (ams::sf::Out<s64> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 112, Result, GetMmcPatrolCount, (ams::sf::Out<u32> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 113, Result, GetAndClearMmcErrorInfo, (ams::sf::Out<fs::StorageErrorInfo> out_sei, ams::sf::Out<s64> out_size, ams::sf::OutBuffer out_buf, s64 size), (out_sei, out_size, out_buf, size)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 114, Result, GetMmcExtendedCsd, (ams::sf::OutBuffer out, s64 size), (out, size)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 200, Result, IsGameCardInserted, (ams::sf::Out<bool> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 202, Result, GetGameCardHandle, (ams::sf::Out<u32> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 208, Result, GetGameCardIdSet, (ams::sf::OutBuffer out, s64 size), (out, size)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 217, Result, GetGameCardErrorReportInfo, (ams::sf::Out<fs::GameCardErrorReportInfo> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 218, Result, GetGameCardDeviceId, (ams::sf::OutBuffer out, s64 size), (out, size))
|
||||
|
||||
AMS_SF_DEFINE_INTERFACE(ams::fssrv::sf, IDeviceOperator, AMS_FSSRV_I_DEVICE_OPERATOR_INTERFACE_INFO, 0x1484E21C)
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
#include <stratosphere/fssrv/sf/fssrv_sf_istorage.hpp>
|
||||
#include <stratosphere/fssrv/sf/fssrv_sf_i_device_operator.hpp>
|
||||
#include <stratosphere/fssrv/sf/fssrv_sf_i_event_notifier.hpp>
|
||||
#include <stratosphere/fs/fs_error_info.hpp>
|
||||
#include <stratosphere/fs/fs_memory_report_info.hpp>
|
||||
|
||||
/* ACCURATE_TO_VERSION: 13.4.0.0 */
|
||||
#define AMS_FSSRV_I_FILE_SYSTEM_PROXY_INTERFACE_INFO(C, H) \
|
||||
@@ -125,7 +127,7 @@
|
||||
/* AMS_SF_METHOD_INFO(C, H, 702, Result, IsAccessFailureDetected, (), (), hos::Version_5_0_0) */ \
|
||||
/* AMS_SF_METHOD_INFO(C, H, 710, Result, ResolveAccessFailure, (), (), hos::Version_5_0_0) */ \
|
||||
/* AMS_SF_METHOD_INFO(C, H, 720, Result, AbandonAccessFailure, (), (), hos::Version_5_0_0) */ \
|
||||
/* AMS_SF_METHOD_INFO(C, H, 800, Result, GetAndClearErrorInfo, (), (), hos::Version_2_0_0) */ \
|
||||
AMS_SF_METHOD_INFO(C, H, 800, Result, GetAndClearErrorInfo, (ams::sf::Out<fs::FileSystemProxyErrorInfo> out), (out), hos::Version_2_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 810, Result, RegisterProgramIndexMapInfo, (const ams::sf::InBuffer &buffer, s32 count), (buffer, count), hos::Version_7_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 1000, Result, SetBisRootForHost, (u32 id, const fssrv::sf::FspPath &path), (id, path), hos::Version_Min, hos::Version_9_2_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 1001, Result, SetSaveDataSize, (s64 size, s64 journal_size), (size, journal_size)) \
|
||||
@@ -136,7 +138,7 @@
|
||||
AMS_SF_METHOD_INFO(C, H, 1006, Result, OutputAccessLogToSdCard, (const ams::sf::InBuffer &buf), (buf)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 1007, Result, RegisterUpdatePartition, (), (), hos::Version_4_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 1008, Result, OpenRegisteredUpdatePartition, (ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out), (out), hos::Version_4_0_0) \
|
||||
/* AMS_SF_METHOD_INFO(C, H, 1009, Result, GetAndClearMemoryReportInfo, (ams::sf::Out<fs::MemoryReportInfo> out), (out), hos::Version_4_0_0) */ \
|
||||
AMS_SF_METHOD_INFO(C, H, 1009, Result, GetAndClearMemoryReportInfo, (ams::sf::Out<fs::MemoryReportInfo> out), (out), hos::Version_4_0_0) \
|
||||
/* AMS_SF_METHOD_INFO(C, H, 1010, Result, SetDataStorageRedirectTarget, (), (), hos::Version_5_1_0, hos::Version_6_2_0) */ \
|
||||
AMS_SF_METHOD_INFO(C, H, 1011, Result, GetProgramIndexForAccessLog, (ams::sf::Out<u32> out_idx, ams::sf::Out<u32> out_count), (out_idx, out_count), hos::Version_7_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 1012, Result, GetFsStackUsage, (ams::sf::Out<u32> out, u32 type), (out, type), hos::Version_9_0_0) \
|
||||
|
||||
@@ -18,3 +18,4 @@
|
||||
#include <stratosphere/gc/impl/gc_types.hpp>
|
||||
#include <stratosphere/gc/impl/gc_gc_crypto.hpp>
|
||||
#include <stratosphere/gc/impl/gc_embedded_data_holder.hpp>
|
||||
#include <stratosphere/gc/gc.hpp>
|
||||
|
||||
30
libraries/libstratosphere/include/stratosphere/gc/gc.hpp
Normal file
30
libraries/libstratosphere/include/stratosphere/gc/gc.hpp
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 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/gc/impl/gc_types.hpp>
|
||||
|
||||
namespace ams::gc {
|
||||
|
||||
struct GameCardIdSet {
|
||||
gc::impl::CardId1 id1;
|
||||
gc::impl::CardId2 id2;
|
||||
gc::impl::CardId3 id3;
|
||||
};
|
||||
static_assert(util::is_pod<GameCardIdSet>::value);
|
||||
static_assert(sizeof(GameCardIdSet) == 0xC);
|
||||
|
||||
}
|
||||
@@ -76,6 +76,11 @@ namespace ams::gc::impl {
|
||||
static_assert(util::is_pod<CardHeaderEncryptedData>::value);
|
||||
static_assert(sizeof(CardHeaderEncryptedData) == 0x70);
|
||||
|
||||
enum MakerCodeForCardId1 : u8 {
|
||||
MakerCodeForCardId1_MegaChips = 0xC2,
|
||||
MakerCodeForCardId1_Lapis = 0xAE,
|
||||
};
|
||||
|
||||
enum MemoryCapacity : u8 {
|
||||
MemoryCapacity_1GB = 0xFA,
|
||||
MemoryCapacity_2GB = 0xF8,
|
||||
@@ -85,6 +90,33 @@ namespace ams::gc::impl {
|
||||
MemoryCapacity_32GB = 0xE2,
|
||||
};
|
||||
|
||||
enum MemoryType : u8 {
|
||||
MemoryType_T1RomFast = 0x01,
|
||||
MemoryType_T2RomFast = 0x02,
|
||||
MemoryType_T1NandFast = 0x09,
|
||||
MemoryType_T2NandFast = 0x0A,
|
||||
MemoryType_T1RomLate = 0x21,
|
||||
MemoryType_T2RomLate = 0x22,
|
||||
MemoryType_T1NandLate = 0x29,
|
||||
MemoryType_T2NandLate = 0x2A,
|
||||
};
|
||||
|
||||
enum CardSecurityNumber : u8 {
|
||||
CardSecurityNumber_0 = 0x00,
|
||||
CardSecurityNumber_1 = 0x01,
|
||||
CardSecurityNumber_2 = 0x02,
|
||||
CardSecurityNumber_3 = 0x03,
|
||||
CardSecurityNumber_4 = 0x04,
|
||||
};
|
||||
|
||||
enum CardType : u8 {
|
||||
CardType_Rom = 0x00,
|
||||
CardType_Writable_Dev_T1 = 0x01,
|
||||
CardType_Writable_Prod_T1 = 0x02,
|
||||
CardType_Writable_Dev_T2 = 0x03,
|
||||
CardType_Writable_Prod_T2 = 0x04,
|
||||
};
|
||||
|
||||
enum AccessControl1ClockRate : u32 {
|
||||
AccessControl1ClockRate_25MHz = 0x00A10011,
|
||||
AccessControl1ClockRate_50MHz = 0x00A10010,
|
||||
@@ -95,6 +127,23 @@ namespace ams::gc::impl {
|
||||
SelSec_T2 = 2,
|
||||
};
|
||||
|
||||
struct CardId1 {
|
||||
MakerCodeForCardId1 maker_code;
|
||||
MemoryCapacity memory_capacity;
|
||||
u8 reserved;
|
||||
MemoryType memory_type;
|
||||
};
|
||||
|
||||
struct CardId2 {
|
||||
CardSecurityNumber card_security_number;
|
||||
CardType card_type;
|
||||
u8 reserved[2];
|
||||
};
|
||||
|
||||
struct CardId3 {
|
||||
u8 reserved[4];
|
||||
};
|
||||
|
||||
struct CardHeader {
|
||||
static constexpr u32 Magic = util::FourCC<'H','E','A','D'>::Code;
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
AMS_SF_METHOD_INFO(C, H, 4, Result, GetApplicationProcessId, (sf::Out<os::ProcessId> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 5, Result, HookToCreateApplicationProcess, (sf::OutCopyHandle out_hook), (out_hook)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 6, Result, ClearHook, (u32 which), (which), hos::Version_6_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 7, Result, GetProgramId, (sf::Out<ncm::ProgramId> out, os::ProcessId process_id), (out, process_id)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 7, Result, GetProgramId, (sf::Out<ncm::ProgramId> out, os::ProcessId process_id), (out, process_id)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 65000, Result, AtmosphereGetProcessInfo, (sf::OutCopyHandle out_process_handle, sf::Out<ncm::ProgramLocation> out_loc, sf::Out<cfg::OverrideStatus> out_status, os::ProcessId process_id), (out_process_handle, out_loc, out_status, process_id)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 65001, Result, AtmosphereGetCurrentLimitInfo, (sf::Out<s64> out_cur_val, sf::Out<s64> out_lim_val, u32 group, u32 resource), (out_cur_val, out_lim_val, group, resource))
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
|
||||
#define AMS_PM_I_INFORMATION_INTERFACE_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 0, Result, GetProgramId, (sf::Out<ncm::ProgramId> out, os::ProcessId process_id), (out, process_id)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 1, Result, GetAppletCurrentResourceLimitValues, (sf::Out<pm::ResourceLimitValues> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 2, Result, GetAppletPeakResourceLimitValues, (sf::Out<pm::ResourceLimitValues> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 1, Result, GetAppletResourceLimitCurrentValue, (sf::Out<pm::ResourceLimitValue> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 2, Result, GetAppletResourceLimitPeakValue, (sf::Out<pm::ResourceLimitValue> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 65000, Result, AtmosphereGetProcessId, (sf::Out<os::ProcessId> out, ncm::ProgramId program_id), (out, program_id)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 65001, Result, AtmosphereHasLaunchedBootProgram, (sf::Out<bool> out, ncm::ProgramId program_id), (out, program_id)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 65002, Result, AtmosphereGetProcessInfo, (sf::Out<ncm::ProgramLocation> out_loc, sf::Out<cfg::OverrideStatus> out_status, os::ProcessId process_id), (out_loc, out_status, process_id))
|
||||
|
||||
@@ -29,8 +29,8 @@ namespace ams::pm::info {
|
||||
Result GetProcessId(os::ProcessId *out_process_id, ncm::ProgramId program_id);
|
||||
Result HasLaunchedBootProgram(bool *out, ncm::ProgramId program_id);
|
||||
|
||||
Result GetAppletCurrentResourceLimitValues(pm::ResourceLimitValues *out);
|
||||
Result GetAppletPeakResourceLimitValues(pm::ResourceLimitValues *out);
|
||||
Result GetAppletResourceLimitCurrentValue(pm::ResourceLimitValue *out);
|
||||
Result GetAppletResourceLimitPeakValue(pm::ResourceLimitValue *out);
|
||||
|
||||
Result GetProcessInfo(ncm::ProgramLocation *out_loc, cfg::OverrideStatus *out_status, os::ProcessId process_id);
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace ams::pm {
|
||||
LaunchFlagsDeprecated_SignalOnStart = (1 << 5),
|
||||
};
|
||||
|
||||
struct ResourceLimitValues {
|
||||
struct ResourceLimitValue {
|
||||
u64 physical_memory;
|
||||
u32 thread_count;
|
||||
u32 event_count;
|
||||
|
||||
@@ -16,11 +16,16 @@
|
||||
#include <stratosphere.hpp>
|
||||
#include "decodersrv_decoder_server_object.hpp"
|
||||
#include "../jpeg/decodersrv_software_jpeg_decoder.hpp"
|
||||
#include "../jpeg/decodersrv_software_jpeg_shrinker.hpp"
|
||||
|
||||
namespace ams::capsrv::server {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr const int JpegShrinkQualities[] = {
|
||||
98, 95, 90, 80, 70, 60, 50, 40, 30, 20, 10, 0
|
||||
};
|
||||
|
||||
Result DecodeJpegImpl(void *dst, size_t dst_size, const void *src_jpeg, size_t src_jpeg_size, u32 width, u32 height, const ScreenShotDecodeOption &option, void *work, size_t work_size) {
|
||||
/* Clear the work memory. */
|
||||
std::memset(work, 0, work_size);
|
||||
@@ -67,6 +72,61 @@ namespace ams::capsrv::server {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ShrinkJpegImpl(u64 *out_size, void *dst, size_t dst_size, const void *src_jpeg, size_t src_jpeg_size, u32 width, u32 height, const ScreenShotDecodeOption &option, void *work, size_t work_size) {
|
||||
/* Validate parameters. */
|
||||
R_UNLESS(util::IsAligned(width, 0x10), capsrv::ResultAlbumOutOfRange());
|
||||
R_UNLESS(util::IsAligned(height, 0x4), capsrv::ResultAlbumOutOfRange());
|
||||
|
||||
R_UNLESS(dst != nullptr, capsrv::ResultInternalJpegOutBufferShortage());
|
||||
R_UNLESS(dst_size != 0, capsrv::ResultAlbumReadBufferShortage());
|
||||
|
||||
R_UNLESS(src_jpeg != nullptr, capsrv::ResultAlbumInvalidFileData());
|
||||
R_UNLESS(src_jpeg_size != 0, capsrv::ResultAlbumInvalidFileData());
|
||||
|
||||
/* Create the input. */
|
||||
const jpeg::SoftwareJpegShrinkerInput shrink_input = {
|
||||
.jpeg = src_jpeg,
|
||||
.jpeg_size = src_jpeg_size,
|
||||
.width = width,
|
||||
.height = height,
|
||||
.fancy_upsampling = option.HasJpegDecoderFlag(ScreenShotDecoderFlag_EnableFancyUpsampling),
|
||||
.block_smoothing = option.HasJpegDecoderFlag(ScreenShotDecoderFlag_EnableBlockSmoothing),
|
||||
};
|
||||
|
||||
/* Create the output. */
|
||||
u64 shrunk_size = 0;
|
||||
s32 shrunk_width = 0, shrunk_height = 0;
|
||||
jpeg::SoftwareJpegShrinkerOutput shrink_output = {
|
||||
.out_size = std::addressof(shrunk_size),
|
||||
.out_width = std::addressof(shrunk_width),
|
||||
.out_height = std::addressof(shrunk_height),
|
||||
.dst = dst,
|
||||
.dst_size = dst_size,
|
||||
};
|
||||
|
||||
/* Try to shrink the jpeg at various quality levels. */
|
||||
for (auto quality : JpegShrinkQualities) {
|
||||
/* Shrink at the current quality. */
|
||||
R_TRY_CATCH(jpeg::SoftwareJpegShrinker::ShrinkRgba8(shrink_output, shrink_input, quality, work, work_size)) {
|
||||
/* If the output buffer isn't large enough to fit the output, we should try at a lower quality. */
|
||||
R_CATCH(capsrv::ResultInternalJpegOutBufferShortage) {
|
||||
continue;
|
||||
}
|
||||
/* Nintendo doesn't catch this result, but our lack of work buffer use makes me think this may be necessary. */
|
||||
R_CATCH(capsrv::ResultInternalJpegWorkMemoryShortage) {
|
||||
continue;
|
||||
}
|
||||
} R_END_TRY_CATCH;
|
||||
|
||||
/* Write the output size. */
|
||||
*out_size = shrunk_size;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
/* Nintendo aborts if no quality succeeds. */
|
||||
AMS_ABORT("ShrinkJpeg should succeed before this point\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result DecoderControlService::DecodeJpeg(const ams::sf::OutNonSecureBuffer &out, const ams::sf::InBuffer &in, u32 width, u32 height, const ScreenShotDecodeOption &option) {
|
||||
@@ -78,4 +138,13 @@ namespace ams::capsrv::server {
|
||||
R_RETURN(DecodeJpegImpl(out.GetPointer(), out.GetSize(), in.GetPointer(), in.GetSize(), width, height, option, work, work_size));
|
||||
}
|
||||
|
||||
Result DecoderControlService::ShrinkJpeg(ams::sf::Out<u64> out_size, const ams::sf::OutNonSecureBuffer &out, const ams::sf::InBuffer &in, u32 width, u32 height, const capsrv::ScreenShotDecodeOption &option) {
|
||||
/* Get the work buffer. */
|
||||
void *work = g_work_memory.jpeg_decoder_memory;
|
||||
size_t work_size = sizeof(g_work_memory.jpeg_decoder_memory);
|
||||
|
||||
/* Call the shrink implementation. */
|
||||
R_RETURN(ShrinkJpegImpl(out_size.GetPointer(), out.GetPointer(), out.GetSize(), in.GetPointer(), in.GetSize(), width, height, option, work, work_size));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,7 +17,8 @@
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#define AMS_CAPSRV_DECODER_CONTROL_SERVICE_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 3001, Result, DecodeJpeg, (const ams::sf::OutNonSecureBuffer &out, const ams::sf::InBuffer &in, u32 width, u32 height, const capsrv::ScreenShotDecodeOption &option), (out, in, width, height, option))
|
||||
AMS_SF_METHOD_INFO(C, H, 3001, Result, DecodeJpeg, (const ams::sf::OutNonSecureBuffer &out, const ams::sf::InBuffer &in, u32 width, u32 height, const capsrv::ScreenShotDecodeOption &option), (out, in, width, height, option)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 4001, Result, ShrinkJpeg, (ams::sf::Out<u64> out_size, const ams::sf::OutNonSecureBuffer &out, const ams::sf::InBuffer &in, u32 width, u32 height, const capsrv::ScreenShotDecodeOption &option), (out_size, out, in, width, height, option))
|
||||
|
||||
AMS_SF_DEFINE_INTERFACE(ams::capsrv::sf, IDecoderControlService, AMS_CAPSRV_DECODER_CONTROL_SERVICE_INTERFACE_INFO, 0xD168E90B)
|
||||
|
||||
@@ -26,6 +27,7 @@ namespace ams::capsrv::server {
|
||||
class DecoderControlService final {
|
||||
public:
|
||||
Result DecodeJpeg(const ams::sf::OutNonSecureBuffer &out, const ams::sf::InBuffer &in, u32 width, u32 height, const ScreenShotDecodeOption &option);
|
||||
Result ShrinkJpeg(ams::sf::Out<u64> out_size, const ams::sf::OutNonSecureBuffer &out, const ams::sf::InBuffer &in, u32 width, u32 height, const capsrv::ScreenShotDecodeOption &option);
|
||||
};
|
||||
static_assert(capsrv::sf::IsIDecoderControlService<DecoderControlService>);
|
||||
|
||||
|
||||
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Copyright (c) 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 "decodersrv_software_jpeg_shrinker.hpp"
|
||||
#include "capsrv_server_jpeg_library_types.hpp"
|
||||
#include "capsrv_server_jpeg_error_handler.hpp"
|
||||
|
||||
|
||||
namespace ams::capsrv::server::jpeg {
|
||||
|
||||
#define CAPSRV_ABORT_UNLESS(expr) do { \
|
||||
const bool __capsrv_assert_res = (expr); \
|
||||
AMS_ASSERT(__capsrv_assert_res); \
|
||||
AMS_ABORT_UNLESS(__capsrv_assert_res); \
|
||||
} while (0)
|
||||
|
||||
#define CAPSRV_ASSERT(expr) do { \
|
||||
const bool __capsrv_assert_res = (expr); \
|
||||
AMS_ASSERT(__capsrv_assert_res); \
|
||||
R_UNLESS(__capsrv_assert_res, capsrv::ResultAlbumError()); \
|
||||
} while (0)
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr s32 ImageSizeHorizonalUnit = 0x10;
|
||||
constexpr s32 ImageSizeVerticalUnitDecode = 0x4;
|
||||
constexpr s32 ImageSizeVerticalUnitEncode = 0x8;
|
||||
|
||||
constexpr s32 RgbColorComponentCount = 3;
|
||||
constexpr s32 RgbaColorComponentCount = 4;
|
||||
|
||||
Result GetRgbBufferSize(size_t *out_size, size_t *out_stride, s32 width, size_t work_size) {
|
||||
/* Calculate the space we need and verify we have enough. */
|
||||
const size_t rgb_width = util::AlignUp(static_cast<size_t>(width), ImageSizeHorizonalUnit);
|
||||
const size_t rgb_stride = rgb_width * RgbColorComponentCount;
|
||||
const size_t rgb_size = rgb_stride * ImageSizeVerticalUnitEncode;
|
||||
R_UNLESS(work_size >= rgb_size, capsrv::ResultInternalJpegWorkMemoryShortage());
|
||||
|
||||
/* Return the output to the caller. */
|
||||
*out_size = rgb_size;
|
||||
*out_stride = rgb_stride;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void SetupEncodingParameter(JpegLibraryType::jpeg_compress_struct *cinfo, const SoftwareJpegShrinkerInput &input, int quality) {
|
||||
/* Set parameters. */
|
||||
cinfo->image_width = static_cast<JpegLibraryType::JDIMENSION>(input.width / 2);
|
||||
cinfo->image_height = static_cast<JpegLibraryType::JDIMENSION>(input.height / 2);
|
||||
cinfo->input_components = RgbColorComponentCount;
|
||||
cinfo->in_color_space = JpegLibraryType::J_COLOR_SPACE::JCS_RGB;
|
||||
|
||||
/* Set defaults/color space. */
|
||||
jpeg_set_defaults(cinfo);
|
||||
jpeg_set_colorspace(cinfo, JpegLibraryType::J_COLOR_SPACE::JCS_YCbCr);
|
||||
|
||||
/* Configure sampling. */
|
||||
/* libjpeg-turbo doesn't actually have this field, as of now. */
|
||||
/* cinfo->do_fancy_downsampling = false; */
|
||||
cinfo->comp_info[0].h_samp_factor = 2;
|
||||
cinfo->comp_info[0].v_samp_factor = 1;
|
||||
cinfo->comp_info[1].h_samp_factor = 1;
|
||||
cinfo->comp_info[1].v_samp_factor = 1;
|
||||
cinfo->comp_info[2].h_samp_factor = 1;
|
||||
cinfo->comp_info[2].v_samp_factor = 1;
|
||||
|
||||
/* Set the quality. */
|
||||
jpeg_set_quality(cinfo, quality, true);
|
||||
|
||||
/* Configure remaining parameters. */
|
||||
cinfo->dct_method = JpegLibraryType::J_DCT_METHOD::JDCT_ISLOW;
|
||||
cinfo->optimize_coding = false;
|
||||
cinfo->write_JFIF_header = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result SoftwareJpegShrinker::ShrinkRgba8(SoftwareJpegShrinkerOutput &output, const SoftwareJpegShrinkerInput &input, int quality, void *work, size_t work_size) {
|
||||
CAPSRV_ABORT_UNLESS(util::IsAligned(input.width, ImageSizeHorizonalUnit));
|
||||
CAPSRV_ABORT_UNLESS(util::IsAligned(input.height, ImageSizeVerticalUnitDecode));
|
||||
|
||||
const u32 shrunk_width = input.width / 2;
|
||||
const u32 shrunk_height = input.height / 2;
|
||||
CAPSRV_ABORT_UNLESS(util::IsAligned(shrunk_width, ImageSizeHorizonalUnit));
|
||||
|
||||
CAPSRV_ABORT_UNLESS(output.dst != nullptr);
|
||||
|
||||
CAPSRV_ABORT_UNLESS(output.out_width != nullptr);
|
||||
CAPSRV_ABORT_UNLESS(output.out_height != nullptr);
|
||||
|
||||
/* Determine work buffer extents. */
|
||||
char *work_start = static_cast<char *>(work);
|
||||
char *work_end = work_start + work_size;
|
||||
|
||||
/* Determine the buffer extents for our linebuffers. */
|
||||
u8 *rgb_buffer = static_cast<u8 *>(static_cast<void *>(work_start));
|
||||
size_t rgb_buffer_size;
|
||||
size_t rgb_buffer_stride;
|
||||
R_TRY(GetRgbBufferSize(std::addressof(rgb_buffer_size), std::addressof(rgb_buffer_stride), input.width, work_size));
|
||||
|
||||
/* The start of the workbuffer is reserved for linebuffer space. */
|
||||
work_start += rgb_buffer_size;
|
||||
|
||||
/* Create our compression structures. */
|
||||
JpegLibraryType::jpeg_decompress_struct dcinfo = {};
|
||||
JpegLibraryType::jpeg_compress_struct ecinfo = {};
|
||||
|
||||
/* Here nintendo creates a work buffer structure containing work_start + work_size. */
|
||||
/* This seems to be a custom patch for/to libjpeg-turbo. */
|
||||
/* It would be desirable for us to mimic this, because it gives Nintendo strong */
|
||||
/* fixed memory usage guarantees. */
|
||||
/* TODO: Determine if it is feasible for us to recreate this ourselves, */
|
||||
/* Either by adding support to the devkitPro libjpeg-turbo portlib or otherwise. */
|
||||
AMS_UNUSED(work_end);
|
||||
|
||||
/* Create our error managers. */
|
||||
JpegErrorHandler jerr_dc = { .result = ResultSuccess(), };
|
||||
JpegErrorHandler jerr_ec = { .result = ResultSuccess(), };
|
||||
jerr_dc.error_exit = JpegErrorHandler::HandleError,
|
||||
jerr_ec.error_exit = JpegErrorHandler::HandleError,
|
||||
|
||||
/* Link our error managers to our compression structures. */
|
||||
dcinfo.err = jpeg_std_error(std::addressof(jerr_dc));
|
||||
ecinfo.err = jpeg_std_error(std::addressof(jerr_ec));
|
||||
|
||||
/* Use setjmp, so that on error our handler will longjmp to return an error result. */
|
||||
if (setjmp(jerr_ec.jmp_buf) == 0) {
|
||||
if (setjmp(jerr_dc.jmp_buf) == 0) {
|
||||
/* Create our decompressor. */
|
||||
jpeg_create_decompress(std::addressof(dcinfo));
|
||||
ON_SCOPE_EXIT { jpeg_destroy_decompress(std::addressof(dcinfo)); };
|
||||
|
||||
/* Setup our memory reader, ensure the header is correct. */
|
||||
jpeg_mem_src(std::addressof(dcinfo), const_cast<unsigned char *>(static_cast<const unsigned char *>(input.jpeg)), input.jpeg_size);
|
||||
R_UNLESS(jpeg_read_header(std::addressof(dcinfo), true) == JPEG_HEADER_OK, capsrv::ResultAlbumInvalidFileData());
|
||||
|
||||
/* Ensure width and height are correct. */
|
||||
R_UNLESS(dcinfo.image_width == input.width, capsrv::ResultAlbumInvalidFileData());
|
||||
R_UNLESS(dcinfo.image_height == input.height, capsrv::ResultAlbumInvalidFileData());
|
||||
|
||||
/* Set output parameters. */
|
||||
dcinfo.out_color_space = JpegLibraryType::J_COLOR_SPACE::JCS_RGB;
|
||||
dcinfo.dct_method = JpegLibraryType::J_DCT_METHOD::JDCT_ISLOW;
|
||||
dcinfo.do_fancy_upsampling = input.fancy_upsampling;
|
||||
dcinfo.do_block_smoothing = input.block_smoothing;
|
||||
dcinfo.scale_num = 1;
|
||||
dcinfo.scale_denom = 2;
|
||||
|
||||
/* Start decompression. */
|
||||
R_UNLESS(jpeg_start_decompress(std::addressof(dcinfo)) == TRUE, capsrv::ResultAlbumInvalidFileData());
|
||||
|
||||
/* Check the parameters. */
|
||||
CAPSRV_ASSERT(dcinfo.output_width == shrunk_width);
|
||||
CAPSRV_ASSERT(dcinfo.output_height == shrunk_height);
|
||||
CAPSRV_ASSERT(dcinfo.out_color_components == RgbColorComponentCount);
|
||||
CAPSRV_ASSERT(dcinfo.output_components == RgbColorComponentCount);
|
||||
|
||||
/* Create our compressor. */
|
||||
jpeg_create_compress(std::addressof(ecinfo));
|
||||
ON_SCOPE_EXIT { jpeg_destroy_compress(std::addressof(ecinfo)); };
|
||||
|
||||
/* Setup our memory writer. */
|
||||
unsigned long out_size = static_cast<unsigned long>(output.dst_size);
|
||||
jpeg_mem_dest(std::addressof(ecinfo), reinterpret_cast<unsigned char **>(std::addressof(output.dst)), std::addressof(out_size));
|
||||
|
||||
/* Setup the encoding parameters. */
|
||||
SetupEncodingParameter(std::addressof(ecinfo), input, quality);
|
||||
|
||||
/* Start compression. */
|
||||
jpeg_start_compress(std::addressof(ecinfo), true);
|
||||
|
||||
/* Parse the scanlines. */
|
||||
{
|
||||
/* Create our linebuffer structure. */
|
||||
JpegLibraryType::JSAMPROW linebuffers[ImageSizeVerticalUnitEncode] = {};
|
||||
for (int i = 0; i < ImageSizeVerticalUnitEncode; i++) {
|
||||
linebuffers[i] = rgb_buffer + rgb_buffer_stride * i;
|
||||
}
|
||||
|
||||
/* While we still have scanlines, parse! */
|
||||
while (dcinfo.output_scanline < shrunk_height) {
|
||||
/* Determine remaining scanlines. */
|
||||
const auto remaining_scanlines = shrunk_height - dcinfo.output_scanline;
|
||||
const auto cur_max_scanlines = std::min<s32>(remaining_scanlines, ImageSizeVerticalUnitEncode);
|
||||
|
||||
/* If we have scanlines to decode, try to do so. */
|
||||
auto writable_scanlines = 0;
|
||||
while (writable_scanlines < cur_max_scanlines) {
|
||||
const auto decoded = jpeg_read_scanlines(std::addressof(dcinfo), linebuffers + writable_scanlines, ImageSizeVerticalUnitDecode);
|
||||
CAPSRV_ASSERT(decoded <= ImageSizeVerticalUnitDecode / 2);
|
||||
|
||||
writable_scanlines += decoded;
|
||||
}
|
||||
|
||||
/* If we have scanlines to write, try to do so. */
|
||||
jpeg_write_scanlines(std::addressof(ecinfo), linebuffers, writable_scanlines);
|
||||
}
|
||||
}
|
||||
|
||||
/* Finish the decompression. */
|
||||
R_UNLESS(jpeg_finish_decompress(std::addressof(dcinfo)) == TRUE, capsrv::ResultAlbumInvalidFileData());
|
||||
|
||||
/* Finish the compression. */
|
||||
jpeg_finish_compress(std::addressof(ecinfo));
|
||||
|
||||
/* Set the output size. */
|
||||
*output.out_size = out_size;
|
||||
} else {
|
||||
/* Some unknown error was caught by our handler. */
|
||||
R_THROW(capsrv::ResultAlbumInvalidFileData());
|
||||
}
|
||||
} else {
|
||||
/* Return the encoding result. */
|
||||
R_THROW(jerr_ec.result);
|
||||
}
|
||||
|
||||
/* Write the size we decoded to output. */
|
||||
*output.out_width = static_cast<s32>(dcinfo.output_width);
|
||||
*output.out_width = static_cast<s32>(dcinfo.output_height);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 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::capsrv::server::jpeg {
|
||||
|
||||
struct SoftwareJpegShrinkerInput {
|
||||
const void *jpeg;
|
||||
size_t jpeg_size;
|
||||
u32 width;
|
||||
u32 height;
|
||||
bool fancy_upsampling;
|
||||
bool block_smoothing;
|
||||
};
|
||||
|
||||
struct SoftwareJpegShrinkerOutput {
|
||||
u64 *out_size;
|
||||
s32 *out_width;
|
||||
s32 *out_height;
|
||||
void *dst;
|
||||
size_t dst_size;
|
||||
};
|
||||
|
||||
class SoftwareJpegShrinker {
|
||||
public:
|
||||
static Result ShrinkRgba8(SoftwareJpegShrinkerOutput &output, const SoftwareJpegShrinkerInput &input, int quality, void *work, size_t work_size);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 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::erpt::srv {
|
||||
|
||||
Result SubmitFsInfo();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,499 @@
|
||||
/*
|
||||
* Copyright (c) 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 "erpt_srv_fs_info.hpp"
|
||||
#include "erpt_srv_context_record.hpp"
|
||||
#include "erpt_srv_context.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
namespace {
|
||||
|
||||
Result SubmitMmcDetailInfo() {
|
||||
/* Submit the mmc cid. */
|
||||
{
|
||||
u8 mmc_cid[fs::MmcCidSize] = {};
|
||||
if (R_SUCCEEDED(fs::GetMmcCid(mmc_cid, sizeof(mmc_cid)))) {
|
||||
/* Clear the serial number from the cid. */
|
||||
fs::ClearMmcCidSerialNumber(mmc_cid);
|
||||
|
||||
/* Create a record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_NANDTypeInfo, sizeof(mmc_cid));
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Add the cid. */
|
||||
R_ABORT_UNLESS(record->Add(FieldId_NANDType, mmc_cid, sizeof(mmc_cid)));
|
||||
|
||||
/* Submit the record. */
|
||||
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
|
||||
}
|
||||
}
|
||||
|
||||
/* Submit the mmc speed mode. */
|
||||
{
|
||||
/* Create a record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_NANDSpeedModeInfo, 0x20);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Get the speed mode. */
|
||||
fs::MmcSpeedMode speed_mode{};
|
||||
const auto res = fs::GetMmcSpeedMode(std::addressof(speed_mode));
|
||||
if (R_SUCCEEDED(res)) {
|
||||
const char *speed_mode_name = "None";
|
||||
switch (speed_mode) {
|
||||
case fs::MmcSpeedMode_Identification:
|
||||
speed_mode_name = "Identification";
|
||||
break;
|
||||
case fs::MmcSpeedMode_LegacySpeed:
|
||||
speed_mode_name = "LegacySpeed";
|
||||
break;
|
||||
case fs::MmcSpeedMode_HighSpeed:
|
||||
speed_mode_name = "HighSpeed";
|
||||
break;
|
||||
case fs::MmcSpeedMode_Hs200:
|
||||
speed_mode_name = "Hs200";
|
||||
break;
|
||||
case fs::MmcSpeedMode_Hs400:
|
||||
speed_mode_name = "Hs400";
|
||||
break;
|
||||
case fs::MmcSpeedMode_Unknown:
|
||||
speed_mode_name = "Unknown";
|
||||
break;
|
||||
default:
|
||||
speed_mode_name = "UnDefined";
|
||||
break;
|
||||
}
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_NANDSpeedMode, speed_mode_name, std::strlen(speed_mode_name)));
|
||||
} else {
|
||||
/* Getting speed mode failed, so add the result. */
|
||||
char res_str[0x20];
|
||||
util::SNPrintf(res_str, sizeof(res_str), "0x%08X", res.GetValue());
|
||||
R_ABORT_UNLESS(record->Add(FieldId_NANDSpeedMode, res_str, std::strlen(res_str)));
|
||||
}
|
||||
|
||||
/* Submit the record. */
|
||||
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
|
||||
}
|
||||
|
||||
/* Submit the mmc extended csd. */
|
||||
{
|
||||
u8 mmc_csd[fs::MmcExtendedCsdSize] = {};
|
||||
if (R_SUCCEEDED(fs::GetMmcExtendedCsd(mmc_csd, sizeof(mmc_csd)))) {
|
||||
/* Create a record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_NANDExtendedCsd, 0);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Add fields from the csd. */
|
||||
R_ABORT_UNLESS(record->Add(FieldId_NANDPreEolInfo, static_cast<u32>(mmc_csd[fs::MmcExtendedCsdOffsetReEolInfo])));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_NANDDeviceLifeTimeEstTypA, static_cast<u32>(mmc_csd[fs::MmcExtendedCsdOffsetDeviceLifeTimeEstTypA])));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_NANDDeviceLifeTimeEstTypB, static_cast<u32>(mmc_csd[fs::MmcExtendedCsdOffsetDeviceLifeTimeEstTypB])));
|
||||
|
||||
/* Submit the record. */
|
||||
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
|
||||
}
|
||||
}
|
||||
|
||||
/* Submit the mmc patrol count. */
|
||||
{
|
||||
u32 count = 0;
|
||||
if (R_SUCCEEDED(fs::GetMmcPatrolCount(std::addressof(count)))) {
|
||||
/* Create a record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_NANDPatrolInfo, 0);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Add the count. */
|
||||
R_ABORT_UNLESS(record->Add(FieldId_NANDPatrolCount, count));
|
||||
|
||||
/* Submit the record. */
|
||||
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SubmitMmcErrorInfo() {
|
||||
/* Get the mmc error info. */
|
||||
fs::StorageErrorInfo sei = {};
|
||||
char log_buffer[erpt::ArrayBufferSizeDefault] = {};
|
||||
size_t log_size = 0;
|
||||
if (R_SUCCEEDED(fs::GetAndClearMmcErrorInfo(std::addressof(sei), std::addressof(log_size), log_buffer, sizeof(log_buffer)))) {
|
||||
/* Submit the error info. */
|
||||
{
|
||||
/* Create a record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_NANDErrorInfo, 0);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Add fields. */
|
||||
R_ABORT_UNLESS(record->Add(FieldId_NANDNumActivationFailures, sei.num_activation_failures));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_NANDNumActivationErrorCorrections, sei.num_activation_error_corrections));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_NANDNumReadWriteFailures, sei.num_read_write_failures));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_NANDNumReadWriteErrorCorrections, sei.num_read_write_error_corrections));
|
||||
|
||||
/* Submit the record. */
|
||||
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
|
||||
}
|
||||
|
||||
/* If we have a log, submit it. */
|
||||
if (log_size > 0) {
|
||||
/* Create a record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_NANDDriverLog, log_size);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Add fields. */
|
||||
R_ABORT_UNLESS(record->Add(FieldId_NANDErrorLog, log_buffer, log_size));
|
||||
|
||||
/* Submit the record. */
|
||||
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SubmitSdCardDetailInfo() {
|
||||
/* Submit the sd card cid. */
|
||||
{
|
||||
u8 sd_cid[fs::SdCardCidSize] = {};
|
||||
if (R_SUCCEEDED(fs::GetSdCardCid(sd_cid, sizeof(sd_cid)))) {
|
||||
/* Clear the serial number from the cid. */
|
||||
fs::ClearSdCardCidSerialNumber(sd_cid);
|
||||
|
||||
/* Create a record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_MicroSDTypeInfo, sizeof(sd_cid));
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Add the cid. */
|
||||
R_ABORT_UNLESS(record->Add(FieldId_MicroSDType, sd_cid, sizeof(sd_cid)));
|
||||
|
||||
/* Submit the record. */
|
||||
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
|
||||
}
|
||||
}
|
||||
|
||||
/* Submit the sd card speed mode. */
|
||||
{
|
||||
/* Create a record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_MicroSDSpeedModeInfo, 0x20);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Get the speed mode. */
|
||||
fs::SdCardSpeedMode speed_mode{};
|
||||
const auto res = fs::GetSdCardSpeedMode(std::addressof(speed_mode));
|
||||
if (R_SUCCEEDED(res)) {
|
||||
const char *speed_mode_name = "None";
|
||||
switch (speed_mode) {
|
||||
case fs::SdCardSpeedMode_Identification:
|
||||
speed_mode_name = "Identification";
|
||||
break;
|
||||
case fs::SdCardSpeedMode_DefaultSpeed:
|
||||
speed_mode_name = "DefaultSpeed";
|
||||
break;
|
||||
case fs::SdCardSpeedMode_HighSpeed:
|
||||
speed_mode_name = "HighSpeed";
|
||||
break;
|
||||
case fs::SdCardSpeedMode_Sdr12:
|
||||
speed_mode_name = "Sdr12";
|
||||
break;
|
||||
case fs::SdCardSpeedMode_Sdr25:
|
||||
speed_mode_name = "Sdr25";
|
||||
break;
|
||||
case fs::SdCardSpeedMode_Sdr50:
|
||||
speed_mode_name = "Sdr50";
|
||||
break;
|
||||
case fs::SdCardSpeedMode_Sdr104:
|
||||
speed_mode_name = "Sdr104";
|
||||
break;
|
||||
case fs::SdCardSpeedMode_Ddr50:
|
||||
speed_mode_name = "Ddr50";
|
||||
break;
|
||||
case fs::SdCardSpeedMode_Unknown:
|
||||
speed_mode_name = "Unknown";
|
||||
break;
|
||||
default:
|
||||
speed_mode_name = "UnDefined";
|
||||
break;
|
||||
}
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_MicroSDSpeedMode, speed_mode_name, std::strlen(speed_mode_name)));
|
||||
} else {
|
||||
/* Getting speed mode failed, so add the result. */
|
||||
char res_str[0x20];
|
||||
util::SNPrintf(res_str, sizeof(res_str), "0x%08X", res.GetValue());
|
||||
R_ABORT_UNLESS(record->Add(FieldId_MicroSDSpeedMode, res_str, std::strlen(res_str)));
|
||||
}
|
||||
|
||||
/* Submit the record. */
|
||||
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
|
||||
}
|
||||
|
||||
/* Submit the area sizes. */
|
||||
{
|
||||
s64 user_area_size = 0;
|
||||
s64 prot_area_size = 0;
|
||||
const Result res_user = fs::GetSdCardUserAreaSize(std::addressof(user_area_size));
|
||||
const Result res_prot = fs::GetSdCardProtectedAreaSize(std::addressof(prot_area_size));
|
||||
if (R_SUCCEEDED(res_user) || R_SUCCEEDED(res_prot)) {
|
||||
/* Create a record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_SdCardSizeSpec, 0);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Add sizes. */
|
||||
if (R_SUCCEEDED(res_user)) {
|
||||
R_ABORT_UNLESS(record->Add(FieldId_SdCardUserAreaSize, user_area_size));
|
||||
}
|
||||
if (R_SUCCEEDED(res_prot)) {
|
||||
R_ABORT_UNLESS(record->Add(FieldId_SdCardProtectedAreaSize, prot_area_size));
|
||||
}
|
||||
|
||||
/* Submit the record. */
|
||||
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SubmitSdCardErrorInfo() {
|
||||
/* Get the sd card error info. */
|
||||
fs::StorageErrorInfo sei = {};
|
||||
char log_buffer[erpt::ArrayBufferSizeDefault] = {};
|
||||
size_t log_size = 0;
|
||||
if (R_SUCCEEDED(fs::GetAndClearSdCardErrorInfo(std::addressof(sei), std::addressof(log_size), log_buffer, sizeof(log_buffer)))) {
|
||||
/* Submit the error info. */
|
||||
{
|
||||
/* Create a record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_SdCardErrorInfo, 0);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Add fields. */
|
||||
R_ABORT_UNLESS(record->Add(FieldId_SdCardNumActivationFailures, sei.num_activation_failures));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_SdCardNumActivationErrorCorrections, sei.num_activation_error_corrections));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_SdCardNumReadWriteFailures, sei.num_read_write_failures));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_SdCardNumReadWriteErrorCorrections, sei.num_read_write_error_corrections));
|
||||
|
||||
/* Submit the record. */
|
||||
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
|
||||
}
|
||||
|
||||
/* If we have a log, submit it. */
|
||||
if (log_size > 0) {
|
||||
/* Create a record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_SdCardDriverLog, log_size);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Add fields. */
|
||||
R_ABORT_UNLESS(record->Add(FieldId_SdCardErrorLog, log_buffer, log_size));
|
||||
|
||||
/* Submit the record. */
|
||||
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SubmitGameCardDetailInfo() {
|
||||
/* Create a record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_GameCardCIDInfo, fs::GameCardCidSize + fs::GameCardDeviceIdSize);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Add the game card cid. */
|
||||
{
|
||||
u8 gc_cid[fs::GameCardCidSize] = {};
|
||||
if (fs::IsGameCardInserted() && R_SUCCEEDED(fs::GetGameCardCid(gc_cid, sizeof(gc_cid)))) {
|
||||
/* Add the cid. */
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardCID, gc_cid, sizeof(gc_cid)));
|
||||
}
|
||||
}
|
||||
|
||||
/* Add the game card device id. */
|
||||
{
|
||||
u8 gc_device_id[fs::GameCardDeviceIdSize] = {};
|
||||
if (fs::IsGameCardInserted() && R_SUCCEEDED(fs::GetGameCardDeviceId(gc_device_id, sizeof(gc_device_id)))) {
|
||||
/* Add the cid. */
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardDeviceId, gc_device_id, sizeof(gc_device_id)));
|
||||
}
|
||||
}
|
||||
|
||||
/* Submit the record. */
|
||||
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SubmitGameCardErrorInfo() {
|
||||
/* Get the game card error info. */
|
||||
fs::GameCardErrorReportInfo ei = {};
|
||||
if (R_SUCCEEDED(fs::GetGameCardErrorReportInfo(std::addressof(ei)))) {
|
||||
/* Create a record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_GameCardErrorInfo, 0);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Add fields. */
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardCrcErrorCount, static_cast<u32>(ei.game_card_crc_error_num)));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardAsicCrcErrorCount, static_cast<u32>(ei.asic_crc_error_num)));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardRefreshCount, static_cast<u32>(ei.refresh_num)));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardReadRetryCount, static_cast<u32>(ei.retry_limit_out_num)));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardTimeoutRetryErrorCount, static_cast<u32>(ei.timeout_retry_num)));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardInsertionCount, static_cast<u32>(ei.insertion_count)));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardRemovalCount, static_cast<u32>(ei.removal_count)));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardAsicInitializeCount, ei.initialize_count));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardAsicReinitializeCount, ei.asic_reinitialize_num));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardAsicReinitializeFailureCount, ei.asic_reinitialize_failure_num));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardAsicReinitializeFailureDetail, ei.asic_reinitialize_failure_detail));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardRefreshSuccessCount, ei.refresh_succeeded_count));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardAwakenCount, ei.awaken_count));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardAwakenFailureCount, ei.awaken_failure_num));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardReadCountFromInsert, ei.read_count_from_insert));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardReadCountFromAwaken, ei.read_count_from_awaken));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardLastReadErrorPageAddress, ei.last_read_error_page_address));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardLastReadErrorPageCount, ei.last_read_error_page_count));
|
||||
|
||||
/* Submit the record. */
|
||||
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SubmitFileSystemErrorInfo() {
|
||||
/* Get the fsp error info. */
|
||||
fs::FileSystemProxyErrorInfo ei = {};
|
||||
if (R_SUCCEEDED(fs::GetAndClearFileSystemProxyErrorInfo(std::addressof(ei)))) {
|
||||
/* Submit FsProxyErrorInfo. */
|
||||
{
|
||||
/* Create a record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_FsProxyErrorInfo, fat::FatErrorNameMaxLength);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Add fields. */
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsRemountForDataCorruptCount, ei.rom_fs_remount_for_data_corruption_count));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsRemountForDataCorruptRetryOutCount, ei.rom_fs_unrecoverable_data_corruption_by_remount_count));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsError, ei.fat_fs_error.error));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsExtraError, ei.fat_fs_error.extra_error));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsErrorDrive, ei.fat_fs_error.drive_id));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsErrorName, ei.fat_fs_error.name, fat::FatErrorNameMaxLength));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsRecoveredByInvalidateCacheCount, ei.rom_fs_recovered_by_invalidate_cache_count));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsSaveDataIndexCount, ei.save_data_index_count));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisSystemFilePeakOpenCount, ei.bis_system_fat_report_info_1.open_file_peak_count));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisSystemDirectoryPeakOpenCount, ei.bis_system_fat_report_info_1.open_directory_peak_count));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisUserFilePeakOpenCount, ei.bis_user_fat_report_info_1.open_file_peak_count));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisUserDirectoryPeakOpenCount, ei.bis_user_fat_report_info_1.open_directory_peak_count));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsSdCardFilePeakOpenCount, ei.sd_card_fat_report_info_1.open_file_peak_count));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsSdCardDirectoryPeakOpenCount, ei.sd_card_fat_report_info_1.open_directory_peak_count));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisSystemUniqueFileEntryPeakOpenCount, ei.bis_system_fat_report_info_2.open_unique_file_entry_peak_count));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisSystemUniqueDirectoryEntryPeakOpenCount, ei.bis_system_fat_report_info_2.open_unique_directory_entry_peak_count));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisUserUniqueFileEntryPeakOpenCount, ei.bis_user_fat_report_info_2.open_unique_file_entry_peak_count));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisUserUniqueDirectoryEntryPeakOpenCount, ei.bis_user_fat_report_info_2.open_unique_directory_entry_peak_count));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsSdCardUniqueFileEntryPeakOpenCount, ei.sd_card_fat_report_info_2.open_unique_file_entry_peak_count));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsSdCardUniqueDirectoryEntryPeakOpenCount, ei.sd_card_fat_report_info_2.open_unique_directory_entry_peak_count));
|
||||
|
||||
/* Submit the record. */
|
||||
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
|
||||
}
|
||||
|
||||
/* Submit FsProxyErrorInfo2. */
|
||||
{
|
||||
/* Create a record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_FsProxyErrorInfo2, 0);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Add fields. */
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsDeepRetryStartCount, ei.rom_fs_deep_retry_start_count));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsUnrecoverableByGameCardAccessFailedCount, ei.rom_fs_unrecoverable_by_game_card_access_failed_count));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisSystemFatSafeControlResult, static_cast<u8>(ei.bis_system_fat_safe_info.result)));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisSystemFatErrorNumber, ei.bis_system_fat_safe_info.error_number));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisSystemFatSafeErrorNumber, ei.bis_system_fat_safe_info.safe_error_number));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisUserFatSafeControlResult, static_cast<u8>(ei.bis_user_fat_safe_info.result)));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisUserFatErrorNumber, ei.bis_user_fat_safe_info.error_number));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisUserFatSafeErrorNumber, ei.bis_user_fat_safe_info.safe_error_number));
|
||||
|
||||
/* Submit the record. */
|
||||
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SubmitMemoryReportInfo() {
|
||||
/* Get the memory report info. */
|
||||
fs::MemoryReportInfo mri = {};
|
||||
if (R_SUCCEEDED(fs::GetAndClearMemoryReportInfo(std::addressof(mri)))) {
|
||||
/* Create a record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_FsMemoryInfo, 0);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Add fields. */
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsPooledBufferPeakFreeSize, mri.pooled_buffer_peak_free_size));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsPooledBufferRetriedCount, mri.pooled_buffer_retried_count));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsPooledBufferReduceAllocationCount, mri.pooled_buffer_reduce_allocation_count));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsBufferManagerPeakFreeSize, mri.buffer_manager_peak_free_size));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsBufferManagerRetriedCount, mri.buffer_manager_retried_count));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsExpHeapPeakFreeSize, mri.exp_heap_peak_free_size));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsBufferPoolPeakFreeSize, mri.buffer_pool_peak_free_size));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsPatrolReadAllocateBufferSuccessCount, mri.patrol_read_allocate_buffer_success_count));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsPatrolReadAllocateBufferFailureCount, mri.patrol_read_allocate_buffer_failure_count));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsBufferManagerPeakTotalAllocatableSize, mri.buffer_manager_peak_total_allocatable_size));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsBufferPoolMaxAllocateSize, mri.buffer_pool_max_allocate_size));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsPooledBufferFailedIdealAllocationCountOnAsyncAccess, mri.pooled_buffer_failed_ideal_allocation_count_on_async_access));
|
||||
|
||||
/* Submit the record. */
|
||||
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result SubmitFsInfo() {
|
||||
/* Temporarily disable auto-abort. */
|
||||
fs::ScopedAutoAbortDisabler aad;
|
||||
|
||||
/* Submit various FS info. */
|
||||
R_TRY(SubmitMmcDetailInfo());
|
||||
R_TRY(SubmitMmcErrorInfo());
|
||||
R_TRY(SubmitSdCardDetailInfo());
|
||||
R_TRY(SubmitSdCardErrorInfo());
|
||||
R_TRY(SubmitGameCardDetailInfo());
|
||||
R_TRY(SubmitGameCardErrorInfo());
|
||||
R_TRY(SubmitFileSystemErrorInfo());
|
||||
R_TRY(SubmitMemoryReportInfo());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "erpt_srv_journal.hpp"
|
||||
#include "erpt_srv_context_record.hpp"
|
||||
#include "erpt_srv_context.hpp"
|
||||
#include "erpt_srv_fs_info.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
@@ -530,9 +531,14 @@ namespace ams::erpt::srv {
|
||||
SubmitResourceLimitContexts();
|
||||
#endif
|
||||
|
||||
if (flags.Test<CreateReportOptionFlag::SubmitFsInfo>()) {
|
||||
/* TODO: 17.0.0 SubmitFsInfo() */
|
||||
/* If we should, submit fs info. */
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
if (hos::GetVersion() >= hos::Version_17_0_0 && flags.Test<CreateReportOptionFlag::SubmitFsInfo>()) {
|
||||
/* NOTE: Nintendo ignores the result of this call. */
|
||||
SubmitFsInfo();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
35
libraries/libstratosphere/source/fs/fs_error_info.cpp
Normal file
35
libraries/libstratosphere/source/fs/fs_error_info.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 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 "fsa/fs_mount_utils.hpp"
|
||||
#include "impl/fs_file_system_proxy_service_object.hpp"
|
||||
#include "impl/fs_file_system_service_object_adapter.hpp"
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
Result GetAndClearFileSystemProxyErrorInfo(FileSystemProxyErrorInfo *out) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_FS_R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
|
||||
/* Get the error info. */
|
||||
AMS_FS_R_TRY(fsp->GetAndClearErrorInfo(out));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -100,4 +100,65 @@ namespace ams::fs {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
bool IsGameCardInserted() {
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
|
||||
/* Open a device operator. */
|
||||
sf::SharedPointer<fssrv::sf::IDeviceOperator> device_operator;
|
||||
AMS_FS_R_ABORT_UNLESS(fsp->OpenDeviceOperator(std::addressof(device_operator)));
|
||||
|
||||
/* Get insertion status. */
|
||||
bool inserted;
|
||||
AMS_FS_R_ABORT_UNLESS(device_operator->IsGameCardInserted(std::addressof(inserted)));
|
||||
|
||||
return inserted;
|
||||
}
|
||||
|
||||
Result GetGameCardCid(void *dst, size_t size) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_FS_R_UNLESS(size >= sizeof(gc::GameCardIdSet), fs::ResultInvalidSize());
|
||||
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
|
||||
/* Open a device operator. */
|
||||
sf::SharedPointer<fssrv::sf::IDeviceOperator> device_operator;
|
||||
AMS_FS_R_TRY(fsp->OpenDeviceOperator(std::addressof(device_operator)));
|
||||
|
||||
/* Get the id set. */
|
||||
gc::GameCardIdSet gc_id_set;
|
||||
AMS_FS_R_TRY(device_operator->GetGameCardIdSet(sf::OutBuffer(std::addressof(gc_id_set), sizeof(gc_id_set)), static_cast<s64>(sizeof(gc_id_set))));
|
||||
|
||||
/* Copy the id set to output. */
|
||||
std::memcpy(dst, std::addressof(gc_id_set), sizeof(gc_id_set));
|
||||
|
||||
R_SUCCEED();
|
||||
|
||||
}
|
||||
|
||||
Result GetGameCardDeviceId(void *dst, size_t size) {
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
|
||||
/* Open a device operator. */
|
||||
sf::SharedPointer<fssrv::sf::IDeviceOperator> device_operator;
|
||||
AMS_FS_R_TRY(fsp->OpenDeviceOperator(std::addressof(device_operator)));
|
||||
|
||||
/* Get the cid. */
|
||||
AMS_FS_R_TRY(device_operator->GetGameCardDeviceId(sf::OutBuffer(dst, size), static_cast<s64>(size)));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetGameCardErrorReportInfo(GameCardErrorReportInfo *out) {
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
|
||||
/* Open a device operator. */
|
||||
sf::SharedPointer<fssrv::sf::IDeviceOperator> device_operator;
|
||||
AMS_FS_R_TRY(fsp->OpenDeviceOperator(std::addressof(device_operator)));
|
||||
|
||||
/* Get the error report info. */
|
||||
AMS_FS_R_TRY(device_operator->GetGameCardErrorReportInfo(out));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 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 "fsa/fs_mount_utils.hpp"
|
||||
#include "impl/fs_file_system_proxy_service_object.hpp"
|
||||
#include "impl/fs_file_system_service_object_adapter.hpp"
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
Result GetAndClearMemoryReportInfo(MemoryReportInfo *out) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_FS_R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
|
||||
/* Get the memory report info. */
|
||||
AMS_FS_R_TRY(fsp->GetAndClearMemoryReportInfo(out));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
109
libraries/libstratosphere/source/fs/fs_mmc.cpp
Normal file
109
libraries/libstratosphere/source/fs/fs_mmc.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (c) 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 "fsa/fs_mount_utils.hpp"
|
||||
#include "impl/fs_file_system_proxy_service_object.hpp"
|
||||
#include "impl/fs_file_system_service_object_adapter.hpp"
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
Result GetMmcCid(void *dst, size_t size) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_FS_R_UNLESS(dst != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
|
||||
/* Open a device operator. */
|
||||
sf::SharedPointer<fssrv::sf::IDeviceOperator> device_operator;
|
||||
AMS_FS_R_TRY(fsp->OpenDeviceOperator(std::addressof(device_operator)));
|
||||
|
||||
/* Get the cid. */
|
||||
AMS_FS_R_TRY(device_operator->GetMmcCid(sf::OutBuffer(dst, size), static_cast<s64>(size)));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetMmcSpeedMode(MmcSpeedMode *out) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_FS_R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
|
||||
/* Open a device operator. */
|
||||
sf::SharedPointer<fssrv::sf::IDeviceOperator> device_operator;
|
||||
AMS_FS_R_TRY(fsp->OpenDeviceOperator(std::addressof(device_operator)));
|
||||
|
||||
/* Get the speed mode. */
|
||||
s64 speed_mode = 0;
|
||||
AMS_FS_R_TRY(device_operator->GetMmcSpeedMode(std::addressof(speed_mode)));
|
||||
|
||||
*out = static_cast<MmcSpeedMode>(speed_mode);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetMmcPatrolCount(u32 *out) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_FS_R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
|
||||
/* Open a device operator. */
|
||||
sf::SharedPointer<fssrv::sf::IDeviceOperator> device_operator;
|
||||
AMS_FS_R_TRY(fsp->OpenDeviceOperator(std::addressof(device_operator)));
|
||||
|
||||
/* Get the patrol count. */
|
||||
AMS_FS_R_TRY(device_operator->GetMmcPatrolCount(out));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetAndClearMmcErrorInfo(StorageErrorInfo *out_sei, size_t *out_log_size, char *out_log_buffer, size_t log_buffer_size) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_FS_R_UNLESS(out_sei != nullptr, fs::ResultNullptrArgument());
|
||||
AMS_FS_R_UNLESS(out_log_size != nullptr, fs::ResultNullptrArgument());
|
||||
AMS_FS_R_UNLESS(out_log_buffer != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
|
||||
/* Open a device operator. */
|
||||
sf::SharedPointer<fssrv::sf::IDeviceOperator> device_operator;
|
||||
AMS_FS_R_TRY(fsp->OpenDeviceOperator(std::addressof(device_operator)));
|
||||
|
||||
/* Get the error info. */
|
||||
s64 log_size = 0;
|
||||
AMS_FS_R_TRY(device_operator->GetAndClearMmcErrorInfo(out_sei, std::addressof(log_size), sf::OutBuffer(out_log_buffer, log_buffer_size), static_cast<s64>(log_buffer_size)));
|
||||
|
||||
*out_log_size = static_cast<size_t>(log_size);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetMmcExtendedCsd(void *dst, size_t size) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_FS_R_UNLESS(dst != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
|
||||
/* Open a device operator. */
|
||||
sf::SharedPointer<fssrv::sf::IDeviceOperator> device_operator;
|
||||
AMS_FS_R_TRY(fsp->OpenDeviceOperator(std::addressof(device_operator)));
|
||||
|
||||
/* Get the csd. */
|
||||
AMS_FS_R_TRY(device_operator->GetMmcExtendedCsd(sf::OutBuffer(dst, size), static_cast<s64>(size)));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -389,6 +389,11 @@ namespace ams::fs {
|
||||
|
||||
/* ... */
|
||||
|
||||
Result GetAndClearErrorInfo(ams::sf::Out<fs::FileSystemProxyErrorInfo> out) {
|
||||
static_assert(sizeof(fs::FileSystemProxyErrorInfo) == sizeof(::FsFileSystemProxyErrorInfo));
|
||||
R_RETURN(::fsGetAndClearErrorInfo(reinterpret_cast<::FsFileSystemProxyErrorInfo *>(out.GetPointer())));
|
||||
}
|
||||
|
||||
Result RegisterProgramIndexMapInfo(const ams::sf::InBuffer &buffer, s32 count) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
@@ -429,6 +434,11 @@ namespace ams::fs {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result GetAndClearMemoryReportInfo(ams::sf::Out<fs::MemoryReportInfo> out) {
|
||||
static_assert(sizeof(fs::MemoryReportInfo) == sizeof(::FsMemoryReportInfo));
|
||||
R_RETURN(::fsGetAndClearMemoryReportInfo(reinterpret_cast<::FsMemoryReportInfo *>(out.GetPointer())));
|
||||
}
|
||||
|
||||
/* ... */
|
||||
|
||||
Result GetProgramIndexForAccessLog(ams::sf::Out<u32> out_idx, ams::sf::Out<u32> out_count) {
|
||||
|
||||
@@ -124,4 +124,90 @@ namespace ams::fs {
|
||||
return inserted;
|
||||
}
|
||||
|
||||
Result GetSdCardSpeedMode(SdCardSpeedMode *out) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_FS_R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
|
||||
/* Open a device operator. */
|
||||
sf::SharedPointer<fssrv::sf::IDeviceOperator> device_operator;
|
||||
AMS_FS_R_TRY(fsp->OpenDeviceOperator(std::addressof(device_operator)));
|
||||
|
||||
/* Get the speed mode. */
|
||||
s64 speed_mode = 0;
|
||||
AMS_FS_R_TRY(device_operator->GetSdCardSpeedMode(std::addressof(speed_mode)));
|
||||
|
||||
*out = static_cast<SdCardSpeedMode>(speed_mode);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetSdCardCid(void *dst, size_t size) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_FS_R_UNLESS(dst != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
|
||||
/* Open a device operator. */
|
||||
sf::SharedPointer<fssrv::sf::IDeviceOperator> device_operator;
|
||||
AMS_FS_R_TRY(fsp->OpenDeviceOperator(std::addressof(device_operator)));
|
||||
|
||||
/* Get the cid. */
|
||||
AMS_FS_R_TRY(device_operator->GetSdCardCid(sf::OutBuffer(dst, size), static_cast<s64>(size)));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetSdCardUserAreaSize(s64 *out) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_FS_R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
|
||||
/* Open a device operator. */
|
||||
sf::SharedPointer<fssrv::sf::IDeviceOperator> device_operator;
|
||||
AMS_FS_R_TRY(fsp->OpenDeviceOperator(std::addressof(device_operator)));
|
||||
|
||||
/* Get the size. */
|
||||
AMS_FS_R_TRY(device_operator->GetSdCardUserAreaSize(out));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetSdCardProtectedAreaSize(s64 *out) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_FS_R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
|
||||
/* Open a device operator. */
|
||||
sf::SharedPointer<fssrv::sf::IDeviceOperator> device_operator;
|
||||
AMS_FS_R_TRY(fsp->OpenDeviceOperator(std::addressof(device_operator)));
|
||||
|
||||
/* Get the size. */
|
||||
AMS_FS_R_TRY(device_operator->GetSdCardProtectedAreaSize(out));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetAndClearSdCardErrorInfo(StorageErrorInfo *out_sei, size_t *out_log_size, char *out_log_buffer, size_t log_buffer_size) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_FS_R_UNLESS(out_sei != nullptr, fs::ResultNullptrArgument());
|
||||
AMS_FS_R_UNLESS(out_log_size != nullptr, fs::ResultNullptrArgument());
|
||||
AMS_FS_R_UNLESS(out_log_buffer != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
|
||||
/* Open a device operator. */
|
||||
sf::SharedPointer<fssrv::sf::IDeviceOperator> device_operator;
|
||||
AMS_FS_R_TRY(fsp->OpenDeviceOperator(std::addressof(device_operator)));
|
||||
|
||||
/* Get the error info. */
|
||||
s64 log_size = 0;
|
||||
AMS_FS_R_TRY(device_operator->GetAndClearSdCardErrorInfo(out_sei, std::addressof(log_size), sf::OutBuffer(out_log_buffer, log_buffer_size), static_cast<s64>(log_buffer_size)));
|
||||
|
||||
*out_log_size = static_cast<size_t>(log_size);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,6 +33,48 @@ namespace ams::fs::impl {
|
||||
R_RETURN(fsDeviceOperatorIsSdCardInserted(std::addressof(m_operator), out.GetPointer()));
|
||||
}
|
||||
|
||||
Result GetSdCardSpeedMode(ams::sf::Out<s64> out) {
|
||||
R_RETURN(fsDeviceOperatorGetSdCardSpeedMode(std::addressof(m_operator), out.GetPointer()));
|
||||
}
|
||||
|
||||
Result GetSdCardCid(ams::sf::OutBuffer out, s64 size) {
|
||||
R_RETURN(fsDeviceOperatorGetSdCardCid(std::addressof(m_operator), out.GetPointer(), out.GetSize(), size));
|
||||
}
|
||||
|
||||
Result GetSdCardUserAreaSize(ams::sf::Out<s64> out) {
|
||||
R_RETURN(fsDeviceOperatorGetSdCardUserAreaSize(std::addressof(m_operator), out.GetPointer()));
|
||||
}
|
||||
|
||||
Result GetSdCardProtectedAreaSize(ams::sf::Out<s64> out) {
|
||||
R_RETURN(fsDeviceOperatorGetSdCardProtectedAreaSize(std::addressof(m_operator), out.GetPointer()));
|
||||
}
|
||||
|
||||
Result GetAndClearSdCardErrorInfo(ams::sf::Out<fs::StorageErrorInfo> out_sei, ams::sf::Out<s64> out_size, ams::sf::OutBuffer out_buf, s64 size) {
|
||||
static_assert(sizeof(::FsStorageErrorInfo) == sizeof(fs::StorageErrorInfo));
|
||||
R_RETURN(fsDeviceOperatorGetAndClearSdCardErrorInfo(std::addressof(m_operator), reinterpret_cast<::FsStorageErrorInfo *>(out_sei.GetPointer()), out_size.GetPointer(), out_buf.GetPointer(), out_buf.GetSize(), size));
|
||||
}
|
||||
|
||||
Result GetMmcCid(ams::sf::OutBuffer out, s64 size) {
|
||||
R_RETURN(fsDeviceOperatorGetMmcCid(std::addressof(m_operator), out.GetPointer(), out.GetSize(), size));
|
||||
}
|
||||
|
||||
Result GetMmcSpeedMode(ams::sf::Out<s64> out) {
|
||||
R_RETURN(fsDeviceOperatorGetMmcSpeedMode(std::addressof(m_operator), out.GetPointer()));
|
||||
}
|
||||
|
||||
Result GetMmcPatrolCount(ams::sf::Out<u32> out) {
|
||||
R_RETURN(fsDeviceOperatorGetMmcPatrolCount(std::addressof(m_operator), out.GetPointer()));
|
||||
}
|
||||
|
||||
Result GetAndClearMmcErrorInfo(ams::sf::Out<fs::StorageErrorInfo> out_sei, ams::sf::Out<s64> out_size, ams::sf::OutBuffer out_buf, s64 size) {
|
||||
static_assert(sizeof(::FsStorageErrorInfo) == sizeof(fs::StorageErrorInfo));
|
||||
R_RETURN(fsDeviceOperatorGetAndClearMmcErrorInfo(std::addressof(m_operator), reinterpret_cast<::FsStorageErrorInfo *>(out_sei.GetPointer()), out_size.GetPointer(), out_buf.GetPointer(), out_buf.GetSize(), size));
|
||||
}
|
||||
|
||||
Result GetMmcExtendedCsd(ams::sf::OutBuffer out, s64 size) {
|
||||
R_RETURN(fsDeviceOperatorGetMmcExtendedCsd(std::addressof(m_operator), out.GetPointer(), out.GetSize(), size));
|
||||
}
|
||||
|
||||
Result IsGameCardInserted(ams::sf::Out<bool> out) {
|
||||
R_RETURN(fsDeviceOperatorIsGameCardInserted(std::addressof(m_operator), out.GetPointer()));
|
||||
}
|
||||
@@ -41,6 +83,19 @@ namespace ams::fs::impl {
|
||||
static_assert(sizeof(::FsGameCardHandle) == sizeof(u32));
|
||||
R_RETURN(fsDeviceOperatorGetGameCardHandle(std::addressof(m_operator), reinterpret_cast<::FsGameCardHandle *>(out.GetPointer())));
|
||||
}
|
||||
|
||||
Result GetGameCardIdSet(ams::sf::OutBuffer out, s64 size) {
|
||||
R_RETURN(fsDeviceOperatorGetGameCardIdSet(std::addressof(m_operator), out.GetPointer(), out.GetSize(), size));
|
||||
}
|
||||
|
||||
Result GetGameCardErrorReportInfo(ams::sf::Out<fs::GameCardErrorReportInfo> out) {
|
||||
static_assert(sizeof(::FsGameCardErrorReportInfo) == sizeof(fs::GameCardErrorReportInfo));
|
||||
R_RETURN(fsDeviceOperatorGetGameCardErrorReportInfo(std::addressof(m_operator), reinterpret_cast<::FsGameCardErrorReportInfo *>(out.GetPointer())));
|
||||
}
|
||||
|
||||
Result GetGameCardDeviceId(ams::sf::OutBuffer out, s64 size) {
|
||||
R_RETURN(fsDeviceOperatorGetGameCardDeviceId(std::addressof(m_operator), out.GetPointer(), out.GetSize(), size));
|
||||
}
|
||||
};
|
||||
static_assert(fssrv::sf::IsIDeviceOperator<RemoteDeviceOperator>);
|
||||
#endif
|
||||
|
||||
@@ -373,6 +373,10 @@ namespace ams::fssrv {
|
||||
|
||||
/* ... */
|
||||
|
||||
Result FileSystemProxyImpl::GetAndClearErrorInfo(ams::sf::Out<fs::FileSystemProxyErrorInfo> out) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::RegisterProgramIndexMapInfo(const ams::sf::InBuffer &buffer, s32 count) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
@@ -413,6 +417,10 @@ namespace ams::fssrv {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FileSystemProxyImpl::GetAndClearMemoryReportInfo(ams::sf::Out<fs::MemoryReportInfo> out) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
/* ... */
|
||||
|
||||
Result FileSystemProxyImpl::GetProgramIndexForAccessLog(ams::sf::Out<u32> out_idx, ams::sf::Out<u32> out_count) {
|
||||
|
||||
@@ -28,13 +28,13 @@ namespace ams::pm::info {
|
||||
R_RETURN(pminfoAtmosphereGetProcessId(reinterpret_cast<u64 *>(out_process_id), static_cast<u64>(program_id)));
|
||||
}
|
||||
|
||||
Result GetAppletCurrentResourceLimitValues(pm::ResourceLimitValues *out) {
|
||||
static_assert(sizeof(pm::ResourceLimitValues) == sizeof(::PmResourceLimitValues));
|
||||
Result GetAppletResourceLimitCurrentValue(pm::ResourceLimitValue *out) {
|
||||
static_assert(sizeof(pm::ResourceLimitValue) == sizeof(::PmResourceLimitValues));
|
||||
R_RETURN(pminfoGetAppletCurrentResourceLimitValues(reinterpret_cast<PmResourceLimitValues *>(out)));
|
||||
}
|
||||
|
||||
Result GetAppletPeakResourceLimitValues(pm::ResourceLimitValues *out) {
|
||||
static_assert(sizeof(pm::ResourceLimitValues) == sizeof(::PmResourceLimitValues));
|
||||
Result GetAppletResourceLimitPeakValue(pm::ResourceLimitValue *out) {
|
||||
static_assert(sizeof(pm::ResourceLimitValue) == sizeof(::PmResourceLimitValues));
|
||||
R_RETURN(pminfoGetAppletPeakResourceLimitValues(reinterpret_cast<PmResourceLimitValues *>(out)));
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MAJOR 1
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MINOR 6
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MICRO 1
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MICRO 2
|
||||
|
||||
#define ATMOSPHERE_RELEASE_VERSION ATMOSPHERE_RELEASE_VERSION_MAJOR, ATMOSPHERE_RELEASE_VERSION_MINOR, ATMOSPHERE_RELEASE_VERSION_MICRO
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ namespace ams::capsrv {
|
||||
|
||||
R_DEFINE_ERROR_RANGE(InternalError, 1024, 2047);
|
||||
R_DEFINE_ERROR_RESULT(InternalJpegEncoderError, 1210);
|
||||
R_DEFINE_ERROR_RESULT(InternalJpegOutBufferShortage, 1211);
|
||||
R_DEFINE_ERROR_RESULT(InternalJpegWorkMemoryShortage, 1212);
|
||||
|
||||
R_DEFINE_ERROR_RANGE(InternalFileDataVerificationError, 1300, 1399);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "ProcessManager",
|
||||
"title_id": "0x0100000000000003",
|
||||
"main_thread_stack_size": "0x00002000",
|
||||
"main_thread_stack_size": "0x00003000",
|
||||
"main_thread_priority": 49,
|
||||
"default_cpu_id": 3,
|
||||
"process_category": 1,
|
||||
|
||||
@@ -18,6 +18,64 @@
|
||||
|
||||
namespace ams::pm::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
template<size_t MaxProcessInfos>
|
||||
class ProcessInfoAllocator {
|
||||
NON_COPYABLE(ProcessInfoAllocator);
|
||||
NON_MOVEABLE(ProcessInfoAllocator);
|
||||
static_assert(MaxProcessInfos >= 0x40, "MaxProcessInfos is too small.");
|
||||
private:
|
||||
util::TypedStorage<ProcessInfo> m_process_info_storages[MaxProcessInfos]{};
|
||||
bool m_process_info_allocated[MaxProcessInfos]{};
|
||||
os::SdkMutex m_lock{};
|
||||
private:
|
||||
constexpr inline size_t GetProcessInfoIndex(ProcessInfo *process_info) const {
|
||||
return process_info - GetPointer(m_process_info_storages[0]);
|
||||
}
|
||||
public:
|
||||
constexpr ProcessInfoAllocator() = default;
|
||||
|
||||
template<typename... Args>
|
||||
ProcessInfo *AllocateProcessInfo(Args &&... args) {
|
||||
std::scoped_lock lk(m_lock);
|
||||
|
||||
for (size_t i = 0; i < MaxProcessInfos; i++) {
|
||||
if (!m_process_info_allocated[i]) {
|
||||
m_process_info_allocated[i] = true;
|
||||
|
||||
std::memset(m_process_info_storages + i, 0, sizeof(m_process_info_storages[i]));
|
||||
|
||||
return util::ConstructAt(m_process_info_storages[i], std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void FreeProcessInfo(ProcessInfo *process_info) {
|
||||
std::scoped_lock lk(m_lock);
|
||||
|
||||
const size_t index = this->GetProcessInfoIndex(process_info);
|
||||
AMS_ABORT_UNLESS(index < MaxProcessInfos);
|
||||
AMS_ABORT_UNLESS(m_process_info_allocated[index]);
|
||||
|
||||
util::DestroyAt(m_process_info_storages[index]);
|
||||
m_process_info_allocated[index] = false;
|
||||
}
|
||||
};
|
||||
|
||||
/* Process lists. */
|
||||
constinit ProcessList g_process_list;
|
||||
constinit ProcessList g_exit_list;
|
||||
|
||||
/* Process Info Allocation. */
|
||||
/* Note: The kernel slabheap is size 0x50 -- we allow slightly larger to account for the dead process list. */
|
||||
constexpr size_t MaxProcessCount = 0x60;
|
||||
constinit ProcessInfoAllocator<MaxProcessCount> g_process_info_allocator;
|
||||
|
||||
}
|
||||
|
||||
ProcessInfo::ProcessInfo(os::NativeHandle h, os::ProcessId pid, ldr::PinId pin, const ncm::ProgramLocation &l, const cfg::OverrideStatus &s) : m_process_id(pid), m_pin_id(pin), m_loc(l), m_status(s), m_handle(h), m_state(svc::ProcessState_Created), m_flags(0) {
|
||||
os::InitializeMultiWaitHolder(std::addressof(m_multi_wait_holder), m_handle);
|
||||
os::SetMultiWaitHolderUserData(std::addressof(m_multi_wait_holder), reinterpret_cast<uintptr_t>(this));
|
||||
@@ -37,10 +95,27 @@ namespace ams::pm::impl {
|
||||
/* Close the process's handle. */
|
||||
os::CloseNativeHandle(m_handle);
|
||||
m_handle = os::InvalidNativeHandle;
|
||||
|
||||
/* Unlink the process from its multi wait. */
|
||||
os::UnlinkMultiWaitHolder(std::addressof(m_multi_wait_holder));
|
||||
}
|
||||
}
|
||||
|
||||
ProcessListAccessor GetProcessList() {
|
||||
return ProcessListAccessor(g_process_list);
|
||||
}
|
||||
|
||||
ProcessListAccessor GetExitList() {
|
||||
return ProcessListAccessor(g_exit_list);
|
||||
}
|
||||
|
||||
ProcessInfo *AllocateProcessInfo(svc::Handle process_handle, os::ProcessId process_id, ldr::PinId pin_id, const ncm::ProgramLocation &location, const cfg::OverrideStatus &override_status) {
|
||||
return g_process_info_allocator.AllocateProcessInfo(process_handle, process_id, pin_id, location, override_status);
|
||||
}
|
||||
|
||||
void CleanupProcessInfo(ProcessListAccessor &list, ProcessInfo *process_info) {
|
||||
/* Remove the process from the list. */
|
||||
list->Remove(process_info);
|
||||
|
||||
/* Delete the process. */
|
||||
g_process_info_allocator.FreeProcessInfo(process_info);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -64,8 +64,8 @@ namespace ams::pm::impl {
|
||||
~ProcessInfo();
|
||||
void Cleanup();
|
||||
|
||||
void LinkToMultiWait(os::MultiWaitType &multi_wait) {
|
||||
os::LinkMultiWaitHolder(std::addressof(multi_wait), std::addressof(m_multi_wait_holder));
|
||||
os::MultiWaitHolderType *GetMultiWaitHolder() {
|
||||
return std::addressof(m_multi_wait_holder);
|
||||
}
|
||||
|
||||
os::NativeHandle GetHandle() const {
|
||||
@@ -232,4 +232,10 @@ namespace ams::pm::impl {
|
||||
}
|
||||
};
|
||||
|
||||
ProcessListAccessor GetProcessList();
|
||||
ProcessListAccessor GetExitList();
|
||||
|
||||
ProcessInfo *AllocateProcessInfo(svc::Handle process_handle, os::ProcessId process_id, ldr::PinId pin_id, const ncm::ProgramLocation &location, const cfg::OverrideStatus &override_status);
|
||||
void CleanupProcessInfo(ProcessListAccessor &list, ProcessInfo *process_info);
|
||||
|
||||
}
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "pm_process_manager.hpp"
|
||||
#include "pm_resource_manager.hpp"
|
||||
|
||||
#include "pm_process_tracker.hpp"
|
||||
#include "pm_process_info.hpp"
|
||||
#include "pm_spec.hpp"
|
||||
|
||||
namespace ams::pm::impl {
|
||||
|
||||
@@ -29,13 +29,7 @@ namespace ams::pm::impl {
|
||||
HookType_Application = (1 << 1),
|
||||
};
|
||||
|
||||
struct LaunchProcessArgs {
|
||||
os::ProcessId *out_process_id;
|
||||
ncm::ProgramLocation location;
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
#define GET_FLAG_MASK(flag) (hos_version >= hos::Version_5_0_0 ? static_cast<u32>(LaunchFlags_##flag) : static_cast<u32>(LaunchFlagsDeprecated_##flag))
|
||||
#define GET_FLAG_MASK(flag) (hos_version >= hos::Version_5_0_0 ? static_cast<u32>(LaunchFlags_##flag) : static_cast<u32>(LaunchFlagsDeprecated_##flag))
|
||||
|
||||
inline bool ShouldSignalOnExit(u32 launch_flags) {
|
||||
const auto hos_version = hos::GetVersion();
|
||||
@@ -70,115 +64,26 @@ namespace ams::pm::impl {
|
||||
return launch_flags & GET_FLAG_MASK(DisableAslr);
|
||||
}
|
||||
|
||||
#undef GET_FLAG_MASK
|
||||
|
||||
template<size_t MaxProcessInfos>
|
||||
class ProcessInfoAllocator {
|
||||
NON_COPYABLE(ProcessInfoAllocator);
|
||||
NON_MOVEABLE(ProcessInfoAllocator);
|
||||
static_assert(MaxProcessInfos >= 0x40, "MaxProcessInfos is too small.");
|
||||
private:
|
||||
util::TypedStorage<ProcessInfo> m_process_info_storages[MaxProcessInfos]{};
|
||||
bool m_process_info_allocated[MaxProcessInfos]{};
|
||||
os::SdkMutex m_lock{};
|
||||
private:
|
||||
constexpr inline size_t GetProcessInfoIndex(ProcessInfo *process_info) const {
|
||||
return process_info - GetPointer(m_process_info_storages[0]);
|
||||
}
|
||||
public:
|
||||
constexpr ProcessInfoAllocator() = default;
|
||||
|
||||
template<typename... Args>
|
||||
ProcessInfo *AllocateProcessInfo(Args &&... args) {
|
||||
std::scoped_lock lk(m_lock);
|
||||
|
||||
for (size_t i = 0; i < MaxProcessInfos; i++) {
|
||||
if (!m_process_info_allocated[i]) {
|
||||
m_process_info_allocated[i] = true;
|
||||
|
||||
std::memset(m_process_info_storages + i, 0, sizeof(m_process_info_storages[i]));
|
||||
|
||||
return util::ConstructAt(m_process_info_storages[i], std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void FreeProcessInfo(ProcessInfo *process_info) {
|
||||
std::scoped_lock lk(m_lock);
|
||||
|
||||
const size_t index = this->GetProcessInfoIndex(process_info);
|
||||
AMS_ABORT_UNLESS(index < MaxProcessInfos);
|
||||
AMS_ABORT_UNLESS(m_process_info_allocated[index]);
|
||||
|
||||
util::DestroyAt(m_process_info_storages[index]);
|
||||
m_process_info_allocated[index] = false;
|
||||
}
|
||||
};
|
||||
#undef GET_FLAG_MASK
|
||||
|
||||
/* Process Tracking globals. */
|
||||
void ProcessTrackingMain(void *);
|
||||
|
||||
constinit os::ThreadType g_process_track_thread;
|
||||
alignas(os::ThreadStackAlignment) constinit u8 g_process_track_thread_stack[16_KB];
|
||||
|
||||
/* Process lists. */
|
||||
constinit ProcessList g_process_list;
|
||||
constinit ProcessList g_dead_process_list;
|
||||
|
||||
/* Process Info Allocation. */
|
||||
/* Note: The kernel slabheap is size 0x50 -- we allow slightly larger to account for the dead process list. */
|
||||
constexpr size_t MaxProcessCount = 0x60;
|
||||
constinit ProcessInfoAllocator<MaxProcessCount> g_process_info_allocator;
|
||||
constinit ProcessTracker g_process_tracker;
|
||||
alignas(os::ThreadStackAlignment) constinit u8 g_process_track_thread_stack[8_KB];
|
||||
|
||||
/* Global events. */
|
||||
constinit os::SystemEventType g_process_event;
|
||||
constinit os::SystemEventType g_hook_to_create_process_event;
|
||||
constinit os::SystemEventType g_hook_to_create_application_process_event;
|
||||
constinit os::SystemEventType g_boot_finished_event;
|
||||
|
||||
/* Process Launch synchronization globals. */
|
||||
constinit os::SdkMutex g_launch_program_lock;
|
||||
os::Event g_process_launch_start_event(os::EventClearMode_AutoClear);
|
||||
os::Event g_process_launch_finish_event(os::EventClearMode_AutoClear);
|
||||
constinit Result g_process_launch_result = ResultSuccess();
|
||||
constinit LaunchProcessArgs g_process_launch_args = {};
|
||||
|
||||
/* Hook globals. */
|
||||
constinit std::atomic<ncm::ProgramId> g_program_id_hook;
|
||||
constinit std::atomic<bool> g_application_hook;
|
||||
|
||||
/* Forward declarations. */
|
||||
Result LaunchProcess(os::MultiWaitType &multi_wait, const LaunchProcessArgs &args);
|
||||
void OnProcessSignaled(ProcessListAccessor &list, ProcessInfo *process_info);
|
||||
|
||||
/* Helpers. */
|
||||
void ProcessTrackingMain(void *) {
|
||||
/* This is the main loop of the process tracking thread. */
|
||||
|
||||
/* Setup multi wait/holders. */
|
||||
os::MultiWaitType process_multi_wait;
|
||||
os::MultiWaitHolderType start_event_holder;
|
||||
os::InitializeMultiWait(std::addressof(process_multi_wait));
|
||||
os::InitializeMultiWaitHolder(std::addressof(start_event_holder), g_process_launch_start_event.GetBase());
|
||||
os::LinkMultiWaitHolder(std::addressof(process_multi_wait), std::addressof(start_event_holder));
|
||||
|
||||
while (true) {
|
||||
auto signaled_holder = os::WaitAny(std::addressof(process_multi_wait));
|
||||
if (signaled_holder == std::addressof(start_event_holder)) {
|
||||
/* Launch start event signaled. */
|
||||
/* TryWait will clear signaled, preventing duplicate notifications. */
|
||||
if (g_process_launch_start_event.TryWait()) {
|
||||
g_process_launch_result = LaunchProcess(process_multi_wait, g_process_launch_args);
|
||||
g_process_launch_finish_event.Signal();
|
||||
}
|
||||
} else {
|
||||
/* Some process was signaled. */
|
||||
ProcessListAccessor list(g_process_list);
|
||||
OnProcessSignaled(list, reinterpret_cast<ProcessInfo *>(os::GetMultiWaitHolderUserData(signaled_holder)));
|
||||
}
|
||||
}
|
||||
void CreateDebuggerEvent() {
|
||||
/* Create debugger hook events. */
|
||||
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_hook_to_create_process_event), os::EventClearMode_AutoClear, true));
|
||||
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_hook_to_create_application_process_event), os::EventClearMode_AutoClear, true));
|
||||
}
|
||||
|
||||
inline u32 GetLoaderCreateProcessFlags(u32 launch_flags) {
|
||||
@@ -195,7 +100,7 @@ namespace ams::pm::impl {
|
||||
}
|
||||
|
||||
bool HasApplicationProcess() {
|
||||
ProcessListAccessor list(g_process_list);
|
||||
auto list = GetProcessList();
|
||||
|
||||
for (auto &process : *list) {
|
||||
if (process.IsApplication()) {
|
||||
@@ -212,19 +117,14 @@ namespace ams::pm::impl {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void CleanupProcessInfo(ProcessListAccessor &list, ProcessInfo *process_info) {
|
||||
/* Remove the process from the list. */
|
||||
list->Remove(process_info);
|
||||
Result LaunchProgramImpl(ProcessInfo **out_process_info, os::ProcessId *out_process_id, const ncm::ProgramLocation &loc, u32 flags) {
|
||||
/* Set the output to nullptr, if we fail. */
|
||||
*out_process_info = nullptr;
|
||||
|
||||
/* Delete the process. */
|
||||
g_process_info_allocator.FreeProcessInfo(process_info);
|
||||
}
|
||||
|
||||
Result LaunchProcess(os::MultiWaitType &multi_wait, const LaunchProcessArgs &args) {
|
||||
/* Get Program Info. */
|
||||
ldr::ProgramInfo program_info;
|
||||
cfg::OverrideStatus override_status;
|
||||
R_TRY(ldr::pm::AtmosphereGetProgramInfo(std::addressof(program_info), std::addressof(override_status), args.location));
|
||||
R_TRY(ldr::pm::AtmosphereGetProgramInfo(std::addressof(program_info), std::addressof(override_status), loc));
|
||||
const bool is_application = (program_info.flags & ldr::ProgramInfoFlag_ApplicationTypeMask) == ldr::ProgramInfoFlag_Application;
|
||||
const bool allow_debug = (program_info.flags & ldr::ProgramInfoFlag_AllowDebug) || hos::GetVersion() < hos::Version_2_0_0;
|
||||
|
||||
@@ -232,14 +132,14 @@ namespace ams::pm::impl {
|
||||
R_UNLESS(!is_application || !HasApplicationProcess(), pm::ResultApplicationRunning());
|
||||
|
||||
/* Fix the program location to use the right program id. */
|
||||
const ncm::ProgramLocation location = ncm::ProgramLocation::Make(program_info.program_id, static_cast<ncm::StorageId>(args.location.storage_id));
|
||||
const ncm::ProgramLocation fixed_location = ncm::ProgramLocation::Make(program_info.program_id, static_cast<ncm::StorageId>(loc.storage_id));
|
||||
|
||||
/* Pin and create the process. */
|
||||
os::NativeHandle process_handle;
|
||||
ldr::PinId pin_id;
|
||||
{
|
||||
/* Pin the program with loader. */
|
||||
R_TRY(ldr::pm::AtmospherePinProgram(std::addressof(pin_id), location, override_status));
|
||||
R_TRY(ldr::pm::AtmospherePinProgram(std::addressof(pin_id), fixed_location, override_status));
|
||||
|
||||
/* If we fail after now, unpin. */
|
||||
ON_RESULT_FAILURE { ldr::pm::UnpinProgram(pin_id); };
|
||||
@@ -263,29 +163,28 @@ namespace ams::pm::impl {
|
||||
ON_RESULT_FAILURE_2 { if (mitm_boost_size > 0 || is_application) { R_ABORT_UNLESS(BoostSystemMemoryResourceLimitForMitm(0)); } };
|
||||
|
||||
/* Ensure resources are available. */
|
||||
resource::WaitResourceAvailable(std::addressof(program_info));
|
||||
WaitResourceAvailable(std::addressof(program_info));
|
||||
|
||||
/* Actually create the process. */
|
||||
R_TRY(ldr::pm::CreateProcess(std::addressof(process_handle), pin_id, GetLoaderCreateProcessFlags(args.flags), resource::GetResourceLimitHandle(std::addressof(program_info))));
|
||||
R_TRY(ldr::pm::CreateProcess(std::addressof(process_handle), pin_id, GetLoaderCreateProcessFlags(flags), GetResourceLimitHandle(std::addressof(program_info))));
|
||||
}
|
||||
|
||||
/* Get the process id. */
|
||||
os::ProcessId process_id = os::GetProcessId(process_handle);
|
||||
|
||||
/* Make new process info. */
|
||||
ProcessInfo *process_info = g_process_info_allocator.AllocateProcessInfo(process_handle, process_id, pin_id, location, override_status);
|
||||
ProcessInfo *process_info = AllocateProcessInfo(process_handle, process_id, pin_id, fixed_location, override_status);
|
||||
AMS_ABORT_UNLESS(process_info != nullptr);
|
||||
|
||||
/* Link new process info. */
|
||||
/* Add the new process info to the process list. */
|
||||
{
|
||||
ProcessListAccessor list(g_process_list);
|
||||
auto list = GetProcessList();
|
||||
list->push_back(*process_info);
|
||||
process_info->LinkToMultiWait(multi_wait);
|
||||
}
|
||||
|
||||
/* Prevent resource leakage if register fails. */
|
||||
ON_RESULT_FAILURE {
|
||||
ProcessListAccessor list(g_process_list);
|
||||
auto list = GetProcessList();
|
||||
process_info->Cleanup();
|
||||
CleanupProcessInfo(list, process_info);
|
||||
};
|
||||
@@ -296,166 +195,73 @@ namespace ams::pm::impl {
|
||||
const u8 *aci_fah = acid_fac + program_info.acid_fac_size;
|
||||
|
||||
/* Register with FS and SM. */
|
||||
R_TRY(fsprRegisterProgram(static_cast<u64>(process_id), static_cast<u64>(location.program_id), static_cast<NcmStorageId>(location.storage_id), aci_fah, program_info.aci_fah_size, acid_fac, program_info.acid_fac_size));
|
||||
R_TRY(sm::manager::RegisterProcess(process_id, location.program_id, override_status, acid_sac, program_info.acid_sac_size, aci_sac, program_info.aci_sac_size));
|
||||
R_TRY(fsprRegisterProgram(static_cast<u64>(process_id), static_cast<u64>(fixed_location.program_id), static_cast<NcmStorageId>(fixed_location.storage_id), aci_fah, program_info.aci_fah_size, acid_fac, program_info.acid_fac_size));
|
||||
R_TRY(sm::manager::RegisterProcess(process_id, fixed_location.program_id, override_status, acid_sac, program_info.acid_sac_size, aci_sac, program_info.aci_sac_size));
|
||||
|
||||
/* Set flags. */
|
||||
if (is_application) {
|
||||
process_info->SetApplication();
|
||||
}
|
||||
if (ShouldSignalOnStart(args.flags) && allow_debug) {
|
||||
if (ShouldSignalOnStart(flags) && allow_debug) {
|
||||
process_info->SetSignalOnStart();
|
||||
}
|
||||
if (ShouldSignalOnExit(args.flags)) {
|
||||
if (ShouldSignalOnExit(flags)) {
|
||||
process_info->SetSignalOnExit();
|
||||
}
|
||||
if (ShouldSignalOnDebugEvent(args.flags) && allow_debug) {
|
||||
if (ShouldSignalOnDebugEvent(flags) && allow_debug) {
|
||||
process_info->SetSignalOnDebugEvent();
|
||||
}
|
||||
|
||||
/* Process hooks/signaling. */
|
||||
if (location.program_id == g_program_id_hook) {
|
||||
if (fixed_location.program_id == g_program_id_hook) {
|
||||
os::SignalSystemEvent(std::addressof(g_hook_to_create_process_event));
|
||||
g_program_id_hook = ncm::InvalidProgramId;
|
||||
} else if (is_application && g_application_hook) {
|
||||
os::SignalSystemEvent(std::addressof(g_hook_to_create_application_process_event));
|
||||
g_application_hook = false;
|
||||
} else if (!ShouldStartSuspended(args.flags)) {
|
||||
} else if (!ShouldStartSuspended(flags)) {
|
||||
R_TRY(StartProcess(process_info, std::addressof(program_info)));
|
||||
}
|
||||
|
||||
*args.out_process_id = process_id;
|
||||
*out_process_id = process_id;
|
||||
*out_process_info = process_info;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void OnProcessSignaled(ProcessListAccessor &list, ProcessInfo *process_info) {
|
||||
/* Reset the process's signal. */
|
||||
svc::ResetSignal(process_info->GetHandle());
|
||||
|
||||
/* Update the process's state. */
|
||||
const svc::ProcessState old_state = process_info->GetState();
|
||||
{
|
||||
s64 tmp = 0;
|
||||
R_ABORT_UNLESS(svc::GetProcessInfo(std::addressof(tmp), process_info->GetHandle(), svc::ProcessInfoType_ProcessState));
|
||||
process_info->SetState(static_cast<svc::ProcessState>(tmp));
|
||||
}
|
||||
const svc::ProcessState new_state = process_info->GetState();
|
||||
|
||||
/* If we're transitioning away from crashed, clear waiting attached. */
|
||||
if (old_state == svc::ProcessState_Crashed && new_state != svc::ProcessState_Crashed) {
|
||||
process_info->ClearExceptionWaitingAttach();
|
||||
}
|
||||
|
||||
switch (new_state) {
|
||||
case svc::ProcessState_Created:
|
||||
case svc::ProcessState_CreatedAttached:
|
||||
case svc::ProcessState_Terminating:
|
||||
break;
|
||||
case svc::ProcessState_Running:
|
||||
if (process_info->ShouldSignalOnDebugEvent()) {
|
||||
process_info->ClearSuspended();
|
||||
process_info->SetSuspendedStateChanged();
|
||||
os::SignalSystemEvent(std::addressof(g_process_event));
|
||||
} else if (hos::GetVersion() >= hos::Version_2_0_0 && process_info->ShouldSignalOnStart()) {
|
||||
process_info->SetStartedStateChanged();
|
||||
process_info->ClearSignalOnStart();
|
||||
os::SignalSystemEvent(std::addressof(g_process_event));
|
||||
}
|
||||
process_info->ClearUnhandledException();
|
||||
break;
|
||||
case svc::ProcessState_Crashed:
|
||||
if (!process_info->HasUnhandledException()) {
|
||||
process_info->SetExceptionOccurred();
|
||||
os::SignalSystemEvent(std::addressof(g_process_event));
|
||||
}
|
||||
process_info->SetExceptionWaitingAttach();
|
||||
break;
|
||||
case svc::ProcessState_RunningAttached:
|
||||
if (process_info->ShouldSignalOnDebugEvent()) {
|
||||
process_info->ClearSuspended();
|
||||
process_info->SetSuspendedStateChanged();
|
||||
os::SignalSystemEvent(std::addressof(g_process_event));
|
||||
}
|
||||
process_info->ClearUnhandledException();
|
||||
break;
|
||||
case svc::ProcessState_Terminated:
|
||||
/* Free process resources, unlink from multi wait. */
|
||||
process_info->Cleanup();
|
||||
|
||||
if (hos::GetVersion() < hos::Version_5_0_0 && process_info->ShouldSignalOnExit()) {
|
||||
os::SignalSystemEvent(std::addressof(g_process_event));
|
||||
} else {
|
||||
/* Handle the case where we need to keep the process alive some time longer. */
|
||||
if (hos::GetVersion() >= hos::Version_5_0_0 && process_info->ShouldSignalOnExit()) {
|
||||
/* Remove from the living list. */
|
||||
list->Remove(process_info);
|
||||
|
||||
/* Add the process to the list of dead processes. */
|
||||
{
|
||||
ProcessListAccessor dead_list(g_dead_process_list);
|
||||
dead_list->push_back(*process_info);
|
||||
}
|
||||
|
||||
/* Signal. */
|
||||
os::SignalSystemEvent(std::addressof(g_process_event));
|
||||
} else {
|
||||
/* Actually delete process. */
|
||||
CleanupProcessInfo(list, process_info);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case svc::ProcessState_DebugBreak:
|
||||
if (process_info->ShouldSignalOnDebugEvent()) {
|
||||
process_info->SetSuspended();
|
||||
process_info->SetSuspendedStateChanged();
|
||||
os::SignalSystemEvent(std::addressof(g_process_event));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Initialization. */
|
||||
Result InitializeProcessManager() {
|
||||
/* Create events. */
|
||||
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_process_event), os::EventClearMode_AutoClear, true));
|
||||
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_hook_to_create_process_event), os::EventClearMode_AutoClear, true));
|
||||
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_hook_to_create_application_process_event), os::EventClearMode_AutoClear, true));
|
||||
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_boot_finished_event), os::EventClearMode_AutoClear, true));
|
||||
CreateProcessEvent();
|
||||
CreateDebuggerEvent();
|
||||
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_boot_finished_event), os::EventClearMode_AutoClear, true));
|
||||
|
||||
/* Initialize resource limits. */
|
||||
R_TRY(resource::InitializeResourceManager());
|
||||
R_TRY(InitializeSpec());
|
||||
|
||||
/* Create thread. */
|
||||
R_ABORT_UNLESS(os::CreateThread(std::addressof(g_process_track_thread), ProcessTrackingMain, nullptr, g_process_track_thread_stack, sizeof(g_process_track_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(pm, ProcessTrack)));
|
||||
os::SetThreadNamePointer(std::addressof(g_process_track_thread), AMS_GET_SYSTEM_THREAD_NAME(pm, ProcessTrack));
|
||||
/* Initialize the process tracker. */
|
||||
g_process_tracker.Initialize(g_process_track_thread_stack, sizeof(g_process_track_thread_stack));
|
||||
|
||||
/* Start thread. */
|
||||
os::StartThread(std::addressof(g_process_track_thread));
|
||||
/* Start the process tracker thread. */
|
||||
g_process_tracker.StartThread();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
/* Process Management. */
|
||||
Result LaunchProgram(os::ProcessId *out_process_id, const ncm::ProgramLocation &loc, u32 flags) {
|
||||
/* Ensure we only try to launch one program at a time. */
|
||||
std::scoped_lock lk(g_launch_program_lock);
|
||||
/* Launch the program. */
|
||||
ProcessInfo *process_info = nullptr;
|
||||
R_TRY(LaunchProgramImpl(std::addressof(process_info), out_process_id, loc, flags));
|
||||
|
||||
/* Set global arguments, signal, wait. */
|
||||
g_process_launch_args = {
|
||||
.out_process_id = out_process_id,
|
||||
.location = loc,
|
||||
.flags = flags,
|
||||
};
|
||||
g_process_launch_start_event.Signal();
|
||||
g_process_launch_finish_event.Wait();
|
||||
|
||||
R_RETURN(g_process_launch_result);
|
||||
/* Register the process info with the tracker. */
|
||||
g_process_tracker.QueueEntry(process_info);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result StartProcess(os::ProcessId process_id) {
|
||||
ProcessListAccessor list(g_process_list);
|
||||
auto list = GetProcessList();
|
||||
|
||||
auto process_info = list->Find(process_id);
|
||||
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
|
||||
@@ -467,7 +273,7 @@ namespace ams::pm::impl {
|
||||
}
|
||||
|
||||
Result TerminateProcess(os::ProcessId process_id) {
|
||||
ProcessListAccessor list(g_process_list);
|
||||
auto list = GetProcessList();
|
||||
|
||||
auto process_info = list->Find(process_id);
|
||||
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
|
||||
@@ -476,7 +282,7 @@ namespace ams::pm::impl {
|
||||
}
|
||||
|
||||
Result TerminateProgram(ncm::ProgramId program_id) {
|
||||
ProcessListAccessor list(g_process_list);
|
||||
auto list = GetProcessList();
|
||||
|
||||
auto process_info = list->Find(program_id);
|
||||
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
|
||||
@@ -484,15 +290,10 @@ namespace ams::pm::impl {
|
||||
R_RETURN(svc::TerminateProcess(process_info->GetHandle()));
|
||||
}
|
||||
|
||||
Result GetProcessEventHandle(os::NativeHandle *out) {
|
||||
*out = os::GetReadableHandleOfSystemEvent(std::addressof(g_process_event));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetProcessEventInfo(ProcessEventInfo *out) {
|
||||
/* Check for event from current process. */
|
||||
{
|
||||
ProcessListAccessor list(g_process_list);
|
||||
auto list = GetProcessList();
|
||||
|
||||
|
||||
for (auto &process : *list) {
|
||||
@@ -528,14 +329,14 @@ namespace ams::pm::impl {
|
||||
|
||||
/* Check for event from exited process. */
|
||||
if (hos::GetVersion() >= hos::Version_5_0_0) {
|
||||
ProcessListAccessor dead_list(g_dead_process_list);
|
||||
auto exit_list = GetExitList();
|
||||
|
||||
if (!dead_list->empty()) {
|
||||
auto &process_info = dead_list->front();
|
||||
out->event = GetProcessEventValue(ProcessEvent::Exited);
|
||||
if (!exit_list->empty()) {
|
||||
auto &process_info = exit_list->front();
|
||||
out->event = GetProcessEventValue(ProcessEvent::Exited);
|
||||
out->process_id = process_info.GetProcessId();
|
||||
|
||||
CleanupProcessInfo(dead_list, std::addressof(process_info));
|
||||
CleanupProcessInfo(exit_list, std::addressof(process_info));
|
||||
R_SUCCEED();
|
||||
}
|
||||
}
|
||||
@@ -546,7 +347,7 @@ namespace ams::pm::impl {
|
||||
}
|
||||
|
||||
Result CleanupProcess(os::ProcessId process_id) {
|
||||
ProcessListAccessor list(g_process_list);
|
||||
auto list = GetProcessList();
|
||||
|
||||
auto process_info = list->Find(process_id);
|
||||
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
|
||||
@@ -557,7 +358,7 @@ namespace ams::pm::impl {
|
||||
}
|
||||
|
||||
Result ClearExceptionOccurred(os::ProcessId process_id) {
|
||||
ProcessListAccessor list(g_process_list);
|
||||
auto list = GetProcessList();
|
||||
|
||||
auto process_info = list->Find(process_id);
|
||||
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
|
||||
@@ -575,7 +376,7 @@ namespace ams::pm::impl {
|
||||
}
|
||||
|
||||
Result GetExceptionProcessIdList(u32 *out_count, os::ProcessId *out_process_ids, size_t max_out_count) {
|
||||
ProcessListAccessor list(g_process_list);
|
||||
auto list = GetProcessList();
|
||||
|
||||
size_t count = 0;
|
||||
|
||||
@@ -596,7 +397,7 @@ namespace ams::pm::impl {
|
||||
}
|
||||
|
||||
Result GetProcessId(os::ProcessId *out, ncm::ProgramId program_id) {
|
||||
ProcessListAccessor list(g_process_list);
|
||||
auto list = GetProcessList();
|
||||
|
||||
auto process_info = list->Find(program_id);
|
||||
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
|
||||
@@ -606,7 +407,7 @@ namespace ams::pm::impl {
|
||||
}
|
||||
|
||||
Result GetProgramId(ncm::ProgramId *out, os::ProcessId process_id) {
|
||||
ProcessListAccessor list(g_process_list);
|
||||
auto list = GetProcessList();
|
||||
|
||||
auto process_info = list->Find(process_id);
|
||||
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
|
||||
@@ -616,7 +417,7 @@ namespace ams::pm::impl {
|
||||
}
|
||||
|
||||
Result GetApplicationProcessId(os::ProcessId *out_process_id) {
|
||||
ProcessListAccessor list(g_process_list);
|
||||
auto list = GetProcessList();
|
||||
|
||||
for (auto &process : *list) {
|
||||
if (process.IsApplication()) {
|
||||
@@ -629,7 +430,7 @@ namespace ams::pm::impl {
|
||||
}
|
||||
|
||||
Result AtmosphereGetProcessInfo(os::NativeHandle *out_process_handle, ncm::ProgramLocation *out_loc, cfg::OverrideStatus *out_status, os::ProcessId process_id) {
|
||||
ProcessListAccessor list(g_process_list);
|
||||
auto list = GetProcessList();
|
||||
|
||||
auto process_info = list->Find(process_id);
|
||||
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
|
||||
@@ -706,33 +507,4 @@ namespace ams::pm::impl {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
/* Resource Limit API. */
|
||||
Result BoostSystemMemoryResourceLimit(u64 boost_size) {
|
||||
R_RETURN(resource::BoostSystemMemoryResourceLimit(boost_size));
|
||||
}
|
||||
|
||||
Result BoostApplicationThreadResourceLimit() {
|
||||
R_RETURN(resource::BoostApplicationThreadResourceLimit());
|
||||
}
|
||||
|
||||
Result BoostSystemThreadResourceLimit() {
|
||||
R_RETURN(resource::BoostSystemThreadResourceLimit());
|
||||
}
|
||||
|
||||
Result GetAppletCurrentResourceLimitValues(pm::ResourceLimitValues *out) {
|
||||
R_RETURN(resource::GetCurrentResourceLimitValues(ResourceLimitGroup_Applet, out));
|
||||
}
|
||||
|
||||
Result GetAppletPeakResourceLimitValues(pm::ResourceLimitValues *out) {
|
||||
R_RETURN(resource::GetPeakResourceLimitValues(ResourceLimitGroup_Applet, out));
|
||||
}
|
||||
|
||||
Result AtmosphereGetCurrentLimitInfo(s64 *out_cur_val, s64 *out_lim_val, u32 group, u32 resource) {
|
||||
R_RETURN(resource::GetResourceLimitValues(out_cur_val, out_lim_val, static_cast<ResourceLimitGroup>(group), static_cast<svc::LimitableResource>(resource)));
|
||||
}
|
||||
|
||||
Result BoostSystemMemoryResourceLimitForMitm(u64 boost_size) {
|
||||
R_RETURN(resource::BoostSystemMemoryResourceLimitForMitm(boost_size));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -48,13 +48,4 @@ namespace ams::pm::impl {
|
||||
Result NotifyBootFinished();
|
||||
Result GetBootFinishedEventHandle(os::NativeHandle *out);
|
||||
|
||||
/* Resource Limit API. */
|
||||
Result BoostSystemMemoryResourceLimit(u64 boost_size);
|
||||
Result BoostApplicationThreadResourceLimit();
|
||||
Result BoostSystemThreadResourceLimit();
|
||||
Result GetAppletCurrentResourceLimitValues(pm::ResourceLimitValues *out);
|
||||
Result GetAppletPeakResourceLimitValues(pm::ResourceLimitValues *out);
|
||||
Result AtmosphereGetCurrentLimitInfo(s64 *out_cur_val, s64 *out_lim_val, u32 group, u32 resource);
|
||||
Result BoostSystemMemoryResourceLimitForMitm(u64 boost_size);
|
||||
|
||||
}
|
||||
|
||||
193
stratosphere/pm/source/impl/pm_process_tracker.cpp
Normal file
193
stratosphere/pm/source/impl/pm_process_tracker.cpp
Normal file
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright (c) 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 "pm_process_tracker.hpp"
|
||||
#include "pm_process_info.hpp"
|
||||
#include "pm_spec.hpp"
|
||||
|
||||
namespace ams::pm::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
/* Global process event. */
|
||||
constinit os::SystemEventType g_process_event;
|
||||
|
||||
}
|
||||
|
||||
void ProcessTracker::Initialize(void *stack, size_t stack_size) {
|
||||
/* Initialize our events. */
|
||||
os::InitializeEvent(std::addressof(m_request_event), false, os::EventClearMode_AutoClear);
|
||||
os::InitializeEvent(std::addressof(m_reply_event), false, os::EventClearMode_AutoClear);
|
||||
|
||||
/* Our process count should initially be zero. */
|
||||
m_process_count = 0;
|
||||
|
||||
/* Create the process tracking thread. */
|
||||
R_ABORT_UNLESS(os::CreateThread(std::addressof(m_thread), ProcessTracker::ThreadFunction, this, stack, stack_size, AMS_GET_SYSTEM_THREAD_PRIORITY(pm, ProcessTrack)));
|
||||
os::SetThreadNamePointer(std::addressof(m_thread), AMS_GET_SYSTEM_THREAD_NAME(pm, ProcessTrack));
|
||||
}
|
||||
|
||||
void ProcessTracker::StartThread() {
|
||||
/* Start thread. */
|
||||
os::StartThread(std::addressof(m_thread));
|
||||
}
|
||||
|
||||
void ProcessTracker::ThreadBody() {
|
||||
/* This is the main loop of the process tracking thread. */
|
||||
|
||||
/* Setup multi wait/holders. */
|
||||
os::MultiWaitType process_multi_wait;
|
||||
os::MultiWaitHolderType enqueue_event_holder;
|
||||
os::InitializeMultiWait(std::addressof(process_multi_wait));
|
||||
os::InitializeMultiWaitHolder(std::addressof(enqueue_event_holder), std::addressof(m_request_event));
|
||||
os::LinkMultiWaitHolder(std::addressof(process_multi_wait), std::addressof(enqueue_event_holder));
|
||||
|
||||
while (true) {
|
||||
auto signaled_holder = os::WaitAny(std::addressof(process_multi_wait));
|
||||
if (signaled_holder == std::addressof(enqueue_event_holder)) {
|
||||
/* TryWait will clear signaled, preventing duplicate notifications. */
|
||||
if (os::TryWaitEvent(std::addressof(m_request_event))) {
|
||||
/* Link the process to our multi-wait. */
|
||||
os::LinkMultiWaitHolder(std::addressof(process_multi_wait), m_queued_process_info->GetMultiWaitHolder());
|
||||
m_queued_process_info = nullptr;
|
||||
|
||||
/* Increment our process count. */
|
||||
++m_process_count;
|
||||
|
||||
/* Reply. */
|
||||
os::SignalEvent(std::addressof(m_reply_event));
|
||||
}
|
||||
} else {
|
||||
/* Some process was signaled. */
|
||||
this->OnProcessSignaled(reinterpret_cast<ProcessInfo *>(os::GetMultiWaitHolderUserData(signaled_holder)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessTracker::QueueEntry(ProcessInfo *process_info) {
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
/* Request to enqueue the process info. */
|
||||
m_queued_process_info = process_info;
|
||||
os::SignalEvent(std::addressof(m_request_event));
|
||||
|
||||
/* Wait for acknowledgement. */
|
||||
os::WaitEvent(std::addressof(m_reply_event));
|
||||
}
|
||||
|
||||
void ProcessTracker::OnProcessSignaled(ProcessInfo *process_info) {
|
||||
/* Get the process list. */
|
||||
auto list = GetProcessList();
|
||||
|
||||
/* Reset the process's signal. */
|
||||
svc::ResetSignal(process_info->GetHandle());
|
||||
|
||||
/* Update the process's state. */
|
||||
const svc::ProcessState old_state = process_info->GetState();
|
||||
{
|
||||
s64 tmp = 0;
|
||||
R_ABORT_UNLESS(svc::GetProcessInfo(std::addressof(tmp), process_info->GetHandle(), svc::ProcessInfoType_ProcessState));
|
||||
process_info->SetState(static_cast<svc::ProcessState>(tmp));
|
||||
}
|
||||
const svc::ProcessState new_state = process_info->GetState();
|
||||
|
||||
/* If we're transitioning away from crashed, clear waiting attached. */
|
||||
if (old_state == svc::ProcessState_Crashed && new_state != svc::ProcessState_Crashed) {
|
||||
process_info->ClearExceptionWaitingAttach();
|
||||
}
|
||||
|
||||
switch (new_state) {
|
||||
case svc::ProcessState_Created:
|
||||
case svc::ProcessState_CreatedAttached:
|
||||
case svc::ProcessState_Terminating:
|
||||
break;
|
||||
case svc::ProcessState_Running:
|
||||
if (process_info->ShouldSignalOnDebugEvent()) {
|
||||
process_info->ClearSuspended();
|
||||
process_info->SetSuspendedStateChanged();
|
||||
os::SignalSystemEvent(std::addressof(g_process_event));
|
||||
} else if (hos::GetVersion() >= hos::Version_2_0_0 && process_info->ShouldSignalOnStart()) {
|
||||
process_info->SetStartedStateChanged();
|
||||
process_info->ClearSignalOnStart();
|
||||
os::SignalSystemEvent(std::addressof(g_process_event));
|
||||
}
|
||||
process_info->ClearUnhandledException();
|
||||
break;
|
||||
case svc::ProcessState_Crashed:
|
||||
if (!process_info->HasUnhandledException()) {
|
||||
process_info->SetExceptionOccurred();
|
||||
os::SignalSystemEvent(std::addressof(g_process_event));
|
||||
}
|
||||
process_info->SetExceptionWaitingAttach();
|
||||
break;
|
||||
case svc::ProcessState_RunningAttached:
|
||||
if (process_info->ShouldSignalOnDebugEvent()) {
|
||||
process_info->ClearSuspended();
|
||||
process_info->SetSuspendedStateChanged();
|
||||
os::SignalSystemEvent(std::addressof(g_process_event));
|
||||
}
|
||||
process_info->ClearUnhandledException();
|
||||
break;
|
||||
case svc::ProcessState_Terminated:
|
||||
/* Unlink from multi wait. */
|
||||
os::UnlinkMultiWaitHolder(process_info->GetMultiWaitHolder());
|
||||
|
||||
/* Free process resources. */
|
||||
process_info->Cleanup();
|
||||
|
||||
if (hos::GetVersion() < hos::Version_5_0_0 && process_info->ShouldSignalOnExit()) {
|
||||
os::SignalSystemEvent(std::addressof(g_process_event));
|
||||
} else {
|
||||
/* Handle the case where we need to keep the process alive some time longer. */
|
||||
if (hos::GetVersion() >= hos::Version_5_0_0 && process_info->ShouldSignalOnExit()) {
|
||||
/* Remove from the living list. */
|
||||
list->Remove(process_info);
|
||||
|
||||
/* Add the process to the list of dead processes. */
|
||||
{
|
||||
GetExitList()->push_back(*process_info);
|
||||
}
|
||||
|
||||
/* Signal. */
|
||||
os::SignalSystemEvent(std::addressof(g_process_event));
|
||||
} else {
|
||||
/* Actually delete process. */
|
||||
CleanupProcessInfo(list, process_info);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case svc::ProcessState_DebugBreak:
|
||||
if (process_info->ShouldSignalOnDebugEvent()) {
|
||||
process_info->SetSuspended();
|
||||
process_info->SetSuspendedStateChanged();
|
||||
os::SignalSystemEvent(std::addressof(g_process_event));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CreateProcessEvent() {
|
||||
/* Create process event. */
|
||||
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_process_event), os::EventClearMode_AutoClear, true));
|
||||
}
|
||||
|
||||
Result GetProcessEventHandle(os::NativeHandle *out) {
|
||||
*out = os::GetReadableHandleOfSystemEvent(std::addressof(g_process_event));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
57
stratosphere/pm/source/impl/pm_process_tracker.hpp
Normal file
57
stratosphere/pm/source/impl/pm_process_tracker.hpp
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 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 "pm_process_info.hpp"
|
||||
|
||||
namespace ams::pm::impl {
|
||||
|
||||
class ProcessTracker {
|
||||
NON_COPYABLE(ProcessTracker);
|
||||
NON_MOVEABLE(ProcessTracker);
|
||||
private:
|
||||
os::ThreadType m_thread;
|
||||
os::EventType m_request_event;
|
||||
os::EventType m_reply_event;
|
||||
os::SdkMutex m_mutex;
|
||||
ProcessInfo *m_queued_process_info;
|
||||
util::Atomic<u32> m_process_count;
|
||||
public:
|
||||
constexpr ProcessTracker() : m_thread(), m_request_event(), m_reply_event(), m_mutex(), m_queued_process_info(nullptr), m_process_count(0) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
void Initialize(void *stack, size_t stack_size);
|
||||
void StartThread();
|
||||
|
||||
void QueueEntry(ProcessInfo *process_info);
|
||||
|
||||
u32 GetProcessCount() const {
|
||||
return m_process_count;
|
||||
}
|
||||
private:
|
||||
void OnProcessSignaled(ProcessInfo *process_info);
|
||||
private:
|
||||
static void ThreadFunction(void *_this) {
|
||||
static_cast<ProcessTracker *>(_this)->ThreadBody();
|
||||
}
|
||||
|
||||
void ThreadBody();
|
||||
};
|
||||
|
||||
void CreateProcessEvent();
|
||||
|
||||
}
|
||||
@@ -14,9 +14,9 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "pm_resource_manager.hpp"
|
||||
#include "pm_spec.hpp"
|
||||
|
||||
namespace ams::pm::resource {
|
||||
namespace ams::pm::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -170,10 +170,18 @@ namespace ams::pm::resource {
|
||||
}
|
||||
|
||||
void WaitApplicationMemoryAvailable() {
|
||||
/* Get firmware version. */
|
||||
const auto fw_ver = hos::GetVersion();
|
||||
|
||||
/* On 15.0.0+, pm considers application memory to be available if there is exactly 96 MB outstanding. */
|
||||
/* This is probably because this corresponds to the gameplay-recording memory. */
|
||||
constexpr u64 AllowedUsedApplicationMemory = 96_MB;
|
||||
|
||||
/* Wait for memory to be available. */
|
||||
u64 value = 0;
|
||||
while (true) {
|
||||
R_ABORT_UNLESS(svc::GetSystemInfo(&value, svc::SystemInfoType_UsedPhysicalMemorySize, svc::InvalidHandle, svc::PhysicalMemorySystemInfo_Application));
|
||||
if (value == 0) {
|
||||
if (value == 0 || (fw_ver >= hos::Version_15_0_0 && value == AllowedUsedApplicationMemory)) {
|
||||
break;
|
||||
}
|
||||
os::SleepThread(TimeSpan::FromMilliSeconds(1));
|
||||
@@ -201,8 +209,8 @@ namespace ams::pm::resource {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
template<auto GetResourceLimitValueImpl>
|
||||
ALWAYS_INLINE Result GetResourceLimitValuesImpl(ResourceLimitGroup group, pm::ResourceLimitValues *out) {
|
||||
template<auto ImplFunction>
|
||||
ALWAYS_INLINE Result GetResourceLimitValueImpl(pm::ResourceLimitValue *out, ResourceLimitGroup group) {
|
||||
/* Sanity check group. */
|
||||
AMS_ABORT_UNLESS(group < ResourceLimitGroup_Count);
|
||||
|
||||
@@ -211,11 +219,11 @@ namespace ams::pm::resource {
|
||||
|
||||
/* Get values. */
|
||||
int64_t values[svc::LimitableResource_Count];
|
||||
R_ABORT_UNLESS(GetResourceLimitValueImpl(std::addressof(values[svc::LimitableResource_PhysicalMemoryMax]), handle, svc::LimitableResource_PhysicalMemoryMax));
|
||||
R_ABORT_UNLESS(GetResourceLimitValueImpl(std::addressof(values[svc::LimitableResource_ThreadCountMax]), handle, svc::LimitableResource_ThreadCountMax));
|
||||
R_ABORT_UNLESS(GetResourceLimitValueImpl(std::addressof(values[svc::LimitableResource_EventCountMax]), handle, svc::LimitableResource_EventCountMax));
|
||||
R_ABORT_UNLESS(GetResourceLimitValueImpl(std::addressof(values[svc::LimitableResource_TransferMemoryCountMax]), handle, svc::LimitableResource_TransferMemoryCountMax));
|
||||
R_ABORT_UNLESS(GetResourceLimitValueImpl(std::addressof(values[svc::LimitableResource_SessionCountMax]), handle, svc::LimitableResource_SessionCountMax));
|
||||
R_ABORT_UNLESS(ImplFunction(std::addressof(values[svc::LimitableResource_PhysicalMemoryMax]), handle, svc::LimitableResource_PhysicalMemoryMax));
|
||||
R_ABORT_UNLESS(ImplFunction(std::addressof(values[svc::LimitableResource_ThreadCountMax]), handle, svc::LimitableResource_ThreadCountMax));
|
||||
R_ABORT_UNLESS(ImplFunction(std::addressof(values[svc::LimitableResource_EventCountMax]), handle, svc::LimitableResource_EventCountMax));
|
||||
R_ABORT_UNLESS(ImplFunction(std::addressof(values[svc::LimitableResource_TransferMemoryCountMax]), handle, svc::LimitableResource_TransferMemoryCountMax));
|
||||
R_ABORT_UNLESS(ImplFunction(std::addressof(values[svc::LimitableResource_SessionCountMax]), handle, svc::LimitableResource_SessionCountMax));
|
||||
|
||||
/* Set to output. */
|
||||
out->physical_memory = values[svc::LimitableResource_PhysicalMemoryMax];
|
||||
@@ -270,8 +278,7 @@ namespace ams::pm::resource {
|
||||
|
||||
}
|
||||
|
||||
/* Resource API. */
|
||||
Result InitializeResourceManager() {
|
||||
Result InitializeSpec() {
|
||||
/* Create resource limit handles. */
|
||||
for (size_t i = 0; i < ResourceLimitGroup_Count; i++) {
|
||||
if (i == ResourceLimitGroup_System) {
|
||||
@@ -451,16 +458,16 @@ namespace ams::pm::resource {
|
||||
}
|
||||
}
|
||||
|
||||
Result GetCurrentResourceLimitValues(ResourceLimitGroup group, pm::ResourceLimitValues *out) {
|
||||
R_RETURN(GetResourceLimitValuesImpl<::ams::svc::GetResourceLimitCurrentValue>(group, out));
|
||||
Result GetResourceLimitCurrentValue(pm::ResourceLimitValue *out, ResourceLimitGroup group) {
|
||||
R_RETURN(GetResourceLimitValueImpl<::ams::svc::GetResourceLimitCurrentValue>(out, group));
|
||||
}
|
||||
|
||||
Result GetPeakResourceLimitValues(ResourceLimitGroup group, pm::ResourceLimitValues *out) {
|
||||
R_RETURN(GetResourceLimitValuesImpl<::ams::svc::GetResourceLimitPeakValue>(group, out));
|
||||
Result GetResourceLimitPeakValue(pm::ResourceLimitValue *out, ResourceLimitGroup group) {
|
||||
R_RETURN(GetResourceLimitValueImpl<::ams::svc::GetResourceLimitPeakValue>(out, group));
|
||||
}
|
||||
|
||||
Result GetLimitResourceLimitValues(ResourceLimitGroup group, pm::ResourceLimitValues *out) {
|
||||
R_RETURN(GetResourceLimitValuesImpl<::ams::svc::GetResourceLimitLimitValue>(group, out));
|
||||
Result GetResourceLimitLimitValue(pm::ResourceLimitValue *out, ResourceLimitGroup group) {
|
||||
R_RETURN(GetResourceLimitValueImpl<::ams::svc::GetResourceLimitLimitValue>(out, group));
|
||||
}
|
||||
|
||||
Result GetResourceLimitValues(s64 *out_cur, s64 *out_lim, ResourceLimitGroup group, svc::LimitableResource resource) {
|
||||
@@ -16,10 +16,10 @@
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::pm::resource {
|
||||
namespace ams::pm::impl {
|
||||
|
||||
Result InitializeSpec();
|
||||
|
||||
/* Resource API. */
|
||||
Result InitializeResourceManager();
|
||||
Result BoostSystemMemoryResourceLimit(u64 boost_size);
|
||||
Result BoostApplicationThreadResourceLimit();
|
||||
Result BoostSystemThreadResourceLimit();
|
||||
@@ -31,9 +31,9 @@ namespace ams::pm::resource {
|
||||
|
||||
void WaitResourceAvailable(const ldr::ProgramInfo *info);
|
||||
|
||||
Result GetCurrentResourceLimitValues(ResourceLimitGroup group, pm::ResourceLimitValues *out);
|
||||
Result GetPeakResourceLimitValues(ResourceLimitGroup group, pm::ResourceLimitValues *out);
|
||||
Result GetLimitResourceLimitValues(ResourceLimitGroup group, pm::ResourceLimitValues *out);
|
||||
Result GetResourceLimitCurrentValue(pm::ResourceLimitValue *out, ResourceLimitGroup group);
|
||||
Result GetResourceLimitPeakValue(pm::ResourceLimitValue *outm, ResourceLimitGroup group);
|
||||
Result GetResourceLimitLimitValue(pm::ResourceLimitValue *out, ResourceLimitGroup group);
|
||||
|
||||
Result GetResourceLimitValues(s64 *out_cur, s64 *out_lim, ResourceLimitGroup group, svc::LimitableResource resource);
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace ams::pm {
|
||||
namespace {
|
||||
|
||||
/* Global bootmode. */
|
||||
BootMode g_boot_mode = BootMode::Normal;
|
||||
constinit BootMode g_boot_mode = BootMode::Normal;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <stratosphere.hpp>
|
||||
#include "pm_debug_monitor_service.hpp"
|
||||
#include "impl/pm_process_manager.hpp"
|
||||
#include "impl/pm_spec.hpp"
|
||||
|
||||
namespace ams::pm {
|
||||
|
||||
@@ -76,7 +77,7 @@ namespace ams::pm {
|
||||
}
|
||||
|
||||
Result DebugMonitorService::AtmosphereGetCurrentLimitInfo(sf::Out<s64> out_cur_val, sf::Out<s64> out_lim_val, u32 group, u32 resource) {
|
||||
R_RETURN(impl::AtmosphereGetCurrentLimitInfo(out_cur_val.GetPointer(), out_lim_val.GetPointer(), group, resource));
|
||||
R_RETURN(impl::GetResourceLimitValues(out_cur_val.GetPointer(), out_lim_val.GetPointer(), static_cast<ResourceLimitGroup>(group), static_cast<svc::LimitableResource>(resource)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <stratosphere.hpp>
|
||||
#include "pm_info_service.hpp"
|
||||
#include "impl/pm_process_manager.hpp"
|
||||
#include "impl/pm_spec.hpp"
|
||||
|
||||
namespace ams::pm {
|
||||
|
||||
@@ -33,12 +34,12 @@ namespace ams::pm {
|
||||
R_RETURN(impl::GetProgramId(out.GetPointer(), process_id));
|
||||
}
|
||||
|
||||
Result InformationService::GetAppletCurrentResourceLimitValues(sf::Out<pm::ResourceLimitValues> out) {
|
||||
R_RETURN(impl::GetAppletCurrentResourceLimitValues(out.GetPointer()));
|
||||
Result InformationService::GetAppletResourceLimitCurrentValue(sf::Out<pm::ResourceLimitValue> out) {
|
||||
R_RETURN(impl::GetResourceLimitCurrentValue(out.GetPointer(), ResourceLimitGroup_Applet));
|
||||
}
|
||||
|
||||
Result InformationService::GetAppletPeakResourceLimitValues(sf::Out<pm::ResourceLimitValues> out) {
|
||||
R_RETURN(impl::GetAppletPeakResourceLimitValues(out.GetPointer()));
|
||||
Result InformationService::GetAppletResourceLimitPeakValue(sf::Out<pm::ResourceLimitValue> out) {
|
||||
R_RETURN(impl::GetResourceLimitPeakValue(out.GetPointer(), ResourceLimitGroup_Applet));
|
||||
}
|
||||
|
||||
/* Atmosphere extension commands. */
|
||||
|
||||
@@ -22,8 +22,8 @@ namespace ams::pm {
|
||||
public:
|
||||
/* Actual command implementations. */
|
||||
Result GetProgramId(sf::Out<ncm::ProgramId> out, os::ProcessId process_id);
|
||||
Result GetAppletCurrentResourceLimitValues(sf::Out<pm::ResourceLimitValues> out);
|
||||
Result GetAppletPeakResourceLimitValues(sf::Out<pm::ResourceLimitValues> out);
|
||||
Result GetAppletResourceLimitCurrentValue(sf::Out<pm::ResourceLimitValue> out);
|
||||
Result GetAppletResourceLimitPeakValue(sf::Out<pm::ResourceLimitValue> out);
|
||||
|
||||
/* Atmosphere extension commands. */
|
||||
Result AtmosphereGetProcessId(sf::Out<os::ProcessId> out, ncm::ProgramId program_id);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <stratosphere.hpp>
|
||||
#include "pm_shell_service.hpp"
|
||||
#include "impl/pm_process_manager.hpp"
|
||||
#include "impl/pm_spec.hpp"
|
||||
|
||||
namespace ams::pm {
|
||||
|
||||
|
||||
@@ -437,7 +437,7 @@ namespace ams::sm::impl {
|
||||
{
|
||||
/* TODO: Convert mitm internal messaging to use tipc? */
|
||||
::Service srv { .session = mitm_info->query_h };
|
||||
R_ABORT_UNLESS(::serviceDispatchInOut(std::addressof(srv), 65000, client_info, should_mitm));
|
||||
R_ABORT_UNLESS((serviceDispatchInOut(std::addressof(srv), 65000, client_info, should_mitm)));
|
||||
}
|
||||
|
||||
/* If we shouldn't mitm, give normal session. */
|
||||
|
||||
@@ -257,10 +257,21 @@ namespace haze {
|
||||
return property_code == -1 || code == property_code;
|
||||
};
|
||||
|
||||
/* Determine how many output elements we will report. */
|
||||
u32 num_output_elements = 0;
|
||||
for (const auto obj_property : SupportedObjectProperties) {
|
||||
if (ShouldIncludeProperty(obj_property)) {
|
||||
num_output_elements++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Begin writing the requested object properties. */
|
||||
PtpDataBuilder db(m_buffers->usb_bulk_write_buffer, std::addressof(m_usb_server));
|
||||
|
||||
R_TRY(db.WriteVariableLengthData(m_request_header, [&] {
|
||||
/* Report the number of elements. */
|
||||
R_TRY(db.Add(num_output_elements));
|
||||
|
||||
for (const auto obj_property : SupportedObjectProperties) {
|
||||
if (!ShouldIncludeProperty(obj_property)) {
|
||||
continue;
|
||||
|
||||
Reference in New Issue
Block a user